強制執行產品分區接口

Android 11 解綁了product分區,使其獨立於systemvendor分區。作為這些更改的一部分,您現在可以控制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: truevendor_available: true的其他庫。

  • VNDK 庫必須在其Android.bp文件中包含vendor_available: true ,以便product二進製文件可以鏈接到 VNDK 庫。

下表總結了用於創建圖像變體的Android.bp屬性。

Android.bp 中的屬性創建的變體
執法前執行後
默認(無)

(包括/system/system_ext/product

(包括/system/system_ext但不包括/product

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:productnative: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_apissdk_version字段來驗證product分區中的 Java 模塊不使用隱藏的 API。 product分區中應用的sdk_version必須填寫 API 的currentsystem_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。

  1. 創建一個為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),)
    
  2. 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設置為truestrict時,構建系統會阻止在其他 makefile 中定義的包安裝到require-artifacts-in-path定義的路徑,並防止當前 makefile 中定義的包安裝在require-artifacts-in-path中定義的路徑之外的工件require-artifacts-in-path

在上面的示例中,將PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS設置為strictoem_system.mk之外的 makefile 不能包含安裝到rootsystem分區的模塊。要包含這些模塊,您必須在oem_system.mk文件本身或包含的 makefile 中定義它們。嘗試將模塊安裝到不允許的路徑會導致構建中斷。要修復中斷,請執行以下操作之一:

  • 選項 1:oem_system.mk中包含的 makefile 中包含系統模塊。這使得它滿足工件路徑要求(因為模塊現在存在於包含的 makefile 中),因此允許安裝到 `require-artifacts-in-path 中的路徑集。

  • 選項 2:將模塊安裝到system_extproduct分區(並且不要將模塊安裝到system分區)。

  • 選項 3:將模塊添加到PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST 。這列出了允許安裝的模塊。

第 2 步:清空允許列表

在此步驟中,您將PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST設為空,以便共享oem_system.mk的所有設備也可以共享單個system映像。要清空允許列表,請將列表中的任何模塊移動到system_extproduct分區或將它們添加到system make 文件中。此步驟是可選的,因為啟用產品接口實施不需要定義通用system映像。但是,清空允許列表有助於使用system_ext定義system邊界。

第 3 步:強制執行本機接口

在此步驟中,您設置PRODUCT_PRODUCT_VNDK_VERSION := current ,然後查找構建和運行時錯誤並解決它們。要檢查設備啟動和日誌並查找和修復運行時鏈接故障:

  1. 設置PRODUCT_PRODUCT_VNDK_VERSION := current

  2. 構建設備並查找構建錯誤。您可能會看到一些缺少產品變體或核心變體的構建中斷。常見的休息包括:

    • 任何具有product_specific: truehidl_interface模塊都不適用於系統模塊。要修復,請將product_specific: true替換為system_ext_specfic: true
    • 模塊可能缺少產品模塊所需的產品變體。要解決此問題,請通過設置vendor_available: true使該模塊可用於product分區,或通過設置product_specific: true將模塊移動到product分區。
  3. 解決構建錯誤並確保設備構建成功。

  4. 刷新映像並在設備啟動和日誌中查找運行時錯誤。

    • 如果測試用例日誌中的linker標記顯示CANNOT LINK EXECUTABLE消息,則 make 文件缺少依賴項(並且在構建時未捕獲)。
    • 要從構建系統中檢查它,請將所需的庫添加到shared_libs:required:字段。
  5. 使用上面給出的指導解決缺少的依賴項。

第 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