用户数据检查点

Android 10 引入了用户数据检查点 (UDC),它允许 Android 在 Android 无线 (OTA) 更新失败时回滚到之前的状态。借助 UDC,如果 Android OTA 更新失败,设备可以安全地回滚到之前的状态。虽然 A/B 更新解决了早期启动的此问题,但在用户数据分区(挂载在 /data 上)被修改时,不支持回滚。

UDC 使设备即使在用户数据分区被修改后也能恢复。UDC 功能通过以下方式实现此目的:文件系统的检查点功能;文件系统不支持检查点时的替代实现;与启动加载程序 A/B 机制集成,同时支持非 A/B 更新;以及支持密钥版本绑定和密钥回滚预防。

用户影响

UDC 功能改善了用户的 OTA 更新体验,因为在 OTA 更新失败时,丢失数据的用户更少。这可以减少遇到更新过程中出现问题的用户的支持电话数量。但是,当 OTA 更新失败时,用户可能会注意到设备多次重启。

工作原理

不同文件系统中的检查点功能

对于 F2FS 文件系统,UDC 将检查点功能添加到上游 4.20 Linux 内核,并将其反向移植到运行 Android 10 的设备支持的所有通用内核。

对于其他文件系统,UDC 使用名为 dm_bow 的设备映射器虚拟设备来实现检查点功能。dm_bow 位于设备和文件系统之间。当分区挂载时,会发出 trim 命令,导致文件系统在所有可用块上发出 trim 命令。dm_bow 拦截这些 trim 命令,并使用它们来设置可用块列表。然后,读取和写入将未经修改地发送到设备,但在允许写入之前,恢复所需的数据会备份到可用块。

检查点流程

当挂载带有 checkpoint=fs/block 标志的分区时,Android 会在驱动器上调用 restoreCheckpoint,以允许设备恢复任何当前检查点。init 然后调用 needsCheckpoint 函数来确定设备是处于启动加载程序 A/B 状态还是已设置更新重试计数。如果任一条件为真,Android 会调用 createCheckpoint 以添加挂载标志或构建 dm_bow 设备。

分区挂载后,将调用检查点代码以发出 trim 命令。然后,启动过程照常继续。在 LOCKED_BOOT_COMPLETE 时,Android 会调用 commitCheckpoint 以提交当前检查点,并且更新照常继续。

管理 Keymaster 密钥

Keymaster 密钥用于设备加密或其他目的。为了管理这些密钥,Android 会延迟密钥删除调用,直到检查点提交。

监控运行状况

运行状况守护程序会验证是否有足够的磁盘空间来创建检查点。运行状况守护程序位于 cp_healthDaemon 中的 Checkpoint.cpp 中。

运行状况守护程序具有以下可配置的行为

检查点 API

UDC 功能使用检查点 API。有关 UDC 使用的其他 API,请参阅 IVold.aidl

void startCheckpoint(int retry)

创建检查点。

框架在准备好开始更新时调用此方法。检查点是在重新启动后挂载 R/W 的检查点文件系统(例如 userdata)之前创建的。如果重试计数为正数,则 API 会处理跟踪重试,并且更新程序会调用 needsRollback 以检查是否需要回滚更新。如果重试计数为 -1,则 API 会听从 A/B 启动加载程序的判断。

在执行正常的 A/B 更新时,不会调用此方法。

void commitChanges()

提交更改。

框架在重新启动后,当更改准备好提交时调用此方法。这在数据(例如图片、视频、短信、服务器接收回执)写入 userdata 之前以及 BootComplete 之前调用。

如果不存在任何活动的检查点更新,则此方法无效。

abortChanges()

强制重启并恢复到检查点。放弃自首次重启以来的所有 userdata 修改。

框架在重新启动后但在 commitChanges 之前调用此方法。当调用此方法时,retry_counter 会减小。会生成日志条目。

bool needsRollback()

确定是否需要回滚。

在非检查点设备上,返回 false。在检查点设备上,在非检查点启动期间返回 true

实现 UDC

参考实现

有关如何实现 UDC 的示例,请参阅 dm-bow.c。有关该功能的其他文档,请参阅 dm-bow.txt

设置

init.hardware.rc 文件中的 on fs 中,确保您具有

mount_all /vendor/etc/fstab.${ro.boot.hardware.platform} --early

init.hardware.rc 文件中的 on late-fs 中,确保您具有

mount_all /vendor/etc/fstab.${ro.boot.hardware.platform} --late

fstab.hardware 文件中,确保 /data 标记为 latemount

/dev/block/bootdevice/by-name/userdata              /data              f2fs
noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier
latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M,checkpoint=fs

添加元数据分区

UDC 需要元数据分区来存储非启动加载程序重试计数和密钥。设置元数据分区并在 /metadata 处提前挂载它。

fstab.hardware 文件中,确保 /metadata 标记为 earlymountfirst_stage_mount

/dev/block/by-name/metadata           /metadata           ext4
noatime,nosuid,nodev,discard,sync
wait,formattable,first_stage_mount

将分区初始化为全零。

将以下行添加到 BoardConfig.mk

BOARD_USES_METADATA_PARTITION := true
BOARD_ROOT_EXTRA_FOLDERS := existing_folders metadata

更新系统

F2FS 系统

对于使用 F2FS 格式化数据的系统,请确保您的 F2FS 版本支持检查点。有关更多信息,请参阅不同文件系统中的检查点功能

checkpoint=fs 标志添加到挂载在 /data 处的设备的 fstab 的 <fs_mgr_flags> 部分。

非 F2FS 系统

对于非 F2FS 系统,必须在内核配置中启用 dm-bow

checkpoint=block 标志添加到挂载在 /data 处的设备的 fstab 的 <fs_mgr_flags> 部分。

检查日志

调用检查点 API 时会生成日志条目。

验证

要测试您的 UDC 实现,请运行 VtsKernelCheckpointTest VTS 测试集。