強制執行產品分區接口

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檔案中帶有product_available: true模組可用於此產品變體。

  • 指定product_specific: true的函式庫或二進位檔案可以連結到在其Android.bp檔案中指定product_specific: trueproduct_available: true其他函式庫。

  • VNDK 庫的Android.bp檔案中必須包含product_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核心、供應商核心、供應商
product_available: true不適用核心產品
vendor_available: trueproduct_available: true不適用核心、產品、供應商
system_ext_specific: truevendor_available: true核心、供應商核心、供應商
product_specific: truevendor_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
@SystemApi
@隱藏API

vendor分區一樣, product分區中的應用程式或 Java 程式庫僅允許使用公共 API 和系統 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分割區的軟體包的 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),)
    
  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設定為strict時, oem_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: true hidl_interface模組都不可用於系統模組。若要修正此問題,請將product_specific: true替換為system_ext_specfic: true
    • 模組可能缺少產品模組所需的產品變型。若要修正此問題,請透過設定product_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}
    
  • 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