36、OTA升级调试:recovery模式、update_engine、AB分区、升级失败分析
OTA升级,说白了就是手机系统“空中换血”。
我做了这么多年Android系统,最怕听到的一句话就是:“用户升级后变砖了”。嗯,今天我们就来聊聊OTA升级背后的那些事——recovery模式怎么用、update_engine怎么工作、AB分区到底好在哪,以及升级失败了该怎么查。
一、OTA升级的整体流程
先看一张图,帮你快速建立整体认知:
二、recovery模式:最后的防线
recovery模式,说白了就是一个轻量级的Linux环境。它不依赖Android框架,所以即使系统挂了,它还能干活。
我遇到过最惨的一次——用户OTA到一半断电了,重启后卡在开机动画。还好recovery模式还在,进去手动刷了完整包才救回来。
2.1 进入recovery模式
常见方式有两种:
- 按键组合:关机状态下,同时按住“音量上 + 电源键”
- adb命令:
adb reboot recovery
2.2 recovery模式下的关键操作
| 操作 | 说明 | 适用场景 |
|---|---|---|
| apply update from ADB | 通过adb sideload推送升级包 | OTA失败后手动刷机 |
| apply update from SD card | 从SD卡读取升级包 | 无电脑时应急使用 |
| wipe data/factory reset | 清除用户数据 | 系统无法启动时恢复 |
| apply update from cache | 从cache分区读取升级包 | OTA下载完成后手动触发 |
adb reboot recovery 配合 adb sideload ota.zip,这样能实时看到日志输出,方便定位问题。
三、update_engine:升级的幕后推手
update_engine是Android OTA升级的核心服务。它负责下载、校验、写入升级包。
说白了,它就是升级流程的“总指挥”。
3.1 update_engine的工作流程
- 接收升级指令:从SystemUI或Settings接收升级请求
- 下载升级包:通过HTTPS从服务器下载
- 校验签名:验证升级包的完整性和合法性
- 写入分区:将升级内容写入非活跃槽位
- 标记成功:写入完成后标记槽位为可启动状态
- 触发重启:通知系统重启进入新系统
3.2 关键日志查看
调试update_engine,我最常用的命令是:
# 查看实时日志
logcat -b main -s update_engine
# 查看详细日志
logcat -b main -s update_engine:* -v time
# 查看状态
dumpsys update_engine
# 查看当前槽位
getprop ro.boot.slot_suffix
重点: 升级失败时,第一件事就是看update_engine的日志。它会告诉你失败的具体原因——是下载超时、校验失败,还是写入分区出错。
四、AB分区:无缝升级的秘密
AB分区,说白了就是系统有两套“副本”。
你想想看,传统升级为什么有风险?因为你在“原地”修改系统,一旦断电或出错,系统就挂了。AB分区解决了这个问题——你在后台升级另一套,升级完切换过去就行。
4.1 AB分区的结构
| 分区 | Slot A | Slot B |
|---|---|---|
| boot | boot_a | boot_b |
| system | system_a | system_b |
| vendor | vendor_a | vendor_b |
| product | product_a | product_b |
4.2 AB分区的优势
- 无缝升级:用户正常使用,后台完成升级
- 安全回滚:升级失败自动回滚到旧系统
- 减少变砖风险:即使升级过程中断电,也不会损坏当前运行的系统
五、升级失败分析:从日志到根因
升级失败,说白了就那几种原因。我总结了一个排查思路:
5.1 常见失败原因
| 失败类型 | 典型日志 | 可能原因 |
|---|---|---|
| 下载失败 | Download failed: HTTP 404 | 服务器地址错误、网络不通 |
| 校验失败 | Signature verification failed | 升级包损坏、签名不匹配 |
| 空间不足 | Not enough space on partition | 目标分区剩余空间不够 |
| 写入失败 | Failed to write to /dev/block/... | 分区损坏、文件系统错误 |
| 启动失败 | Failed to boot from slot B | 内核不兼容、驱动缺失 |
5.2 我的排查步骤
- 看日志:先看update_engine日志,找到失败点
- 看槽位:用
getprop ro.boot.slot_suffix确认当前槽位 - 看分区:用
ls -l /dev/block/by-name/检查分区是否存在 - 看空间:用
df -h检查分区剩余空间 - 看签名:用
openssl验证升级包签名
我曾经遇到过一个坑: 某次升级总是失败,日志显示“写入分区成功”,但重启后还是旧系统。查了半天,发现是bootloader的槽位切换逻辑有bug——它只检查了boot分区,没检查system分区。升级后system分区写入了新内容,但boot分区还是旧的,导致启动时加载了旧内核,自然进不了新系统。
解决方案:在update_engine中增加槽位完整性校验,确保所有关键分区都升级成功后再切换。
5.3 强制回滚操作
如果升级失败,系统会自动回滚。但有时候需要手动干预:
# 查看当前活跃槽位
getprop ro.boot.slot_suffix
# 强制切换到另一个槽位(需要root)
su
echo 0 > /sys/block/mmcblk0/device/slot
# 或者通过fastboot切换
fastboot set_active a
fastboot reboot
六、调试工具与技巧
最后,分享几个我常用的调试命令:
# 查看升级状态
dumpsys update_engine
# 查看槽位信息
bootctl hal-info
# 查看分区表
ls -l /dev/block/by-name/
# 查看升级包信息
unzip -l ota.zip
# 查看升级脚本
unzip -p ota.zip META-INF/com/google/android/updater-script
ota_debug_YYYYMMDD_HHMMSS.log,方便后续追溯。
嗯,OTA升级调试其实没那么玄乎。掌握了recovery模式、update_engine、AB分区这几个核心概念,再配合日志分析,大部分问题都能搞定。记住——升级失败不可怕,可怕的是不知道怎么查。
公众号:蓝海资料掘金营,微信deep3321