動態系統更新 (DSU) 可讓您製作 Android 系統映像檔,讓使用者從網際網路下載並試用,而不會導致目前系統映像檔毀損。本文件說明如何支援 DSU。
核心需求
如要瞭解核心需求,請參閱「實作動態分割區」。
此外,DSU 會依賴 device-mapper-verity (dm-verity) 核心功能來驗證 Android 系統映像檔。因此,您必須啟用下列核心設定:
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
分區需求
自 Android 11 起,DSU 要求 /data
分區使用 F2FS 或 ext4 檔案系統。F2FS 可提供更佳效能,因此建議使用,但差異應不明顯。
以下列舉 Pixel 裝置動態系統更新的時間範例:
- 使用 F2FS:
- 109 秒、8 GB 使用者、867 MB 系統、檔案系統類型:F2FS:encryption=aes-256-xts:aes-256-cts
- 104 秒、8G 使用者、867M 系統、檔案系統類型:F2FS:encryption=ice
- 使用 ext4:
- 135 秒、8G 使用者、867M 系統、檔案系統類型:ext4:encryption=aes-256-xts:aes-256-cts
如果在您的平台上需要更長的時間,建議您檢查掛載標記是否包含任何會執行「同步」寫入作業的標記,或者您可以明確指定「非同步」標記,以獲得更佳的效能。
metadata
分割區 (16 MB 以上) 用於儲存與已安裝圖片相關的資料。必須在第一階段安裝期間掛載。
userdata
分區必須使用 F2FS 或 ext4 檔案系統。使用 F2FS 時,請納入 Android 通用核心中提供的所有 F2FS 相關修補程式。
DSU 是使用核心/通用 4.9 開發及測試。建議使用核心 4.9 以上版本來使用這項功能。
供應商 HAL 行為
Weaver HAL
weaver HAL 會提供固定數量的插槽,用於儲存使用者金鑰。DSU 會使用兩個額外的鍵盤插槽。如果 OEM 廠商有 weaver HAL,則需要有足夠的槽來放置通用系統映像檔 (GSI) 和主機映像檔。
守門人 HAL
Gatekeeper HAL 需要支援大型 USER_ID
值,因為 GSI 會將 UID 偏移 +1000000 至 HAL。
驗證開機程序
如果您想在鎖定狀態下支援開發人員 GSI 映像檔的啟動作業,而無須停用已驗證的啟動功能,請在 device/<device_name>/device.mk
檔案中加入下列行,以便納入開發人員 GSI 金鑰:
$(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)
復原保護
使用 DSU 時,下載的 Android 系統映像檔必須比裝置上的目前系統映像檔更新。方法是比較兩個系統映像檔 (Prop: com.android.build.system.security_patch ->
'2019-04-05'
) 的 Android 驗證開機程序 (AVB) AVB 屬性描述項中的安全性修補程式等級。
如果裝置未使用 AVB,請將目前系統映像檔的安全性修補程式等級,放入核心 cmdline 或 bootconfig 中,並使用 Bootloader:androidboot.system.security_patch=2019-04-05
。
硬體需求
啟動 DSU 執行個體時,系統會分配兩個暫存檔案:
- 用於儲存
GSI.img
的邏輯分區 (1~1.5 G) - 8 GB 空白
/data
分割區,做為執行 GSI 的沙箱
建議您在啟動 DSU 執行個體前,至少保留 10 GB 的可用空間。DSU 也支援從 SD 卡配置。當 SD 卡存在時,系統會將最高優先順序分配給 SD 卡。對於可能沒有足夠內部儲存空間的低功耗裝置,支援 SD 卡至關重要。如果有 SD 卡,請確認該 SD 卡未採用。DSU 不支援採用的 SD 卡。
可用的前端
您可以使用 adb
、OEM 應用程式或一鍵 DSU 載入器 (在 Android 11 以上版本中) 啟動 DSU。
使用 ADB 啟動 DSU
如要使用 ADB 啟動 DSU,請輸入下列指令:
$ simg2img out/target/product/.../system.img system.raw
$ gzip -c system.raw > system.raw.gz
$ adb push system.raw.gz /storage/emulated/0/Download
$ adb shell am start-activity \
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity \
-a android.os.image.action.START_INSTALL \
-d file:///storage/emulated/0/Download/system.raw.gz \
--el KEY_SYSTEM_SIZE $(du -b system.raw|cut -f1) \
--el KEY_USERDATA_SIZE 8589934592
使用應用程式啟動 DSU
DSU 的主要進入點是 android.os.image.DynamicSystemClient.java
API:
public class DynamicSystemClient {
...
...
/**
* Start installing DynamicSystem from URL with default userdata size.
*
* @param systemUrl A network URL or a file URL to system image.
* @param systemSize size of system image.
*/
public void start(String systemUrl, long systemSize) {
start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
}
您必須在裝置上將這個應用程式打包/預先安裝。由於 DynamicSystemClient
是系統 API,因此您無法使用一般 SDK API 建構應用程式,也無法在 Google Play 上發布應用程式。此應用程式的用途如下:
- 使用供應商定義的配置,擷取圖片清單和對應的網址。
- 將清單中的圖片與裝置進行比對,並顯示相容的圖片供使用者選取。
請按以下方式叫用
DynamicSystemClient.start
:DynamicSystemClient aot = new DynamicSystemClient(...) aot.start( ...URL of the selected image..., ...uncompressed size of the selected image...);
網址會指向經過 GZIP 壓縮的非稀疏系統映像檔,您可以使用下列指令建立此檔案:
$ simg2img ${OUT}/system.img ${OUT}/system.raw
$ gzip ${OUT}/system.raw
$ ls ${OUT}/system.raw.gz
檔案名稱應符合以下格式:
<android version>.<lunch name>.<user defined title>.raw.gz
例如:
o.aosp_taimen-userdebug.2018dev.raw.gz
p.aosp_taimen-userdebug.2018dev.raw.gz
一鍵 DSU 載入器
Android 11 推出了單鍵 DSU 載入器,這是開發人員設定中的前端。
圖 1. 啟動 DSU 載入器
開發人員按一下「DSU Loader」按鈕時,系統會從網路擷取預先設定的 DSU JSON 描述符,並在浮動選單中顯示所有適用的圖片。選取圖片即可開始 DSU 安裝作業,通知列會顯示進度。
圖 2. DSU 映像檔安裝進度
根據預設,DSU 載入器會載入包含 GSI 映像檔的 JSON 描述元。以下各節將說明如何製作 OEM 簽署的 DSU 套件,並從 DSU 載入器載入這些套件。
功能旗標
DSU 功能位於 settings_dynamic_android
功能旗標下方。使用 DSU 前,請確認已啟用對應的功能旗標。
圖 3. 啟用功能旗標
在執行使用者版本的裝置上,可能無法使用功能旗標 UI。在這種情況下,請改用 adb
指令:
$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1
供應商在 GCE 上主機系統映像檔 (選用)
Google Compute Engine (GCE) 儲存桶是系統映像檔的其中一個可能儲存位置。版本管理員會使用 GCP 儲存空間主控台新增/刪除/變更已發布的系統映像檔。
圖片必須設為公開存取,如下所示:
圖 4. GCE 中的公開存取權
如要將項目設為公開,請參閱 Google Cloud 說明文件。
ZIP 檔案中的多重分割 DSU
自 Android 11 起,DSU 可擁有多個分區。舉例來說,除了 system.img
之外,它還可以包含 product.img
。裝置啟動時,第一階段 init
會偵測已安裝的 DSU 分區,並在啟用已安裝的 DSU 時,暫時取代裝置端分區。DSU 套件可能包含裝置上沒有對應分割區的分割區。
圖 5. 含有多個分區的 DSU 程序
由 OEM 簽署的 DSU
為確保在裝置上執行的所有圖片均獲得裝置製造商授權,DSU 套件中的所有圖片都必須簽署。舉例來說,假設有一個 DSU 套件,其中包含兩個分區映像檔,如下所示:
dsu.zip {
- system.img
- product.img
}
system.img
和 product.img
都必須先由 OEM 金鑰簽署,才能放入 ZIP 檔案。一般做法是使用非對稱式演算法,例如 RSA,其中私密金鑰用於簽署套件,而公開金鑰則用於驗證。第一個階段的 RAM 磁碟必須包含配對公用金鑰,例如 /avb/*.avbpubkey
。如果裝置已採用 AVB,現有的簽署程序就足以處理。以下各節將說明簽署程序,並強調用於驗證 DSU 套件中圖像的 AVB 公開金鑰位置。
DSU JSON 描述元
DSU JSON 描述元會說明 DSU 套件。它支援兩種原始類型。首先,include
原始元素包含額外的 JSON 描述項,或將 DSU 載入器重新導向至新位置。例如:
{
"include": ["https://.../gsi-release/gsi-src.json"]
}
第二個是 image
原始元素,用於描述已發布的 DSU 套件。圖片基本元素內含多個屬性:
name
和details
屬性是對話方塊中顯示的字串,供使用者選取。cpu_api
、vndk
和os_version
屬性用於相容性檢查,下一節將說明這些屬性。選用的
pubkey
屬性會描述與用於簽署 DSU 套件的密鑰配對的公開金鑰。指定後,DSU 服務就能檢查裝置是否有用於驗證 DSU 套件的金鑰。這樣可避免安裝未知的 DSU 套件,例如將由 OEM-A 簽署的 DSU 安裝到 OEM-B 製造的裝置。選用的
tos
屬性會指向說明對應 DSU 套件服務條款的文字檔案。當開發人員選取具有服務條款屬性的 DSU 套件時,會開啟圖 6 所示的對話方塊,要求開發人員在安裝 DSU 套件前接受服務條款。圖 6. 服務條款對話方塊
以下是 GSI 的 DSU JSON 描述符,供您參考:
{
"images":[
{
"name":"GSI+GMS x86",
"os_version":"10",
"cpu_abi": "x86",
"details":"exp-QP1A.190711.020.C4-5928301",
"vndk":[
27,
28,
29
],
"pubkey":"",
"tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
"uri":"https://.../gsi/gsi_gms_x86-exp-QP1A.190711.020.C4-5928301.zip"
},
{
"name":"GSI+GMS ARM64",
"os_version":"10",
"cpu_abi": "arm64-v8a",
"details":"exp-QP1A.190711.020.C4-5928301",
"vndk":[
27,
28,
29
],
"pubkey":"",
"tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
"uri":"https://.../gsi/gsi_gms_arm64-exp-QP1A.190711.020.C4-5928301.zip"
},
{
"name":"GSI ARM64",
"os_version":"10",
"cpu_abi": "arm64-v8a",
"details":"exp-QP1A.190711.020.C4-5928301",
"vndk":[
27,
28,
29
],
"pubkey":"",
"uri":"https://.../gsi/aosp_arm64-exp-QP1A.190711.020.C4-5928301.zip"
},
{
"name":"GSI x86_64",
"os_version":"10",
"cpu_abi": "x86_64",
"details":"exp-QP1A.190711.020.C4-5928301",
"vndk":[
27,
28,
29
],
"pubkey":"",
"uri":"https://.../gsi/aosp_x86_64-exp-QP1A.190711.020.C4-5928301.zip"
}
]
}
相容性管理
以下是用於指定 DSU 套件與本機裝置相容性的幾個屬性:
cpu_api
是用來描述裝置架構的字串。這是必要屬性,會與ro.product.cpu.abi
系統屬性進行比較。兩者的值必須完全相符。os_version
是選用整數,用於指定 Android 版本。舉例來說,在 Android 10 中,os_version
為10
,在 Android 11 中,os_version
為11
。指定此屬性時,其值必須等於或大於ro.system.build.version.release
系統屬性。這項檢查可避免在 Android 11 供應商裝置上啟動 Android 10 GSI 映像檔,因為目前不支援這項操作。允許在 Android 10 裝置上啟動 Android 11 GSI 映像檔。vndk
是選用陣列,用於指定 DSU 套件中包含的所有 VNDK。指定後,DSU 載入器會檢查是否已納入從ro.vndk.version
系統屬性擷取的數字。
為安全起見,撤銷 DSU 金鑰
在極少數的情況下,如果用於簽署 DSU 映像檔的 RSA 金鑰組遭到入侵,請盡快更新 RAM 磁碟機,以移除遭到入侵的金鑰。除了更新啟動分區之外,您也可以使用 HTTPS 網址中的 DSU 金鑰撤銷清單 (金鑰黑名單) 封鎖遭到入侵的金鑰。
DSU 金鑰撤銷清單包含已撤銷的 AVB 公開金鑰清單。在 DSU 安裝期間,系統會使用撤銷清單驗證 DSU 映像檔中的公開金鑰。如果發現映像檔包含已撤銷的公開金鑰,DSU 安裝程序就會停止。
金鑰撤銷清單網址應為 HTTPS 網址,以確保安全強度,並在資源字串中指定:
frameworks/base/packages/DynamicSystemInstallationService/res/values/strings.xml@key_revocation_list_url
字串的值是 https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json
,這是 Google 發布的 GSI 金鑰的撤銷清單。這個資源字串可重疊及自訂,讓採用 DSU 功能的 OEM 提供及維護自己的鍵黑名單。這樣一來,原始設備製造商 (OEM) 就能在不更新裝置的 RAM 磁碟映像檔的情況下,封鎖特定公開金鑰。
撤銷清單的格式如下:
{
"entries":[
{
"public_key":"bf14e439d1acf231095c4109f94f00fc473148e6",
"status":"REVOKED",
"reason":"Key revocation test key"
},
{
"public_key":"d199b2f29f3dc224cca778a7544ea89470cbef46",
"status":"REVOKED",
"reason":"Key revocation test key"
}
]
}
public_key
是已撤銷金鑰的 SHA-1 摘要,格式請參閱「產生 AVB 公開金鑰」一節。status
表示金鑰的撤銷狀態。目前唯一支援的值是REVOKED
。reason
:(選用) 描述撤銷原因的字串。
DSU 程序
本節說明如何執行幾個 DSU 設定程序。
產生新的金鑰組
使用 openssl
指令,以 .pem
格式 (例如大小為 2048 位元) 產生 RSA 私密/公開金鑰組:
$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem
私密金鑰可能無法存取,且只能儲存在硬體安全性模組 (HSM) 中。在這種情況下,您可能會在產生金鑰後取得 x509 公開金鑰憑證。如要瞭解如何從 x509 憑證產生 AVB 公開金鑰,請參閱「將配對公開金鑰新增至 RAM 磁碟」一節。
如要將 x509 憑證轉換為 PEM 格式,請按照下列步驟操作:
$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem
如果憑證已是 PEM 檔案,請略過這個步驟。
將配對公開金鑰新增至 RAM 磁碟
oem_cert.avbpubkey
必須置於 /avb/*.avbpubkey
下方,才能驗證已簽署的 DSU 套件。首先,將 PEM 格式的公開金鑰轉換為 AVB 公開金鑰格式:
$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey
然後按照下列步驟,在第一階段的 RAM 磁碟中加入公開金鑰。
新增預先建構的模組來複製
avbpubkey
。例如,請新增device/<company>/<board>/oem_cert.avbpubkey
和device/<company>/<board>/avb/Android.mk
,並加入以下內容:include $(CLEAR_VARS) LOCAL_MODULE := oem_cert.avbpubkey LOCAL_MODULE_CLASS := ETC LOCAL_SRC_FILES := $(LOCAL_MODULE) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb else LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb endif include $(BUILD_PREBUILT)
讓 droidcore 目標依附於新增的
oem_cert.avbpubkey
:droidcore: oem_cert.avbpubkey
在 JSON 描述元中產生 AVB 公鑰屬性
oem_cert.avbpubkey
採用 AVB 公開金鑰二進位格式。請先使用 SHA-1 將其轉換為可讀格式,再放入 JSON 描述符:
$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20
這會是 JSON 描述元 pubkey
屬性的內容。
"images":[
{
...
"pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
...
},
簽署 DSU 套件
請使用下列任一方法簽署 DSU 套件:
方法 1:重複使用原始 AVB 簽署程序所產生的構件,製作 DSU 套件。另一種方法是從發布套件中擷取已簽署的圖片,然後使用擷取的圖片直接製作 ZIP 檔案。
方法 2:如果私密金鑰可用,請使用下列指令簽署 DSU 分割區。DSU 套件 (ZIP 檔案) 中的每個
img
都會個別簽署:$ key_len=$(openssl rsa -in oem_cert_pri.pem -text | grep Private-Key | sed -e 's/.*(\(.*\) bit.*/\1/') $ for partition in system product; do avbtool add_hashtree_footer \ --image ${OUT}/${partition}.img \ --partition_name ${partition} \ --algorithm SHA256_RSA${key_len} \ --key oem_cert_pri.pem done
如要進一步瞭解如何使用 avbtool
新增 add_hashtree_footer
,請參閱「使用 avbtool」一文。
在本機驗證 DSU 套件
建議您使用下列指令,驗證所有本機映像檔與配對公開金鑰是否相符:
for partition in system product; do
avbtool verify_image --image ${OUT}/${partition}.img --key oem_cert_pub.pem
done
預期的輸出結果如下所示:
Verifying image dsu/system.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/system.img
: Successfully verified sha1 hashtree of dsu/system.img for image of 898494464 bytes
Verifying image dsu/product.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/product.img
: Successfully verified sha1 hashtree of dsu/product.img for image of 905830400 bytes
製作 DSU 套件
以下範例會建立包含 system.img
和 product.img
的 DSU 套件:
dsu.zip {
- system.img
- product.img
}
兩個映像檔都簽署完畢後,請使用下列指令建立 ZIP 檔案:
$ mkdir -p dsu
$ cp ${OUT}/system.img dsu
$ cp ${OUT}/product.img dsu
$ cd dsu && zip ../dsu.zip *.img && cd -
自訂單鍵 DSU
根據預設,DSU 載入器會指向 GSI 映像檔的中繼資料,也就是 https://...google.com/.../gsi-src.json
。
OEM 廠商可以定義指向自有 JSON 描述符的 persist.sys.fflag.override.settings_dynamic_system.list
屬性,藉此覆寫清單。舉例來說,原始設備製造商 (OEM) 可能會提供 JSON 中繼資料,其中包含 GSI 和 OEM 專屬圖片,如下所示:
{
"include": ["https://dl.google.com/.../gsi-src.JSON"]
"images":[
{
"name":"OEM image",
"os_version":"10",
"cpu_abi": "arm64-v8a",
"details":"...",
"vndk":[
27,
28,
29
],
"spl":"...",
"pubkey":"",
"uri":"https://.../....zip"
},
}
原始設備製造商可以連結已發布的 DSU 中繼資料,如圖 7 所示。
圖 7. 鏈結已發布的 DSU 中繼資料