導入 A/B 更新

想要導入 A/B 系統更新的 OEM 和 SoC 供應商,必須確保其引導程式導入 boot_control HAL,並將正確的參數傳遞至核心。

實作啟動控制 HAL

支援 A/B 的引導程式必須在 hardware/libhardware/include/hardware/boot_control.h 實作 boot_control HAL。您可以使用 system/extras/bootctl 公用程式和 system/extras/tests/bootloader/ 測試導入作業。

您也必須實作下方所示的狀態機器:

圖 1. 系統啟動載入程式狀態機器人

設定核心

如要實作 A/B 系統更新,請按照下列步驟操作:

  1. 視需要選取下列核心修補程式系列:
  2. 確認核心指令列引數包含下列額外引數:
    skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
    ... 其中 <public-key-id> 值是用於驗證 Verity 資料表簽名的公開金鑰 ID (詳情請參閱 dm-verity)。
  3. 將含有公開金鑰的 .X509 憑證新增至系統鑰匙圈:
    1. 將以 .der 格式編寫的 .X509 憑證複製到 kernel 目錄的根目錄。如果 .X509 憑證的格式為 .pem 檔案,請使用下列 openssl 指令,將格式從 .pem 轉換為 .der
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. 建構 zImage,將憑證納入系統鑰匙圈。如要確認,請檢查 procfs 項目 (需要啟用 KEYS_CONFIG_DEBUG_PROC_KEYS):
      angler:/# cat /proc/keys
      
      1c8a217e I------     1 perm 1f010000     0     0 asymmetri
      Android: 7e4333f9bba00adfe0ede979e28ed1920492b40f: X509.RSA 0492b40f []
      2d454e3e I------     1 perm 1f030000     0     0 keyring
      .system_keyring: 1/4
      成功納入 .X509 憑證,表示系統鑰匙圈中含有公開金鑰 (醒目顯示表示公開金鑰 ID)。
    3. 將空格替換為 #,並在核心指令列中以 <public-key-id> 的形式傳遞。例如,傳遞 Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f 取代 <public-key-id>

設定建構變數

支援 A/B 的引導程序必須符合下列建構變數條件:

必須為 A/B 測試目標定義
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
      boot \
      system \
      vendor
    以及透過 update_engine 更新的其他分區 (無線電、Bootloader 等)
  • PRODUCT_PACKAGES += \
      update_engine \
      update_verifier
如需範例,請參閱 /device/google/marlin/+/android-7.1.0_r1/device-common.mk。您可以選擇在編譯一節中所述的 dex2oat 步驟後 (但在重新啟動前) 執行。
強烈建議用於 A/B 目標
  • 定義 TARGET_NO_RECOVERY := true
  • 定義 BOARD_USES_RECOVERY_AS_BOOT := true
  • 請勿定義 BOARD_RECOVERYIMAGE_PARTITION_SIZE
無法為 A/B 測試目標定義
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
偵錯版本可選 PRODUCT_PACKAGES_DEBUG += update_engine_client

設定分區 (插槽)

A/B 裝置不需要復原分區或快取分區,因為 Android 不再使用這些分區。資料分區現在用於下載的 OTA 套件,而復原映像檔程式碼則位於啟動分區。所有進行 A/B 測試的分區都應命名如下 (運算單元一律命名為 ab 等):boot_aboot_bsystem_asystem_bvendor_avendor_b

快取

對於非 A/B 更新,快取分區用於儲存下載的 OTA 套件,並在套用更新時暫存區塊。我們從未找到適合的方式來設定快取分割區的大小:所需的大小取決於您要套用的更新項目。最糟糕的情況是快取分區的大小與系統映像檔相同。使用 A/B 更新時,您不需要儲存區塊 (因為您一律會寫入目前未使用的分區),而使用串流 A/B 時,您也不需要先下載整個 OTA 套件再套用。

復原

復原 RAM 磁碟現在已包含在 boot.img 檔案中。進入復原模式時,系統啟動載入程式「無法」skip_initramfs 選項放入核心指令列。

對於非 A/B 更新,復原分割區會包含用於套用更新的程式碼。系統會在一般啟動的系統映像檔中執行 update_engine,以套用 A/B 更新。復原模式仍可用於執行原廠資料重設作業,以及側載更新套件 (這就是「復原」這個名稱的由來)。復原模式的程式碼和資料會儲存在 RAM 磁碟中的一般開機分割區;為了開機至系統映像檔,啟動載入程式會告知核心略過 RAM 磁碟 (否則裝置會開機至復原模式)。復原模式很小 (大部分內容都已位於啟動分區),因此啟動分區不會增加。

Fstab

slotselect 引數必須位於 A/B 測試版區塊的行中。例如:

