動態系統更新

動態系統更新 (DSU) 可讓您建立具有 ,即可從網際網路下載並嘗試執行,而不會有損毀的風險 目前的系統映像檔本文說明如何支援 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 秒,8G 使用者,867M,檔案系統類型: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 時 包含所有 F2FS 相關修補程式 Android 通用核心

DSU 是以核心/常見 4.9 進行開發與測試。建議使用 核心 4.9 以上版本。

供應商 HAL 行為

Weaver HAL

武器 HAL 提供固定數量的運算單元,用於儲存使用者金鑰。DSU 會耗用兩個額外的主要運算單元如果 OEM 有武器 HAL 足以容納一般系統映像檔 (GSI) 和主機映像檔。

總機 HAL

總機人員 HAL 必須 支援大型 USER_ID 值,因為 GSI 會將 UID 偏移至 HAL +1000000。

驗證啟動程序

如要支援啟動開發人員 GSI 映像檔 處於鎖定狀態,而未停用 驗證開機程序,並加入開發人員 GSI 金鑰。 新增至 device/<device_name>/device.mk 檔案:

$(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 卡時,請勿採用。DSU 不支援 已採用 SD 卡

可用的前端

您可以使用 adb、OEM 應用程式或單鍵 DSU 載入器 (位於 Android 11 以上版本)。

使用 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 上發布。本應用程式的用途如下:

  1. 透過供應商定義的配置,擷取圖片清單和對應的網址。
  2. 將清單中的圖片與裝置進行比對,並顯示相容的圖片 供使用者選取
  3. 按照下列方式叫用 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 載入器, 是開發人員設定中的前端。

啟動 DSU 載入器

圖 1. 啟動 DSU 載入器

開發人員按一下「DSU 載入器」按鈕時,會擷取預先設定好的 DSU 載入器 網路上的 DSU JSON 描述元,並在 浮動式選單選取映像檔,開始 DSU 安裝作業和進度 會顯示在通知列中。

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 儲存空間控制台 新增/刪除/變更發布的系統映像檔

圖片必須開放公開存取,如下所示:

GCE 中的公開存取權

圖 4. GCE 中的公開存取權

如需公開項目的相關程序,請參閱 Google Cloud 說明文件

ZIP 檔案中多個分區 DSU

從 Android 11 開始,DSU 可以有多個 例如,除了product.img system.img。裝置啟動時,第一個階段 init 會偵測 安裝 DSU 分區,並暫時取代裝置端的分區, 已安裝的 DSU 已啟用DSU 套件包含的分區可能含有 裝置上都有對應的分區

包含多個分區的 DSU 程序

圖 5. 包含多個分區的 DSU 程序

原始設備製造商 (OEM) 簽署的 DSU

確保裝置上執行的所有映像檔都經過裝置授權 製造商,就必須簽署 DSU 套件中的所有圖片。例如: 假設有一個 DSU 套件內含兩個分區映像檔,如下所示:

dsu.zip {
    - system.img
    - product.img
}

system.imgproduct.img 必須先使用 OEM 金鑰簽署 儲存至 ZIP 檔案常見做法是使用非對稱式 演算法,以 RSA 為例,其密鑰是簽署套件, 公開金鑰的用途是驗證金鑰。第一個階段 ramdisk 必須包含 公開金鑰,例如 /avb/*.avbpubkey。如果裝置已採用 AVB, 現有的簽署程序就能滿足這些需求以下各節將說明 並標明用於設定檔的 AVB pubkey 位置 驗證 DSU 套件中的映像檔

DSU JSON 描述元

DSU JSON 描述元用於描述 DSU 套件。支援兩個原始物件。 首先,include 基本包含額外的 JSON 描述元或重新導向 移到新位置的 DSU 載入器例如:

{
    "include": ["https://.../gsi-release/gsi-src.json"]
}

第二,image 基元用於說明已發布的 DSU 套件。內部實景 圖像基元有幾個屬性:

  • namedetails 屬性是顯示在這裡的字串 供使用者選取的對話方塊

  • cpu_apivndkos_version 屬性的用途包括 我們將在下一節說明相容性檢查。

  • 選用的 pubkey 屬性用於說明公開資訊 金鑰,與用於簽署 DSU 套件的密鑰配對。 指定後,DSU 服務就能檢查裝置是否具有金鑰 用於驗證 DSU 套件這可以避免安裝無法辨識的 DSU 套件,例如安裝 OEM-A 簽署的 DSU 原始設備製造商 (OEM)

  • 選用的 tos 屬性指向描述文字檔 服務條款。開發人員 會選取已指定服務條款屬性的 DSU 套件, 對話方塊隨即會開啟,要求開發人員接受條款 才能安裝 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_version10, Android 11 的 os_version11。 當此情況 屬性時,其必須等於或大於 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) 封鎖 而非更新裝置的 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 pubkey 專區。
  • 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 公用金鑰憑證 。請參閱將配對 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

接著,請按照下列步驟,在第一個階段 ramdisk 中加入公開金鑰。

  1. 新增預先建構的模組來複製 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)
    
  2. 將 droidcore 目標設為依附於新增的 oem_cert.avbpubkey

    droidcore: oem_cert.avbpubkey
    

在 JSON 描述元中產生 AVB pubkey 屬性

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.imgproduct.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) 可定義 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 中繼資料。

鏈結已發布的 DSU 中繼資料

圖 7. 鏈結已發布的 DSU 中繼資料