實施虛擬 A/B

若要在新設備上實現虛擬 A/B,或改造已啟動的設備,您必須更改設備特定的代碼。

建立標誌

使用虛擬 A/B 的設備必須配置為 A/B 設備,並且必須使用動態分區啟動

對於使用虛擬 A/B 啟動的設備,將它們設定為繼承虛擬 A/B 設備基本配置:

$(call inherit-product, \
    $(SRC_TARGET_DIR)/product/virtual_ab_ota.mk)

使用虛擬 A/B 啟動的裝置只需要BOARD_SUPER_PARTITION_SIZE一半的板大小,因為 B 插槽不再位於 super 中。也就是說, BOARD_SUPER_PARTITION_SIZE必須大於或等於sum(更新群組的大小) + 開銷,而後者必須大於或等於sum(分割區的大小) + 開銷

對於 Android 13 及更高版本,若要使用 Virtual A/B 啟用壓縮快照,請繼承下列基本配置:

$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_ramdisk.mk)
$(call inherit-product, \
    $(SRC_TARGET_DIR)/product/virtual_ab_ota/android_t_baseline.mk)

這使得使用者空間快照能夠在使用無操作壓縮方法的同時使用 Virtual A/B。然後,您可以將壓縮方法配置為支援的方法之一: gzzstdlz4

PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD := lz4

對於 Android 12,若要使用 Virtual A/B 啟用壓縮快照,請繼承下列基本配置:

$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_ramdisk.mk)
$(call inherit-product, \
    $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk)

異或壓縮

對於升級到 Android 13 及更高版本的設備,預設不啟用XOR 壓縮功能。若要啟用 XOR 壓縮,請將以下內容新增至裝置的.mk檔案。

PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true

對於從android_t_baseline.mk繼承的設備,預設啟用 XOR 壓縮。

使用者空間合併

對於升級到 Android 13 及更高版本的設備,預設情況下不啟用設備映射器分層中所述的用戶空間合併過程。若要啟用使用者空間合併,請將以下行新增至裝置的.mk檔案:

PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true

在使用 13 及更高版本啟動的裝置上預設啟用用戶空間合併。

啟動控制HAL

啟動控制 HAL為 OTA 用戶端提供控制啟動槽的介面。虛擬 A/B 需要對引導控制 HAL 進行次要版本升級,因為需要額外的 API 來確保引導程式在刷新/恢復原廠設定期間受到保護。有關 HAL 定義的最新版本,請參閱IBootControl.haltypes.hal

// hardware/interfaces/boot/1.1/types.hal
enum MergeStatus : uint8_t {
    NONE, UNKNOWN, SNAPSHOTTED, MERGING, CANCELLED };

// hardware/interfaces/boot/1.1/IBootControl.hal
package android.hardware.boot@1.1;
interface IBootControl extends @1.0::IBootControl {
    setSnapshotMergeStatus(MergeStatus status)
        generates (bool success);
    getSnapshotMergeStatus()
        generates (MergeStatus status);
}
// Recommended implementation

Return<bool> BootControl::setSnapshotMergeStatus(MergeStatus v) {
    // Write value to persistent storage
    // e.g. misc partition (using libbootloader_message)
    // bootloader rejects wipe when status is SNAPSHOTTED
    // or MERGING
}

Fstab 更改

元資料分割區的完整性對於啟動過程至關重要,尤其是在應用 OTA 更新之後。因此,在first_stage_init掛載元資料分區之前必須對其進行檢查。為了確保發生這種情況,請將check fs_mgr 標誌新增至/metadata的條目。下面提供了一個範例:

/dev/block/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard,sync wait,formattable,first_stage_mount,check

內核要求

若要啟用快照,請將CONFIG_DM_SNAPSHOT設定為true

對於使用 F2FS 的設備,請將f2fs: export FS_NOCOW_FL 標誌包含到使用者核心補丁中以修復檔案固定。包括f2fs:也支援對齊固定檔案核心補丁。

Virtual A/B 依賴核心版本 4.3 中新增的功能: snapshotsnapshot-merge目標中的溢位狀態位元。所有搭載 Android 9 及更高版本的裝置都應該已經具有核心版本 4.4 或更高版本。

若要啟用壓縮快照,支援的最低核心版本為 4.19。設定CONFIG_DM_USER=mCONFIG_DM_USER=y 。如果使用前者(模組),則該模組必須載入到第一階段 ramdisk 中。這可以透過將以下行新增至裝置 Makefile 來實現:

BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD := dm-user.ko

升級到 Android 11 的裝置上的改造

升級到 Android 11 時,使用動態分割區啟動的裝置可以選擇改造虛擬 A/B。更新過程與使用虛擬 A/B 啟動的裝置基本上相同,但有一些細微差別:

  • COW 檔案的位置- 對於啟動設備,OTA 用戶端在使用/data中的空間之前先使用超級分割區中所有可用的空白空間。對於改造設備,超級分割區中始終有足夠的空間,因此永遠不會在/data上建立 COW 檔案。

  • 建造時功能標誌- 對於改造虛擬 A/B 的設備, PRODUCT_VIRTUAL_AB_OTAPRODUCT_VIRTUAL_AB_OTA_RETROFIT都設為true ,如下:

    (call inherit-product, \
        (SRC_TARGET_DIR)/product/virtual_ab_ota_retrofit.mk)
    
  • 超級分割區大小— 使用虛擬 A/B 啟動的裝置可以將BOARD_SUPER_PARTITION_SIZE減半,因為 B 插槽不在超級分割區中。改造虛擬 A/B 的裝置保留舊的超級分割區大小,因此BOARD_SUPER_PARTITION_SIZE大於或等於2 * ​​sum(更新群組的大小) + 開銷,而開銷又大於或等於2 * ​​sum(分割區的大小) + 開銷

