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 、/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: true 和 product_available:
true |
無 | core、product、vendor |
system_ext_specific: true 和 vendor_available:
true |
core、vendor | core、vendor |
product_specific: true 和 vendor_available:
true |
核心、供應商 | 產品、供應商 |
建構時間強制執行 (Android.mk)
啟用原生介面強制執行功能後,安裝至 product
分區的原生模組就會具有隻能連結至其他 native:product
或 native: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_apis
和 sdk_version
欄位,確認 product
分區中的 Java 模組不會使用隱藏的 API。product
區隔區中應用程式的 sdk_version
必須填入 current
、system_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。
建立定義
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
中定義路徑以外的成果。
在上述範例中,PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
設為 strict
,oem_system.mk
以外的 makefile 就無法納入安裝至 root
或 system
分割區的模組。如要加入這些模組,您必須在 oem_system.mk
檔案本身或已加入的 makefile 中定義這些模組。嘗試將模組安裝至不允許的路徑會導致建構中斷。如要修正中斷情形,請執行下列其中一項操作:
方法 1:在
oem_system.mk
包含的 makefiles 中加入系統模組。如此一來,就能符合構件路徑要求 (模組現在位於隨附的 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_specific: true
。 - 模組可能缺少產品模組所需的產品子類。如要修正這個問題,請設定
product_available: true
,讓該模組可供product
分區使用,或是設定product_specific: true
,將模組移至product
分區。
- 任何具有
解決建構錯誤,並確保裝置建構成功。
閃燈映像檔,並在裝置啟動和記錄中查看執行階段錯誤。
- 如果測試案例記錄檔中的
linker
標記顯示CANNOT LINK EXECUTABLE
訊息,表示製作檔案缺少依附元件 (且未在建構期間擷取)。 - 如要透過建構系統檢查,請將必要程式庫新增至
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