Android Pony EXpress(APEX)コンテナ形式は、Android 10 で導入されたもので、低レベルのシステム モジュールのインストール フローで使用されます。この形式を使用すると、標準の Android アプリモデルに適合しないシステム コンポーネントのアップデートが容易になります。対象となるコンポーネントには、ネイティブのサービスやライブラリ、Hardware Abstraction Layer(HAL)、ランタイム(ART)、クラス ライブラリなどがあります。
「APEX」という用語は、APEX ファイルに対しても使用されます。
背景
Android は、パッケージ インストーラ アプリ(Google Play ストア アプリなど)を使用した、標準アプリモデル(サービスやアクティビティなど)に適合するモジュールの更新をサポートしていますが、同様のモデルを低レベルの OS コンポーネントに適用しようとすると、次のような不都合が生じます。
- APK ベースのモジュールは起動シーケンスの初期には使用できません。パッケージ マネージャーは、アプリに関する情報の中心となるリポジトリで、アクティビティ マネージャーからのみ起動できます。これは起動手順の後半で使用できます。
- APK 形式(特にマニフェスト)は Android アプリ向けであり、システム モジュールは必ずしも適合しません。
設計
このセクションでは、APEX ファイル形式の概要設計と、APEX ファイルを管理するサービスである APEX マネージャーについて説明します。
APEX にこの設計が採用された理由の詳細については、APEX の開発中に検討された代替案をご覧ください。
APEX 形式
これは APEX ファイルの形式です。
図 1. APEX ファイル形式
最上位の APEX ファイルは ZIP ファイルであり、その中に保存されるファイルは非圧縮で、4 KB 境界に配置されます。
APEX ファイルに含まれる 4 つのファイルは次のとおりです。
apex_manifest.json
AndroidManifest.xml
apex_payload.img
apex_pubkey
apex_manifest.json
ファイルには、APEX ファイルを識別するパッケージ名とバージョンが含まれます。これは JSON 形式の ApexManifest
プロトコル バッファです。
AndroidManifest.xml
ファイルを使うことで、APEX ファイルは ADB、PackageManager、パッケージ インストーラ アプリ(Google Play ストアなど)のような APK 関連ツールやインフラストラクチャを利用できます。たとえば、APEX ファイルは aapt
などの既存のツールを利用して、ファイルの基本的なメタデータを検査できます。このファイルには、パッケージ名とバージョン情報が含まれています。この情報は通常、apex_manifest.json
でも参照できます。
APEX を扱う新しいコードとシステムに対しては AndroidManifest.xml
よりも apex_manifest.json
をおすすめします。AndroidManifest.xml
には既存のアプリ公開ツールに使用されるその他のターゲティング情報が含まれている可能性があります。
apex_payload.img
は dm-verity で保護された ext4 ファイル システム イメージです。このイメージは、ループバック デバイスを介してランタイムにマウントされます。具体的には、libavb
ライブラリを使用してハッシュツリーとメタデータ ブロックが作成されます。ファイル システムのペイロードは解析されません(イメージをマウントするだけで動作するため)。通常のファイルは apex_payload.img
ファイル内に含まれます。
apex_pubkey
は、ファイル システム イメージへの署名に使用される公開鍵です。ランタイムにはこの鍵を使用して、ダウンロードした APEX が組み込みパーティション内の APEX と同じエンティティにより署名されていることを確認します。
APEX 命名ガイドライン
プラットフォームの進展に伴う新しい APEX 間の名称の競合を避けるために、次の命名ガイドラインを使用します。
com.android.*
- AOSP APEX 用に予約されています。会社やデバイスに固有ではありません。
com.<companyname>.*
- 会社用に予約されています。その会社の複数のデバイスで使用される可能性があります。
com.<companyname>.<devicename>.*
- 特定のデバイス(またはデバイスのサブセット)に固有の APEX 用に予約されています。
APEX マネージャー
APEX マネージャー(または apexd
)は、APEX ファイルの検証、インストール、アンインストールを行うスタンドアロンのネイティブ プロセスです。このプロセスは起動シーケンスの早い段階で開始され、準備が整います。通常、APEX ファイルはデバイスの /system/apex
の下にプリインストールされています。APEX マネージャーは、利用可能なアップデートがない場合、デフォルトでこれらのパッケージを使用します。
APEX のアップデートは、PackageManager クラスを使用して次のように行われます。
- APEX ファイルが、パッケージ インストーラ アプリ、ADB などのソースを介してダウンロードされます。
- パッケージ マネージャーがインストール プロシージャを開始します。ファイルが APEX であることを認識すると、パッケージ マネージャーは APEX マネージャーに制御を渡します。
- APEX マネージャーが APEX ファイルを検証します。
- APEX ファイルが検証されると、次回の起動時に APEX ファイルが有効になるよう APEX マネージャーの内部データベースが更新されます。
- インストール リクエスタは、パッケージ検証が成功したときにブロードキャストを受信します。
- インストールを続行するために、システムを再起動する必要があります。
次回起動時に APEX マネージャーが開始され、内部データベースが読み込まれてリストされた APEX ファイルごとに次の処理が行われます。
- APEX ファイルを検証します。
- APEX ファイルからループバック デバイスを作成します。
- ループバック デバイスの上にデバイス マッパー ブロック デバイスを作成します。
- デバイス マッパー ブロック デバイスを
/apex/name@ver
など一意のパスにマウントします。
内部データベースにリストされているすべての APEX ファイルがマウントされると、APEX マネージャーは他のシステム コンポーネントにバインダー サービスを提供して、インストールされた APEX ファイルに関する情報をクエリします。たとえば、他のシステム コンポーネントは、デバイスにインストールされている APEX ファイルのリストや、特定の APEX がマウントされているパスをクエリできるため、ファイルにアクセスが可能です。
APK ファイルである APEX ファイル
APEX ファイルは、AndroidManifest.xml
ファイルを含み、APK 署名スキームの署名が付いた ZIP アーカイブであるため、有効な APK ファイルです。したがって、APEX ファイルは、パッケージ インストーラ アプリ、署名ユーティリティ、パッケージ マネージャーなどの APK ファイルのインフラストラクチャを利用できます。
APEX ファイル内の AndroidManifest.xml
ファイルは最小限のもので、パッケージの name
、versionCode
、および詳細なターゲティングの必要性に応じて targetSdkVersion
、minSdkVersion
、maxSdkVersion
を含んでいます。この情報により、パッケージ インストーラ アプリや ADB などの既存のチャネルを介して APEX ファイルを配信できます。
サポートされるファイル形式
APEX 形式でサポートされるファイル形式は次のとおりです。
- ネイティブ共有ライブラリ
- ネイティブ実行可能ファイル
- JAR ファイル
- データファイル
- 構成ファイル
APEX がこれらのファイル形式をすべて更新できるとは限りません。ファイル形式を更新できるかどうかは、プラットフォームと、そのファイル形式のインターフェース定義の安定性によって決まります。
署名オプション
APEX ファイルは 2 つの方法で署名されます。まず、apex_payload.img
(厳密には、apex_payload.img
に追加される vbmeta 記述子)ファイルが鍵で署名されます。
次に、APEX 全体が APK 署名スキーム v3 を使用して署名されます。このプロセスでは 2 つの異なる鍵が使用されます。
デバイス側には、vbmeta 記述子への署名に使用された秘密鍵に対応する公開鍵がインストールされます。APEX マネージャーは、公開鍵を使用して、インストールがリクエストされている APEX を検証します。各 APEX は、異なる鍵で署名する必要があり、ビルド時と実行時の両方で必要です。
組み込みパーティション内の APEX
APEX ファイルは、/system
のような組み込みパーティションに置くことができます。パーティションはすでに dm-verity で保護済みのため、APEX ファイルはループバック デバイスにそのままマウントされます。
APEX が組み込みパーティションに存在する場合、APEX パッケージに同じパッケージ名と、同等以上のバージョンのコードを指定することで APEX を更新できます。新しい APEX は /data
に格納され、APK と同様に、新たにインストールされたバージョンは組み込みパーティションにすでに存在するバージョンをシャドウします。しかし、APK とは異なり、APEX の新たにインストールされたバージョンは再起動後にのみ有効になります。
カーネルの要件
Android デバイスで APEX メインライン モジュールをサポートするには、ループバック ドライバと dm-verity という Linux カーネル機能が必要です。ループバック ドライバは APEX モジュールのファイル システム イメージをマウントし、dm-verity は APEX モジュールを検証します。
ループバック ドライバと dm-verity のパフォーマンスは、APEX モジュールを使用する際のシステム パフォーマンスを適切なものにするうえで重要です。
サポートされているカーネル バージョン
APEX メインライン モジュールは、カーネル バージョン 4.4 以降を使用するデバイスでサポートされています。Android 10 以降を搭載してリリースされる新しいデバイスで APEX モジュールをサポートするためには、カーネル バージョン 4.9 以降を使用する必要があります。
必要なカーネルパッチ
APEX モジュールをサポートするために必要なカーネルパッチは、Android 共通ツリーに含まれています。APEX をサポートするパッチを入手するには、Android 共通ツリーの最新バージョンを使用してください。
カーネル バージョン 4.4
このバージョンは、Android 9 から Android 10 にアップグレードしたデバイスで APEX モジュールをサポートする場合にのみ使用できます。必要なパッチを取得するには、android-4.4
ブランチからのダウンマージを強くおすすめします。カーネル バージョン 4.4 に必要な個別パッチは次のとおりです。
- UPSTREAM: loop: add ioctl for changing logical block size(4.4)
- BACKPORT: block/loop: set hw_sectors(4.4)
- UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl(4.4)
- ANDROID: mnt: Fix next_descendent(4.4)
- ANDROID: mnt: remount should propagate to slaves of slaves(4.4)
- ANDROID: mnt: Propagate remount correctly(4.4)
- Revert "ANDROID: dm verity: add minimum prefetch size"(4.4)
- UPSTREAM: loop: drop caches if offset or block_size are changed(4.4)
カーネル バージョン 4.9 / 4.14 / 4.19
カーネル バージョン 4.9 / 4.14 / 4.19 に必要なパッチを入手するには、android-common
ブランチからダウンマージします。
必要なカーネル構成オプション
Android 10 で導入された APEX モジュールをサポートするための基本的な構成要件を次に示します。アスタリスク(*)付きの項目は、Android 9 以前からの既存の要件です。
(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support
カーネル コマンドライン パラメータの要件
APEX をサポートするには、カーネル コマンドライン パラメータが次の要件を満たしていることを確認します。
loop.max_loop
は設定しないでくださいloop.max_part
は 8 以下でなければなりません
APEX をビルドする
このセクションでは、Android ビルドシステムを使用して APEX をビルドする方法について説明します。
以下は、apex.test
という名前の APEX 用の Android.bp
の例です。
apex {
name: "apex.test",
manifest: "apex_manifest.json",
file_contexts: "file_contexts",
// libc.so and libcutils.so are included in the apex
native_shared_libs: ["libc", "libcutils"],
binaries: ["vold"],
java_libs: ["core-all"],
prebuilts: ["my_prebuilt"],
compile_multilib: "both",
key: "apex.test.key",
certificate: "platform",
}
apex_manifest.json
の例:
{
"name": "com.android.example.apex",
"version": 1
}
file_contexts
の例:
(/.*)? u:object_r:system_file:s0
/sub(/.*)? u:object_r:sub_file:s0
/sub/file3 u:object_r:file3_file:s0
ファイル形式と APEX 内での配置
ファイル形式 | APEX 内での配置 |
---|---|
共有ライブラリ | /lib および /lib64 (x86 に変換された ARM 用は /lib/arm ) |
実行可能ファイル | /bin |
Java ライブラリ | /javalib |
事前ビルド | /etc |
推移的依存関係
APEX ファイルには、ネイティブ共有ライブラリや実行可能ファイルの推移的依存関係が自動的に含まれます。たとえば、libFoo
が libBar
に依存している場合、native_shared_libs
プロパティにリストされているのが libFoo
のみであっても、両方のライブラリが含まれます。
複数の ABI を処理する
デバイスのプライマリとセカンダリ両方のアプリケーション バイナリ インターフェース(ABI)に native_shared_libs
プロパティをインストールします。APEX が 1 つの ABI(32 ビットのみ、または 64 ビットのみ)を有するデバイスを対象としている場合、対応する ABI を含むライブラリのみがインストールされます。
デバイスのプライマリ ABI にのみ、次のように binaries
プロパティをインストールします。
- デバイスが 32 ビットのみの場合、32 ビット バリアントのバイナリのみがインストールされます。
- デバイスが 64 ビットのみの場合、64 ビット バリアントのバイナリのみがインストールされます。
ネイティブ ライブラリとバイナリの ABI を細かく制御するには、multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]
プロパティを使用します。
first
: デバイスのプライマリ ABI と一致します。これはバイナリのデフォルトです。lib32
: サポートされている場合、デバイスの 32 ビット ABI と一致します。lib64
: サポートされている場合、デバイスの 64 ビット ABI と一致します。prefer32
: サポートされている場合、デバイスの 32 ビット ABI と一致します。32 ビット ABI がサポートされていない場合、64 ビット ABI と一致します。both
: 両方の ABI と一致します。これはnative_shared_libraries
のデフォルトです。
java
、libraries
、および prebuilts
プロパティは ABI に依存しません。
この例は 32 / 64 をサポートしていて、32 を選ばないデバイスを対象としています。
apex {
// other properties are omitted
native_shared_libs: ["libFoo"], // installed for 32 and 64
binaries: ["exec1"], // installed for 64, but not for 32
multilib: {
first: {
native_shared_libs: ["libBar"], // installed for 64, but not for 32
binaries: ["exec2"], // same as binaries without multilib.first
},
both: {
native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
binaries: ["exec3"], // installed for 32 and 64
},
prefer32: {
native_shared_libs: ["libX"], // installed for 32, but not for 64
},
lib64: {
native_shared_libs: ["libY"], // installed for 64, but not for 32
},
},
}
vbmeta 署名
各 APEX に個別の鍵で署名します。新しい鍵が必要な場合、公開鍵と秘密鍵のペアを作成して apex_key
モジュールを作成します。その鍵で APEX に署名するには、key
プロパティを使用します。公開鍵は avb_pubkey
という名前で APEX に自動的に含まれます。
# create an rsa key pairopenssl genrsa -out foo.pem 4096
# extract the public key from the key pairavbtool extract_public_key --key foo.pem --output foo.avbpubkey
# in Android.bpapex_key { name: "apex.test.key", public_key: "foo.avbpubkey", private_key: "foo.pem", }
上記の例では、公開鍵の名前(foo
)が鍵の ID になります。APEX への署名に使用された鍵の ID は、APEX 内に記述されます。実行時に、apexd
がデバイス内の同じ ID の公開鍵を使用して APEX を検証します。
APEX 署名
APK に署名するのと同じ方法で APEX に署名します。APEX には 2 回署名します(ミニ ファイル システム(apex_payload.img
ファイル)に対して 1 回と、ファイル全体に対して 1 回)。
ファイルレベルで APEX に署名するには、certificate
プロパティを次の 3 つの方法のいずれかに設定します。
- 未設定: 値が設定されていない場合、APEX は
PRODUCT_DEFAULT_DEV_CERTIFICATE
にある証明書で署名されます。フラグが設定されていない場合、パスはデフォルトでbuild/target/product/security/testkey
です。 <name>
: APEX は、PRODUCT_DEFAULT_DEV_CERTIFICATE
と同じディレクトリにある<name>
証明書で署名されます。:<name>
: APEX は、<name>
という名前の Soong モジュールで定義された証明書で署名されます。証明書モジュールは次のように定義できます。
android_app_certificate {
name: "my_key_name",
certificate: "dir/cert",
// this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}
APEX をインストールする
APEX をインストールするには、ADB を使用します。
adb install apex_file_name
adb reboot
apex_manifest.json
で supportsRebootlessUpdate
が true
に設定されていて、現在インストールされている APEX が使用されていない場合(例: 含まれているサービスが停止している場合)、--force-non-staged
フラグで再起動することなく新しい APEX をインストールできます。
adb install --force-non-staged apex_file_name
APEX を使用する
再起動後、APEX は /apex/<apex_name>@<version>
ディレクトリにマウントされます。同じ APEX の複数のバージョンを同時にマウントできます。
これらのうち、最新バージョンに対応するマウントパスが /apex/<apex_name>
にバインド マウントされます。
クライアントは、バインド マウントされたパスを使用して、APEX からファイルを読み取りまたは実行できます。
APEX は通常、次のように使用されます。
- OEM または ODM は、デバイスの出荷時に APEX を
/system/apex
にプリロードします。 - APEX 内のファイルは、
/apex/<apex_name>/
パスを介してアクセスします。 - APEX の更新版が
/data/apex
にインストールされると、パスは再起動後に新しい APEX を指します。
APEX を使用してサービスを更新する
APEX を使用してサービスを更新するには、次の手順を行います。
システム パーティション内のサービスを更新可能としてマークします。
updatable
オプションをサービス定義に追加します。/system/etc/init/myservice.rc: service myservice /system/bin/myservice class core user system ... updatable
更新されるサービス用の新規の
.rc
ファイルを作成します。既存のサービスを再定義するには、override
オプションを使用します。/apex/my.apex/etc/init.rc: service myservice /apex/my.apex/bin/myservice class core user system ... override
サービス定義は、APEX の .rc
ファイルでのみ定義できます。APEX ではアクション トリガーはサポートされていません。
更新可能としてマークされたサービスが、APEX が有効になる前に開始されると、APEX の有効化が完了するまで開始が遅延します。
APEX アップデートをサポートするシステムを設定する
APEX ファイルのアップデートをサポートするには、次のようにシステム プロパティを true
に設定します。
<device.mk>:
PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true
BoardConfig.mk:
TARGET_FLATTEN_APEX := false
または、次のように設定します。
<device.mk>:
$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
フラット化 APEX
以前のデバイスでは、古いカーネルを更新して APEX を完全にサポートすることが原理的または現実的に不可能な場合があります。たとえば、CONFIG_BLK_DEV_LOOP=Y
の指定なしにビルドされたカーネルもありますが、その場合は APEX 内のイメージ ファイル システムをマウントできません。
フラット化 APEX は、古いカーネルのデバイスでも有効化できるように特別にビルドされた APEX です。フラット化 APEX 内のファイルは、組み込みパーティションの下のディレクトリにそのままインストールされます。たとえば、フラット化 APEX である my.apex
内の lib/libFoo.so
は /system/apex/my.apex/lib/libFoo.so
にインストールされます。
フラット化 APEX の有効化には、ループデバイスは必要ありません。/system/apex/my.apex
ディレクトリ全体がそのまま /apex/name@ver
にバインド マウントされます。
フラット化 APEX は、APEX の更新版をネットワークからダウンロードして更新することはできません。ダウンロードした APEX はフラット化できないためです。フラット化 APEX は、通常の OTA を介してのみ更新できます。
フラット化 APEX がデフォルト設定です。つまり、APEX アップデートをサポートするため非フラット化 APEX をビルドするよう明示的にデバイスを設定(方法は上述のとおり)しない限り、デフォルトでは APEX はすべてフラット化されます。
1 つのデバイスにフラット化 APEX と非フラット化 APEX を混在させることはサポートされていません。1 つのデバイスの APEX は、すべて非フラット化またはすべてフラット化されている必要があります。
これは、Mainline などのプロジェクト向けにビルド済みで署名済みの APEX を出荷する場合に特に重要です。ソースからビルドされ、事前に署名されていない APEX も、フラット化せずに適切な鍵を使用して署名する必要があります。デバイスは、APEX によるサービスの更新で説明されているように updatable_apex.mk
から継承されます。
圧縮 APEX
Android 12 以降では、更新可能な APEX パッケージによる保存容量への影響を軽減する APEX 圧縮がサポートされています。APEX のアップデートをインストールすると、プリインストールされたバージョンは使用されなくなりますが、占有する容量は同じです。その占有スペースは利用できなくなります。
APEX 圧縮は、読み取り専用パーティション(/system
パーティションなど)で高度に圧縮された APEX ファイルを使用することで、このような保存容量への影響を最小限に抑えます。Android 12 以降では、DEFLATE zip 圧縮アルゴリズムが使用されます。
圧縮では、以下に対する最適化は行われません。
起動シーケンスの非常に早い段階でマウントする必要があるブートストラップ APEX。
更新不可能な APEX。圧縮は、APEX の更新版が
/data
パーティションにインストールされている場合にのみ意味があります。更新可能な APEX の一覧については、モジュラー システム コンポーネントをご覧ください。動的共有ライブラリの APEX。
apexd
により常にそのような APEX の両方のバージョン(プリインストールされているバージョンとアップグレードされたバージョン)が有効にされるため、圧縮しても意味がありません。
圧縮 APEX ファイルの形式
圧縮 APEX ファイルの形式は次のとおりです。
図 2. 圧縮 APEX ファイルの形式
圧縮 APEX ファイルは、最上位レベルでは、圧縮レベル 9、デフレート形式の元の apex ファイルと、非圧縮状態で保存された他のファイルからなる zip ファイルです。
APEX ファイルは、次の 4 つのファイルで構成されます。
original_apex
: 圧縮レベル 9 のデフレート形式。これは元の非圧縮 APEX ファイルです。apex_manifest.pb
: そのまま保存AndroidManifest.xml
: そのまま保存apex_pubkey
: そのまま保存
apex_manifest.pb
、AndroidManifest.xml
、apex_pubkey
ファイルは、original_apex
内の対応するファイルのコピーとなっています。
圧縮 APEX をビルドする
圧縮 APEX は、system/apex/tools
にある apex_compression_tool.py
ツールでビルドできます。
ビルドシステムには APEX 圧縮に関連するパラメータがあります。
Android.bp
では、APEX ファイルが圧縮可能かどうかを compressible
プロパティで制御します。
apex {
name: "apex.test",
manifest: "apex_manifest.json",
file_contexts: "file_contexts",
compressible: true,
}
PRODUCT_COMPRESSED_APEX
プロダクト フラグは、ソースからビルドされたシステム イメージに圧縮 APEX ファイルを含める必要があるかどうかを制御します。
ローカルテストの場合は、OVERRIDE_PRODUCT_COMPRESSED_APEX=
を true
に設定することで、ビルドで APEX を圧縮するようにできます。
ビルドシステムによって生成された圧縮 APEX ファイルには、.capex
という拡張子があります。この拡張子により、APEX ファイルの圧縮版と非圧縮版を簡単に区別できます。
サポートされている圧縮アルゴリズム
Android 12 では、deflate-zip 圧縮のみがサポートされています。
起動時に圧縮 APEX ファイルを有効にする
圧縮 APEX を有効にする前に、非圧縮版の中の original_apex
ファイルを圧縮解除して /data/apex/decompressed
ディレクトリに展開します。圧縮解除された APEX ファイルは /data/apex/active
ディレクトリにハードリンクされます。
以上の手順の例として、次をご覧ください。
/system/apex/com.android.foo.capex
は、versionCode が 37 で、圧縮 APEX が有効になっているとします。
/system/apex/com.android.foo.capex
内のoriginal_apex
ファイルは、圧縮解除されて/data/apex/decompressed/com.android.foo@37.apex
に展開されます。restorecon /data/apex/decompressed/com.android.foo@37.apex
を実行して、SELinux ラベルが正しいことを確認します。/data/apex/decompressed/com.android.foo@37.apex
に対して、その妥当性を確認するために、検証チェックが実行されます。apexd
により、/data/apex/decompressed/com.android.foo@37.apex
にバンドルされている公開鍵が/system/apex/com.android.foo.capex
にバンドルされている公開鍵に等しいかを検証します。/data/apex/decompressed/com.android.foo@37.apex
ファイルは、/data/apex/active/com.android.foo@37.apex
ディレクトリにハードリンクされています。- 非圧縮 APEX ファイルに対する通常のアクティベーション ロジックが、
/data/apex/active/com.android.foo@37.apex
に対して実行されます。
OTA との相互作用
圧縮 APEX ファイルは OTA 配信とアプリに影響します。OTA アップデートには、デバイスでアクティブになっているバージョンよりも高いバージョン レベルの圧縮 APEX ファイルが含まれる可能性があるため、デバイスを再起動して OTA アップデートを適用する前に、ある程度の空き容量を確保しておく必要があります。
OTA システムをサポートするために、apexd
は以下の 2 つのバインダ API を公開しています。
calculateSizeForCompressedApex
- OTA パッケージ内の APEX ファイルを圧縮解除するために必要なサイズを計算します。これを使用して、OTA をダウンロードする前にデバイスに空き容量が十分あるかを確認できます。reserveSpaceForCompressedApex
-apexd
で OTA パッケージ内の圧縮 APEX ファイルを圧縮解除する際に使用するディスク上のスペースを予約します。
A/B OTA アップデートの場合、apexd
によりインストール後の OTA ルーティンの一環としてバックグラウンドで圧縮解除が試みられます。圧縮解除が失敗した場合、OTA アップデートを適用する起動の間に、apexd
により圧縮解除が実行されます。
APEX の開発中に検討された代替案
AOSP が APEX ファイル形式を設計する際に検討した選択肢と、その取捨選択の理由を以下に説明します。
通常のパッケージ管理システム
Linux ディストリビューションには dpkg
や rpm
のような、強力かつ堅牢で成熟したパッケージ管理システムがあります。しかし、インストール後にパッケージを保護できないため、APEX には採用されませんでした。検証が行われるのはパッケージのインストール時のみのため、その後気づかぬうちに攻撃者によりパッケージの完全性が損なわれる恐れがあります。これは、すべてのシステム コンポーネントが読み取り専用ファイル システムに格納され、すべての I/O に対して dm-verity によりその完全性が保護される Android にとっては回帰です。いかなるシステム コンポーネントの改ざんも禁止するか、検出可能にして、侵害された場合はデバイスの起動を拒否できるようにする必要があります。
完全性のための dm-crypt
APEX コンテナ内のファイルは、dm-verity で保護される組み込みパーティション(たとえば、/system
パーティションなど)からのもので、パーティションのマウント後でもファイルの変更はできません。同じレベルのセキュリティをファイルに提供するために、APEX 内のすべてのファイルは、ハッシュツリーおよび vbmeta 記述子とペアになったファイル システム イメージに格納されます。dm-verity がないと、/data
パーティション内の APEX は、検証およびインストール後に意図しない変更が行われる可能性があります。
実際、/data
パーティションは dm-crypt などの暗号化レイヤでも保護されます。これは改ざんに対してある程度保護を行いますが、主な目的はプライバシーであり、完全性ではありません。攻撃者が /data
パーティションへのアクセス権を獲得した場合は、それ以上の保護はできません。これも、/system
パーティションにすべてのシステム コンポーネントを置くことに対する回帰です。
APEX ファイル内のハッシュツリーと dm-verity を組み合わせることで、同じレベルのコンテンツ保護を実現しています。
/system から /apex にパスをリダイレクトする
APEX でパッケージ化されたシステム コンポーネント ファイルは、/apex/<name>/lib/libfoo.so
のような新しいパスを介してアクセスできます。ファイルが /system
パーティションの一部であったときは、/system/lib/libfoo.so
のようなパスでアクセスできました。APEX ファイルのクライアント(他の APEX ファイルやプラットフォーム)は、新しいパスを使用する必要があります。パスの変更の結果、既存のコードを更新しなければならない場合があります。
パスの変更を回避する方法の 1 つは、APEX ファイル内のファイル コンテンツを /system
パーティションにオーバーレイすることです。しかし、Android チームは、/system
パーティションにファイルをオーバーレイしないという決定をしました。オーバーレイするファイルの数が増えると(1 つずつ増えるとしても)、パフォーマンスに影響する可能性があるためです。
もう 1 つの選択肢は、open
、stat
、readlink
などのファイル アクセス機能をハイジャックして、/system
で始まるパスが /apex
下の対応するパスにリダイレクトされるようにすることでした。パスを受け取る関数をすべて変更するのは現実的でないため、Android チームはこの選択肢を放棄しました。たとえば、一部のアプリでは関数を実装する Bionic を静的にリンクしています。このような場合、そのようなアプリにはリダイレクトされません。