Android Pony EXpress (APEX) 容器格式是在 Android 10 中引入的,它用於較低級別系統模塊的安裝流程。這種格式有利於不適合標準 Android 應用程序模型的系統組件的更新。一些示例組件包括本機服務和庫、硬件抽象層 ( HAL )、運行時 ( ART ) 和類庫。
術語“APEX”也可以指 APEX 文件。
背景
儘管 Android 支持通過軟件包安裝程序應用程序(例如 Google Play 商店應用程序)更新適合標準應用程序模型(例如服務、活動)的模塊,但對較低級別的操作系統組件使用類似的模型具有以下缺點:
- 基於 APK 的模塊不能在啟動順序的早期使用。包管理器是有關應用程序信息的中央存儲庫,只能從活動管理器啟動,活動管理器在啟動過程的後期階段準備就緒。
- APK 格式(尤其是清單)是為 Android 應用程序設計的,系統模塊並不總是合適。
設計
本節介紹 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.xml
。 AndroidManifest.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類,如下所示。
- APEX 文件是通過軟件包安裝程序應用程序、ADB 或其他來源下載的。
- 包管理器開始安裝過程。在識別出該文件是 APEX 後,包管理器將控制權轉移給 APEX 管理器。
- APEX 管理器驗證 APEX 文件。
- 如果 APEX 文件經過驗證,APEX 管理器的內部數據庫將更新,以反映 APEX 文件在下次啟動時被激活。
- 安裝請求者在成功驗證軟件包後會收到廣播。
- 要繼續安裝,必須重新啟動系統。
下次啟動時,APEX 管理器啟動,讀取內部數據庫,並對列出的每個 APEX 文件執行以下操作:
- 驗證 APEX 文件。
- 從 APEX 文件創建環回設備。
- 在環回設備之上創建設備映射器塊設備。
- 將設備映射器塊設備安裝到唯一路徑(例如,
/apex/ name @ ver
)。
當內部數據庫中列出的所有 APEX 文件都已安裝時,APEX 管理器為其他系統組件提供綁定器服務,以查詢有關已安裝 APEX 文件的信息。例如,其他系統組件可以查詢設備中安裝的 APEX 文件的列表,或者查詢安裝特定 APEX 的確切路徑,以便可以訪問這些文件。
APEX文件是APK文件
APEX 文件是有效的 APK 文件,因為它們是包含AndroidManifest.xml
文件的簽名 zip 存檔(使用 APK 簽名方案)。這允許 APEX 文件使用 APK 文件的基礎設施,例如軟件包安裝程序應用程序、簽名實用程序和軟件包管理器。
APEX 文件內的AndroidManifest.xml
文件很小,由包name
、 versionCode
和可選的targetSdkVersion
、 minSdkVersion
和maxSdkVersion
組成,用於細粒度定位。此信息允許通過現有渠道(例如軟件包安裝程序應用程序和 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
的默認值。
java
、 libraries
和prebuilts
屬性與 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 pairopenssl genrsa -out foo.pem 4096
# extract the public key from the key pairavbtool extract_public_key --key foo.pem --output foo.avbpubkey
# in Android.bpapex_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 通常按如下方式使用:
- 設備發貨時,OEM 或 ODM 會在
/system/apex
下預加載 APEX。 - APEX 中的文件可通過
/apex/<apex_name>/
路徑訪問。 - 當 APEX 的更新版本安裝在
/data/apex
中時,該路徑在重新啟動後指向新的 APEX。
使用 APEX 更新服務
要使用 APEX 更新服務:
將系統分區中的服務標記為可更新。將
updatable
選項添加到服務定義中。/system/etc/init/myservice.rc: service myservice /system/bin/myservice class core user system ... updatable
為更新的服務創建新的
.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 文件的格式。
圖 2.壓縮的 APEX 文件格式
在頂層,壓縮的 APEX 文件是一個 zip 文件,其中包含壓縮形式的原始 apex 文件,壓縮級別為 9,並且其他文件未壓縮存儲。
四個文件組成一個 APEX 文件:
-
original_apex
:壓縮級別為 9 的壓縮 這是原始的、未壓縮的APEX 文件。 -
apex_manifest.pb
:僅存儲 AndroidManifest.xml
:僅存儲apex_pubkey
:僅存儲
apex_manifest.pb
、 AndroidManifest.xml
和apex_pubkey
文件是original_apex
中相應文件的副本。
構建壓縮的 APEX
可以使用位於system/apex/tools
的apex_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。
-
/system/apex/com.android.foo.capex
中的original_apex
文件被解壓到/data/apex/decompressed/com.android.foo@37.apex
。 - 執行
restorecon /data/apex/decompressed/com.android.foo@37.apex
以驗證它是否具有正確的SELinux標籤。 - 對
/data/apex/decompressed/com.android.foo@37.apex
執行驗證檢查以確保其有效性:apexd
檢查/data/apex/decompressed/com.android.foo@37.apex
中捆綁的公鑰驗證它是否等於/system/apex/com.android.foo.capex
中捆綁的那個。 -
/data/apex/decompressed/com.android.foo@37.apex
文件硬鏈接到/data/apex/active/com.android.foo@37.apex
目錄。 - 未壓縮 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 發行版具有dpkg
和rpm
等包管理系統,它們功能強大、成熟且健壯。但是,它們沒有被 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
分區上覆蓋文件,因為這可能會影響性能,因為覆蓋的文件數量會增加(甚至可能一個接一個地堆疊)增加。
另一種選擇是劫持文件訪問函數,例如open
、 stat
和readlink
,以便以/system
開頭的路徑被重定向到/apex
下的相應路徑。 Android 團隊放棄了這個選項,因為更改所有接受路徑的函數是不可行的。例如,一些應用程序靜態鏈接 Bionic,它實現了功能。在這種情況下,這些應用程序不會被重定向。