OTA for A/B Devices without Dynamic Partitions

Android 10 supports Dynamic Partitions, a userspace partitioning system that can create, resize, and destroy partitions during an over-the-air (OTA) update. This document describes how OTA clients resize dynamic partitions during an update for A/B devices that launched without dynamic partitions support and how OTA clients upgrade to Android 10.

Background

During an update of an A/B device to support Dynamic Partitions, the GUID partition table (GPT) on the device is preserved, so there's no super partition on the device. Metadata is stored at system_a and system_b, but this can be customized by changing BOARD_SUPER_PARTITION_METADATA_DEVICE.

In each of the block devices, there are two metadata slots. Only one metadata slot in each block device is used. For example, Metadata 0 at system_a and Metadata 1 at system_b correspond to partitions at the A and B slots, respectively. At runtime, it doesn't matter which slot is being updated.

In this document, the metadata slots are called Metadata S (source) and Metadata T (target). Similarly, partitions are referred to as system_s, vendor_t, and so on.

For more information about build system configurations, see Upgrading devices.

For more information about how partitions belong to update groups, see Board configuration changes for new devices.

An example of metadata on a device is:

  • Physical block device system_a
    • Metadata 0
      • Group foo_a
        • Logical (dynamic) partition system_a
        • Logical (dynamic) partition product_services_a
        • Other partitions updated by Foo
      • Group bar_a
        • Logical (dynamic) partition vendor_a
        • Logical (dynamic) partition product_a
        • Other partitions updated by Bar
    • Metadata 1 (not used)
  • Physical block device system_b
    • Metadata 0 (not used)
    • Metadata 1
      • Group foo_b
        • Logical (dynamic) partition system_b
        • Logical (dynamic) partition product_services_b
        • Other partitions updated by Foo
      • Group bar_b
        • Logical (dynamic) partition vendor_b
        • Logical (dynamic) partition product_b
        • Other partitions updated by Bar

You can use the lpdump tool under system/extras/partition_tools to dump the metadata on your device. For example:

lpdump --slot 0 /dev/block/by-name/system_a
lpdump --slot 1 /dev/block/by-name/system_b

Retrofitting an update

On devices running Android 9 and earlier, the OTA client on the device doesn't support mapping dynamic partitions before the update. An additional set of patches is created so that mapping can be applied directly to the existing physical partitions.

The OTA generator builds the final super.img file that contains the content of all dynamic partitions, then splits the image into multiple images matching the sizes of the physical block devices corresponding to system, vendor, and so on. These images are named super_system.img, super_vendor.img, and so on. The OTA client applies these images to the physical partitions, rather than applying the images for the logical (dynamic) partitions.

Because the OTA client doesn't know how to map dynamic partitions, all post-install steps are disabled automatically for these partitions when the update package is generated. See Implementing A/B Updates: Configuring post-installation for more details.

The update flow is the same as in Android 9.

Before the update:

ro.boot.dynamic_partitions=
ro.boot.dynamic_partitions_retrofit=

After the update:

ro.boot.dynamic_partitions=true
ro.boot.dynamic_partitions_retrofit=true

Future updates after retrofit

After the retrofit update, the OTA client is updated to work with dynamic partitions. The extents for source partitions never span across target physical partitions.

Update flow using a regular update package

  1. Initialize super partition metadata.
    1. Construct new metadata M from Metadata S (source metadata). For example, if Metadata S uses [system_s, vendor_s, product_s] as block devices, then the new metadata M uses [system_t, vendor_t, product_t] as block devices. All groups and partitions are discarded in M.
    2. Add target groups and partitions according to the dynamic_partition_metadata field in the update manifest. The size of each partition can be found in new_partition_info.
    3. Write M to Metadata T.
    4. Map the added partitions on the device mapper as writable.
  2. Apply the update on the block devices as usual.
    1. If necessary, map the source partitions on the device mapper as read-only. This is necessary for sideloading because the source partitions aren't mapped prior to the update.
    2. Apply a full or delta update to all block devices at the target slot.
    3. Mount the partitions to do the post-install step, then unmount.
  3. Unmap the target partitions.

