Android OTA升级

针对Android系统OTA升级,MTK平台有相关介绍文档:https://online.mediatek.com/apps/faq/detail?faqid=FAQ27117&list=SW

概念一:OTA包的构建

  • AOSP full build:Android原生提供的全量包的构建,意思就是可以从任何一个比它老的版本通过OTA升级到此版本,因为是全量,所以通常这类包的大小和平时的镜像文件差不多大
  • MTK split build:MTK提供的增量包的构建,即针对两个固定版本之间的差异制作而成的,因此它只能在固定两个版本之间进行OTA升级,因为是差分增量,所以通常这类包的大小和两个版本的差异相关,可以小到几M,在不发达的国家因为网络条件比较差,通常采用此方式进行推送

概念二:AB系统

  • AB系统:AB系统并不是一个系统,Google官方把其描述为A/B无缝系统更新,MTK喜欢把带有此功能的软件配置称为AB系统,即当前软件支持AB系统更新。开启此功能的系统可以实现A/B无缝更新功能,即在不重启或者进入recovery模式下进行OTA更新,即你可以一边正常使用手机,后台守护进程update_engine来进行更新,所以叫做无缝更新。其原理是底层使用了两套分区,详细概念可以参考:A/B(无缝)系统更新 | Android Open Source Project
  • No-AB系统:MTK吧不支持A/B无缝系统更新的软件版本称为No-AB系统,从Google角度,A/B无缝功能在Android 7就开始有了,但是直到Android 13要求强制支持使能,即现在的系统都是强制开启了此功能。开启此功能的系统,ro.build.ab_update属性被设置为true

概念三:OTA升级的方式

  • Recovery Mode:进入Recovery模式进行升级,此种方式在AB系统和No-AB系统上都可以使用,此方式需要进入recovery模式,因此升级过程中,用户肯定是无法使用的。

Recovery UI可以提供SD卡方式和adb sideload d:\update.zip方式进行升级

三方应用可以通过调用android.os.RecoverySystem$installPackage接口实现

  • Update Engine:它是Google为了实现A/B无缝更新新造了一个守护进程,因此只有在AB系统上才能使用此方式,此方式不需要进入recovery或者重启,升级过程中你可以继续使用手机,update_engine升级完毕之后会设置标记位,在下一次重启之后就会进入对应的分区槽启动系统

三方应用可以通过调用android.osUpdateEngine$applyPayload接口实现

注意一:AB系统通过Recovery方式升级,升级包放在/data目录无法被挂载

注意二:AB系统不建议使用Recovery方式进行升级,参考MTK案例

一、Recovery OTA流程

1、processPackage 解密升级包

如上代码逻辑,只针对/data目录下面的升级包进行处理,中间写了处理监听器progressListener传递到uncrypt方法中用来在UI界面显示进度条,其uncrypt在RecoverySystemService中实现:

如上代码在RecoverySystemService中实现,正常的recovery升级日志中将会有如下sys日志打印:

2、verifyPackage 校验升级包

3、installPackage 安装升级包

如上代码安装升级包通常以关键日志开始,但是此打印在main日志中:

接下来的流程如下

1)data目录固定block.map路径

如果processed为true表示已经对升级包进行了处理,这个时候如果block.map不存在直接报错;如果processed为false,会将升级包的路径写入到uncrypt_file文件中,并且删除block.map文件。

这里有点疑问?为什么processed为false的时候会直接删除block.map,接下来为拼接的命令路径又是/cache/recovery/block.map呢?这会导致进入recovery系统的时候根本就挂载不上此文件,从上文的注释来看升级包位于/data目录的话通过block.map来记录升级包路径,这逻辑有点前后矛盾。block.map到底有什么意义?

2)主系统向recovery系统写命令

这段代码先是拼接了command,如下为升级包放在/data目录下的命令,可以看出这个命令是给到native层的recovery进程使用,recovery进程接受擦除升级等命令,其中__update_package看起来就是升级命令,其recovery日志如下:

3)主系统的systemservice进程如何与recovery系统通信?

main系统和recovery系统实际上是两个不同的系统,他们的载体也是不同的分区,所以可以把他们当成永不相交的两个平行世界,在系统启动的时候bootloader会根据标识来选择进入哪一套系统,他们都会启动他们自己的init进程,以及其他一些服务例如adb。他们之间的通信和交互如下:

接下来看看main系统是如何把这个command和升级包传递到recovery小系统的呢?如上图,他们通过/cache/recovery为媒介进行交互,其中main系统调用的BCB就是其中的大功臣。

如上代码获取了RecoverySystemService的实例执行rs.setupBcb(command)):

通过socket的通信方式,其socket定义为/dev/socket/uncrypt,这一块详细流程参考:

https://www.cnblogs.com/liang123/p/6325225.html

4)为升级包申请元数据

最开始看这里的代码觉得很奇怪,传递的升级包绝对路径的名称,然后new ZipFile,根据提示来看这里的文件还不能超过100MB,当时想的是随便一个全量升级包都超过100M呢,最后仔细观察如上圈红代码,是从获取升级包的元数据。此段逻辑在日志中也有体现,如下main日志:

5)重启进入recovery系统

这里调用了PowerManager的reboot命令进行重启,并且重启原因为recovery升级,如下日志:

