实施虚拟 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 插槽不再处于超级状态。也就是说, BOARD_SUPER_PARTITION_SIZE必须大于或等于sum(size of update groups) + overhead ,而后者又必须大于或等于sum(size of partitions) + overhead

要使用虚拟 A/B 启用压缩快照,请改为继承以下基本配置:

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

引导控制 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:也支持对齐的固定文件内核补丁。

虚拟 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之前,根据设备状态完成恢复或回滚中的合并:

  • 如果新版本之前成功启动,请完成迁移。
  • 否则,回滚到旧插槽:
    • 对于动态分区,回滚到之前的状态。
    • 对于静态分区,将活动槽设置为旧槽。

如果设备解锁,bootloader 和fastbootd都可以擦除/data分区。虽然fastbootd可以强制迁移完成,但引导加载程序不能。引导加载程序不知道是否正在进行合并,也不知道/data中的哪些块构成操作系统分区。设备必须通过执行以下操作来防止用户在不知不觉中使设备无法操作(变砖):

  1. 实现引导控制 HAL,以便引导加载程序可以读取由setSnapshotMergeStatus()方法设置的值。
  2. 如果合并状态为MERGING ,或者合并状态为SNAPSHOTTED并且插槽已更改为新更新的插槽,则必须在引导加载程序中拒绝userdata用户数据、 metadata或存储合并状态的分区的请求。
  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()));
}

快速启动工具更改

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 大小下。