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

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

本页将介绍在未搭载动态分区支持的 A/B 设备更新期间,OTA 客户端如何调整动态分区的大小,以及 OTA 客户端如何升级到 Android 10。

背景

在更新 A/B 设备以支持动态分区期间,会保留设备上的 GUID 分区表 (GPT),因此设备上将不存在 super 分区。元数据存储在 system_asystem_b 中,但可以通过更改 BOARD_SUPER_PARTITION_METADATA_DEVICE 对此进行自定义。

每个块存储设备中都有两个元数据槽位,其中只有一个元数据槽位会被使用。例如,位于 system_a 的元数据 0 和位于 system_b 的元数据 1 分别对应于 A 槽位和 B 槽位的分区。在运行时,更新哪个槽位并不重要。

在本页中,我们将元数据槽位称为元数据 S(来源)和元数据 T(目标)。类似地,将分区称为 system_svendor_t 等。

如需详细了解构建系统配置,请参阅升级设备

如需详细了解哪些分区属于哪些更新组,请参阅适用于新设备的板卡配置更改

下面的示例显示了设备上的元数据:

  • 物理块设备 system_a
    • 元数据 0
      • foo_a
        • 逻辑(动态)分区 system_a
        • 逻辑(动态)分区 product_services_a
        • 其他由 Foo 更新的分区
      • bar_a
        • 逻辑(动态)分区 vendor_a
        • 逻辑(动态)分区 product_a
        • 其他由 Bar 更新的分区
    • 元数据 1(未使用)
  • 物理块设备 system_b
    • 元数据 0(未使用)
    • 元数据 1
      • 组 foo_b
        • 逻辑(动态)分区 system_b
        • 逻辑(动态)分区 product_services_b
        • 其他由 Foo 更新的分区
      • 组 bar_b
        • 逻辑(动态)分区 vendor_b
        • 逻辑(动态)分区 product_b
        • 其他由 Bar 更新的分区

您可以使用 system/extras/partition_tools 下的 lpdump 工具转储设备上的元数据。例如:

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

改造更新

在搭载 Android 9 及更低版本的设备上,设备上的 OTA 客户端不支持在更新之前映射动态分区。为此,我们额外创建了一组补丁,以便直接映射到现有的物理分区。

OTA 生成器会构建包含所有动态分区内容的最终 super.img 文件,然后将该映像拆分为多个映像,这些映像的大小与 system、vendor 等分区所对应的物理块存储设备的大小相匹配。这些映像被命名为 super_system.imgsuper_vendor.img 等。 OTA 客户端会将这些映像应用到物理分区而不是逻辑(动态)分区。

由于 OTA 客户端不知道如何映射动态分区,因此在生成更新软件包时会自动停用针对这些分区的所有安装后步骤。如需了解详情,请参阅配置安装后步骤

更新流程与 Android 9 中相同。

更新之前:

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

更新之后:

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

改造后的未来更新

改造更新后,OTA 客户端将被更新为可以处理动态分区。源分区的区段从不跨越多个目标物理分区。

使用常规更新软件包的更新流程

  1. 初始化 super 分区元数据。
    1. 根据元数据 S(源元数据)构建新的元数据 M。 例如,如果元数据 S 使用 [system_svendor_sproduct_s] 作为块存储设备,新的元数据 M 将使用 [system_tvendor_tproduct_t] 作为块存储设备。在元数据 M 中会舍弃所有组和分区。
    2. 根据更新清单中的 dynamic_partition_metadata 字段添加目标组和分区。可在 new_partition_info 中找到每个分区的大小。
    3. 将元数据 M 写入元数据 T。
    4. 将设备映射器上添加的分区映射为可写入。
  2. 在块设备上应用更新。
    1. 根据需要在设备映射器上将源分区映射为只读分区。对于旁加载来说,这是必要的,因为并未在更新之前映射源分区。
    2. 将完整或增量更新应用到目标槽位上的所有块设备。
    3. 装载分区以运行安装后脚本,然后再卸载分区。
  3. 取消映射目标分区。

使用改造更新软件包的更新流程

如果将改造更新软件包应用到已启用动态分区的设备上,OTA 客户端会直接将拆分的 super.img 文件应用到块存储设备上。更新流程与改造更新类似。如需了解详情,请参阅改造更新

例如,假设存在以下情况:

  • 槽位 A 为活动槽位。
  • system_a 在槽位 0 中包含活动元数据。
  • system_avendor_a 以及 product_a 被用作块设备。

当 OTA 客户端收到改造更新软件包时,它会将 super_system.img 应用到物理 system_b 上,将 super_vendor.img 应用到物理 vendor_b 上,将 super_product.img 应用到物理 product_b 上。 物理块设备 system_b 中包含在启动时用于映射逻辑 system_bvendor_bproduct_b 的正确元数据。

生成更新软件包

增量 OTA

为改造设备生成增量 OTA 时,具体属于哪种更新取决于基础版本是否定义了 PRODUCT_USE_DYNAMIC_PARTITIONSPRODUCT_RETROFIT_DYNAMIC_PARTITIONS

  • 如果基础 build 定义这两个变量,那么这便是改造更新。更新软件包包含拆分的 super.img 文件,并且会停用安装后步骤。
  • 如果基础 build 定义了这两个变量,那么这与动态分区的典型更新相同。更新软件包包含逻辑(动态)分区的映像。可以启用安装后步骤。

完整 OTA

系统会针对改造设备生成两个完整的 OTA 软件包。

  • $(PRODUCT)-ota-retrofit-$(TAG).zip 始终包含拆分的 super.img,并且会停用改造更新的安装后步骤。
    • 该软件包是通过为 ota_from_target_files 脚本提供一个额外的参数 --retrofit_dynamic_partitions 生成的。
    • 该软件包可以应用于所有 build。
  • $(PRODUCT)-ota-$(TAG).zip 包含用于未来更新的逻辑映像。
    • 请仅将该软件包应用于启用了动态分区的 build。有关强制执行此操作的详细信息,请参见下文。

拒绝对旧 build 进行非改造更新

请仅将常规的完整 OTA 软件包应用于启用了动态分区的 build。如果因 OTA 服务器配置错误而将这些软件包推送给了搭载 Android 9 或更低版本的设备,将会造成这些设备无法启动。Android 9 及更低版本上的 OTA 客户端无法区分改造 OTA 软件包和常规的完整 OTA 软件包,因此客户端不会拒绝完整的软件包。

为防止设备接受完整的 OTA 软件包,您可以要求执行安装后步骤以检查现有的设备配置。例如:

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 \

如果将常规 OTA 软件包应用于未启用动态分区的设备,OTA 客户端会在安装后运行 check_dynamic_partitions 并拒绝更新。