ベンダー APEX

APEX ファイル形式を使用すると、低レベルの Android OS モジュールをパッケージ化してインストールできます。ネイティブ サービスやライブラリ、HAL 実装、ファームウェア、構成ファイルなどのコンポーネントを独立してビルドでき、インストールできます。

ベンダー APEX は、ビルドシステムによって自動的に /vendor パーティションにインストールされます。他のパーティションの APEX と同様、実行時に apexd によって有効化されます。

ユースケース

ベンダー イメージのモジュール化

APEX により、ベンダー イメージの機能実装の自然なバンドルとモジュール化が容易になります。

ベンダー イメージが、個別にビルドされたベンダー APEX の組み合わせとしてビルドされている場合、デバイス メーカーは、デバイスで必要な特定のベンダー実装を簡単に選択できます。メーカーは、提供された APEX がいずれもニーズを満たさない場合や、新しいカスタム ハードウェアがある場合、新しいベンダー APEX を作成することもできます。

たとえば OEM は、AOSP Wi-Fi 実装 APEX、SoC Bluetooth 実装 APEX、カスタム OEM テレフォニー実装 APEX でデバイスを構成できます。

ベンダー APEX がない場合、ベンダー コンポーネント間の依存関係が多い実装では、調整とトラッキングを慎重に行う必要があります。機能間通信の任意の点で、明確に定義されたインターフェースによってすべてのコンポーネント(構成ファイルと追加ライブラリを含む)を APEX にラップすることで、さまざまなコンポーネントの互換性が確保されます。

デベロッパーの反復処理

ベンダー APEX は、Wi-Fi HAL のような機能実装全体をベンダー APEX 内にバンドルすることで、ベンダー モジュールの開発時に、デベロッパーによる反復処理を高速化します。デベロッパーは、ベンダー イメージ全体を再ビルドする代わりに、ベンダー APEX をビルドして個別に push し、変更をテストできます。

これにより、主に 1 つの機能領域で作業し、その機能領域のみを反復する場合に、デベロッパーの反復サイクルが簡素化されてスピードアップします。

機能領域が APEX に自然にバンドルされると、その機能領域に対する変更のビルド、push、テストのプロセスが簡素化されます。たとえば APEX を再インストールすると、APEX に含まれるバンドル ライブラリまたは構成ファイルが自動的に更新されます。

機能領域を APEX にバンドルすると、デバイスの動作不良があった場合のデバッグや復元も簡素化します。たとえば、新しいビルドでテレフォニーがうまく機能していない場合、デベロッパーは(フルビルドをフラッシュせずに)古いテレフォニー実装 APEX をデバイスにインストールし、正常な動作が復元するかどうかを確認できます。

ワークフローの例:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

基本情報

デバイスの要件、ファイル形式の詳細、インストール手順など、APEX の一般的な情報については、APEX ファイル形式のメインページをご覧ください。

Android.bpvendor: true プロパティを設定すると、APEX モジュールはベンダー APEX になります。

apex {
  ..
  vendor: true,
  ..
}

バイナリと共有ライブラリ

APEX には、安定したインターフェースがある場合を除き、APEX ペイロード内に推移的依存関係が含まれます。

ベンダー APEX の依存関係の安定版ネイティブ インターフェースには、stubs を含む cc_library と LLNDK ライブラリが含まれます。これらの依存関係はパッケージ化から除外され、依存関係は APEX マニフェストに記録されます。マニフェストは linkerconfig によって処理されるため、実行時に外部のネイティブ依存関係を使用できます。

次のスニペットでは、APEX にバイナリ(my_service)と安定版でない依存関係(*.so ファイル)の両方が含まれています。

apex {
  ..
  vendor: true,
  binaries: ["my_service"],
  ..
}

次のスニペットの APEX には、前述のとおり、共有ライブラリ my_standalone_lib と安定版でない依存関係が含まれます。

apex {
  ..
  vendor: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

APEX を小さくする

APEX は不安定な依存関係がバンドルされているため、大きくなる可能性があります。静的リンクを使用することをおすすめします。libc++.solibbase.so などの一般的なライブラリは、HAL バイナリに静的にリンクできます。安定したインターフェースを提供するために依存関係を作成することもできます。依存関係は APEX にバンドルされません。

HAL 実装

HAL 実装を定義するには、次の例のように、ベンダー APEX 内で対応するバイナリとライブラリを提供します。

HAL 実装を完全にカプセル化するために、APEX は関連する VINTF フラグメントと init スクリプトも指定する必要があります。

VINTF フラグメント

フラグメントが APEX の etc/vintf にある場合、VINTF フラグメントをベンダー APEX から提供できます。

VINTF フラグメントを APEX に埋め込むには、prebuilts プロパティを使用します。

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

クエリ API

VINTF フラグメントが APEX に追加されたら、libbinder_ndk API を使用して HAL インターフェースと APEX 名のマッピングを取得します。

  • AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default") : HAL インスタンスが APEX で定義されている場合は true
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...): HAL インスタンスを定義する APEX 名を取得します。
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...): パススルー HAL を開くために使用します。

init スクリプト

APEX には、(A)APEX ペイロード内のビルド済みテキスト ファイル、または(B)/vendor/etc の通常の init スクリプトという 2 つの方法で init スクリプトを含めることができます。同じ APEX に対して両方を設定できます。

APEX 内の init スクリプト:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

ベンダー APEX の init スクリプトには、service 定義と on <property or event> ディレクティブを含めることができます。

