实现虚拟 A/B - 补丁程序

从下列补丁程序中挑选最合适的来解决对应的已知问题。

旁加载时正确检查可分配的空间

在 super 分区小于“2 * 各个更新组大小的总和”的虚拟 A/B 设备上,旁加载完整 OTA 软件包的操作可能会失败,并且恢复日志 /tmp/recovery.log 中会显示下列内容:

The maximum size of all groups with suffix _b (...) has exceeded half of allocatable space for dynamic partitions ...

以下是一个日志示例:

[INFO:dynamic_partition_control_android.cc(1020)] Will overwrite existing partitions. Slot A may be unbootable until update finishes!
[...]
[ERROR:dynamic_partition_control_android.cc(803)] The maximum size of all groups with suffix _b (2147483648) has exceeded half of allocatable space for dynamic partitions 1073741824.

如果您遇到此问题,请择优挑选 CL 1399393,重新构建并刷写 boot 分区或 recovery 分区(如果设备不将 recovery 分区用作 boot 分区的话)。

修复了合并期间的分段错误

应用 OTA 更新后,在 VAB 合并过程中,调用 update_engine_client --cancel 会导致 CleanupPreviousUpdateAction 崩溃。如果 markSlotSuccessful 延迟出现,说明还可能会存在野指针错误。

这个问题通过添加 StopActionInternal 函数得到了解决。CleanupPreviousUpdateAction 会在销毁时取消待处理任务,其维护着一个用于跟踪消息循环中待处理任务的任务 ID 的变量。销毁时,待处理任务会被取消,以避免分段错误。

确保对 Android 11 源代码树进行以下更改,以修复合并过程中 update_engine 内出现的 SIGSEGV 崩溃问题:

  • CL 1439792(CL 1439372 的先决条件)
  • CL 1439372CleanupPreviousUpdateAction:在销毁时取消待处理任务)
  • CL 1663460(解决了 markSlotSuccessful 延迟出现时可能存在的野指针错误)

修复了 OTA 更新后 VAB 槽位切换出错的问题

在 Android 11 及更高版本中,如果在 OTA 更新后未能在设备上同步槽位切换,会导致设备处于无法使用的状态。如果 IBootControl HAL 的槽位切换实现执行写入,您必须立即清空这些写入。如果写入操作未被清空,并且在合并开始后,但硬件清空槽位切换写入之前设备重新启动,则设备可能会还原到之前的槽位,因而无法启动。

如需查看代码解决方案示例,请查看此 CL:CL 1535570

防止 update_engine 提前合并

设备启动(Android 11 及更高版本)并且启动完成后,update_engine 会调用 ScheduleWaitMarkBootSuccessful()WaitForMergeOrSchedule()。这会启动合并流程。不过,设备会重新启动,并恢复到原来的槽位。但由于合并已经开始,设备将无法启动,进而无法操作。

将以下更改添加到源代码树中。请注意,CL 1664859 是可选的。

  • CL 1439792(CL 1439372 的先决条件)。
  • CL 1439372CleanupPreviousUpdateAction:在销毁时取消待处理任务)
  • CL 1663460(解决了 markSlotSuccessful 延迟出现时可能存在的野指针错误)
  • CL 1664859(可选 - 为 CleanupPreviousUpdateAction 添加 unittest

防止因跳过元数据而导致数据丢失或损坏

在 Android 11 及更高版本中,如果存储设备具有易失性回写缓存,则在某些情况下,系统会跳过已完成的合并的元数据,导致数据丢失或损坏。

条件:

  1. 完成一组异常的合并操作后,系统会调用 merge_callback()
  2. 元数据已在跟踪合并完成情况的 COW 设备中更新。(对 COW 设备的这一更新操作已得到彻底清空)。

结果:由于未清空存储设备中最近合并的缓存,系统发生崩溃。

请参阅以下内容,找到问题的解决办法:

确保 dm-verity 配置正确无误

在 Android 11 及更高版本中,您可能会无意中使用以下 dm-verity 选项配置设备:

  • 内核中的 CONFIG_DM_VERITY_AVB=y
  • 配置为使用任意 verity 模式(例如 AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE),而未配置 AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO 的引导加载程序。

采用这种设备配置时,任何 verity 错误都会导致 vbmeta 分区损坏,并使非 A/B 设备变得无法操作。同样,如果合并已经开始,A/B 设备也可能会变得无法操作。请仅使用 AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO verity 模式。

  1. 在内核中设置 CONFIG_DM_VERITY_AVB=n
  2. 请改为将设备配置为使用 AVB_HASHTREE_ERROR_MODE_MANAGED_RESTART_AND_EIO 模式。

如需了解详情,通常可以参阅以下 verity 文档:处理 dm-verity 错误

在紧急系统关机期间出现 I/O 错误时跳过 verity 工作

在 Android 11 及更高版本中,如果调用了紧急系统关机(例如,在过热关机时),dm 设备可能会保持活动状态,但块存储设备将无法继续处理 I/O 请求。在此状态下,由新 dm I/O 请求或传输中的 I/O 请求处理的 I/O 错误会导致 verity 损坏状态,这是误判。

如需在关机期间出现 I/O 错误时跳过 verity 工作,请使用以下命令:

CL 1847875 (在关机期间出现 I/O 错误时跳过 verity 工作)

确保 DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED 处于关闭状态

运行 4.19 或更早版本内核的 Android Go 设备可能在其内核配置中包含 DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED=y。此设置与虚拟 A/B 不兼容,并且有一个已知问题,即这两者一起启用时会导致罕见的网页损坏问题。

对于内核 4.19 及更早版本,请在内核配置中设置 CONFIG_DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED=n 将其停用。

对于内核 5.4 及更高版本,这段代码已被移除,配置选项不可用。

确认已正确配置合并后的文件

如果您分别构建系统映像和供应商映像,然后使用 merge_target_files 将两者合并,虚拟 A/B 配置可能会在合并过程中被错误地丢弃。 如需验证合并后的目标文件中虚拟 A/B 配置是正确的,请应用以下补丁:CL 2084183(在动态分区信息中合并相同的键/值对)

更新必要组件

从 Android 13 开始,snapuserd 已从供应商 ramdisk 移至通用 ramdisk。如果您的设备即将升级到 Android 13,供应商 ramdisk 和通用 ramdisk 都可能包含 snapuserd 的副本。在这种情况下,虚拟 A/B 需要 snapuserd 的系统副本。为了确保正确的 snapuserd 副本已准备就绪,请应用 CL 2031243(将 Snapuserd 复制到 first_stage_ramdisk)。