<path-to-block-device>/vendor  /vendor  ext4  ro
wait,verify=<path-to-block-device>/metadata,slotselect

請勿將分割區命名為 vendor。而是會選取分區 vendor_avendor_b,並掛載至 /vendor 掛載點。

核心插槽引數

您應透過特定裝置樹狀結構 (DT) 節點 (/firmware/android/slot_suffix) 或 androidboot.slot_suffix 核心指令列或 bootconfig 引數,傳遞目前的插槽後置字串。

根據預設,Fastboot 會在 A/B 裝置上刷新目前的插槽。如果更新套件也包含其他非目前插槽的映像檔,fastboot 也會閃記這些映像檔。可用的選項包括:

  • --slot SLOT。覆寫預設行為,並提示 fastboot 將傳入做為引數的插槽刷新。
  • --set-active [SLOT]。將時間間隔設為有效。如果未指定選用引數,則會將目前的空格設為有效。
  • fastboot --help:取得指令詳細資料。

如果引導程序實作快速啟動功能,則應支援 set_active <slot> 指令,將目前的有效插槽設為指定的插槽 (這也必須清除該插槽的無法啟動標記,並將重試次數重設為預設值)。引導程式也應支援下列變數:

  • has-slot:<partition-base-name-without-suffix>。如果指定的分割區支援分頁,就會傳回「是」,否則傳回「否」。
  • current-slot。傳回下次將從中啟動的運算單元後置字串。
  • slot-count。傳回整數,代表可用時段的數量。目前支援兩個時段,因此這個值為 2
  • slot-successful:<slot-suffix>。如果已將指定的插槽標示為已成功啟動,則會傳回「是」;否則會傳回「否」。
  • slot-unbootable:<slot-suffix>。如果指定的插槽標示為無法啟動,則會傳回「是」;否則傳回「否」。
  • slot-retry-count:<slot-suffix>。嘗試啟動指定時段時,剩餘的重試次數。

如要查看所有變數,請執行 fastboot getvar all

產生 OTA 套件

OTA 套件工具會遵循與非 A/B 裝置相同的指令。target_files.zip 檔案必須透過定義 A/B 目標的建構變數來產生。OTA 套件工具會自動辨識並產生 A/B 更新程式所需的格式套件。

例如:

  • 如要產生完整 OTA,請按照下列步驟操作:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • 如要產生增量 OTA,請按照下列步驟操作:
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

設定分區

update_engine 可更新同一個磁碟中定義的任何一組 A/B 分割區。一組分區具有共同的前置字串 (例如 systemboot) 和每個插槽的後置字串 (例如 _a)。負載產生器定義更新的分區清單是由 AB_OTA_PARTITIONS 產生變數設定。

舉例來說,如果包含一組分區 bootloader_abooloader_b (_a_b 是插槽後置字元),您可以在產品或電路板設定中指定下列項目,以更新這些分區:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

update_engine 更新的所有分區不得由系統的其他部分修改。在增量或差異更新期間,系統會使用目前版位的二進位資料,產生新版位中的資料。任何修改都可能導致新版時段資料在更新期間驗證失敗,進而導致更新失敗。

設定安裝後程序

您可以使用一組鍵/值組合,為每個更新的分區設定不同的安裝後步驟。如要在新映像檔中執行位於 /system/usr/bin/postinst 的程式,請指定相對於系統分區中檔案系統根目錄的路徑。

舉例來說,usr/bin/postinstsystem/usr/bin/postinst (如果未使用 RAM 磁碟)。此外,請指定要傳遞至 mount(2) 系統呼叫的檔案系統類型。將下列內容新增至產品或裝置 .mk 檔案 (如適用):

AB_OTA_POSTINSTALL_CONFIG += \
  RUN_POSTINSTALL_system=true \
  POSTINSTALL_PATH_system=usr/bin/postinst \
  FILESYSTEM_TYPE_system=ext4

編譯應用程式

在重新啟動至新的系統映像檔前,應用程式可在背景編譯。如要在背景編譯應用程式,請在產品的裝置設定 (位於產品的 device.mk 中) 中新增下列內容:

  1. 在建構中加入原生元件,確保編譯指令碼和二進位檔會編譯並納入系統映像檔。
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. 將編譯指令碼連結至 update_engine,以便做為安裝後步驟執行。
      # A/B OTA dexopt update_engine hookup
      AB_OTA_POSTINSTALL_CONFIG += \
        RUN_POSTINSTALL_system=true \
        POSTINSTALL_PATH_system=system/bin/otapreopt_script \
        FILESYSTEM_TYPE_system=ext4 \
        POSTINSTALL_OPTIONAL_system=true
    

如要瞭解如何在未使用的第二個系統分區中安裝預選檔案,請參閱「首次啟動時安裝 DEX_PREOPT 檔案」。