实现虚拟 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 必须大于或等于“各个更新组大小的总和 + 开销”,而后又必须大于或等于“各个分区大小的总和 + 开销”。

对于 Android 13 及更高版本,如需使用虚拟 A/B 启用压缩快照,请继承以下基本配置:

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

此配置会使用虚拟 A/B 启用用户空间快照,同时使用免运维压缩方法。然后,您可以将压缩方法配置为一种受支持的方法,即 zstdlz4。对于 Android 15,可以根据设备需求进一步自定义压缩。如需了解详情,请参阅微调压缩

PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD := lz4
PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 65536

对于 Android 12,如需使用虚拟 A/B 启用压缩快照,请继承以下基本配置:

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

XOR 压缩

对于升级到 Android 13 及更高版本的设备,XOR 压缩功能默认处于停用状态。如需启用 XOR 压缩,请将下面这行代码添加到设备的 .mk 文件中。

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

对于从 android_t_baseline.mk 继承的设备,XOR 压缩功能默认处于启用状态。

用户空间合并

在使用虚拟 A/B 的现代版本(Android T 及更高版本)中,快照合并过程完全在用户空间中进行。这项更改由 snapuserd 和 dm-user 实现。在发布时搭载 Android 13 及更高版本的设备上,用户空间合并默认处于启用状态;对于升级到 Android 13 及更高版本的旧版设备,可以使用以下方式设置此属性:

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

启动控件 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 的变更

metadata 分区的完整性对于启动流程至关重要,特别是在刚应用 OTA 更新后。因此,必须在 first_stage_init 装载 metadata 分区之前检查 metadata 分区。为此,请将 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 flag to user 内核补丁,以修复文件固定问题。此外,也请添加 f2fs: support aligned pinned file 内核补丁。

虚拟 A/B 依赖于内核版本 4.3 中添加的功能:snapshotsnapshot-merge 目标中的 overflow 状态位。所有搭载 Android 9 及更高版本的设备应该都已有内核版本 4.4 或更高版本。

如需启用压缩快照,支持的最低内核版本为 4.19。设置 CONFIG_DM_USER=mCONFIG_DM_USER=y。如果使用前者(模块),则模块必须在第一阶段 ramdisk 中加载。这可以通过在设备 makefile 中添加以下行来实现:

BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD := dm-user.ko

Fastboot 工具的变更

Android 11 对 fastboot 协议做出了以下变更:

  • getvar snapshot-update-status - 用于返回启动控件 HAL 传达给引导加载程序的值:
    • 如果状态为 MERGING,引导加载程序必须返回 merging
    • 如果状态为 SNAPSHOTTED,引导加载程序必须返回 snapshotted
    • 否则,引导加载程序必须返回 none
  • snapshot-update merge - 用于完成合并操作,并在必要时启动到恢复/fastbootd 映像。此命令仅在 snapshot-update-statusmerging 时有效,且仅在 fastbootd 中受支持。
  • snapshot-update cancel - 用于将启动控件 HAL 的合并状态设置为 CANCELLED。此命令在设备处于锁定状态时无效。
  • erasewipe - 在对 metadatauserdata 或者保存引导控件 HAL 的合并状态的分区执行 erasewipe 命令之前,应检查快照合并状态。如果状态为 MERGINGSNAPSHOTTED,设备应中止操作。
  • set_active - 在执行更改活动槽位的 set_active 命令之前,应检查快照合并状态。如果状态为 MERGING,设备应中止操作。如果相应状态为 SNAPSHOTTED,可以安全地更改槽位。

上述变更旨在防止意外地使设备不可启动,但它们可能会对自动化工具造成破坏。如果命令用作刷写所有分区的组件(例如运行 fastboot flashall),建议使用以下流程:

  1. 查询 getvar snapshot-update-status
  2. 如果快照更新状态为 mergingsnapshotted,请发出 snapshot-update cancel 命令。
  3. 继续执行刷写步骤。

降低存储空间要求

对于没有在 super 中分配完整的 A/B 存储空间且预计会在必要时使用 /data 的设备,强烈建议使用块映射工具。块映射工具可使 build 之间的块分配保持一致,从而减少对快照的非必要写入。如需了解相关内容,请参阅缩减 OTA 大小

OTA 压缩算法

可以针对不同的性能指标调整 OTA 软件包。Android 提供了几种受支持的压缩方法(lz4zstdnone),这些方法在安装时间、COW 空间使用、启动时间和快照合并时间之间存在一些权衡取舍。具有压缩功能的虚拟 AB 的默认启用选项是 lz4 compression method

微调压缩

您可以通过以下两种方法进一步自定义压缩算法:压缩级别(以牺牲速度为代价实现的压缩量)和压缩因子(可压缩窗口的最大大小)。压缩级别适用于 zstd 等某些算法,要更改级别,就需要在速度和压缩比率之间进行权衡。压缩因子用于描述 OTA 安装期间使用的最大压缩窗口大小。默认值设为 64k,但可以通过自定义 build 参数 PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR 进行替换。支持的压缩因子有 4k、8k、16k、32k、64k、128k 和 256k。

PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 65536

Pixel 8 Pro 上的增量 OTA

安装时间(无安装后阶段) COW 使用的空间 OTA 后启动时间 快照合并时间
lz4 18 分 15 秒 2.5GB 32.7 秒 98.6 秒
zstd 24 分 49 秒 2.05 GB 36.3 秒 133.2 秒
16 分 42 秒 4.76 GB 28.7 秒 76.6 秒

Pixel 8 Pro 上的完整 OTA

安装时间(无安装后阶段) COW 使用的空间 OTA 后启动时间 快照合并时间
lz4 15 分 11 秒 4.16 GB 17.6 秒 82.2 秒
zstd 16 分 19 秒 3.46 GB 21.0 秒 106.3 秒
13 分 33 秒 6.39 GB 18.5 秒 92.5 秒