本頁介紹 Android 原始設備製造商可使用的幾種機制,讓產品線共用系統映像檔 (SSI)。此外,本文也建議以 AOSP 建構的一般系統映像檔 (GSI) 為基礎,建立 OEM 擁有的 SSI。
背景
Android 開放原始碼計畫 (AOSP) 架構符合 Mainline 架構,可與舊版供應商實作項目維持回溯相容性。舉例來說,使用 Android 10 Android 開放原始碼計畫來源建構的通用系統映像檔 (GSI),可在任何執行 Android 8 以上版本的 Treble 相容裝置上執行。
為此,Mainline 將 Android 分成兩個不同部分:硬體專屬的供應商實作項目,以及通用的 Android OS 架構。每個元件都會安裝在不同的分割區中,其中供應商分割區用於硬體專屬軟體,系統分割區則用於一般作業系統。兩者之間會強制執行版本化介面,稱為供應商介面 (VINTF)。有了這個分區系統,原始設備製造商就能修改系統分區,而不影響供應商分區,反之亦然。
過去,SoC 供應商和 OEM 會大幅修改隨附於消費裝置的 Android 架構版本 (詳情請參閱「Android 版本發布生命週期」)。由於這些架構擴充功能很少考慮到回溯相容性,因此裝置專屬的修改內容大幅增加了後續作業系統升級的複雜度和財務成本。在 Android 10 (API 級別 29) 以下版本中,生態系統缺少明確的標準化架構,合作夥伴無法建構 Android 架構的模組化擴充功能。
本頁面說明 SoC 供應商和原始設備製造商 (OEM) 如何建構共用系統映像檔 (SSI)。SSI 是從 Android 作業系統來源建構的統一框架映像檔,可在多部裝置上重複使用。透過這個分割架構,SSI 可維持與供應商實作項目之間的回溯相容性,大幅降低 Android 作業系統升級的成本和複雜度。
如需實作詳細資料,請參閱「以 GSI 為基礎的 SSI 建議步驟」。這些步驟是模組化步驟,您可以視架構選擇實作特定階段 (例如「步驟 1:為 OEM 系統映像檔 (OEM GSI) 繼承 generic_system.mk」),而不必實作所有階段。
SSI 總覽
透過 SSI,產品專屬的軟體元件和原始設備製造商 (OEM) 擴充功能會放置在新的 /product 分區。/product 分區中的元件會使用定義完善的穩定介面,與 /system 分區中的元件互動。原始設備製造商 (OEM) 可以選擇建構一個 SSI,或建構少量 SSI,供多個裝置 SKU 使用。Android 作業系統新版本發布後,原始設備製造商 (OEM) 只要更新一次 SSI,即可使用最新 Android 版本。他們可以重複使用 SSI 更新多部裝置,不必更新 /product 分區。
原始設備製造商和 SoC 供應商可以建構包含自訂功能和修改內容的 SSI。本頁面提供的機制和最佳做法,可供原始設備製造商達成下列主要目標:
- 在多個裝置 SKU 中重複使用 SSI。
- 透過模組化擴充功能更新 Android 系統,讓作業系統升級作業更加簡單。
將產品專屬元件劃分為產品分割區的核心概念,與 Mainline 將 SoC 專屬元件劃分為供應商分割區類似。產品介面 (類似 VINTF) 可讓 SSI 與產品分割區通訊。就 SSI 而言,元件一詞是指安裝到映像檔的所有資源、二進位檔、文字和程式庫,這些映像檔會成為分割區。
SSI 周圍的分區
圖 1 顯示 SSI 周圍的分割區,以及各分割區和介面上版本化的介面和政策。本節將詳細說明每個分割區和介面。
圖 1. SSI 周圍的分區和介面。
圖片和分割區
本節資訊會區分「映像檔」和「分割區」這兩個詞彙。
- 映像檔是概念上的軟體,可獨立更新。
- 分區是可獨立更新的實體儲存空間位置。
圖 1 中的各節定義如下:
SSI:OEM 通用的映像檔,可存在於多部裝置中,不含任何硬體或產品專屬元件。根據定義,使用特定 SSI 的所有裝置都會共用該 SSI 中的所有內容。SSI 由單一
/system映像檔,或/system和/system_ext分割區組成。產品映像檔:一組產品或裝置專屬元件,代表 Android OS 的 OEM 自訂項目和擴充功能。請將 SoC 專屬元件放在
/vendor分區。SoC 供應商也可以將適當的元件 (例如獨立於 SoC 的元件) 放在/product分區。舉例來說,如果 SoC 供應商為 OEM 客戶提供獨立於 SoC 的元件 (可選擇是否隨產品出貨),SoC 供應商可以將該元件放在產品映像檔中。元件的位置取決於其用途,而非擁有權。供應商映像檔:SoC 專屬元件的集合。
ODM 映像檔:一組主機板專用元件,並非由 SoC 提供。通常供應商映像檔由 SoC 供應商擁有,而 ODM 映像檔則由裝置製造商擁有。如果沒有個別的
/odm分割區,SoC 供應商和 ODM 映像檔都會合併到/vendor分割區。
/system_ext 分區
/system_ext 分區為選用項目。如果自訂功能和擴充功能與 AOSP 型元件緊密結合,請使用這個分割區。這個磁碟分割區應是 /system 磁碟分割區的 OEM 專用擴充功能,且這兩個磁碟分割區之間沒有定義介面。/system_ext 分區中的元件可以對 /system 分區發出非公開 API 呼叫,而 /system 分區中的元件可以對 /system_ext 分區發出非公開 API 呼叫。
由於這兩個磁碟分割區緊密耦合,因此發布新版 Android 時,這兩個磁碟分割區會一併升級。為舊版 Android 建立的 /system_ext 磁碟分割區,不必與下一個 Android 版本的 /system 磁碟分割區相容。
如要將模組安裝至 /system_ext 分區,請在 Android.bp 檔案中新增 system_ext_specific:
true。如果裝置沒有 /system_ext 分區,請將這類模組安裝至 /system 分區的 ./system_ext 子目錄。
歷史:/system_ext 分區的原始設計目標,是將所有 OEM 專屬元件 (無論是否為通用元件) 放置在 /product 分區中。不過,一次移動所有元件並不可行,特別是當某些元件與 /system 分區緊耦合時。如要將緊耦合的元件移至 /product 分區,必須擴充產品介面。這通常需要大量重構元件本身,耗費大量時間和精力。/system_ext 分區最初是暫時存放這些元件的地方,這些元件尚未準備好移至 /product 分區。SSI 的目標是最終淘汰 /system_ext 分區。
不過,/system_ext 分區有助於盡可能讓 /system 分區接近 AOSP。使用 SSI 時,大部分的升級工作都集中在 /system 和 /system_ext 分區的元件。如果系統映像檔是從與 AOSP 盡可能相似的來源建構而成,您就可以將升級工作重心放在 system_ext 映像檔上。
圖片之間的介面
SSI 有兩個主要介面,分別用於供應商和產品圖片:
供應商介面 (VINTF):VINTF 是供應商和 ODM 映像檔中元件的介面。產品和系統映像檔中的元件只能透過這個介面與供應商和 ODM 映像檔互動。舉例來說,供應商映像檔無法依附於系統映像檔的私人部分,反之亦然。這是 Treble 架構 (現為更廣泛的 Mainline 架構的一部分) 中定義的內容,該架構會將映像檔分割為系統和供應商分割區。介面是透過下列機制說明:
- HIDL (Passthrough HAL 僅適用於
system和system_ext模組) - 穩定版 AIDL
- 設定
- 系統屬性 API
- 設定檔結構定義 API
- VNDK
- Android SDK API
- Java SDK 程式庫
- HIDL (Passthrough HAL 僅適用於
產品介面:產品介面是 SSI 與產品圖片之間的介面。定義穩定的介面可將產品元件與 SSI 中的系統元件分離。
啟用 SSI
本節說明如何在 Android 11 以上版本中支援 SSI。
取消組合元件
如要從系統元件取消綁定 /product 分區,/product 分區必須與已透過 Mainline 取消綁定的 /vendor 分區採用相同的強制執行政策。
- 內建介面:
/product分區中的內建模組必須與其他分區解除綁定。產品模組唯一允許的依附元件,是/system分區中的部分 VNDK 程式庫 (包括 LLNDK)。產品應用程式依附的 JNI 程式庫必須是 NDK 程式庫。 - Java 介面:
/product分區中的 Java (應用程式) 模組無法使用隱藏版 API,因為這些 API 不穩定。這些模組只能使用/system分區中的公開 API 和系統 API,以及/system或/system_ext分區中的 Java SDK 程式庫。您可以為自訂 API 定義 Java SDK 程式庫。
強制執行產品介面
為確保 /product 分區未綁定,原始設備製造商可透過為內建模組設定 PRODUCT_PRODUCT_VNDK_VERSION:= current,以及為 Java 模組設定 PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE:= true,讓裝置強制執行產品介面。如果裝置的 PRODUCT_SHIPPING_API_LEVEL 大於或等於 30,系統會自動設定這些變數。詳情請參閱「強制執行產品分區介面」。
以 GSI 為基礎的 SSI 建議步驟
圖 2. 以 GSI 為基礎的 SSI 建議分割區。
通用系統映像檔 (GSI) 是直接從 Android 開放原始碼計畫建構的系統映像檔。這項工具可用於法規遵循測試 (例如 CTS-on-GSI),以及做為參考平台,供應用程式開發人員在沒有搭載必要 Android 版本的實體裝置時,測試應用程式的相容性。
原始設備製造商也可以使用 GSI 製作 SSI。如「映像檔和分區」一文所述,SSI 包含 AOSP 定義元件的系統映像檔,以及原始設備製造商定義元件的 system_ext 映像檔。使用 GSI 做為 system 映像檔時,原始設備製造商可以專注於升級 system_ext 映像檔。
本節提供指引,協助 OEM 廠商使用 AOSP 或接近 AOSP 的系統映像檔,將自訂項目模組化至 /system_ext 和 /product 分區。如果原始設備製造商 (OEM) 是從 Android 開放原始碼計畫來源建構系統映像檔,則可將建構的系統映像檔替換為 Android 開放原始碼計畫提供的 GSI。不過,原始設備製造商不必一次就完成最後一個步驟 (直接使用 GSI)。
步驟 1:為 OEM 系統映像檔 (OEM GSI) 繼承 generic_system.mk
透過繼承 generic_system.mk (在 Android 11 中名為 mainline_system.mk,並在 AOSP 中重新命名為 generic_system.mk),系統映像檔 (OEM GSI) 包含 AOSP GSI 的所有檔案。OEM 可以修改這些檔案,讓 OEM GSI 除了 AOSP GSI 檔案外,還包含 OEM 專有檔案。
圖 3. 為 OEM 的系統映像檔繼承 generic_system.mk。
步驟 2:讓 OEM GSI 擁有與 AOSP GSI 相同的檔案清單
OEM GSI 在這個階段無法有其他檔案,因此請將 OEM 專屬檔案移至 system_ext 或 product 分割區。
圖 4. 將新增的檔案移出 OEM GSI。
步驟 3:定義允許清單,限制 OEM GSI 中修改的檔案
如要檢查修改後的檔案,原始設備製造商可以使用 compare_images 工具,並比較 AOSP GSI 與原始設備製造商 GSI。從 AOSP lunch 目標 generic_system_* 取得 AOSP GSI。
定期使用 allowlist 參數執行 compare_images 工具,即可監控允許清單以外的差異。這可防止對 OEM GSI 進行額外修改。
圖 5. 定義允許清單,減少 OEM GSI 中修改的檔案清單。
步驟 4:讓 OEM GSI 與 AOSP GSI 具有相同的二進位檔
清理許可清單後,原始設備製造商就能將 AOSP GSI 用於自家產品的系統映像檔。如要清理許可清單,原始設備製造商可以放棄 OEM GSI 中的變更,也可以將變更上傳至 AOSP,讓 AOSP GSI 包含這些變更。
圖 6. 讓 OEM GSI 與 AOSP GSI 具有相同的二進位檔。
定義 SSI
原始設備製造商可參考下列指引定義 SSI。
在建構時保護 /system 分割區
為避免 /system 分區中出現任何產品專屬變更,並定義 OEM GSI,OEM 可以使用名為 require-artifacts-in-path 的 Makefile 巨集,防止在呼叫巨集後宣告任何系統模組。請參閱「步驟 1:建立 Makefile 並啟用構件路徑檢查」一節中的範例。
OEM 可以定義清單,允許在 /system 分區中暫時安裝產品專屬模組。不過,清單必須為空白,才能讓 OEM GSI 適用於所有 OEM 產品。這個程序是用於定義 OEM GSI,可獨立於 AOSP GSI 的步驟。
將 /system_ext 分區設為通用
/system_ext 分區可能因裝置而異,因為當中可能含有裝置專屬的系統隨附模組。由於 SSI 包含 /system 和 /system_ext 分區,/system_ext 分區的差異會阻礙 OEM 定義 SSI。OEM 可以擁有自己的 SSI,並移除所有差異,讓 /system_ext 分區成為通用分區,藉此在多部裝置之間共用該 SSI。
本節提供建議,說明如何讓 /system_ext 分區成為通用分區。
公開系統分區中隱藏的 API
許多產品專屬應用程式無法安裝在產品分割區中,因為這些應用程式使用隱藏版 API,而產品分割區禁止使用這類 API。如要將裝置專用應用程式移至產品分割區,請移除隱藏 API 的使用權。
從應用程式中移除隱藏 API 的最佳方式,是找出替代的公開或系統 API 來取代。如果沒有可取代隱藏 API 的 API,原始設備製造商可以為 AOSP 貢獻心力,為自家裝置定義新的系統 API。
或者,原始設備製造商也可以在 /system_ext 分區中建立自己的 Java SDK 程式庫,定義自訂 API。這個程式庫可以使用系統分區中的隱藏 API,並將 API 提供給產品或供應商分區中的應用程式。原始設備製造商必須凍結面向產品的 API,確保向後相容性。
取代停用特定 SKU 的應用程式
Android 16 已淘汰並移除舊版機制,該機制會使用架構資源疊加 (config_disableApksUnlessMatchedSku_apk_list 和 config_disableApkUnlessMatchedSku_skus_list),根據硬體 SKU 選擇性停用 APK。詳情請參閱 aosp/3444399。
建議改用 SKU 特定目錄中的 install-in-user-type 系統設定。這種做法可防止在特定 SKU 上為任何使用者安裝套件,而不只是在安裝後停用。
在映像檔中加入所有 APK (系統映像檔中所有 SKU 的所有潛在應用程式的超集),通常位於
/product分割區。請確認裝置 SKU 在
ro.boot.hardware.sku系統屬性中設定正確 (系統會在啟動時使用此屬性識別裝置 SKU)。使用
sku_<SKU_NAME>命名慣例,在/product/etc/sysconfig/下方建立 SKU 專屬的 sysconfig 子目錄。系統會自動從符合ro.boot.hardware.sku屬性的目錄載入設定。範例路徑:/product/etc/sysconfig/sku_basic_model/。設定禁止安裝應用程式。在 SKU 專屬目錄中,建立 XML 設定檔 (例如
disabled_apps.xml),並使用<do-not-install-in>標記排除特定套件。
XML 範例 (/product/etc/sysconfig/sku_basic_model/disabled_apps.xml):
<?xml version="1.0" encoding="utf-8"?>
<config>
<!-- Prevents this package from being installed for ANY user on this SKU -->
<install-in-user-type package="com.example.premium.feature.app" >
<do-not-install-in user-type="FULL" />
<do-not-install-in user-type="SYSTEM" />
</install-in-user-type>
</config>
以下比較這兩種方法:
| 功能 | Android 15 以下版本 | Android 16 以上版本 |
|---|---|---|
| 設定方法 | 架構資源疊加層 | SystemConfig XML 檔案 |
| 邏輯位置 | config.xml (資源重疊) |
/product/etc/sysconfig/sku_<name>/ |
| 結果 | 使用 PackageManager 停用應用程式 | 禁止使用者安裝應用程式 |
| 穩定性 | 系統服務可以重新啟用 | 使用者從未安裝套件 |
如果需要更精細的控制 (也就是停用通常會預設安裝在所有 SKU 中的應用程式),Android 也支援 sysconfig 中的 disabled-in-sku 和 enabled-in-sku-override 標記:
<disabled-in-sku package="com.example.app" />會全面停用應用程式。如果將
<enabled-in-sku-override package="com.example.app" />放在對應的sku_<name>目錄中,即可為特定 SKU 重新啟用應用程式。
定義 RRO,而非使用靜態資源疊加
靜態資源疊加層會操控疊加的套件,但可能會阻礙定義 SSI,因此請確保 RRO 的屬性已開啟並正確設定。OEM 只要依下列方式設定屬性,就能將所有自動產生的疊加層設為 RRO。
PRODUCT_ENFORCE_RRO_TARGETS := *
PRODUCT_ENFORCE_RRO_EXCLUDED_OVERLAYS := # leave it empty
如需詳細設定,請手動定義 RRO,而非依賴自動產生的 RRO。如需詳細資訊,請參閱「在執行階段變更應用程式資源的值」。OEM 也可以使用 android:requiredSystemPropertyName 和 android:requiredSystemPropertyValue 屬性,定義取決於系統屬性的條件式 RRO。
常見問題 (FAQ)
以下是 SSI 的常見問題。
我可以定義多個 SSI 嗎?
這取決於裝置 (或裝置群組) 的共通性和特徵。如要讓 system_ext 分區通用,OEM 可以嘗試按照「讓 system_ext 分區通用」一文所述操作。如果裝置群組有許多差異,最好定義多個 SSI。
我可以從 generic_system.mk 中移除與實作項目衝突的模組嗎?
否。GSI 包含可啟動及測試的最低模組集。如果您認為某個模組並非必要,請回報錯誤,更新 generic_system.mk 檔案。