強制執行產品分區介面

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 Core Core
product_specific: true Core 產品
vendor: true 販賣機 販賣機
vendor_available: true core、vendor core、vendor
product_available: true core, product
vendor_available: trueproduct_available: true core、product、vendor
system_ext_specific: truevendor_available: true core、vendor core、vendor
product_specific: truevendor_available: true 核心、供應商 產品、供應商

建構時間強制執行 (Android.mk)

啟用原生介面強制執行功能後,安裝至 product 分區的原生模組就會具有隻能連結至其他 native:productnative:vndk 模組的 native:product 連結類型。如果嘗試連結至上述以外的任何模組,建構系統會產生連結類型檢查錯誤。

執行階段強制執行

啟用原生介面強制執行功能後,系統程序就不會允許系統程序使用 product 程式庫,進而為 product 程序建立 product 區段,該程序無法連結至 product 分區之外的程式庫 (但是這類程序可以連結至 VNDK 程式庫)。嘗試違反執行階段連結設定會導致程序失敗,並產生 CANNOT LINK EXECUTABLE 錯誤訊息。

強制執行 Java 介面

如要啟用強制執行 Java 介面,請將 PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE 設為 true。(當目標的運送 API 級別大於 29 時,系統會自動將值設為 true)。啟用後,執行會允許或禁止下列存取權:

API /system /system_ext /product /vendor /data
公用 API
@SystemApi
@hide API

如同 vendor 區隔,product 區隔中的應用程式或 Java 程式庫只能使用公開和系統 API,不允許連結使用隱藏 API 的程式庫。這項限制包括在建構期間連結,以及在執行階段進行反射。

建構時間強制執行機制

在建構期間,Make 和 Soong 會檢查 platform_apissdk_version 欄位,確認 product 分區中的 Java 模組不會使用隱藏的 API。product 區隔區中應用程式的 sdk_version 必須填入 currentsystem_current 或 API 的數字版本,且 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 中定義路徑以外的成果。

在上述範例中,PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS 設為 strictoem_system.mk 以外的 makefile 就無法納入安裝至 rootsystem 分割區的模組。如要加入這些模組,您必須在 oem_system.mk 檔案本身或已加入的 makefile 中定義這些模組。嘗試將模組安裝至不允許的路徑會導致建構中斷。如要修正中斷情形,請執行下列其中一項操作:

  • 方法 1:oem_system.mk 包含的 makefiles 中加入系統模組。如此一來,就能符合構件路徑要求 (模組現在位於隨附的 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_specific: true
    • 模組可能缺少產品模組所需的產品子類。如要修正這個問題,請設定 product_available: true,讓該模組可供 product 分區使用,或是設定 product_specific: true,將模組移至 product 分區。
  3. 解決建構錯誤,並確保裝置建構成功。

  4. 閃燈映像檔,並在裝置啟動和記錄中查看執行階段錯誤。

    • 如果測試案例記錄檔中的 linker 標記顯示 CANNOT LINK EXECUTABLE 訊息,表示製作檔案缺少依附元件 (且未在建構期間擷取)。
    • 如要透過建構系統檢查,請將必要程式庫新增至 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