APEX 檔案格式

Android Pony EXpress (APEX) 容器格式是在 Android 10 中推出,用於低階系統模組的安裝流程。這個格式有助於更新不符合標準 Android 應用程式模型的系統元件。例如原生服務和程式庫、硬體抽象層 (HAL)、執行階段 (ART) 和類別程式庫。

「APEX」一詞也可以指 APEX 檔案。

背景

雖然 Android 支援透過套件安裝程式應用程式 (例如 Google Play 商店應用程式) 更新符合標準應用程式模型 (例如服務、活動) 的模組,但針對較低層級的 OS 元件使用類似的模型,有以下缺點:

  • APK 型模組無法在啟動序列中使用,套件管理員是應用程式資訊的中央存放區,只能透過活動管理員啟動,後者在啟動程序的後續階段準備就緒。
  • APK 格式 (尤其是資訊清單) 是專為 Android 應用程式和系統模組而設計,因此不一定適合。

設計

本節將說明 APEX 檔案格式的高階設計,以及 APEX 管理員 (用於管理 APEX 檔案的服務)。

如要進一步瞭解為何選擇這種 APEX 設計,請參閱「開發 APEX 時考慮的替代方案」。

APEX 格式

此為 APEX 檔案格式。

APEX 檔案格式

圖 1. APEX 檔案格式

在頂層,APEX 檔案是一個 ZIP 檔案,將檔案在未壓縮的情況下儲存在 4 KB 的邊界內。

APEX 檔案中的四個檔案如下:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

apex_manifest.json 檔案包含套件名稱和版本,用於識別 APEX 檔案。這是 JSON 格式的 ApexManifest 通訊協定緩衝區。

AndroidManifest.xml 檔案可讓 APEX 檔案使用 APK 相關工具和基礎架構,例如 ADB、PackageManager 和套件安裝應用程式 (例如 Play 商店)。舉例來說,APEX 檔案可以使用 aapt 等現有工具檢查檔案的基本中繼資料。這個檔案包含套件名稱和版本資訊。您通常也可在 apex_manifest.json 中查看這項資訊。

針對處理 APEX 的新程式碼和系統,建議使用 apex_manifest.json 而非 AndroidManifest.xmlAndroidManifest.xml 可能包含其他指定資訊,可供現有應用程式發布工具使用。

apex_payload.img 是 dm-verity 支援的 ext4 檔案系統映像檔。映像檔會在執行階段透過迴圈裝置掛載。具體來說,雜湊樹狀結構和中繼資料區塊是使用 libavb 程式庫建立。系統不會剖析檔案系統酬載 (因為映像檔必須可掛接)。apex_payload.img 檔案會納入一般檔案。

apex_pubkey 是用來簽署檔案系統映像檔的公開金鑰。在執行階段,這個金鑰可確保下載的 APEX 是由在內建區隔中簽署相同 APEX 的實體簽署。

APEX 命名規範

為避免新 APEX 在平台進步時發生命名衝突,請遵循下列命名規範:

  • com.android.*
    • 預留給 Android 開放原始碼計畫 APEXs 使用。不是所有公司或裝置獨有的。
  • com.<companyname>.*
    • 保留給公司使用。可能由該公司的多部裝置使用。
  • com.<companyname>.<devicename>.*
    • 保留給特定裝置 (或部分裝置) 專屬的 APEX。

APEX 管理員

APEX 管理員 (或 apexd) 是獨立的原生程序,負責驗證、安裝及解除安裝 APEX 檔案。這個程序會在開機程序的早期啟動並準備就緒。APEX 檔案通常會預先安裝在 /system/apex 下的裝置上。如果沒有可用的更新,APEX 管理員預設會使用這些套件。

APEX 的更新序列會使用 PackageManager 類別,如下所示。

  1. 您可以透過套件安裝程式應用程式、ADB 或其他來源下載 APEX 檔案。
  2. 套件管理工具會啟動安裝程序。一旦辨識出檔案是 APEX,套件管理工具就會將控制權轉移至 APEX 管理工具。
  3. APEX 管理員會驗證 APEX 檔案。
  4. 如果 APEX 檔案已通過驗證,APEX 管理員的內部資料庫就會更新,以反映 APEX 檔案會在下次啟動時啟用。
  5. 安裝要求者在套件驗證成功後會收到廣播。
  6. 如要繼續安裝,必須重新啟動系統。
  7. 下次啟動時,APEX 管理員會啟動並讀取內部資料庫,並針對列出的每個 APEX 檔案執行下列操作:

    1. 驗證 APEX 檔案。
    2. 從 APEX 檔案建立回送裝置。
    3. 在回送裝置上方建立裝置對應工具區塊裝置。
    4. 將裝置對應工具區塊裝置掛接至不重複的路徑 (例如 /apex/name@ver)。

