動態系統更新 (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:
- 109s,8G用戶,867M系統,檔案系統類型:F2FS:加密=aes-256-xts:aes-256-cts
- 104s,8G用戶,867M系統,檔案系統類型:F2FS:加密=ice
- 使用 ext4:
- 135s,8G用戶,867M系統,檔案系統類型:ext4:加密=aes-256-xts:aes-256-cts
如果在您的平台上需要更長的時間,您可能需要檢查掛載標誌是否包含任何使「同步」寫入的標誌,或者您可以明確指定「非同步」標誌以獲得更好的效能。
需要metadata
分割區(16 MB 或更大)來儲存與已安裝映像相關的資料。它必須在第一階段安裝期間安裝。
userdata
分割區必須使用F2FS或ext4檔案系統。使用 F2FS 時,請包含Android 通用核心中可用的所有 F2FS 相關補丁。
DSU 是使用 kernel/common 4.9 開發和測試的。建議使用核心 4.9 及更高版本來實現此功能。
供應商 HAL 行為
韋弗·哈爾
Weaver HAL 提供固定數量的插槽來儲存使用者金鑰。 DSU 消耗兩個額外的密鑰槽。如果 OEM 有 weaver HAL,則需要有足夠的插槽用於通用系統映像 (GSI) 和主機映像。
看門人哈爾
Gatekeeper 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.5G) - 8 GB 空
/data
分割區作為運行 GSI 的沙箱
我們建議在啟動 DSU 實例之前預留至少 10 GB 的可用空間。 DSU 也支援從 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 上。這個應用程式的目的是:
- 使用供應商定義的方案取得圖像清單和對應的 URL。
- 將清單中的影像與裝置進行匹配,並顯示相容的影像供使用者選擇。
像這樣呼叫
DynamicSystemClient.start
:DynamicSystemClient aot = new DynamicSystemClient(...) aot.start( ...URL of the selected image..., ...uncompressed size of the selected image...);
該 URL 指向經過 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 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 運算引擎 (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
在放入 ZIP 檔案之前都必須由 OEM 金鑰簽署。通常的做法是使用非對稱演算法,例如RSA,其中秘密金鑰用於對套件進行簽名,公鑰用於驗證它。第一階段 ramdisk 必須包含配對公鑰,例如/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 金鑰對遭到洩漏時,應盡快更新 ramdisk 以刪除洩漏的金鑰。除了更新啟動分割區之外,您還可以使用來自 HTTPS URL 的 DSU 金鑰撤銷清單(金鑰黑名單)來封鎖受損的金鑰。
DSU 金鑰撤銷清單包含已撤銷的 AVB 公鑰清單。在 DSU 安裝期間,DSU 映像內的公鑰將透過撤銷清單進行驗證。如果發現映像包含已撤銷的公鑰,則 DSU 安裝過程將停止。
金鑰撤銷清單URL應為HTTPS URL以確保安全強度,並在資源字串中指定:
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 提供了一種在不更新裝置的 ramdisk 映像的情況下阻止某些公鑰的方法。
撤銷清單的格式為:
{
"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
格式的 RSA 私密金鑰/公鑰對(例如,大小為 2048 位元):
$ 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 公鑰的說明,請參閱將配對公鑰新增至 ramdisk部分。
若要將 x509 憑證轉換為 PEM 格式:
$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem
如果證書已經是 PEM 文件,請跳過此步驟。
將配對公鑰新增至 ramdisk
oem_cert.avbpubkey
必須放在/avb/*.avbpubkey
下以驗證已簽署的 DSU 包。首先將PEM格式的公鑰轉換為AVB公鑰格式:
$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey
然後按照以下步驟將公鑰包含在第一階段 ramdisk 中。
新增預先建置模組以複製
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 pubkey 屬性
oem_cert.avbpubkey
採用 AVB 公鑰二進位格式。在將其放入 JSON 描述符之前,使用 SHA-1 使其可讀:
$ 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 可能會提供包含 GSI 以及 OEM 專有影像的 JSON 元數據,如下所示:
{
"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"
},
}
OEM 可以連結已發布的 DSU 元數據,如圖 7 所示。
圖 7.連結已發布的 DSU 元數據