Update flow using a retrofit update package

If the retrofit update package is applied on a device that already enables dynamic partitions, the OTA client applies the split super.img file on block devices directly. The update flow is similar to a retrofit update. See Retrofitting an update for details.

For example, assume the following:

  • Slot A is the active slot.
  • system_a contains the active metadata at slot 0.
  • system_a, vendor_a, and product_a are used as block devices.

When the OTA client receives a retrofit update package, it applies super_system.img on physical system_b, super_vendor.img on physical vendor_b, and super_product.img on physical product_b. The physical block device system_b contains the correct metadata to map the logical system_b, vendor_b, and product_b at boot time.

Generating update packages

Incremental OTA

When generating incremental OTAs for retrofit devices, the updates depend on whether or not the base build defines PRODUCT_USE_LOGICAL_PARTITIONS and PRODUCT_RETROFIT_DYNAMIC_PARTITIONS:

  • If the base build doesn't define the variables, this is a retrofitting update. The update package contains the split super.img file and disables the post-install step.
  • If the base build does define the variables, this is the same as a typical update with dynamic partitions. The update package contains the images for logical (dynamic) partitions. The post-install step can be enabled.

Full OTA

Two full OTA packages are generated for retrofit devices.

  • $(PRODUCT)-ota-retrofit-$(TAG).zip always contains split super.img and disables the post-install step for retrofitting update.
    • It's generated with an additional argument --retrofit_dynamic_partitions to the ota_from_target_files script.
    • It can be applied to all builds.
  • $(PRODUCT)-ota-$(TAG).zip contains logical images for future updates.
    • Only apply this to builds with dynamic partitions enabled. See details below on enforcing this.

Rejecting non-retrofit update on old builds

The regular full OTA package should only be applied to builds with dynamic partitions enabled. If the OTA server is configured incorrectly and pushes these packages to devices running Android 9 or lower, devices fail to boot. The OTA client on Android 9 and lower can't tell the difference between a retrofit OTA package and a regular full OTA package, so the client won't reject the full package.

To prevent the device from accepting the full OTA package, you can require a post-install step to check the existing device configuration. For example:

device/device_name/dynamic_partitions/check_dynamic_partitions

#!/system/bin/sh
DP_PROPERTY_NAME="ro.boot.dynamic_partitions"
DP_RETROFIT_PROPERTY_NAME="ro.boot.dynamic_partitions_retrofit"

DP_PROPERTY=$(getprop ${DP_PROPERTY_NAME})
DP_RETROFIT_PROPERTY=$(getprop ${DP_RETROFIT_PROPERTY_NAME})

if [ "${DP_PROPERTY}" != "true" ] || [ "${DP_RETROFIT_PROPERTY}" != "true" ] ; then
    echo "Error: applied non-retrofit update on build without dynamic" \
         "partitions."
    echo "${DP_PROPERTY_NAME}=${DP_PROPERTY}"
    echo "${DP_RETROFIT_PROPERTY_NAME}=${DP_RETROFIT_PROPERTY}"
    exit 1
fi

device/device_name/dynamic_partitions/Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= check_dynamic_partitions
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := check_dynamic_partitions
LOCAL_PRODUCT_MODULE := true
include $(BUILD_PREBUILT)

device/device_name/device.mk

PRODUCT_PACKAGES += check_dynamic_partitions

# OPTIONAL=false so that the error in check_dynamic_partitions will be
# propagated to OTA client.
AB_OTA_POSTINSTALL_CONFIG += \
    RUN_POSTINSTALL_product=true \
    POSTINSTALL_PATH_product=bin/check_dynamic_partitions \
    FILESYSTEM_TYPE_product=ext4 \
    POSTINSTALL_OPTIONAL_product=false \

When the regular OTA package is applied on a device without dynamic partitions enabled, the OTA client runs check_dynamic_partitions as a post-install step and rejects the update.