Android 10 用户数据检查点

一、概述

Android 10引入了UDC(user data checkpoint)功能,该功能允许Android在OTA升级失败时回滚到之前的状态。虽然A/B updater能解决早期启动失败回滚问题,但在userdata分区(/data)发生变化的情况下,仍无法支持回滚。有了UDC,即使userdata分区发生了变化,设备仍能将其还原。UDC功能通过文件系统的checkpoint功能来实现这一点。当文件系统不支持checkpoint时,UDC可作为替代实现方式,与bootloader的A/B机制集成,同时还支持Non-A/B升级以及key版本绑定和密钥防回滚。

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

二、工作方式

1.不同文件系统中的checkpoint功能:

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

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

2.checkpoint功能执行过程:

挂载具有checkpoint=fs/block标记的分区时,Android在磁盘上调用restoreCheckpoint,以允许设备恢复所有当前的checkpoint。然后,init调用needsCheckpoint函数来确定设备是处于bootloader A/B升级状态,还是已设置升级retry count值。无论处于哪一种状态,Android都会调用createCheckpoint来添加挂载标记或构建dm_bow设备。

挂载分区后,系统将调用checkpoint代码来发出剪裁命令。然后,启动过程会继续正常进行。在LOCKED_BOOT_COMPLETE时,Android调用commitCheckpoint来提交当前checkpoint,升级会继续正常进行。

3.管理keymaster密钥:

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

注:UDC不会造成任何其他安全或隐私问题。借助UDC,Android会将数据写入userdata分区上其他位置。

4.监控运行状况:

运行状况守护进程会检查是否有足够的磁盘空间来创建checkpoint。运行状况守护进程位于 Checkpoint.cpp中的cp_healthDaemon内。运行状况守护进程具有以下可配置的行为:

1
2
3
ro.sys.cp_msleeptime:控制设备检查磁盘使用情况的频率。
ro.sys.cp_min_free_bytes:控制运行状况守护进程查找的最小值。
ro.sys.cp_commit_on_full:控制运行状况守护进程在磁盘已满时,是重新启动设备还是提交checkpoint并继续运行。

三、checkpoint API

UDC功能需要使用checkpoint API(IVoid.aidl)。

1.void startCheckpoint(int retry):

创建一个检查点。framework会在准备好开始升级时调用此方法。系统会先创建checkpoint,然后,带有checkpoint的文件系统(如 userdata)会在重新启动之后挂载读写操作。如果retry counter为正值,API会处理跟踪重试,并且升级程序会调用needsRollback以检查是否需要回滚升级。如果retry counter为 -1,则API会遵循 A/B 升级bootloader的判断。执行正常的A/B升级时不会调用此方法。

2.void commitChanges():

提交更改。准备好提交更改后,framework会在重启之后调用此方法。此方法在数据(如照片、视频、短信和服务器接收回执)写入userdata和BootComplete之前调用。如果不存在带有checkpoint的有效升级,则此方法不起作用。

3.abortChanges():

强制重启并还原到checkpoint的状态。放弃自首次重启之后的所有userdata修改。framework在重启之后,执行commitChanges之前调用此方法。调用此方法时,retry_counter会减少。系统会生成日志条目。

4.bool needsRollback():

确定是否需要回滚。在非checkpoint设备上,会返回 false。在checkpoint设备上,在非checkpoint启动期间会返回true。

四、UDC的参考实现(dm-bow.c)

1.设置:

在init.hardware.rc文件的on fs阶段挂载fstab文件:

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

在 init.hardware.rc 文件的on late-fs阶段挂载fstab文件:

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

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

1
2
3
/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

2.添加metadata分区:

UDC需要使用metadata分区来存储非bootloader retry counter和key。设置metadata分区并提前将其挂载在 /metadata 中。在fstab.hardware文件中,确保将/metadata标记为earlymount或first_stage_mount:

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

将以下行添加到 BoardConfig.mk把分区初始化:

1
2
BOARD_USES_METADATA_PARTITION := true
BOARD_ROOT_EXTRA_FOLDERS := existing_folders metadata

3.升级系统

(1)F2FS系统:确保F2FS版本支持checkpoint。对于在挂载/data的设备,将 checkpoint=fs标记添加到fstab的data的<fs_mgr_flags>部分。

(2)非F2FS系统:必须在kernel config中启用dm-bow。对于在挂载/data的设备,将checkpoint=block 标记添加到fstab的data的<fs_mgr_flags>部分。

4.检查日志:

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

5.验证:

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

Android官网链接:

https://source.android.com/devices/tech/ota/user-data-checkpoint