導入 A/B 更新

如要實作 A/B 系統更新,OEM 和 SoC 供應商必須確保其開機載入程式實作 boot_control HAL,並將正確參數傳遞至核心。

實作啟動控制 HAL

支援 A/B 的開機載入程式必須在 boot_control 實作 hardware/libhardware/include/hardware/boot_control.h 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 更新的其他分割區 (無線電、開機載入程式等)
  • 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

設定分區 (時段)

Android 不再使用復原或快取分區,因此 A/B 裝置不需要這些分區。下載的 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 更新,復原磁碟分割區會包含用於套用更新的程式碼。A/B 更新是由update_engine在一般啟動的系統映像檔中執行。系統仍會使用復原模式來執行恢復原廠設定和側載更新套件 (「復原」這個名稱就是由此而來)。復原模式的程式碼和資料會儲存在 ramdisk 的一般開機分割區中;如要啟動系統映像檔,啟動載入程式會告知核心略過 ramdisk (否則裝置會啟動復原模式)。復原模式很小 (且大部分已位於開機磁碟分割區),因此開機磁碟分割區不會變大。

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 掛接點。

核心 slot 引數

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

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

  • --slot SLOT。覆寫預設行為,並提示 Fastboot 刷寫以引數形式傳入的插槽。
  • --set-active [SLOT]。將時段設為有效。如未指定任何選用引數,則目前時段會設為有效。
  • fastboot --help。查看指令詳細資料。

如果開機載入程式實作了 Fastboot,就應支援 set_active <slot> 指令,將目前作用中的插槽設為指定插槽 (這也必須清除該插槽的無法開機標記,並將重試次數重設為預設值)。開機載入程式也應支援下列變數:

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

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

產生 OTA 套件

OTA 套件工具的指令與非 A/B 裝置的指令相同。您必須先定義 A/B 目標的建構變數,才能產生 target_files.zip 檔案。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 make 變數設定。

舉例來說,如果包含一對分區 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 檔案」。