4. Recovery系统架构
Recovery 并不是一个“独立操作系统”,而是 Android 在正常系统之外启动的一套最小化运行环境。它由 bootloader 引导进入 recovery 分区,内核启动后挂载 ramdisk,最终执行 /sbin/recovery 这个二进制程序。理解 Recovery 架构,核心是看清六个组成部分如何协作:recovery 二进制负责主流程,init.rc负责启动环境,recovery.fstab负责分区挂载,UI 引擎负责显示与交互,命令解析器负责执行脚本,内核接口负责底层设备访问。
一、recovery 二进制:主流程的调度中心
recovery 二进制是 Recovery 模式的入口程序,通常位于 ramdisk 的 /sbin/recovery。它的主流程可以概括为:解析启动参数、初始化日志、挂载分区、加载 UI、读取命令文件、执行命令、最后重启或关机。源码入口在 bootable/recovery/recovery.cpp,核心函数是 main()。
int main(int argc, char **argv) {
// 1. 解析参数,如 --wipe_data、--update_package
// 2. 初始化 recovery.fstab 分区表
// 3. 挂载 /cache、/data、/sdcard
// 4. 创建 ScreenRecoveryUI 或 WearRecoveryUI
// 5. 读取 /cache/recovery/command
// 6. 执行 install_package / wipe_data 等动作
// 7. 写入 last_log,重启系统
}
recovery 二进制并不直接操作硬件,而是通过调用 librecovery、libfs_mgr、libcutils 等库完成分区挂载、文件写入和 UI 绘制。它更像一个“总指挥”,把分区、UI、命令、内核接口串联起来。
二、init.rc:启动环境与属性配置
Recovery 的 init.rc 位于 ramdisk 根目录,与正常系统的 init.rc 不同,它只保留最小化服务。典型内容包括:创建 /sbin、/proc、/sys 等目录,设置 ro.recovery 属性,启动 recovery 服务,并配置 on boot 触发条件。
on early-init
mkdir /sbin 0755
mkdir /cache 0770
mount tmpfs tmpfs /tmp
service recovery /sbin/recovery
seclabel u:r:recovery:s0
oneshot
on property:sys.boot_completed=1
write /proc/sys/kernel/sysrq 0
init.rc 决定了 recovery 进程以什么身份、在什么时机、以什么权限启动。移植时如果 recovery 无法启动,首先要检查 init.rc 中服务路径、权限和 seclabel 是否正确。
三、recovery.fstab:分区挂载的“地图”
recovery.fstab 描述每个分区的挂载点、文件系统类型、设备节点和挂载参数。它决定了 recovery 能识别哪些分区、能否正确挂载 /system、/data、/cache、/sdcard。常见格式如下:
/system ext4 /dev/block/by-name/system
/data ext4 /dev/block/by-name/userdata
/cache ext4 /dev/block/by-name/cache
/misc emmc /dev/block/by-name/misc
/sdcard vfat /dev/block/mmcblk1p1
不同 Android 版本 fstab 格式略有差异,Android 10 之后逐步迁移到 system/etc/recovery.fstab 与 first_stage_mount。移植时若出现“Can't mount /cache”或“unknown volume for path”,多半是 fstab 中设备节点或文件系统类型写错。
四、UI 引擎与命令解析器
UI 引擎负责在 recovery 模式下显示菜单、进度条和日志。Android 提供 ScreenRecoveryUI(文字界面)和 WearRecoveryUI(手表界面),开发者也可以自定义 UI。UI 引擎通过 framebuffer 或 DRM 直接绘制,不依赖 SurfaceFlinger。
命令解析器负责读取 /cache/recovery/command 和 /cache/recovery/last_command,把 --wipe_data、--update_package=/sdcard/update.zip 等参数转换为具体动作。它还支持 --locale、--reason、--shutdown_after 等选项。
| 组件 | 关键文件/接口 | 作用 | 常见问题 |
|---|---|---|---|
| recovery 二进制 | /sbin/recovery | 主流程调度 | 启动即退出、段错误 |
| init.rc | /init.rc | 启动服务与属性 | 服务未启动、权限不足 |
| recovery.fstab | /etc/recovery.fstab | 分区挂载 | 挂载失败、分区找不到 |
| UI 引擎 | ScreenRecoveryUI | 显示与交互 | 花屏、按键无响应 |
| 命令解析器 | /cache/recovery/command | 执行升级/清除 | 命令不生效、参数错误 |
| 内核接口 | /dev/block、sysfs | 底层设备访问 | 设备节点缺失、ioctl 失败 |
五、内核接口:recovery 与硬件的桥梁
Recovery 通过内核接口访问存储、电源和显示设备。常见接口包括:/dev/block/by-name/* 分区设备节点、/sys/class/leds 指示灯、/proc/mtd MTD 分区信息、ioctl 控制块设备、reboot() 系统调用重启。移植新平台时,必须确保内核已导出正确的分区节点,否则 recovery.fstab 无法挂载。
adb shell 进入 recovery,执行 ls -l /dev/block/by-name/ 确认设备节点是否存在;若不存在,检查内核 dts 中分区定义与 bootloader 分区表是否一致。
六、案例与复盘:一次 recovery 无法挂载 /cache 的排查
某项目移植后进入 recovery,界面提示 Can't mount /cache。排查步骤:第一,查看 /tmp/recovery.log,发现 failed to mount /dev/block/by-name/cache;第二,执行 ls -l /dev/block/by-name/,发现没有 cache 节点;第三,检查内核 dts,发现 cache 分区被误写成 cache_a;第四,修改 fstab 与 dts 后重新编译,问题解决。
总结来看,Recovery 架构的六个部分各司其职:recovery 二进制是大脑,init.rc 是启动开关,recovery.fstab 是地图,UI 引擎是脸面,命令解析器是执行手,内核接口是神经末梢。只有六者协同,Recovery 才能稳定完成恢复出厂、OTA 升级、分区挂载等关键任务。