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_manifest.json 處理新程式碼和 APEX 系統。AndroidManifest.xmlAndroidManifest.xml 可能會包含額外的指定目標資訊,可供現有的應用程式發布工具使用。

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

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

APEX 命名規範

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

  • com.android.*
    • 保留給 Android 開放原始碼計畫的 APEX 使用。不限於任何公司或裝置。
  • 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 描述元) 檔案會使用金鑰簽署。接著,整個 APEX 會使用 APK 簽署配置 v3 進行簽署。這個程序會使用兩個不同的金鑰。

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

內建分區中的 APEX

APEX 檔案可位於內建分區 (例如 /system) 中。分割區已在 dm-verity 上方,因此 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)
  • 回移植:區塊/迴圈:設定 hw_sectors (4.4)
  • 上游:迴圈:在相容性 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 中的轉譯 arm)
可執行檔 /bin
Java 程式庫 /javalib
預先建構 /etc

遞移依附元件

APEX 檔案會自動納入原生共用程式庫或可執行檔的間接依附元件。舉例來說,如果 libFoo 依附於 libBar,當 native_shared_libs 屬性中只列出 libFoo 時,系統就會納入這兩個程式庫。

處理多個 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 必須全部未扁平化或全部扁平化。對於 Mainline 等專案來說,這一點尤其重要,因為您必須為這些專案提供預先簽署的 APEX 預先建構項目。未預先簽署的 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

您可以使用位於 system/apex/toolsapex_compression_tool.py 工具建構壓縮的 APEX。

建構系統中提供多個與 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,版本代碼為 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,實作這些函式。在這種情況下,這些應用程式不會重新導向。