供應商 APEX

您可以使用 APEX 檔案格式來封裝及安裝較低層級的 Android OS 模組。可讓您獨立建構及安裝元件,例如原生服務和程式庫、HAL 實作、韌體、設定檔等等。

供應商 APEX 會由建構系統自動安裝在 /vendor 區段中,並由 apexd 在執行階段啟用,就像其他區段中的 APEX 一樣。

用途

供應商映像檔模組化

APEX 可將供應商映像檔的功能實作自然組合和模組化。

當供應商映像檔結合獨立建構的供應商 APEX 時,裝置製造商能夠輕鬆挑選裝置中的特定廠商實作項目。如果製造商提供的 APEX 不符合需求,或沒有全新的自訂硬體,製造商甚至可以建立新的廠商 APEX。

舉例來說,原始設備製造商 (OEM) 可以選擇使用 AOSP Wi-Fi 實作 APEX、SoC 藍牙實作 APEX 和自訂 OEM 電話系統實作 APEX 來組合裝置。

如果沒有供應商 APEX,供應商元件之間有這麼多相依項目的實作方式,就需要仔細協調和追蹤。只要在 APEX 中包裝所有元件 (包括設定檔和額外程式庫),並在任何跨功能通訊點使用明確定義的介面,就能讓不同元件可互換。

開發人員疊代

供應商 APEX開發人員就能建構並個別推送供應商 APEX,藉此測試變更,而不必重新建構整個供應商的映像檔。

對於主要在單一功能領域工作,且只想針對該功能領域進行迭代的開發人員而言,這可簡化並加快開發人員的迭代週期。

將特徵區域的自然繫結至 APEX 中,也簡化了該地圖項目區域的建構、推送及測試變更程序。舉例來說,重新安裝 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.bp 中,設定 vendor: true 屬性可將 APEX 模組設為供應商 APEX。

apex {
  ..
  vendor: true,
  ..
}

二進位檔和共用程式庫

除非 APEX 酬載具有穩定的介面,否則 APEX 會在 APEX 酬載中納入傳遞式依附元件。

供應商 APEX 依附元件的穩定原生介面包括 cc_librarystubs 以及 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 片段和初始化指令碼。

VINTF 片段

如果片段位於 APEX 的 etc/vintf,即可從供應商 APEX 提供 VINTF 片段。

使用 prebuilts 屬性,將 VINTF 片段嵌入 APEX。

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。

初始化指令碼

APEX 可以透過兩種方式加入初始化指令碼:(A) APEX 酬載內預先建構的文字檔案,或 (B) /vendor/etc 中的一般初始化指令碼。您可以為同一個 APEX 設定這兩項功能。

APEX 中的初始化指令碼:

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

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

供應商 APEX 中的初始化指令碼可以包含 service 定義和 on <property or event> 指令。

請確認 service 定義會指向同一個 APEX 中的二進位檔。舉例來說,com.android.foo APEX 可能會定義名為 foo-service 的服務。

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

使用 on 指示時請特別小心。由於 APEX 中的初始化指令碼會在 APEX 啟用「後」剖析及執行,因此無法使用某些事件或屬性。使用 apex.all.ready=true 盡快觸發動作。 Bootstrap 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

必須明確安裝核心模組。以下是供應商分割區中的初始化指令碼範例,顯示透過 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

在啟用 APEX 之前,應先提供 keymint 等部分 HAL 服務。這些 HAL 通常會在 init 指令碼的服務定義中設定 early_hal。另一個例子是 animation 類別,通常會比 post-fs-data 事件更早啟動。如果這類早期 HAL 服務封裝在供應商 APEX 中,請將 Apex 設為 "vendorBootstrap": true,以便提早啟用。請注意,啟動 APEX 只能透過 /vendor/apex 等預先建構位置啟用,而無法從 /data/apex 啟用。

系統屬性

架構為支援廠商 APEX 而讀取的系統屬性如下:

  • input_device.config_file.apex=<apex name>:設定後,系統會從 APEX 的 /etc/usr 目錄搜尋輸入設定檔 (*.idc*.kl*.kcm)。
  • ro.vulkan.apex=<apex name> - 設定後,系統會從 APEX 載入 Vulkan 驅動程式。由於早期 HAL 會使用 Vulkan 驅動程式,因此請設為 APEX Bootstrap APEX,並設定該連接器命名空間顯示。

使用 setprop 指令,在初始化指令碼中設定系統屬性。

其他開發功能

開機時的 APEX 選項

範例:

開發人員也可以安裝多個供應商 APEX 版本,這些版本共用相同的 APEX 名稱和金鑰,然後選擇在每次啟動期間要啟用的版本,並使用持久性 sysprop。對於某些開發人員用途而言,這可能比使用 adb install 安裝新的 APEX 副本更簡單。

用途範例:

  • 安裝 3 個版本的 Wi-Fi HAL 供應商 APEX:品質確保團隊可以使用某個版本執行手動或自動化測試,然後重新啟動至另一個版本並重新執行測試,再比較最終結果。
  • 安裝 2 個版本的相機 HAL 供應商 APEX,分別為現行實驗版本: 測試人員可以使用實驗版本,無需下載及安裝額外檔案,因此可以輕鬆切換回現行版本。

在啟動期間,apexd 會尋找符合特定格式的 sysprops,以啟用正確的 APEX 版本。

屬性鍵預期格式為:

  • Bootconfig
    • 用於在 BoardConfig.mk 中設定預設值。
    • androidboot.vendor.apex.<apex name>
  • Persistent 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;

如果裝置支援在刷新後更新開機設定 (例如透過 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";