4. Recovery系统架构

Recovery 并不是一个“独立操作系统”,而是 Android 在正常系统之外启动的一套最小化运行环境。它由 bootloader 引导进入 recovery 分区,内核启动后挂载 ramdisk,最终执行 /sbin/recovery 这个二进制程序。理解 Recovery 架构,核心是看清六个组成部分如何协作:recovery 二进制负责主流程,init.rc负责启动环境,recovery.fstab负责分区挂载,UI 引擎负责显示与交互,命令解析器负责执行脚本,内核接口负责底层设备访问。

Recovery 系统架构与启动链路 Bootloader 选择 recovery 模式 Linux Kernel 加载 recovery ramdisk init 进程 解析 init.rc /sbin/recovery 主二进制启动 recovery 二进制主流程 初始化 → 挂载分区 → 加载 UI → 执行命令 recovery.fstab 分区表:/system /data /cache /sdcard /misc UI 引擎 文字菜单 / 图形界面 按键 / 触摸事件处理 命令解析器 解析 /cache/recovery/ command 与脚本 内核接口 mtd / block / sysfs ioctl / reboot / mknod Linux 内核与硬件层 eMMC / UFS / MTD / 分区设备节点 / 电源管理

一、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 无法挂载。

移植提示:如果 recovery 能启动但无法挂载分区,优先用 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.fstab 当成普通 fstab 直接复制;忽略 seclabel 导致 recovery 服务被 SELinux 拒绝;UI 引擎与内核 framebuffer 格式不匹配导致花屏;命令解析器读取的 command 文件权限不对,导致 OTA 包无法自动升级。

总结来看,Recovery 架构的六个部分各司其职:recovery 二进制是大脑,init.rc 是启动开关,recovery.fstab 是地图,UI 引擎是脸面,命令解析器是执行手,内核接口是神经末梢。只有六者协同,Recovery 才能稳定完成恢复出厂、OTA 升级、分区挂载等关键任务。