针对不带动态分区的 A/B 设备进行 OTA

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

本页面介绍了 OTA 客户端在为启动时不支持动态分区的 A/B 设备更新期间如何调整动态分区的大小,以及 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_aproduct_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

  • 如果基本版本定义这些变量,则这是改造更新。更新包包含拆分的 super.img 文件,并禁用安装后步骤。
  • 如果基本版本确实定义了这些变量,则这与使用动态分区的典型更新相同。更新包包含逻辑(动态)分区的映像。可以启用安装后步骤。

完整 OTA

为改造设备生成两个完整 OTA 包。

  • $(PRODUCT)-ota-retrofit-$(TAG).zip 始终包含拆分的 super.img,并禁用改造更新的安装后步骤。
    • 它是使用 ota_from_target_files 脚本的附加参数 --retrofit_dynamic_partitions 生成的。
    • 它可以应用于所有版本。
  • $(PRODUCT)-ota-$(TAG).zip 包含用于未来更新的逻辑映像。
    • 仅将此应用于启用了动态分区的版本。有关强制执行此操作的详细信息,请参见下文。

拒绝旧版本上的非改造更新

仅将常规完整 OTA 包应用于启用了动态分区的版本。如果 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 作为安装后步骤并拒绝更新。