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
内。
运行状况守护程序具有以下可配置的行为:
ro.sys.cp_msleeptime
:控制设备检查磁盘使用情况的频率。ro.sys.cp_min_free_bytes
:控制运行状况守护程序查找的最小值。ro.sys.cp_commit_on_full
:控制运行状况守护程序在磁盘已满时,是重新启动设备还是提交检查点并继续运行。
检查点 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
标记为 earlymount
或 first_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
测试集。