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 |
core、vendor | 產品、供應商 |
建構時間強制執行 (Android.mk)
啟用原生介面強制執行功能後,安裝至 product
分區的原生模組就會具有 native:product
連結類型,且只能連結至其他 native:product
或 native: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_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
中包含的 Makefile 中加入系統模組。這樣一來,系統就會符合構件路徑規定 (因為模組現在位於已納入的 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