APEX 文件格式

Android Pony EXpress (APEX) 容器格式是在 Android 10 中引入的,它用於較低級別系統模塊的安裝流程。這種格式有利於不適合標準 Android 應用程序模型的系統組件的更新。一些示例組件包括本機服務和庫、硬件抽象層 ( HAL )、運行時 ( ART ) 和類庫。

術語“APEX”也可以指 APEX 文件。

背景

儘管 Android 支持通過軟件包安裝程序應用程序(例如 Google Play 商店應用程序)更新適合標準應用程序模型(例如服務、活動)的模塊,但對較低級別的操作系統組件使用類似的模型具有以下缺點:

  • 基於 APK 的模塊不能在啟動順序的早期使用。包管理器是有關應用程序信息的中央存儲庫,只能從活動管理器啟動,活動管理器在啟動過程的後期階段準備就緒。
  • APK 格式(尤其是清單)是為 Android 應用程序設計的,系統模塊並不總是合適。

設計

本節介紹 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 Store)。例如,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.*
    • 為 AOSP APEX 保留。並非任何公司或設備所獨有。
  • com.<companyname>.*
    • 為公司保留。可能被該公司的多個設備使用。
  • com.<companyname>.<devicename>.*
    • 為特定設備(或設備子集)特有的 APEX 保留。

頂點經理

APEX 管理器(或apexd )是一個獨立的本機進程,負責驗證、安裝和卸載 APEX 文件。該進程在啟動序列的早期啟動並準備就緒。 APEX 文件通常預安裝在設備上的/system/apex下。如果沒有可用的更新,APEX 管理器默認使用這些軟件包。

APEX的更新順序使用PackageManager類,如下所示。

  1. APEX 文件是通過軟件包安裝程序應用程序、ADB 或其他來源下載的。
  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 文件,因為它們是包含AndroidManifest.xml文件的簽名 zip 存檔(使用 APK 簽名方案)。這允許 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-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 模塊。

所需的內核補丁

支持 APEX 模塊所需的內核補丁包含在 Android 公共樹中。要獲取支持 APEX 的補丁,請使用最新版本的 Android 通用樹。

內核版本4.4

此版本僅支持從 Android 9 升級到 Android 10 並希望支持 APEX 模塊的設備。要獲取所需的補丁,強烈建議從android-4.4分支進行向下合併。以下是內核版本 4.4 所需的各個補丁的列表。

  • UPSTREAM:循環:添加 ioctl 來更改邏輯塊大小( 4.4
  • 後向端口:塊/循環:設置 hw_sectors ( 4.4 )
  • UPSTREAM:循環:在 compat ioctl 中添加 LOOP_SET_BLOCK_SIZE ( 4.4 )
  • ANDROID:mnt:修復 next_descendent ( 4.4 )
  • ANDROID:mnt:重新安裝應該傳播到從屬的從屬( 4.4
  • ANDROID:mnt:正確傳播重新安裝( 4.4
  • 恢復“ANDROID:dm verity:添加最小預取大小”( 4.4
  • UPSTREAM:循環:如果 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
        },
    },
}

vb元簽名

使用不同的密鑰對每個 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簽約

簽署 APEX 的方式與簽署 APK 的方式相同。對 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>的 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 通常按如下方式使用:

  1. 設備發貨時,OEM 或 ODM 會在/system/apex下預加載 APEX。
  2. APEX 中的文件可通過/apex/<apex_name>/路徑訪問。
  3. 當 APEX 的更新版本安裝在/data/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。例如,內核可能是在沒有CONFIG_BLK_DEV_LOOP=Y情況下構建的,這對於在 APEX 內掛載文件系統映像至關重要。

Flattened 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 也應該是非扁平化的,並使用正確的密鑰進行簽名。設備應繼承自updatable_apex.mk ,如使用 APEX 更新服務中所述。

壓縮 APEX

Android 12 及更高版本具有 APEX 壓縮功能,可減少可更新 APEX 包的存儲影響。安裝 APEX 更新後,雖然不再使用其預安裝版本,但仍佔用相同的空間量。所佔用的空間仍然不可用。

APEX 壓縮通過在只讀分區(例如/system分區)上使用高度壓縮的 APEX 文件集來最大限度地減少這種存儲影響。 Android 12 及更高版本使用 DEFLATE zip 壓縮算法。

壓縮不會對以下內容提供優化:

  • 需要在引導序列的早期安裝的引導 APEX。

  • 不可更新的 APEX。僅當 APEX 的更新版本安裝在/data分區上時,壓縮才有用。模塊化系統組件頁面上提供了可更新 APEX 的完整列表。

  • 動態共享庫 APEX。由於apexd始終激活此類 APEX 的兩個版本(預安裝和升級),因此壓縮它們不會增加價值。

壓縮 APEX 文件格式

這是壓縮 APEX 文件的格式。

Diagram shows the format of a compressed APEX file

圖 2.壓縮的 APEX 文件格式

在頂層,壓縮的 APEX 文件是一個 zip 文件,其中包含壓縮形式的原始 apex 文件,壓縮級別為 9,並且其他文件未壓縮存儲。

四個文件組成一個 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. 未壓縮 APEX 文件的常規激活邏輯在/data/apex/active/com.android.foo@37.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 中的系統組件文件可以通過/apex/<name>/lib/libfoo.so等新路徑訪問。當文件是/system分區的一部分時,可以通過/system/lib/libfoo.so等路徑訪問它們。 APEX 文件(其他 APEX 文件或平台)的客戶端必須使用新路徑。由於路徑更改,您可能需要更新現有代碼。

儘管避免路徑更改的一種方法是將 APEX 文件中的文件內容覆蓋到/system分區上,但 Android 團隊決定不在/system分區上覆蓋文件,因為這可能會影響性能,因為覆蓋的文件數量會增加(甚至可能一個接一個地堆疊)增加。

另一種選擇是劫持文件訪問函數,例如openstatreadlink ,以便以/system開頭的路徑被重定向到/apex下的相應路徑。 Android 團隊放棄了這個選項,因為更改所有接受路徑的函數是不可行的。例如,一些應用程序靜態鏈接 Bionic,它實現了功能。在這種情況下,這些應用程序不會被重定向。