14、升级状态机与日志:升级状态流转、last_install日志分析、升级失败原因排查

做OTA升级开发,最怕什么?

不是代码写不出来,而是升级失败了,你连怎么失败的都不知道。

我早期做这个模块时,就吃过这个亏。客户反馈说“升级到一半手机重启了”,我盯着logcat看了半天,发现关键的日志已经被重启冲掉了。后来我才意识到,last_install日志才是真正的“黑匣子”。

今天我们就来彻底搞懂升级状态机是怎么跑的,日志怎么看,失败原因怎么定位。

14.1 升级状态机:系统升级的“骨架”

升级状态机,说白了就是系统在升级过程中经历的各个阶段。每个阶段都有明确的入口和出口,状态之间通过事件驱动流转。

Android的Recovery模式里,核心状态机定义在install.cpp中。我把它简化成下面这张图,你一看就明白:

升级状态机核心流转图 IDLE (空闲) VERIFY (校验) FAILED (失败) INSTALL (安装) SUCCESS (成功) REBOOT (重启) 收到升级包 校验失败 校验通过 安装完成 准备重启 重启失败 重试

这张图里,我特意把失败状态画成了红色。为什么?因为实际项目中,80%的问题都出在从VERIFY到INSTALL这个阶段。

核心要点:状态机不是单向的。失败后可以重试,但重试次数有限。默认是3次,超过后进入永久失败状态,需要人工干预。

14.2 last_install日志:升级的“黑匣子”

每次升级结束后,Recovery会把关键信息写入/cache/recovery/last_install文件。这个文件不会因为重启而丢失,是排查问题的第一手资料。

我习惯在分析问题时,先看这个文件。它记录了:

  • 升级结果:成功还是失败
  • 失败原因:错误码和错误描述
  • 升级包路径:从哪里读取的升级包
  • 时间戳:每个阶段的时间点

一个典型的last_install日志长这样:

# 示例:last_install 日志
install_result: 0
install_path: /data/ota_package/update.zip
install_recovery: --update_package=/data/ota_package/update.zip
install_time: 2024-03-15 14:23:45
install_stage: 3
install_error: 0

这里install_result: 0表示成功。如果非0,那就是失败。常见的错误码我整理了一张表:

错误码 含义 常见原因
0 成功 -
1 校验失败 升级包损坏、签名不匹配
2 设备空间不足 /cache分区满了
3 安装脚本错误 updater-script语法问题
4 设备兼容性错误 升级包与设备型号不匹配
5 未知错误 需要进一步分析logcat

我的小技巧:如果last_install里显示install_error: 1,别急着怀疑升级包。先检查一下设备时间是否正确。我遇到过好几次,设备时间被重置到1970年,导致签名校验失败。

14.3 升级失败原因排查:实战三板斧

升级失败了,别慌。按照我总结的“三板斧”来排查,大部分问题都能定位:

第一板斧:看last_install

这是最快的方式。直接cat /cache/recovery/last_install,看错误码。如果错误码是0但升级失败了,那说明问题出在升级完成后、系统重启的阶段。这时候要看logcat。

第二板斧:看recovery日志

Recovery模式下的日志保存在/cache/recovery/log。这个文件记录了Recovery启动后的所有输出,包括挂载分区、校验升级包、执行脚本等细节。

我曾经遇到一个案例:升级包校验通过,但安装到一半失败了。last_install里显示错误码3。我打开recovery日志,发现是updater-script里有一条mount命令写错了分区路径。这种问题,不看日志根本猜不到。

第三板斧:看kernel日志

如果前两个日志都没问题,那可能是底层驱动或硬件问题。这时候要看dmesg/proc/last_kmsg。比如,升级过程中突然断电,或者emmc有坏块,都会在kernel日志里留下痕迹。

注意:有些设备在升级失败后会自动重启,导致recovery日志被清空。这时候,last_install是唯一能保留的信息。所以,永远不要只依赖logcat

14.4 状态机与日志的联动分析

理解了状态机,再看日志就清晰多了。每个状态对应日志中的一段记录。我习惯把日志按状态分段看:

  1. IDLE阶段:检查是否有升级包,日志里会打印check_for_update_package
  2. VERIFY阶段:校验签名和哈希,日志里会打印verify_package和校验结果
  3. INSTALL阶段:执行安装脚本,日志里会打印install_package和每一步的执行结果
  4. REBOOT阶段:准备重启,日志里会打印reboot和重启参数

举个例子,如果日志停在verify_package: failed,那问题就在校验阶段。如果日志停在install_package: 50%,那问题就在安装过程中。

实战经验:我遇到过一种情况,日志显示install_package: 100%,但设备就是重启不了。后来发现是misc分区写坏了,导致重启指令没被正确保存。这种问题,看last_install是看不出来的,必须结合kernel日志。

14.5 总结

升级状态机是骨架,last_install日志是血肉。两者结合,才能准确判断问题出在哪里。

我个人习惯在开发阶段,每次升级后都自动备份last_install和recovery日志到/data分区。这样即使设备反复重启,也能保留现场。

嗯,最后提醒一句:别等到线上出问题了才去学怎么看日志。平时多看看正常升级的日志,知道“正常”长什么样,出问题时才能一眼看出“异常”。


公众号:蓝海资料掘金营,微信deep3321