強制執行產品劃分介面

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 core、vendor 產品、供應商

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

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

執行階段強制執行

啟用原生介面強制執行時,bionic linker 的連結器設定將不允許系統程序使用 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 中包含的 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_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