To implement virtual A/B on a new device, or to retrofit a launched device, you need to make changes to device-specific code.
Build flags
Devices that use virtual A/B must be configured as an A/B device and must launch with dynamic partitions.
For devices launching with virtual A/B, set them to inherit the virtual A/B device base configuration:
$(call inherit-product, \
$(SRC_TARGET_DIR)/product/virtual_ab_ota.mk)
Devices launching with virtual A/B need only half as much board size for
BOARD_SUPER_PARTITION_SIZE
because B slots are no longer in super. That is,
BOARD_SUPER_PARTITION_SIZE
should be greater than or equal to
sum(size of update groups) + overhead, which, in turn, should be greater
than or equal to sum(size of partitions) + overhead.
Boot control HAL
The boot control HAL provides an interface for OTA clients to controls boot slots. Virtual A/B requires a minor version upgrade of the boot control HAL because additional APIs are needed to tell bootloader to take care during flashing/factory reset. See IBootControl.hal and types.hal for the latest version of the HAL definition.
// 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 changes
The integrity of the metadata partition is essential to the boot process,
especially right after an OTA update is applied. So, the metadata partition
should be checked before first_stage_init
mounts it. To do so, add the
check
fs_mgr flag to
the entry for /metadata
. For example:
/dev/block/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard,sync wait,formattable,first_stage_mount,check
Kernel requirements
To enable snapshotting, set CONFIG_DM_SNAPSHOT
to true
.
For devices using F2FS, include the f2fs: export FS_NOCOW_FL flag to user kernel patch to fix file pinning. Additionally, include the f2fs: support aligned pinned file kernel patch as well.
Virtual A/B relies on features added in kernel version 4.3: the overflow
status bit in the snapshot
and snapshot-merge
targets. All devices
launching with Android 9 and later should already have kernel version 4.4
or later.
Retrofitting on devices upgrading to Android 11
When upgrading to Android 11, devices that launched with dynamic partitions can optionally retrofit virtual A/B. The update process is mostly the same as for devices launching with virtual A/B, with some minor differences:
-
Location of COW files — For launch devices, the
OTA client uses all available empty space in the super partition before
using space in
/data
. For retrofit devices, there's always enough space in the super partition so that the COW file is never created on/data
. -
Build-time feature flags — For devices retrofitting
virtual A/B, both
PRODUCT_VIRTUAL_AB_OTA
andPRODUCT_VIRTUAL_AB_OTA_RETROFIT
are set to `true`:$(call inherit-product, \ $(SRC_TARGET_DIR)/product/virtual_ab_ota_retrofit.mk)
-
Super partition size — Devices launching with
virtual A/B can cut
BOARD_SUPER_PARTITION_SIZE
in half because B slots are no longer in the super partition. Devices retrofitting virtual A/B will keep the old super partition size, soBOARD_SUPER_PARTITION_SIZE
is greater than or equal to 2 * sum(size of update groups) + overhead, which in turn is greater than or equal to 2 * sum(size of partitions) + overhead.
Bootloader changes
During the merge step of an update, /data
holds the only whole instance of
the Android OS. Once the migration starts, the native system
, vendor
, and
product
partitions are incomplete until the copy finishes. If the device is
factory reset during this process, either by recovery or the Systems settings
dialog, then the device would be unbootable.
Before erasing /data
, finish the merge in recovery or rollback depending on
the device state:
- If the new build booted successfully before, finish the migration.
- Otherwise, rollback to the old slot:
- For dynamic partitions, roll back to the previous state.
- For static partitions, set the active slot to the old slot.
Both the bootloader and fastbootd can erase the /data
partition if the device
is unlocked. While fastbootd can force the migration to complete, the
bootloader can’t. The bootloader doesn’t know whether or not a merge is in
progress, or what blocks in /data
constitute the OS partitions. Devices
should prevent the user from bricking the device without knowing the risks by
doing the following:
- Implement the boot control HAL so that the bootloader can read the value
set by
setSnapshotMergeStatus()
. - If the merge status is
MERGING
, or if the merge status isSNAPSHOTTED
and the slot has changed to the newly updated slot, then in the bootloader reject requests to wipeuserdata
,metadata
, or the partition storing the merge status. - Implement the
fastboot snapshot-update cancel
command so that users can signal to the bootloader that they want to bypass this protection mechanism. - Modify custom flashing tools or scripts to issue
fastboot snapshot-update cancel
when flashing the entire device. This is safe to issue because flashing the entire device removes the OTA. Tooling can detect this command at runtime by implementingfastboot getvar snapshot-update-status
. This command helps differentiate between error conditions.
Example
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 tooling changes
Android 11 makes the following changes to the fastboot protocol:
getvar snapshot-update-status
— Returns the value that the boot control HAL communicated to the bootloader:- If the state is
MERGING
, the bootloader must returnmerging
. - If the state is
SNAPSHOTTED
, the bootloader must returnsnapshotted
. - Otherwise, the bootloader must return
none
.
- If the state is
snapshot-update merge
— Completes a merge operation, booting to recovery/fastbootd if necessary. This command is valid only ifsnapshot-update-status
ismerging
, and is only supported in fastbootd.snapshot-update cancel
— Sets the boot control HAL's merge status toCANCELLED
. This command is invalid when the device is locked.erase
orwipe
— Anerase
orwipe
ofmetadata
,userdata
, or a partition holding the merge status for the boot control HAL should check the snapshot merge status. If the status isMERGING
orSNAPSHOTTED
, the device should abort the operation.set_active
— Aset_active
command that changes the active slot should check the snapshot merge status. If the status isMERGING
, the device should abort the operation. The slot can safely be changed in theSNAPSHOTTED
state.
These changes are designed to prevent accidentally making a device unbootable,
but they can be disruptive to automated tooling. When the commands are used as
a component of flashing all partitions, such as running fastboot flashall
,
it's recommended to use the following flow:
- Query
getvar snapshot-update-status
. - If
merging
orsnapshotted
, issuesnapshot-update cancel
. - Proceed with flashing steps.
Reducing storage requirements
Devices that don't have full A/B storage allocated in super, and are expecting
to use /data
as necessary, are strongly recommended to use the block mapping
tool. The block mapping tool keeps block allocation consistent between builds,
reducing unnecessary writes to the snapshot. This is documented under
Reducing OTA Size.
Additional patches
Cherry-pick the following patches to address the following known issues.
Sideloading a full OTA package on a Virtual A/B device that has a super
partition with a size smaller than 2 * sum(size of update groups) may fail
with the following in recovery log /tmp/recovery.log
:
The maximum size of all groups with suffix _b (...) has exceeded half of allocatable space for dynamic partitions ...
Here is an example of the log:
[INFO:dynamic_partition_control_android.cc(1020)] Will overwrite existing partitions. Slot Amay be unbootable until update finishes!
[...]
[ERROR:dynamic_partition_control_android.cc(803)] The maximum size of all groups with suffix _b (2147483648) has exceeded half of allocatable space for dynamic partitions 1073741824.
If you encounter this issue, cherry pick CL 1399393, rebuild and flash the boot partition or recovery partition if the device does not use recovery as boot.