service 定義が同じ APEX 内のバイナリを参照していることを確認します。たとえば、com.android.foo APEX は foo-service という名前のサービスを定義できます。

on foo-service /apex/com.android.foo/bin/foo
  ...

on ディレクティブを使用する場合は注意が必要です。APEX の init スクリプトの解析と実行は APEX が有効になった後に行われるため、一部のイベントやプロパティは使用できなくなります。できる限り早くアクションを実行するには apex.all.ready=true を使用してください。ブートストラップ APEX では on init を使用できますが、on early-init は使用できません。

ファームウェア

例:

次のように、prebuilt_firmware モジュール タイプでベンダー APEX にファームウェアを埋め込みます。

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

prebuilt_firmware モジュールは、APEX の <apex name>/etc/firmware ディレクトリにインストールされます。ueventd/apex/*/etc/firmware ディレクトリをスキャンしてファームウェア モジュールを見つけます。

APEX の file_contexts は、実行時に ueventd がそれらのファイルにアクセスできるようにするために、ファームウェア ペイロード エントリに適切なラベルを付ける必要があります。通常は、vendor_file ラベルで十分です。次に例を示します。

(/.*)? u:object_r:vendor_file:s0

カーネル モジュール

次のように、カーネル モジュールをベンダー APEX にビルド済みモジュールとして埋め込みます。

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

APEX の file_contexts は、カーネル モジュールのペイロード エントリに適切なラベルを付ける必要があります。次に例を示します。

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

カーネル モジュールは明示的にインストールする必要があります。ベンダー パーティションにおける次の init スクリプトの例は、insmod を介したインストールを示しています。

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

ランタイム リソース オーバーレイ

例:

rros プロパティを使用して、ベンダー APEX にランタイム リソース オーバーレイを埋め込みます。

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

その他の構成ファイル

ベンダー APEX は、ベンダー APEX でのビルド済みとしてベンダー パーティションに通常存在するその他のさまざまな構成ファイルをサポートしており、今後さらに追加される予定です。

例:

ベンダー APEX のブートストラップ

keymint などの一部の HAL サービスは、APEX が有効になる前に使用可能である必要があります。通常、これらの HAL は、init スクリプトのサービス定義で early_hal を設定します。もう 1 つの例は animation クラスです。これは通常、post-fs-data イベントの前に開始されます。このような早期 HAL サービスがベンダー APEX にパッケージ化されている場合は、APEX マニフェストで apex を "vendorBootstrap": true にして、早期に有効にできるようにします。ブートストラップ APEX は、/vendor/apex などのビルド済みの場所からのみ有効にでき、/data/apex からは有効にできません。

システム プロパティ

ベンダー APEX をサポートするためにフレームワークが読み取るシステム プロパティは次のとおりです。

  • input_device.config_file.apex=<apex name> - 設定すると、入力構成ファイル(*.idc*.kl*.kcm)が APEX の /etc/usr ディレクトリから検索されます。
  • ro.vulkan.apex=<apex name> - 設定すると、Vulkan ドライバは APEX から読み込まれます。Vulkan ドライバは初期の HAL で使用されるため、APEX を ブートストラップ APEX にして、そのリンカー ネームスペースを公開するように構成します。

setprop コマンドを使用して、init スクリプトでシステム プロパティを設定します。

その他の開発機能

起動時の APEX 選択

例:

デベロッパーは、同じ APEX 名とキーを共有する複数のバージョンのベンダー APEX をインストールし、永続 sysprop を使用して起動ごとに有効にするバージョンを選択することもできます。デベロッパーのユースケースによっては、adb install を使用して APEX の新しいコピーをインストールするよりも、こちらの方が簡単な場合があります。

ユースケースの例:

  • 3 つのバージョンの Wi-Fi HAL ベンダー APEX をインストールする: QA チームは、あるバージョンを使用して手動テストまたは自動テストを行い、次に別のバージョンで再起動してテストをもう一度行い、最終結果を比較できます。
  • 2 つのバージョンのカメラ HAL ベンダー APEX(現行と試験運用版)をインストールする: dogfood ユーザーは、その他のファイルのダウンロードやインストールをすることなく試験運用版を使用できるため、簡単に入れ替えられます。

apexd は起動中に特定の形式に従った sysprop を探し、適切な APEX バージョンを有効にします。

プロパティキーに期待される形式は次のとおりです。

  • bootconfig
    • BoardConfig.mk で、デフォルト値を設定するために使用します。
    • androidboot.vendor.apex.<apex name>
  • 永続 sysprop
    • すでに起動しているデバイスに設定されているデフォルト値を変更するために使用します。
    • bootconfig 値が存在する場合はオーバーライドします。
    • persist.vendor.apex.<apex name>

プロパティの値は、有効にする必要がある APEX のファイル名にする必要があります。

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

また、デフォルト バージョンは、BoardConfig.mk で bootconfig を使用して構成する必要があります。

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

デバイスが起動したら、永続 sysprop を設定して有効なバージョンを変更します。

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

デバイスがフラッシュ後の bootconfig の更新をサポートしている場合(fastboot oem コマンドを使用するなど)、マルチインストールされた APEX の bootconfig プロパティを変更すると、起動時に有効になるバージョンも変更されます。

Cuttlefish ベースの仮想リファレンス デバイスの場合、--extra_bootconfig_args コマンドを使用して起動時に bootconfig プロパティを直接設定できます。次に例を示します。

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";