用户数据检查点

Android 10 引入了用户数据检查点 (UDC) 功能,此功能允许 Android 在 Android 无线下载 (OTA) 更新失败时回滚至之前的状态。借助 UDC,如果 Android OTA 更新失败,设备可以安全地回滚至之前的状态。虽然 A/B 更新针对前期启动解决了这一问题,但此方法在用户数据分区(装载在 /data 中)发生变化的情况下,仍无法支持回滚。

借助 UDC,即使用户数据分区发生了变化,设备仍能将其还原。UDC 功能通过文件系统的检查点功能来实现这一点。当文件系统不支持检查点时,UDC 可作为替代实现方式,与引导加载程序的 A/B 机制集成,同时还支持非 A/B 更新以及密钥版本绑定和密钥回滚预防。

对用户的影响

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

运作方式

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

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

对于其他文件系统,UDC 使用名为 dm_bow 的设备映射器虚拟设备来实现检查点功能。dm_bow 位于设备和文件系统之间。装载分区后,系统会发出修剪命令,从而引发文件系统对所有可用块发出修剪命令。dm_bow 会截获这些修剪命令并使用它们来设置一个可用块列表。然后,系统会将读写操作(未经修改)发送到设备,不过,在允许执行写入操作之前,会先将恢复操作所需的数据备份到可用块。

检查点功能执行过程

装载具有 checkpoint=fs/block 标志的分区时,Android 会对磁盘调用 restoreCheckpoint,以允许设备恢复所有当前的检查点。然后,init 调用 needsCheckpoint 函数来确定设备是处于引导加载程序 A/B 更新状态,还是已设置更新重试计数。无论处于这两种当中的哪一种状态,Android 都会调用 createCheckpoint 来添加装载标志或构建 dm_bow 设备。

装载分区后,系统将调用检查点代码来发出修剪命令。然后,启动过程会继续正常进行。在 LOCKED_BOOT_COMPLETE 时,Android 调用 commitCheckpoint 来提交当前检查点,更新会继续正常进行。

管理 keymaster 密钥

Keymaster 密钥用于设备加密或其他目的。为管理这些密钥,Android 特地推迟到提交检查点之后才调用密钥删除命令。

监控运行状况

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

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

检查点 API

UDC 功能需要使用检查点 API。如需了解 UDC 使用的其他 API,请参阅 IVold.aidl

void startCheckpoint(int retry)

创建一个检查点。

框架会在准备好开始更新时调用此方法。系统会先创建检查点,然后,带有检查点的文件系统(如 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

添加 metadata 分区

UDC 需要使用 metadata 分区来存储非引导加载程序重试计数和密钥。设置 metadata 分区并提前将其装载在 /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 版本支持检查点。如需了解详情,请参阅不同文件系统中的检查点功能

对于在 /data 装载的设备,请将 checkpoint=fs 标志添加到 fstab 的 <fs_mgr_flags> 部分。

非 F2FS 系统

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

对于在 /data 装载的设备,请将 checkpoint=block 标志添加到 fstab 的 <fs_mgr_flags> 部分。

检查日志

调用 Checkpoint API 时,会生成日志条目。

验证

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