OTA升级失败排查
声明
郑重声明:博文为原创内容,可以转载或引用,但必须在明显位置标明原文作者和出处,未经同意不得擅自修改本文内容!
博客地址:http://blog.csdn.net/luzhenrong45
摘要
OTA是Android设备进行系统更新的一种重要方式,它能通过网络或sdcard的方式进行系统更新升级。当然,对于OTA这个流程,前面已经进行过详细介绍,有兴趣可以查看文档《Android Recovery升级原理》。此处,针对OTA升级失败的一些可能原因和排查手段进行简单介绍。(这里只针对RK平台)
1. Recovery OTA升级流程图
首先先回顾一下,OTA从应用层到Recovery升级的整个流程。
1.1 应用层升级流程
首先,在Android应用层部分,OTA系统升级流程。大概的流程图如下所示:
在应用层下载升级包后,会调用RecoverySystem.installPackage(Context context, File packageFile)函数来发起安装过程,这个过程主要的原理,实际上只是往 /cache/recovery/command 写入ota升级包存放路径,然后重启到recovery模式,仅此而已。
1.2 Recovery升级流程
进入Recovery模式之后,便开始对下载的升级包进行升级,整体的流程图如下所示:
详细过程,请查看文档《Android Recovery升级原理》。
2. OTA升级失败排查手段
工欲善其事,必先利其器。排查OTA升级失败问题,一般依靠的是升级过程日志。而获取升级过程日志,一般有以下几种方式:
- 开机能进系统的,直接读取/cache/recovery目录下的日志文件
- Recovery模式下使用串口实时输出升级日志
- 利用RK工具dump flash芯片中的cache分区,手动挂载分区镜像到电脑查看日志
下面对以上三种方式进行概要说明:
2.1 方式一:读取cache日志文件
对于Recovery OTA升级,一旦升级完毕,不管成功与否,最终重启到Main System之前,都会将OTA升级的过程日志和升级结果保存到cache分区。因此,针对OTA升级失败后,开机还能进入系统的,可以使用这种方式查看升级日志。更具体地说,是将Recovery升级信息保存到以下几个文件:
/cache/recovery/last_log
/cache/recovery/last_install
/cache/recovery/last_locale
- last_locale:最近一次recovery使用的语言,如在更新时界面显示的提示语的语种,英语、日语等
- last_install:最近一次recovery升级结果,1为成功,0为失败
- last_log:最近一次recovery升级的过程日志,一般来讲,分析失败问题依靠的是这个last_log日志文件
如果OTA升级成功,install_package安装函数会返回 INSTALL_SUCCESS 的安装结果。last_log 日志类似如下:
I:[try_update_binary] return INSTALL_SUCCESS, function finish.
I:[really_install_package] try_update_binary return INSTALL_SUCCESS.
I:[really_install_package] function finish.
I:[install_package] really_install_package return INSTALL_SUCCESS.
I:[install_package] done.
sf_updatefile_install status =0 ,wipe_cache = 0
I:[sf_install_all_packages] function finish.
I:[sf_scan_directory] sf_install_all_packages return 1, install packages success.
I:[sf_scan_directory] function finish.
I:now go to finish_recovery and save log.
I:[finish_recovery] copy recovery logs to /cache/recovery .
而如果Android设备在OTA升级过程中出现中断或失败,则install_package安装函数会返回INSTALL_ERROR 或 INSTALL_CORRUPT:
E:Error in /mnt/sdcard/ota/001SF-UPDATE-JW-RK30-MCU-34180.zip
(Status 7), return INSTALL_ERROR!!!
E:[really_install_package] try_update_binary return INSTALL_ERROR !!!
I:[really_install_package] function finish.
E:[install_package] really_install_package return INSTALL_ERROR !!!
I:[install_package] done.
sf_updatefile_install status =1 ,wipe_cache = 0
Installation aborted.
......
scanDirectory install fail: No such file or directory
E:[sf_scan_directory] sf_install_all_packages return -1, install packages fail !!!
I:[sf_scan_directory] function finish.
不过,使用这种方法,缺点也很明显,如果升级到一半出现宕机,相关的日志是无法保存下来的。
2.2 方式二:Recovery模式下使用串口实时输出升级日志
上面说过,进入Recovery模式操作之后,在重启到Main System之前,会将日志保存到cache目录下的日志文件中,也就是说,默认Recovery是将日志重定向到文件中。我们只能将升级操作完成之后,再重启到主系统查看日志。如果系统升级失败开不了机,那么,查看日志就变得比较麻烦。另外,有时候,我们也想实时查看升级过程中的日志,比如升级操作进行到哪一步,我们就想看那一步相应的日志,这样的话,方式一就无法满足我们的需求。
事实上,如果看过Recovery的代码,可以知道,其实Recovery的过程日志,也是可以实时重定向到串口设备的,这样一来,就可以直接使用串口线和串口软件来查看Recovery实时输出的日志。
如果要将Recovery械下的日志实时输出到串口之中,需要修改一下Recovery代码,使日志文件默认重定向到串口设备。
假设设备的串口对应的是/dev/FIQ0, 首先需要在内核配置文件中开启FIQ串口输出选项(只针对 RK平台):
+CONFIG_CMDLINE="console=ttyFIQ0 androidboot.console=ttyFIQ0"
+# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
+CONFIG_CMDLINE_EXTEND=y
当然,Recovery日志默认重定向到日志中,要想将Recovery模式下的打印日志输出重定向到串口之中,需要修改源码目录 下的bootadble/recovery/recovery.cpp文件,添加以下代码,将标准输出,错误输出等日志重定向到/dev/ttyFIQO串口:
这样一来,使用串口线连接盒子的串口,当Recovery有日志输出的时候,就会实时打印到串口设备,使用串口软件即可查看日志。
2.3 方式三:利用工具dump flash芯片中的cache分区(推荐使用)
使用串口这种方式优点是可以实时打印升级日志,但缺点也很明显,需要修改修改代码,然后重新编译一个新的recovery.img烧写到设备里面。对于已经在使用的客户盒子,显然不合适。另外,对于升级失败的盒子,我们往往会尽可能地保留现象环境,方便排查问题。
因此,针对客户返修的升级失败导致开不了机的设备,事实上,方式一和方式二这两种手段都显得有点心有余而力不足。当然,只要肯想,办法总是有的。
从前面的介绍可以知道,recovery升级完毕,会将日志保存到cache分区。一般来讲,如果是因为盒子升级失败而导致开机黑屏,cache分区是不会被影响到的。而cache文件,说白了,其实就是一个文件系统格式为ext4的分区文件,如果有办法将固化在flash里面的cache分区dump出来,那将这个cache分区镜像以ext4的方式挂载到电脑,不就可以取出里面的日志文件!
那么,此种方式是否可行?
答案当然是肯定的!以Rockchip平台为例,Rockchip提供了两个linux开源工具 ——– rkflashkit 和 rkflashtool镜像烧写,但这两个工具还可用于将存储在flash芯片中的各个分区dump出来。两个工具使用方法大同小异,以 rkflash为例,介绍一下如何在不破坏现场环境的情况下将开机黑屏的盒子的升级日志取出来。
- 首先需要先安装rkflashkit
sudo apt-get install build-essential fakeroot
git clone https://github.com/linuxerwang/rkflashkit
cd rkflashkit
./waf debian
sudo apt-get install python-gtk2
sudo dpkg -i rkflashkit_0.1.2_all.deb
- 使用rkflashkit需要先让设备进入bootloader状态,即刷机状态。rkflashkit有图形界面和命令行两种模块:
(1)图形界面
sudo rkflashkit
(2)命令行模式
$ rkflashkit --help
Usage: <cmd> [args] [<cmd> [args]...]
part List partition
flash @<PARTITION> <IMAGE FILE> Flash partition with image file
cmp @<PARTITION> <IMAGE FILE> Compare partition with image file
backup @<PARTITION> <IMAGE FILE> Backup partition to image file
erase @<PARTITION> Erase partition
reboot Reboot device
For example, flash device with boot.img and kernel.img, then reboot:
sudo rkflashkit flash @boot boot.img @kernel.img kernel.img reboot
比如查看各个分区的大小和起始地址,偏移地址,只需要输入rkflash part就可以一目了然:
recovery$ sudo rkflashkit part
Found devices:
0x2207:0x310b
============= 2017-03-14 10:53:57 ============
Reading flash information
Loading partition information
CMDLINE:console=ttyFIQ0 androidboot.console=ttyFIQ0 init=/init initrd=0x62000000,0x00800000 mtdparts=rk29xxnand:0x00002000@0x00002000(misc),0x00004000@0x00004000(kernel),0x00008000@0x00008000(boot),0x00008000@0x00010000(recovery),0x000C0000@0x00018000(backup),0x00040000@0x000D8000(cache),0x00100000@0x00118000(userdata),0x00002000@0x00218000(kpanic),0x00100000@0x0021A000(system),-@0x0033A000(user)
Partitions:
misc (0x00002000 @ 0x00002000) 4 MiB
kernel (0x00004000 @ 0x00004000) 8 MiB
boot (0x00008000 @ 0x00008000) 16 MiB
recovery (0x00008000 @ 0x00010000) 16 MiB
backup (0x000C0000 @ 0x00018000) 384 MiB
cache (0x00040000 @ 0x000D8000) 128 MiB
userdata (0x00100000 @ 0x00118000) 512 MiB
kpanic (0x00002000 @ 0x00218000) 4 MiB
system (0x00100000 @ 0x0021A000) 512 MiB
user (0x00B56000 @ 0x0033A000) 5804 MiB
Done!
言归正传,现在介绍如何将cache分区dump到文件cache.img来并取出升级日志:
(1)dump cache分区
sudo rkflashkit backup @cache cache.img
(2)在PC上以ext4格式挂载cache镜像
sudo mount -t ext4 cache.img /mnt/cache-dir/
(3)从cache-dir目录取出log日志文件
/mnt/cache-dir/recovery/last_log
/mnt/cache-dir/recovery/last_install
/mnt/cache-dir/recovery/last_locale
剩下的,就是分析log日志,定位出升级失败的原因了。
2017/10/16更新:增加MTK平台dump分区方法
如果是MTK平台,也同样可以使用工具导出cache分区,其方法大同小异。具体可参考博文《 mtk平台dump系统分区》
3. OTA升级失败的一些可能原因
一般来说,OTA升级失败的可能原因,包括但不限于以下几种:
下载的包是错误的
这个无可厚非flash损坏(只读或颗粒损坏,硬件问题)
如果flash硬件损坏,比如变成只读或者颗粒损坏,那么,有可能升级过程无法正常格式化或正常解压,就会出现安装失败的情况。system分区无法格式化
之前有同事在开发的时候,往OTA升级包人工额外插入了一些升级脚本,结果弄巧成拙,这些脚本会导致system分区一直被占用从而无法顺利完成格式化操作,进而导致升级失败问题。升级过程强制或意外断电
理论上升级过程意外断电,下次重启,由于BCB机制的导引,仍然会进入Recovery继续完成升级操作。但也有可能由于意外断电导致flash出现问题,从而影响正常升级手动改包
之前有遇到个别客户,不知出于何种目的,没有按常规方式进行升级,而是手动进recovery模式自行升级,结果包取错了…手动进Recvoery升级,cache分区没挂载导致差分包升级失败
早期的系统,手动进Recovery的时候,cache分区默认是没有挂载的。因此,如果在这种情况下升级差分包,就会导致升级失败,因为差分包升级是依赖于cache分区的,它需要往cache分区存放一些中间文件,这种情况下也会导致升级失败。缺包升级
Android ota的升级支持完整包和差分包升级两种方式,有的是采用完整包升级,有的是采用差分包升级。有些设备为了节省空间,采用了A完整包+B差分包的方式来对B进行ROM升级。如果由于某些原因,导致升级的时候只升级了其中一个包,另外一个包没有升级成功,也是会出现升级失败开不了机的风险。等等等等…..
4. 再啰嗦一句
OTA升级失败的原因,肯定不止以上几个,但掌握了上面介绍的几种排查手段,分析OTA失败原因,一般也不会太难。
修改说明
作者 | 版本 | 修改时间 | 修改说明 |
---|---|---|---|
WalkAloner | V1.0 | 2017/03/14 | 首版 |
WalkAloner | V1.1 | 2017/10/16 | 增加mtk平台dump系统分区说明 |