具有动态分区的非 A/B 设备的 OTA

Android 10 支持动态分区,这是一种可以在无线下载 (OTA) 更新期间创建和销毁分区以及调整分区大小的用户空间分区系统。

本页将介绍 OTA 客户端如何在非 A/B 设备更新期间调整动态分区的大小。

对于非 A/B 设备,使用更新软件包内的 updater 应用动态分区的 OTA 更新。

更新启动设备

本部分适用于支持动态分区的非 A/B 设备;这些设备从 Android 10 升级到更高版本。

生成更新软件包

OTA 更新软件包由位于 build/make/tools/releasetools 下的 ota_from_target_files 脚本生成。默认情况下,该脚本会生成一个更新 systemvendor 分区的软件包。如果存在其他动态分区(例如 productproduct_servicesodm),必须在设备专属代码中生成其更新。

如需生成更新,请在扩展 Python 模块中实现 FullOTA_GetBlockDifferences()IncrementalOTA_GetBlockDifferences()。这两个函数会返回 BlockDifference 对象列表,每个对象描述将应用于分区的更新补丁程序。不应对这两个函数返回的分区进行手动修改或在其他地方验证,例如在 *_InstallBegin()*_InstallEnd() 中。

更新生成示例:

# 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()]

更新流程

在后台,以下函数会添加到 edify 脚本中:

  • unmap_partition(name)
    • 如果已映射,则取消映射分区,否则不执行任何操作。
    • 在成功时返回字符串 t,或者在失败时返回空字符串。
  • map_partition(name)
    • 如果尚未映射,则映射分区。
    • 在成功时返回映射块设备的绝对路径,或者在失败时返回空字符串。
  • update_dynamic_partitions(op_list)
    • 对动态分区元数据应用指定的运算列表,必要时取消映射分区。
    • 在成功时返回 t,或者在失败时返回空字符串。

update_dynamic_partitionsop_list 参数指向更新软件包中的文件。文件中的每一行都指定一个运算。如果任何运算失败,update_dynamic_partitions 会立即返回一个空字符串。这些运算是:

  • resize partition-name size
    • 取消映射分区,然后将其调整为适当大小 (size)。
  • remove partition_name
    • 取消映射分区,然后将其删除。
  • add partition-name group-name
    • 将新分区添加到指定的组。
    • 如果该组不存在或该分区已存在,则中止。
  • move partition-name group-name
    • 将分区移动到指定的组。
    • 如果该组不存在或该分区不存在,则中止。
  • add_group group-name maximum-size
    • 添加具有指定名称和大小上限的组。
    • 如果该组已存在,则中止。
    • maximum_size 为 0 表示对组中的分区没有大小限制。需要进行额外测试以确保组中的分区不超过设备上的可用空间。
  • resize_group group-name maximum-size
    • 将组的大小调整为指定的大小上限。
    • 如果该组不存在,则中止。
    • maximum_size 为 0 表示对组中的分区没有大小限制。需要进行额外测试以确保组中的分区不超过设备上的可用空间。
  • remove_group group-name
    • 删除组。
    • 如果该组中有分区,则中止。
  • remove_all_groups
    • 取消映射设备映射器中的所有分区。
    • 删除所有分区和组。

增量 OTA

增量 OTA 更新使用以下逻辑:

  1. 收缩分区/删除分区/将分区移出组(以便有足够的空间来收缩组)
  2. 收缩组(以便有足够的空间来成长组)
  3. 成长组(以便我们有足够的空间来成长/添加分区)
  4. 成长分区/添加分区/将分区移动到新组

详细而言,使用以下逻辑生成 update-script

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), …)

使用以下逻辑生成 update_dynamic_partitionsop_list 文件:

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

完整 OTA

完整 OTA 更新使用以下逻辑:

  1. 删除所有现有组和分区
  2. 添加组
  3. 添加分区

详细而言,使用以下逻辑生成 update-script

update_dynamic_partitions(op_list)

for each adding partition:
    block_image_update(map_partition(name), …)

使用以下逻辑生成 update_dynamic_partitionsop_list 文件:

remove_all_groups
for each adding group:
    add_group
for each adding partition:
    add
for each adding partition:
    resize