引導程式更改

在更新的合併步驟中, /data保存 Android 作業系統的唯一完整實例。遷移開始後,本機systemvendorproduct分區在複製完成前都是不完整的。如果在此過程中透過恢復或透過系統設定對話方塊將裝置重設為原廠設置,則裝置將無法啟動。

在擦除/data之前,根據裝置狀態完成復原或回滾中的合併:

  • 如果新版本之前已成功啟動,請完成遷移。
  • 否則,回滾到舊槽:
    • 對於動態分割區,回滾到之前的狀態。
    • 對於靜態分割區,將活動插槽設定為舊插槽。

如果裝置解鎖,引導程式和fastbootd都可以擦除/data分區。雖然fastbootd可以強制遷移完成,但引導程式卻不能。引導程式不知道合併是否正在進行,也不知道/data中的哪些區塊構成作業系統分割區。設備必須透過執行以下操作來防止使用者在不知不覺中使設備無法運作(變磚):

  1. 實作引導控制HAL,以便引導程式可以讀取setSnapshotMergeStatus()方法設定的值。
  2. 如果合併狀態為MERGING ,或合併狀態為SNAPSHOTTED且插槽已變更為新更新的插槽,則必須在引導程式中拒絕擦除userdatametadata或儲存合併狀態的分割區的請求。
  3. 實作fastboot snapshot-update cancel命令,以便使用者可以向引導程式發出訊號,表明他們想要繞過此保護機制。
  4. 修改自訂刷新工具或腳本,以在刷新整個裝置時發出fastboot snapshot-update cancel 。這是安全的,因為刷新整個裝置會刪除 OTA。工具可以透過實作fastboot getvar snapshot-update-status在執行時檢測此指令。此命令有助於區分錯誤情況。

例子

struct VirtualAbState {
    uint8_t StructVersion;
    uint8_t MergeStatus;
    uint8_t SourceSlot;
};

bool ShouldPreventUserdataWipe() {
    VirtualAbState state;
    if (!ReadVirtualAbState(&state)) ...
    return state.MergeStatus == MergeStatus::MERGING ||
           (state.MergeStatus == MergeStatus::SNAPSHOTTED &&
            state.SourceSlot != CurrentSlot()));
}

Fastboot 工具更改

Android 11 對 fastboot 協定進行了以下更改:

  • getvar snapshot-update-status — 傳回引導控制 HAL 與引導程式通訊的值:
    • 如果狀態為MERGING ,則引導程式必須傳回merging
    • 如果狀態為SNAPSHOTTED ,則引導程式必須傳回snapshotted
    • 否則,引導程式必須傳回none
  • snapshot-update merge — 完成合併操作,如有必要,引導至 recovery/fastbootd。此指令僅在snapshot-update-statusmerging時有效,且僅在 fastbootd 中支援。
  • snapshot-update cancel — 將啟動控制 HAL 的合併狀態設定為CANCELLED 。當設備鎖定時,該命令無效。
  • erasewipeerasewipe metadatauserdata或儲存啟動控制 HAL 合併狀態的分割區應檢查快照合併狀態。若狀態為MERGINGSNAPSHOTTED ,設備應中止操作。
  • set_active — 更改活動插槽的set_active指令應檢查快照合併狀態。如果狀態為MERGING ,設備應中止操作。可以在SNAPSHOTTED狀態下安全地更改插槽。

這些變更旨在防止意外導致設備無法啟動,但它們可能會對自動化工具造成破壞。當指令用作刷新所有分割區的元件時,例如執行fastboot flashall ,建議使用下列流程:

  1. 查詢getvar snapshot-update-status
  2. 如果mergingsnapshotted ,請發出snapshot-update cancel
  3. 繼續執行閃爍步驟。

減少儲存需求

強烈建議未在 super 中指派完整 A/B 儲存並希望根據需要使用/data使用區塊對映工具。區塊映射工具使建置之間的區塊分配保持一致,從而減少對快照的不必要寫入。這記錄在「減少 OTA 大小」下。

OTA壓縮法

OTA 套件可以針對不同的效能指標進行調整。 Android 提供了多種支援的壓縮方法( gzlz4zstdnone ),這些方法在安裝時間、COW 空間使用、啟動時間和快照合併時間之間進行權衡。為具有壓縮功能的虛擬 ab 啟用的預設選項是gz compression method 。 (注意:壓縮方法之間的相對性能因 CPU 速度和存儲吞吐量而異,而這可能會根據設備而變化。下面生成的所有 OTA 包均禁用 PostInstall,這會稍微減慢啟動時間。完整 OTA 的動態分割區總大小未壓縮時為4.81 GB )。

Pixel 6 Pro 上的增量 OTA

安裝時間(不含安裝後階段) COW空間使用情況OTA 後啟動時間快照合併時間
廣州24分鐘1.18GB 40.2秒45.5秒
lz4 13分鐘1.49GB 37.4秒37.1秒
沒有任何13分鐘2.90GB 37.6秒40.7秒

Pixel 6 Pro 上的完整 OTA

安裝時間(不含安裝後階段) COW 空間使用情況OTA 後啟動時間快照合併時間
廣州23分鐘2.79GB 24.9秒41.7秒
lz4 12分鐘3.46GB 20.0秒25.3秒
沒有任何10分鐘4.85GB 20.6秒29.8秒