Android 10 supports dynamic partitions, a userspace partitioning system that can create, resize, and destroy partitions during over-the-air (OTA) updates.
This page describes how OTA clients resize dynamic partitions during an update for non-A/B devices.
For non-A/B devices, the OTA update for dynamic partitions is applied
using the updater
inside the update package.
Update launch devices
This section applies to non-A/B devices that launch with dynamic partitions support; these devices upgrade from Android 10 to higher releases.
Generate update packages
OTA update packages are generated by the
ota_from_target_files
script, located under
build/make/tools/releasetools
. By default, the script
generates a package that updates the system
and
vendor
partitions. If there are additional dynamic
partitions, such as product
,
product_services
, or odm
, their
updates must be generated in
device-specific
code.
To generate updates, in the extended Python module, implement
FullOTA_GetBlockDifferences()
and
IncrementalOTA_GetBlockDifferences()
. These two
functions return a list of BlockDifference
objects,
each describing the update patch that would be applied on a
partition. Partitions returned by these two functions shouldn't be
modified manually or verified elsewhere, for example in
*_InstallBegin()
or *_InstallEnd()
.
Example of an update generation:
# device/yoyodyne/tardis/releasetools.py import os from common import BlockDifference, EmptyImage, GetUserImage # The joined list of user image partitions of source and target builds. # - Items should be added to the list if new dynamic partitions are added. # - Items should not be removed from the list even if dynamic partitions are # deleted. When generating an incremental OTA package, this script needs to # know that an image is present in source build but not in target build. USERIMAGE_PARTITIONS = [ "product", "odm", ] def GetUserImages(input_tmp, input_zip): return {partition: GetUserImage(partition, input_tmp, input_zip) for partition in USERIMAGE_PARTITIONS if os.path.exists(os.path.join(input_tmp, "IMAGES", partition + ".img"))} def FullOTA_GetBlockDifferences(info): images = GetUserImages(info.input_tmp, info.input_zip) return [BlockDifference(partition, image) for partition, image in images.items()] def IncrementalOTA_GetBlockDifferences(info): source_images = GetUserImages(info.source_tmp, info.source_zip) target_images = GetUserImages(info.target_tmp, info.target_zip) # Use EmptyImage() as a placeholder for partitions that will be deleted. for partition in source_images: target_images.setdefault(partition, EmptyImage()) # Use source_images.get() because new partitions are not in source_images. return [BlockDifference(partition, target_image, source_images.get(partition)) for partition, target_image in target_images.items()]
Update flow
Behind the scenes, the following functions are added to the edify script:
unmap_partition(name)
- Unmap the partition if mapped, otherwise do nothing.
- Return the string
t
on success, or an empty string on failure.
map_partition(name)
- Map the partition if not already mapped.
- Return the absolute path of the mapped block device on success, or an empty string on failure.
update_dynamic_partitions(op_list)
- Apply the given operation list on dynamic partition metadata, unmapping partitions if necessary.
-
Return
t
on success, or an empty string on failure.
The op_list
argument to
update_dynamic_partitions
points to a file in the
update package. Each line in the file specifies an operation. If any
operation fails, update_dynamic_partitions
immediately
returns an empty string. The operations are:
resize partition-name size
- Unmap the partition, then resize it to size.
remove partition_name
- Unmap the partition, then remove it.
add partition-name group-name
- Add a new partition to the specified group.
- Abort if the group doesn't exist or if the partition already exists.
move partition-name group-name
- Move the partition to the specified group.
- Abort if the group doesn't exist or partition doesn't exist.
-
add_group group-name maximum-size
- Add a group with the given name and maximum size.
- Abort if the group already exists.
- A maximum_size of 0 means there are no size limits on partitions in the group. Additional testing is required to ensure that partitions in the group don't exceed the available space on the device.
-
resize_group group-name maximum-size
- Resize the group to the given maximum size.
- Abort if the group doesn't exist.
- A maximum_size of 0 means there are no size limits on partitions in the group. Additional testing is required to ensure that partitions in the group don't exceed the available space on the device.
remove_group group-name
- Remove a group.
- Abort if there are partitions in the group.
remove_all_groups
- Unmap all partitions from the device mapper.
- Remove all partitions and groups.
Incremental OTA
Incremental OTA updates use the following logic:
- Shrink partitions/delete partitions/move partitions out of group (so that there's enough space to shrink groups)
- Shrink groups (so that there's enough space to grow groups)
- Grow groups (so that we have enough space to grow/add partitions)
- Grow partitions/add partitions/move partitions to new group
In detail, update-script
is generated with this
logic:
for each shrinking partition: block_image_update(map_partition(name), …) update_dynamic_partitions(op_list) for each growing / adding partition: block_image_update(map_partition(name), …)
The op_list
file for
update_dynamic_partitions
is generated with this
logic:
for each deleting partition: remove for each partition that changes groups: move to "default" for each shrinking partition: resize for each shrinking / removing group: resize_group / remove_group for each growing / adding group: resize_group / add_group for each adding partition: add for each growing / adding partition: resize for each partition that changes groups: move to target group
Full OTA
Full OTA updates use the following logic:
- Delete all existing groups and partitions
- Add groups
- Add partitions
In detail, update-script
is generated with this
logic:
update_dynamic_partitions(op_list) for each adding partition: block_image_update(map_partition(name), …)
The op_list
file for
update_dynamic_partitions
is generated with this
logic:
remove_all_groups for each adding group: add_group for each adding partition: add for each adding partition: resize