當內部資料庫中列出的所有 APEX 檔案皆已掛接時,APEX 管理員會為其他系統元件提供繫結器服務,以查詢已安裝 APEX 檔案的相關資訊。舉例來說,其他系統元件可以查詢裝置中安裝的 APEX 檔案清單,或查詢特定 APEX 掛載的確切路徑,以便存取檔案。

APEX 檔案是 APK 檔案

APEX 檔案是有效的 APK 檔案,因為它們是使用 APK 簽名配置簽署的 ZIP 封存檔 (內含 AndroidManifest.xml 檔案)。如此一來,APEX 檔案即可使用 APK 檔案的基礎架構,例如套件安裝程式應用程式、簽署公用程式和套件管理員。

APEX 檔案中的 AndroidManifest.xml 檔案內容極少,只包含套件 nameversionCode,以及選用的 targetSdkVersionminSdkVersionmaxSdkVersion,用於精細指定目標。有了這項資訊,即可透過現有管道 (例如套件安裝程式和 ADB) 提供 APEX 檔案。

支援的檔案類型

APEX 格式支援下列檔案類型:

  • 原生共用程式庫
  • 原生執行檔
  • JAR 檔案
  • 資料檔案
  • 設定檔

這並不表示 APEX 可以更新所有這些檔案類型。檔案類型是否能更新取決於平台,以及檔案類型介面定義的穩定性。

簽署選項

APEX 檔案有兩種簽署方式。首先,apex_payload.img (具體來說,附加至 apex_payload.img 的 vbmeta 描述元) 檔案會使用金鑰簽署。接著,系統會使用 APK 簽署配置 v3 簽署整個 APEX。這個程序會使用兩個不同的金鑰。

在裝置端,已安裝與用來簽署 vbmeta 描述元的私密金鑰相對應的公開金鑰。APEX 管理員會使用公開金鑰驗證要求安裝的 APEX。每個 APEX 都必須使用不同的金鑰簽署,並在建構和執行階段強制執行。

內建分區中的 APEX

APEX 檔案可位於內建分區,例如 /system。這個分區已經過 dm 驗證,因此 APEX 檔案會直接掛接在回送裝置上。

如果內建分區中含有 APEX,您可以提供套件名稱相同且大於或等於版本代碼的 APEX 套件,藉此更新 APEX。新的 APEX 會儲存在 /data 中,與 APK 類似,新安裝的版本會遮蔽內建分區中已存在的版本。但與 APK 不同的是,新安裝的 APEX 版本只會在重新啟動後啟用。

核心需求

如要在 Android 裝置上支援 APEX 主要模組,則需要下列 Linux 核心功能:迴圈式驅動程式和 dm-verity。迴圈驅動程式會在 APEX 模組中掛載檔案系統映像檔,並由 dm-verity 驗證 APEX 模組。

使用 APEX 模組時,迴圈介面驅動程式和 dm-verity 的效能,對於達到良好的系統效能至關重要。

支援的核心版本

搭載核心 4.4 以上版本的裝置均支援 APEX 主系列模組。搭載 Android 10 以上版本的新裝置必須使用 4.9 以上版本的核心,才能支援 APEX 模組。

必要的核心修補程式

Android 通用樹狀結構包含支援 APEX 模組所需的核心修補程式。如要取得支援 APEX 的修補程式,請使用 Android 通用樹狀結構的最新版本。

核心版本 4.4

