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
分區中的原生模組位於產品子類,而非核心子類。產品子類可使用
product_available: true
檔案中含有Android.bp
的模組。指定
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 |
核心、供應商 | 核心、供應商 |
product_available: true |
無 | 核心、產品 |
「vendor_available: true 」和「product_available:
true 」 |
無 | 核心、產品、供應商 |
「system_ext_specific: true 」和「vendor_available:
true 」 |
核心、供應商 | 核心、供應商 |
「product_specific: true 」和「vendor_available:
true 」 |
核心、供應商 | 產品、供應商 |
強制執行建構時間 (Android.mk)
啟用原生介面強制執行功能後,安裝至 product
分割區的原生模組會具有 native:product
連結類型,只能連結至其他 native:product
或 native:vndk
模組。如果嘗試連結至這些模組以外的任何模組,建構系統會產生連結類型檢查錯誤。
執行階段強制執行
啟用原生介面強制執行功能後,bionic 連結器的連結器設定會禁止系統程序使用 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 |
---|---|---|---|---|---|
Public 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。
建立 makefile,定義
system
分區的套件。舉例來說,您可以建立包含下列內容的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
訊息,表示 make 檔案缺少依附元件 (且未在建構時擷取)。 - 如要透過建構系統檢查,請將所需程式庫新增至
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 sma
ller 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