4、Recovery分区:传统recovery模式、recovery-from-boot.patch机制、AB升级下的recovery变化
Recovery分区,说白了就是Android系统的一个「救急工具箱」。我刚开始接触Android底层时,觉得这玩意儿就是个刷机界面,后来踩过几次坑才明白——它其实是系统升级的最后一道防线。
今天咱们就把它掰开揉碎了讲清楚。从传统模式讲起,再到那个神奇的patch机制,最后聊聊AB分区时代Recovery的进化。
4.1 传统Recovery模式
传统Recovery模式,大家应该不陌生。手机进不去系统了,按音量键+电源键,就进入一个黑底蓝字的菜单界面。嗯,就是那个。
它的核心职责其实就三件事:
- 应用OTA升级包——把下载好的升级包刷进去
- 双清/恢复出厂——擦除data分区
- adb sideload——通过电脑推送刷机包
它的启动流程是这样的:
Bootloader → 读取misc分区 → 判断启动模式
├── 正常模式 → 启动boot.img → 进入系统
└── Recovery模式 → 启动recovery.img → 进入Recovery
这里有个关键点:misc分区。它就像一个「传令兵」,Bootloader通过读取它来决定下一步干什么。我在项目中遇到过一个问题——misc分区被意外写坏了,结果手机怎么按都进不去Recovery,最后只能拆机用烧录器重写。所以,千万别小看这个只有几KB的分区。
传统Recovery的局限性:它和boot分区是独立的。如果boot分区坏了,系统起不来,但Recovery还能用。反过来,如果Recovery分区本身坏了...那就真成砖了。
4.2 recovery-from-boot.patch机制
这个机制,说实话,是Google早期的一个「神来之笔」。为什么要搞它?
你想想看,Recovery分区如果被破坏了怎么办?比如刷机刷到一半断电了,或者恶意软件把Recovery分区给抹了。传统方案下,这就得返厂了。
Google的解法很巧妙——把Recovery的「种子」藏在boot分区里。
具体做法是这样的:
- 编译系统时,生成一个
recovery-from-boot.p文件 - 这个文件其实是Recovery镜像和boot镜像的差分补丁
- 把它放在
/system/etc/目录下 - 每次开机时,
init进程会检查Recovery分区是否完整 - 如果不完整,就用boot分区+补丁文件,重新生成Recovery分区
看一段关键代码你就明白了:
// system/core/recovery/recovery-persist.cpp
static bool recovery_needs_patching() {
// 检查recovery分区的头部签名
// 如果签名不对,说明被破坏了
if (verify_recovery_signature() != 0) {
return true; // 需要修复
}
return false;
}
static int apply_recovery_patch() {
// 从boot分区读取原始数据
// 用recovery-from-boot.p生成新的recovery镜像
// 写入recovery分区
return apply_patch("/dev/block/boot",
"/dev/block/recovery",
"/system/etc/recovery-from-boot.p");
}
我曾经在一个项目里遇到过这种情况:客户反馈手机OTA升级后Recovery进不去了。排查了半天,发现是recovery-from-boot.p文件被某个安全软件给删了。嗯,从那以后我就在代码里加了个保护——把这个文件设为不可删除。
个人经验:这个机制在Android 7.0之前非常有用。但到了AB分区时代,它的重要性就降低了。为什么呢?往下看。
4.3 AB升级下的Recovery变化
AB分区,也叫无缝升级。Google从Android 7.0开始引入,彻底改变了升级的玩法。
在AB分区架构下,Recovery的角色发生了根本性变化:
| 对比项 | 传统模式 | AB模式 |
|---|---|---|
| 升级方式 | 重启进入Recovery,离线升级 | 后台在线升级,下次重启生效 |
| Recovery分区 | 独立分区,必须存在 | 可以合并到boot分区 |
| recovery-from-boot.p | 必须存在 | 可以去掉 |
| 升级失败恢复 | 依赖Recovery修复 | 回滚到旧slot即可 |
说白了,AB分区把Recovery的「救急」职责给分担了。为什么?
因为AB分区有两个slot——A和B。你平时用A,升级包在后台写到B。写完后重启,如果B起不来,Bootloader自动回滚到A。整个过程用户无感知。
这样一来,Recovery就不再是「必需品」了。Google甚至允许厂商把Recovery合并到boot分区里,做成一个recovery内核+ramdisk的形式:
// BoardConfig.mk 中的配置
// 传统方式:独立recovery分区
BOARD_RECOVERYIMAGE_PARTITION_SIZE := 0x1000000
// AB方式:recovery合并到boot
BOARD_USES_RECOVERY_AS_BOOT := true
TARGET_NO_RECOVERY := true
我参与过一个项目,客户坚持要保留独立Recovery分区,理由是「万一两个slot都坏了呢?」。说实话,这个概率极低,但既然客户要求,我们就保留了。不过我们把recovery-from-boot.p去掉了——因为AB分区下,boot和recovery是同一份镜像,不需要补丁修复。
注意:虽然AB分区下Recovery不再是必须的,但很多厂商仍然保留它。原因有两个:一是用户习惯(很多人还是想进Recovery双清),二是工厂生产时需要Recovery来刷写分区。所以,别急着删。
4.4 实际项目中的选择建议
讲了这么多,到底该怎么选?我个人的建议是这样的:
- 新项目,用AB分区:用户体验好,升级失败能自动回滚。Recovery可以合并到boot,省一个分区。
- 低端设备,内存小:可以考虑传统模式+recovery-from-boot.p。因为AB分区需要双倍分区空间,低端设备吃不消。
- 特殊需求,比如医疗设备:建议保留独立Recovery分区。这类设备对稳定性要求极高,多一层保障总是好的。
最后说一句:Recovery分区虽然看起来不起眼,但它是系统升级的「压舱石」。设计得好,能省很多售后麻烦。设计得不好...嗯,我见过因为Recovery分区太小,导致大升级包刷不进去的案例,最后只能返厂。
所以,别小看它。