動態系統更新 (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 秒、8 GB 使用者、867 MB 系統、檔案系統類型: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
武器 HAL 提供固定數量的運算單元,用於儲存使用者金鑰。DSU 會使用兩個額外的鍵盤插槽。如果 OEM 廠商有 weaver HAL,則需要有足夠的槽來放置通用系統映像檔 (GSI) 和主機映像檔。
守門人 HAL
總機人員 HAL 必須
支援大型 USER_ID
值,因為 GSI 會將 UID 偏移至 HAL
+1000000。
驗證啟動程序
如果您想在鎖定狀態下支援開發人員 GSI 映像檔的啟動作業,而無須停用已驗證的啟動功能,請在 device/<device_name>/device.mk
檔案中加入下列行,以便納入開發人員 GSI 金鑰:
$(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)
復原保護機制
使用 DSU 時,下載的 Android 系統映像檔必須比裝置上的目前系統映像檔更新。做法是比對安全性修補程式
的
Android 驗證開機程序
(AVB)
AVB 屬性描述元
兩個系統映像檔:Prop: com.android.build.system.security_patch ->
'2019-04-05'
對於未使用 AVB 的裝置,請加入目前系統的安全性修補程式等級
複製到核心 cmdline 或 bootconfig 中,並載入系統啟動載入程式:
androidboot.system.security_patch=2019-04-05
。
硬體需求
啟動 DSU 執行個體時,系統會分配兩個暫存檔案:
- 用來儲存
GSI.img
的邏輯分區 (1~1.5 G) - 8 GB 的空白
/data
分區,做為執行 GSI 的沙箱
建議您先保留至少 10 GB 的可用空間,再啟動 DSU 執行個體。DSU 也支援從 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 載入器」按鈕時,會擷取預先設定好的 DSU 載入器 網路上的 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 的系統映像檔 (選用)
系統映像檔的儲存位置之一是 Compute Engine (GCE) 值區。發布管理員會使用 GCP 儲存空間控制台 新增/刪除/變更發布的系統映像檔
圖片必須開放公開存取,如下所示:
圖 4. GCE 中的公開存取權
如要將項目設為公開,請參閱 Google Cloud 說明文件。
ZIP 檔案中的多重分割 DSU
自 Android 11 起,DSU 可擁有多個分區。例如,除了product.img
system.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 套件如果有指定的話,DSU 載入程式會檢查該數字是否 從ro.vndk.version
系統屬性擷取而來。
基於安全考量,撤銷 DSU 金鑰
在極少數的情況下,用於簽署 DSU 圖像的 RSA 金鑰組 則應盡快更新 ramdisk 程序,以移除 遭盜用的金鑰。除了更新啟動分區外, 透過 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) 就能在不更新裝置的 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 公開金鑰憑證。請參閱將配對 pubkey 新增至 ramdisk 一節。 一節,瞭解如何從 x509 憑證產生 AVB 公開金鑰。
如何將 x509 憑證轉換為 PEM 格式:
$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem
如果憑證已是 PEM 檔案,請略過這個步驟。
將配對 pubkey 新增至 ramdisk
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.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"
},
}
如圖 7 所示,原始設備製造商 (OEM) 可鏈結已發布的 DSU 中繼資料。
圖 7. 鏈結已發布的 DSU 中繼資料