Android 11 解綁了product
分區,使其獨立於system
和vendor
分區。作為這些更改的一部分,您現在可以控制product
分區對本機和 Java 接口的訪問(這類似於接口強制對vendor
分區的工作方式)。
強制執行本機接口
要啟用本機接口實施,請將PRODUCT_PRODUCT_VNDK_VERSION
設置為current
。 (當目標的運輸 API 級別大於 29 時,版本會自動設置為current
版本。)強制允許:
-
product
分區中要鏈接的本機模塊:- 靜態或動態到
product
分區中的其他模塊,包括靜態、共享或頭庫。 - 動態到
system
分區中的 VNDK 庫。
- 靜態或動態到
-
product
分區中未捆綁 APK 中的 JNI 庫鏈接到/product/lib
或/product/lib64
中的庫(這是對 NDK 庫的補充)。
執行不允許其他鏈接到除product
分區以外的分區。
構建時間強制 (Android.bp)
在 Android 11 中,除了核心和供應商圖像變體之外,系統模塊還可以創建產品圖像變體。當啟用本機接口強制時( PRODUCT_PRODUCT_VNDK_VERSION
設置為current
):
product
分區中的本機模塊位於產品變體而不是核心變體中。在其
Android.bp
文件中帶有vendor_available: true
的模塊可用於產品變體和供應商變體。指定
product_specific: true
的庫或二進製文件可以鏈接到在其Android.bp
文件中指定product_specific: true
或vendor_available: true
的其他庫。VNDK 庫必須在其
Android.bp
文件中包含vendor_available: true
,以便product
二進製文件可以鏈接到 VNDK 庫。
下表總結了用於創建圖像變體的Android.bp
屬性。
Android.bp 中的屬性 | 創建的變體 | |
---|---|---|
執法前 | 執行後 | |
默認(無) | 核 (包括 | 核 (包括 |
system_ext_specific: true | 核 | 核 |
product_specific: true | 核 | 產品 |
vendor: true | 小販 | 小販 |
vendor_available: true | 核心,供應商 | 核心、產品、供應商 |
system_ext_specific: true AND vendor_available: true | 核心,供應商 | 核心、產品、供應商 |
product_specific: true AND vendor_available: true | 核心,供應商 | 產品,供應商 |
構建時間強制 (Android.mk)
啟用本機接口強制後,安裝到product
分區的本機模塊具有一個native:product
鏈接類型,該鏈接類型只能鏈接到其他native:product
或native:vndk
模塊。嘗試鏈接到除這些以外的任何模塊會導致構建系統生成鏈接類型檢查錯誤。
運行時強制
啟用本機接口強制時,仿生鏈接器的鏈接器配置不允許系統進程使用product
,為product
進程創建一個product
部分,該部分無法鏈接到product
分區之外的庫(但是,此類進程可以鏈接到 VNDK 庫)。嘗試違反運行時鏈接配置會導致進程失敗並生成CANNOT LINK EXECUTABLE
錯誤消息。
強制執行 Java 接口
要啟用 Java 接口強制,請將PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE
設置為true
。 (當目標的運輸 API 級別大於 29 時,該值自動設置為true
。)啟用時,強制允許/禁止以下訪問。
API | /系統 | /system_ext | /產品 | /小販 | /數據 |
---|---|---|---|---|---|
公共 API | |||||
@SystemApi | |||||
@hide API |
與vendor
分區一樣, product
分區中的應用程序或 Java 庫只允許使用公共和系統 API;不允許鏈接到使用隱藏 API 的庫。此限制包括構建時的鏈接和運行時的反射。
構建時間執行
在構建時,Make 和 Soong 通過檢查platform_apis
和sdk_version
字段來驗證product
分區中的 Java 模塊不使用隱藏的 API。 product
分區中應用的sdk_version
必須填寫 API 的current
、 system_current
或數字版本, platform_apis
字段必須為空。
運行時強制
Android 運行時驗證product
分區中的應用不使用隱藏 API,包括反射。詳見非SDK接口限制。
啟用產品界面實施
使用本節中的步驟來啟用產品界面實施。
步 | 任務 | 必需的 |
---|---|---|
1 | 定義您自己的系統 makefile,指定system 分區的包,然後在device.mk 中設置工件路徑要求檢查(以防止非系統模塊安裝到system 分區)。 | ñ |
2 | 清理允許列表。 | ñ |
3 | 實施本機接口並識別運行時鏈接故障(可以與 Java 實施並行運行)。 | 是 |
4 | 實施 Java 接口並驗證運行時行為(可以與本機實施並行運行)。 | 是 |
5 | 檢查運行時行為。 | 是 |
6 | 使用產品接口強制更新device.mk 。 | 是 |
第 1 步:創建 makefile 並啟用工件路徑檢查
在此步驟中,您將定義system
makefile。
創建一個為
system
分區定義包的生成文件。例如,使用以下內容創建oem_system.mk
文件:$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk) # Applications PRODUCT_PACKAGES += \ CommonSystemApp1 \ CommonSystemApp2 \ CommonSystemApp3 \ # Binaries PRODUCT_PACKAGES += \ CommonSystemBin1 \ CommonSystemBin2 \ CommonSystemBin3 \ # Libraries PRODUCT_PACKAGES += \ CommonSystemLib1 \ CommonSystemLib2 \ CommonSystemLib3 \ PRODUCT_SYSTEM_NAME := oem_system PRODUCT_SYSTEM_BRAND := Android PRODUCT_SYSTEM_MANUFACTURER := Android PRODUCT_SYSTEM_MODEL := oem_system PRODUCT_SYSTEM_DEVICE := generic # For system-as-root devices, system.img should be mounted at /, so we # include ROOT here. _my_paths := \ $(TARGET_COPY_OUT_ROOT)/ \ $(TARGET_COPY_OUT_SYSTEM)/ \ $(call require-artifacts-in-path, $(_my_paths),)
在
device.mk
文件中,繼承system
分區的通用 makefile 並啟用工件路徑要求檢查。例如:$(call inherit-product, $(SRC_TARGET_DIR)/product/oem_system.mk) # Enable artifact path requirements checking PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := strict
關於工件路徑要求
當PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
設置為true
或strict
時,構建系統會阻止在其他 makefile 中定義的包安裝到require-artifacts-in-path
定義的路徑,並防止當前 makefile 中定義的包安裝在require-artifacts-in-path
中定義的路徑之外的工件require-artifacts-in-path
。
在上面的示例中,將PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
設置為strict
, oem_system.mk
之外的 makefile 不能包含安裝到root
或system
分區的模塊。要包含這些模塊,您必須在oem_system.mk
文件本身或包含的 makefile 中定義它們。嘗試將模塊安裝到不允許的路徑會導致構建中斷。要修復中斷,請執行以下操作之一:
選項 1:在
oem_system.mk
中包含的 makefile 中包含系統模塊。這使得它滿足工件路徑要求(因為模塊現在存在於包含的 makefile 中),因此允許安裝到 `require-artifacts-in-path 中的路徑集。選項 2:將模塊安裝到
system_ext
或product
分區(並且不要將模塊安裝到system
分區)。選項 3:將模塊添加到
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST
。這列出了允許安裝的模塊。
第 2 步:清空允許列表
在此步驟中,您將PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST
設為空,以便共享oem_system.mk
的所有設備也可以共享單個system
映像。要清空允許列表,請將列表中的任何模塊移動到system_ext
或product
分區或將它們添加到system
make 文件中。此步驟是可選的,因為啟用產品接口實施不需要定義通用system
映像。但是,清空允許列表有助於使用system_ext
定義system
邊界。
第 3 步:強制執行本機接口
在此步驟中,您設置PRODUCT_PRODUCT_VNDK_VERSION := current
,然後查找構建和運行時錯誤並解決它們。要檢查設備啟動和日誌並查找和修復運行時鏈接故障:
設置
PRODUCT_PRODUCT_VNDK_VERSION := current
。構建設備並查找構建錯誤。您可能會看到一些缺少產品變體或核心變體的構建中斷。常見的休息包括:
- 任何具有
product_specific: true
的hidl_interface
模塊都不適用於系統模塊。要修復,請將product_specific: true
替換為system_ext_specfic: true
。 - 模塊可能缺少產品模塊所需的產品變體。要解決此問題,請通過設置
vendor_available: true
使該模塊可用於product
分區,或通過設置product_specific: true
將模塊移動到product
分區。
- 任何具有
解決構建錯誤並確保設備構建成功。
刷新映像並在設備啟動和日誌中查找運行時錯誤。
- 如果測試用例日誌中的
linker
標記顯示CANNOT LINK EXECUTABLE
消息,則 make 文件缺少依賴項(並且在構建時未捕獲)。 - 要從構建系統中檢查它,請將所需的庫添加到
shared_libs:
或required:
字段。
- 如果測試用例日誌中的
使用上面給出的指導解決缺少的依賴項。
第 4 步:實施 Java 接口
在此步驟中,您設置PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true
,然後查找並修復生成的構建錯誤。查找兩種特定類型的錯誤:
鏈接類型錯誤。此錯誤表明應用鏈接到具有更廣泛
sdk_version
的 Java 模塊。要解決此問題,您可以擴大應用程序的sdk_version
或限制庫的sdk_version
。示例錯誤:error: frameworks/base/packages/SystemUI/Android.bp:138:1: module "SystemUI" variant "android_common": compiles against system API, but dependency "telephony-common" is compiling against private API.Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.
符號錯誤。此錯誤表示無法找到符號,因為它位於隱藏的 API 中。要修復,請使用可見(非隱藏)API 或尋找替代方法。示例錯誤:
frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java:1051: error: cannot find symbol ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( ^ symbol: class ProxyAuthenticate location: class SipSessionGroup.SipSessionImpl
第 5 步:檢查運行時行為
在此步驟中,您將驗證運行時行為是否符合預期。對於可調試的應用程序,您可以使用StrictMode.detectNonSdkApiUsage
(當應用程序使用隱藏 API 時生成日誌)記錄隱藏 API 的使用情況。或者,您可以使用veridex靜態分析工具來獲取使用類型(鏈接或反射)、限制級別和調用堆棧。
Veridex 語法:
./art/tools/veridex/appcompat.sh --dex-file={apk file}
示例驗證結果:
#1: Linking greylist-max-o Landroid/animation/AnimationHandler;-><init>()V use(s): Lcom/android/systemui/pip/phone/PipMotionHelper;-><init>(Landroid/content/Context;Landroid/app/IActivityManager;Landroid/app/IActivityTaskManager;Lcom/android/systemui/pip/phone/PipMenuActivityController;Lcom/android/internal/policy/PipSnapAlgorithm;Lcom/android/systemui/statusbar/FlingAnimationUtils;)V #1332: Reflection greylist Landroid/app/Activity;->mMainThread use(s): Landroidx/core/app/ActivityRecreator;->getMainThreadField()Ljava/lang/reflect/Field;
有關 veridex 使用的詳細信息,請參閱使用 veridex 工具進行測試。
第 6 步:更新 device.mk
在修復所有構建和運行時故障並驗證運行時行為是否符合預期後,在device.mk
中設置以下內容:
-
PRODUCT_PRODUCT_VNDK_VERSION := current
-
PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true