实施 Virtual A/B

要在新设备上实现 Virtual A/B,或者要改造已发布的设备,您必须更改特定于设备的代码。

构建标志

使用 Virtual A/B 的设备必须配置为 A/B 设备,并且必须随动态分区一起启动

对于随 Virtual A/B 一起启动的设备,请将其设置为继承 Virtual A/B 设备基本配置

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

随 Virtual A/B 一起启动的设备,BOARD_SUPER_PARTITION_SIZE 的主板尺寸只需一半,因为 B 插槽不再位于超级分区中。也就是说,BOARD_SUPER_PARTITION_SIZE 必须大于或等于更新组大小总和 + 开销,而后者又必须大于或等于分区大小总和 + 开销

对于 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/vabc_features.mk)

这样可以在使用无操作压缩方法的同时,为 Virtual A/B 启用用户空间快照。然后,您可以将压缩方法配置为支持的方法之一,即 zstdlz4。对于 Android 15,可以进一步自定义压缩以满足设备需求。如需了解详情,请参阅微调压缩

PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD := lz4
PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 65536

对于 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)

XOR 压缩

对于升级到 Android 13 及更高版本的设备,默认情况下未启用XOR 压缩功能。要启用 XOR 压缩,请将以下代码添加到设备的 .mk 文件中。

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

对于从 android_t_baseline.mk 继承的设备,默认情况下会启用 XOR 压缩。

用户空间合并

在 Virtual A/B 的现代版本(Android T 及更高版本)中,快照合并过程完全在用户空间中进行。snapuserd 和 dm-user 使此更改成为可能。随 Android 13 及更高版本一起启动的设备默认启用用户空间合并,对于旧设备升级,可以使用以下属性进行设置

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

启动控制 HAL

启动控制 HAL 为 OTA 客户端提供了一个用于控制启动插槽的接口。Virtual A/B 需要启动控制 HAL 的次要版本升级,因为需要额外的 API 来确保在刷写或恢复出厂设置期间保护引导加载程序。请参阅 IBootControl.haltypes.hal 以获取最新版本的 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:将 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

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。当设备锁定时,此命令无效。
  • erasewipe — 对 metadatauserdata 或保存启动控制 HAL 合并状态的分区执行 erasewipe 操作应检查快照合并状态。如果状态为 MERGINGSNAPSHOTTED,则设备应中止该操作。
  • set_active — 更改活动插槽的 set_active 命令应检查快照合并状态。如果状态为 MERGING,则设备应中止该操作。可以在 SNAPSHOTTED 状态下安全地更改插槽。

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

  1. 查询 getvar snapshot-update-status
  2. 如果 mergingsnapshotted,则发出 snapshot-update cancel
  3. 继续执行刷写步骤。

减少存储要求

强烈建议在超级分区中未分配完整 A/B 存储空间,并且希望根据需要使用 /data 的设备使用块映射工具。块映射工具可使构建之间的块分配保持一致,从而减少对快照的不必要写入。这在减小 OTA 大小下进行了说明。

OTA 压缩算法

可以针对不同的性能指标调整 OTA 软件包。Android 提供了多种受支持的压缩方法(lz4zstdnone),这些方法在安装时间、COW 空间使用量、启动时间和快照合并时间之间进行权衡。为启用压缩的虚拟 ab 启用的默认选项是 lz4 压缩方法

微调压缩

可以通过两种方法进一步自定义压缩算法:(压缩级别)(以速度为代价实现的压缩量)和(压缩因子)(最大可压缩窗口大小)。压缩级别适用于某些算法,例如 zstd,更改级别会在速度和压缩率之间进行权衡。压缩因子描述 OTA 安装期间使用的最大压缩窗口大小。默认设置为 64k,但可以通过自定义构建参数 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.5 GB 32.7 秒 98.6 秒
zstd 24 分 49 秒 2.05 GB 36.3 秒 133.2 秒
none 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 秒
none 13 分 33 秒 6.39 GB 18.5 秒 92.5 秒