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
檔案中帶有product_available: true
模組可用於此產品變體。指定
product_specific: true
的函式庫或二進位檔案可以連結到在其Android.bp
檔案中指定product_specific: true
或product_available: true
其他函式庫。VNDK 庫的
Android.bp
檔案中必須包含product_available: true
,以便product
二進位檔案可以連結到 VNDK 庫。
下表總結了用於建立圖像變體Android.bp
屬性。
Android.bp 中的屬性 | 創建的變體 | |
---|---|---|
執行前 | 執行後 | |
預設(無) | 核 (包括 | 核 (包括 |
system_ext_specific: true | 核 | 核 |
product_specific: true | 核 | 產品 |
vendor: true | 小販 | 小販 |
vendor_available: true | 核心、供應商 | 核心、供應商 |
product_available: true | 不適用 | 核心產品 |
vendor_available: true 且product_available: true | 不適用 | 核心、產品、供應商 |
system_ext_specific: true 和vendor_available: true | 核心、供應商 | 核心、供應商 |
product_specific: true 和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 | |||||
@SystemApi | |||||
@隱藏API |
與vendor
分區一樣, product
分區中的應用程式或 Java 程式庫僅允許使用公共 API 和系統 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
分割區的軟體包的 makefile。例如,使用以下內容建立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
。 - 模組可能缺少產品模組所需的產品變型。若要修正此問題,請透過設定
product_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}
veridex 結果範例:
#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