這個版本僅支援從 Android 9 升級至 Android 10 的裝置,且需要支援 APEX 模組。如要取得必要的修補程式,強烈建議您從 android-4.4 分支版本向下合併。以下是核心 4.4 版本所需的個別修補程式。

  • 上游:迴圈:新增 ioctl 以變更邏輯區塊大小 (4.4)
  • BACKPORT:區塊/迴圈:設定 hw_sectors (4.4)
  • UPSTREAM:迴圈:在相容性 ioctl 中新增 LOOP_SET_BLOCK_SIZE (4.4)
  • ANDROID: mnt:修正 next_descendent (4.4)
  • ANDROID: mnt: remount 應傳播到奴役的斜線 (4.4)
  • ANDROID: mnt:正確傳播重新掛接 (4.4)
  • 還原「ANDROID: dm verity: 如要新增預先擷取大小下限」(4.4)
  • 上游:迴圈:如果 offset 或 block_size 已變更,則捨棄快取 (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 (/lib/arm 適用於 x86 中已翻譯實驗組)
可執行檔 /bin
Java 程式庫 /javalib
預先建構 /etc

遞移依附元件

APEX 檔案會自動包含原生共用程式庫或執行檔的遞移依附元件。舉例來說,如果 libFoo 依附 libBar,則只有在 native_shared_libs 屬性中只列出 libFoo 時,才會納入兩個 lib。

處理多個 ABI

為裝置的主要和次要應用程式二進位檔介面 (ABI) 安裝 native_shared_libs 屬性。如果 APEX 指定的裝置只有單一 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 的預設值。

javalibrariesprebuilts 屬性適用於跨 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 模組。並透過 key 屬性,使用金鑰簽署 APEX。公開金鑰會自動納入 APEX,名稱為 avb_pubkey

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_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 兩次;一次針對迷你檔案系統 (apex_payload.img 檔案),一次針對整個檔案。

如要在檔案層級簽署 APEX,請使用下列三種方式之一設定 certificate 屬性:

  • 未設定:如果未設定任何值,APEX 會使用位於 PRODUCT_DEFAULT_DEV_CERTIFICATE 的憑證簽署。如果未設定任何旗標,路徑預設為 build/target/product/security/testkey
  • <name>:APEX 使用與 PRODUCT_DEFAULT_DEV_CERTIFICATE 相同目錄中的 <name> 憑證進行簽署。
  • :<name>:APEX 會使用由 <name> 這個 Song 模組定義的憑證進行簽署。憑證模組可定義如下:
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

如果 supportsRebootlessUpdateapex_manifest.json 中設為 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 通常用於以下用途:

  1. OEM 或 ODM 會在裝置出貨時,在 /system/apex 下預先載入 APEX。
  2. 你可以透過 /apex/<apex_name>/ 路徑存取 APEX 中的檔案。
  3. /data/apex 中安裝新版 APEX 時,路徑會在重新啟動後指向新的 APEX。

使用 APEX 更新服務

如何使用 APEX 更新服務:

  1. 將系統分區中的服務標示為可更新。將 updatable 選項新增至服務定義。

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. 為更新後的服務建立新的 .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 更新

將下列系統屬性設為 true,以支援 APEX 檔案更新。

<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 都會經過彙整。

不支援在裝置中混用已平坦化和未平坦化的 APEX。裝置上的 APEX 必須全部不拆除或全部壓平合併。如要運送預先簽署的 APEX,以用於 Mainline 等專案,這一點尤其重要。未預先簽署 (亦即從來源建構) 的 APEX 也應採用不拆除的方式,並以適當的金鑰簽署。如使用 APEX 更新服務一文所述,裝置應繼承 updatable_apex.mk

壓縮的 APEX

Android 12 以上版本提供 APEX 壓縮功能,可減少可更新 APEX 套件對儲存空間的影響。安裝 APEX 後,雖然預先安裝的版本已不再使用,但仍會佔用相同大小的空間。該已佔用空間仍無法使用。

APEX 壓縮在唯讀分區 (例如 /system 分區) 上使用高壓縮的 APEX 檔案組合,可將對儲存空間的影響降到最低。Android 12 以上版本會使用 DEFLATE zip 壓縮演算法。

壓縮功能無法對下列項目進行最佳化:

  • 必須在開機序列的初期掛載的引導 APEX。

  • 不可更新的 APEX。只有在 /data 區段上安裝更新版的 APEX 時,壓縮功能才有益。如需可更新的 APEX 完整清單,請參閱「模組化系統元件」頁面。

  • 動態共用程式庫 APEX。apexd 一律會啟用這類 APEX 的兩個版本 (已預先安裝與升級),因此壓縮它們不會增加任何價值。

壓縮的 APEX 檔案格式

這是 APEX 壓縮檔的格式。

圖表顯示壓縮 APEX 檔案的格式

圖 2. 壓縮的 APEX 檔案格式

在頂層,壓縮的 APEX 檔案是一個 ZIP 檔案,包含壓縮等級為 9 的原始 Apex 檔案,其他未壓縮過的檔案。

APEX 檔案是由四個檔案組成:

  • original_apex:以壓縮等級 9 表示,這是原始未經壓縮的原始 APEX 檔案
  • apex_manifest.pb:僅儲存
  • AndroidManifest.xml:僅儲存
  • apex_pubkey:僅儲存

apex_manifest.pbAndroidManifest.xmlapex_pubkey 檔案是其對應的 original_apex 檔案副本。

建立壓縮的 APEX

已壓縮的 APEX 可以使用位於 system/apex/toolsapex_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 視為啟用的壓縮 APEX,並使用 versionCode 37。

  1. /system/apex/com.android.foo.capex 中的 original_apex 檔案會解壓縮至 /data/apex/decompressed/com.android.foo@37.apex
  2. 系統會執行 restorecon /data/apex/decompressed/com.android.foo@37.apex,確認其是否具有正確的 SELinux 標籤。
  3. 系統會對 /data/apex/decompressed/com.android.foo@37.apex 執行驗證檢查,確保其有效性:apexd 會檢查 /data/apex/decompressed/com.android.foo@37.apex 中所附帶的公開金鑰,驗證其是否與 /system/apex/com.android.foo.capex 中所附帶的相同。
  4. /data/apex/decompressed/com.android.foo@37.apex 檔案會硬連結至 /data/apex/active/com.android.foo@37.apex 目錄。
  5. 系統會在 /data/apex/active/com.android.foo@37.apex 上執行未壓縮 APEX 檔案的一般啟用邏輯。

與 OTA 互動

壓縮的 APEX 檔案會影響 OTA 提交和應用程式。由於 OTA 更新可能包含壓縮的 APEX 檔案,其版本等級可能高於裝置上目前使用的版本,因此裝置重新啟動前,必須先保留一定空間的可用空間,才能套用 OTA 更新。

為支援 OTA 系統,apexd 會公開以下兩個 Binder API:

  • calculateSizeForCompressedApex - 計算解壓縮 OTA 套件中 APEX 檔案所需的大小。這可用於在 OTA 下載前,驗證裝置是否有足夠的空間。
  • reserveSpaceForCompressedApex - 保留磁碟上的空間,供 apexd 用來在 OTA 套件中解壓縮壓縮的 APEX 檔案。

在 A/B OTA 更新的情況下,apexd 會嘗試在背景執行解壓縮作業,做為安裝後 OTA 例行程序的一部分。如果解壓縮作業失敗,apexd 會在套用 OTA 更新的啟動期間執行解壓縮作業。

開發 APEX 時考慮的替代方案

以下是 AOSP 在設計 APEX 檔案格式時考慮的部分選項,以及為何納入或排除這些選項。

一般套件管理系統

Linux 發行版具有各種套件管理系統,例如 dpkgrpm,這些系統十分強大、成熟且完善。不過,由於這些方法無法在安裝後保護套件,因此並未採用於 APEX。只有在安裝套件時才會驗證。攻擊者可能會破壞已安裝套件的完整性,並且被察覺。這是 Android 的回歸問題,因為所有系統元件都儲存在唯讀檔案系統中,而每項 I/O 的完整性都受到 dm-verity 保護。您必須禁止竄改任何系統元件,或是禁止裝置偵測,讓裝置在遭入侵時拒絕啟動。

使用 dm-crypt 確保完整性

APEX 容器中的檔案來自受 dm-verity 保護的內建分割區 (例如 /system 分割區),即使分割區已掛載,也禁止修改檔案。為提供相同程度的檔案安全性,APEX 中的所有檔案都會儲存在檔案系統映像檔中,並與雜湊樹狀結構和 vbmeta 描述元配對。在沒有 dm-verity 的情況下,/data 分區中的 APEX 會使憑證在經過驗證和安裝後才進行非預期修改。

事實上,/data 分區也受到 dm-crypt 等加密層的保護。雖然這提供了一定程度的防竄改功能,但其主要用途是保護隱私權,而非完整性。當攻擊者取得 /data 分區的存取權時,就無法再提供進一步的保護,這又是相較於將所有系統元件置於 /system 分區的情況,又是一種退步。APEX 檔案中的雜湊樹狀結構和 dm-verity 可提供相同等級的內容保護。

將路徑從 /system 重新導向至 /apex

透過 /apex/<name>/lib/libfoo.so 等新路徑,即可存取 APEX 中封裝的系統元件檔案。當檔案屬於 /system 分區時,就可以透過 /system/lib/libfoo.so 等路徑存取。APEX 檔案的用戶端 (其他 APEX 檔案或平台) 必須使用新路徑。由於路徑有所變更,您可能需要更新現有程式碼。

雖然避免路徑變更的方法是將 APEX 檔案中的檔案內容重疊至 /system 分區,但 Android 團隊決定不將檔案重疊在 /system 分區上,因為重疊的檔案數量增加 (甚至可能相互堆疊) 也可能會影響效能。

另一個選項是劫持檔案存取函式,例如 openstatreadlink,以便將以 /system 開頭的路徑重新導向至 /apex 下方的對應路徑。Android 團隊捨棄了這個選項,因為您無法變更所有接受路徑的函式。舉例來說,某些應用程式會以靜態方式連結 Bionic,以便實作函式。 在這種情況下,這些應用程式不會重新導向。