为什么需要调用reboot才能进入recovery模式,因为recovery小系统和main系统根本就是两个世界,如下reboot之后的系统启动流程图:

4、uncrypt进程解析

Native层的uncrypt进程的在rc中的定义,从其主函数可以看出来他主要具有三种功能,其触发场景分别如下:

1)uncrypt_wrapper:RecoverySystem$processPackage进行解密

前文介绍processPackage的时候已经说明了,RecoverySystem对升级包的处理,实际上就是对升级包进行加密处理,但实际实现逻辑的进程就是uncrypt进程,如下processPackage是如何调用到uncrypt解密处理:

即对升级包处理完成之后会创建写入到/cache/recovery/block.map文件中,如下日志:

为什么需要对data目录的升级包进行uncrypt解密处理呢?

随着android系统的更新,在android7.0中,recovery模式中已经不在挂载data分区,而且data分区可以设置加密,这样更促进了在recovery分区中不挂载data分区,加强了用户的安全性。但是这样就会有问题,当升级包较大时cache分区是放不下的,增大cache分区只会浪费资源,最好的办法还是把它放在data分区下。但是因为加密和不挂载的原因导致在recovery模式下是无法使用升级包的。而uncrypt机制就是解决这个问题的。它的基本原理就是,在正常模式下记录升级包所在的磁盘区块号,而且如果加密了就通过读进行解密,再绕过加密写到存储磁盘上。当记录好了磁盘区块号后,启动到recovery模式后就可以不挂载data分区,直接根据区块号读取升级包中的数据进行升级。下面记录代码的分析流程。

https://www.cnblogs.com/startkey/articles/11213034.html此篇文章对齐做了详细的说明

2)setup-bcb:RecoverySystem$installPackage设置recovery命令

如上在调用installPackage接收到RecoverySystemService发来的命令写入到bootloader的域中,最后重启后bootloader根据此命令来启动recovery小系统,并将命令传递给recovery进程。如上逻辑对应日志如下:

3)clear-bcb:RecoverySystem$installPackage状态恢复

RecoverySystem$installPackage

5、Recovery进程解析

这里的Starting recovery日志只是说明recovery进程被启动,如下日志:

1)Recovery相关功能介绍

start_recovery函数被定义在system/bootable/recovery/recovery.cpp,此文件没有单独的main,因此此部分逻辑还是属于recovery进程的,通过注释可以了解到此文件主要实现ota升级和恢复出厂设置两大功能:

如上文的recovery.log中讲解析RecoverySystemService传递过来的命令:

Command: "/system/bin/recovery" "--update_package=@/cache/recovery/block.map" "--locale=en-US"

2)update_package执行升级流程

Recovery.log对应的日志如下:

3)Recovery OTA升级的真正逻辑

6、Recovery分区挂载

1)读取分区表配置文件

2)特殊分区的检查

注意:AB系统已经不支持cache了,所以AB系统这里的日志绝对会挂载失败,但奇怪的是,我进入recovery模式还是能够进入cache目录,如下recovery.log:

3)打印分区列表

recovery.log日志如下:

4)分区表配置文件的生成

根据如上代码可以了解到分区表配置文件路径/etc/recovery.fstab,我们可以进入recovery模式执行如下命令:adb pull /etc/recovery.fstab,如下内容

连续pull了多台机器的recovery分区表,但是和代码中配置的格式完全不一样,最初以为是从代码里面直接copy过去的,结果完全搜索不到关键字

根据如下文档了解到由TARGET_RECOVERY_FSTAB拼接出来的,详情可以参考:

https://blog.csdn.net/xiaocui92/article/details/79161206

7、案例

问题描述:客户自己开发的三方应用,通过调用android.os.RecoverySystem$installPackage接口进行OTA升级,此接口传递的OTA升级包路径为/data/media/0/Download/update.zip,升级失败

以往案例:通过CSDN搜索此类问题,之前的案例基本上是AVC Selinux权限导致的block.map文件没有成功写入到recovery模式下,可以参考链接:

https://blog.csdn.net/tkwxty/article/details/106101317

最后结论:反复做了测试,在main系统的时候,/cache/recovery/block.map文件存在,但进入recovery系统的时候,/cache目录下是空的,从官方文档得来,AB系统目前并不支持把升级包放到/data目录,原文如下:

二、UpdateEngine OTA流程

相关推荐
每次的天空22 分钟前
Android学习总结之应用启动流程(从点击图标到界面显示)
android·学习
一清三白2 小时前
Android Studio 连接雷电模拟器教程
android
姜行运2 小时前
数据结构【栈和队列附顺序表应用算法】
android·c语言·数据结构·算法
wang_peng3 小时前
android studio 基础
android·ide·android studio
〆、风神5 小时前
EasyExcel 数据字典转换器实战:注解驱动设计
android·java·注解
stevenzqzq5 小时前
Android studio xml布局预览中 Automotive和Autotive Distant Display的区别
android·xml·android studio
QING6186 小时前
Kotlin commonPrefixWith用法及代码示例
android·kotlin·源码阅读
QING6186 小时前
Kotlin groupByTo用法及代码示例
android·kotlin·源码阅读
兰琛11 小时前
Compose组件转换XML布局
android·xml·kotlin
水w13 小时前
【Android Studio】解决报错问题Algorithm HmacPBESHA256 not available
android·开发语言·android studio