针对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案例
1、Recovery OTA流程
Android OTA 升级 之 Recovery-CSDN博客
2、UpdateEngine OTA流程
Android OTA 升级 之 UpdateEngine-CSDN博客
3、AB系统 VS 传统系统
在阅读UpdateEngine流程之前一定要先了解AB系统和传统系统到底有哪些差异,如果这个不了解,可能有些懵逼。
参考网上大佬:https://blog.csdn.net/guyongqiangx/article/details/71334889
3.1 分区槽的引入
3.1.1 分区槽的概念
A/B系统就是设备上有A
和B
两套可以工作的系统(用户数据只有一份,为两套系统共用),简单来讲,可以理解为一套系统分区,另外一套为备份分区。其系统版本可能一样;也可能不一样,其中一个是新版本,另外一个旧版本,通过升级,将旧版本也更新为新版本。当然,设备出厂时这两套系统肯定是一样的。
PS:我们对这两套系统的感知通常表现为分区,即烧录镜像的时候会存在两份类似命名的img,例如system_a.img和system_b.img,有可能烧录镜像的时候并不会向ROM中写入两份img,但肯定会在分区中预留其空间。
分区名 | 传统系统 | AB系统 |
bootloader | 存放用于引导linux的bootloader | 存放用于引导linux的bootloader |
recovery | 存放Recovery系统的linux kernel文件和ramdisk | 并不是简单的被删掉了 |
boot | 存放Android主系统的linux kernel文件和用于挂载system和其他分区的ramdisk | |
boot_a/b | 分别用于存放两套系统各自的linux kernel文件和用于挂载system和其他分区的ramdisk | |
system | Android主系统分区,包括Android的系统应用程序和库文件 | |
system_a/b | Android主系统分区,分别用于存放两套系统各自的系统应用程序和库文件 | |
userdata | 用户数据分区,存放用户数据,包括用户安装的应用程序和使用时生成的数据 | 用户数据分区,存放用户数据,包括用户安装的应用程序和使用时生成的数据 |
cache | 临时存放数据的分区,通常用于存放OTA的升级包 | 被删掉了,这就是为什么AB系统不支持吧ota包存放在此目录 |
misc | 存放Android主系统和Recovery系统跟bootloader通信的数据 | 存放Android主系统和Recovery系统跟bootloader通信的数据,由于存放方式和分区名字没有强制要求,所以部分实现上保留了misc分区 |
PS:AB分区对应两套分区,我们把他称为分区槽,每个槽都对应一套boot和super。启动过程中,bootloader会根据条件来决定启动哪一个槽之后,例如这次启动选择slot A,bootloader加载启动boot_a里面的kernel,然后加载启动system_a中的init进程,然后init进程启动update_engine相关联的进程。
3.1.2 分区编译的差异
传统系统和AB系统,针对各个分区的编译也发生了比较大的整改,针对这块我也没有主要去研究它,从大佬的文章可以做出如下总结:
- AB系统相关宏控必现按照如下方式定义
# A/B系统必须定义的变量
AB_OTA_UPDATER := true
AB_OTA_PARTITIONS := boot system vendor
BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
TARGET_NO_RECOVERY := true
BOARD_USES_RECOVERY_AS_BOOT := true
PRODUCT_PACKAGES += update_engine update_verifier
# A/B系统可选定义的变量
PRODUCT_PACKAGES_DEBUG += update_engine_client
# A/B系统不能定义的变量
# BOARD_RECOVERYIMAGE_PARTITION_SIZE
# BOARD_CACHEIMAGE_PARTITION_SIZE
# BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
-
AB系统的recovery.img文件编译不再单独生成,传统方式的recovery.img现在叫做boot.img
-
AB系统的boot.img文件,编译的时候包含kernel和recovery模式的ramdisk
-
AB系统的system.img文件,传统方式下system.img由$(PRODUCT_OUT)/system文件夹打包而成,A/B系统下,制作时将$(PRODUCT_OUT)/root和$(PRODUCT_OUT)/system合并到一起,生成一个完整的带有rootfs的system.img
-
AB系统的userdata.img文件,跟原来一样,打包$(PRODUCT_OUT)/data文件夹而成
-
AB系统的cache.img文件不再单独生成cache.img,因此AB系统不再支持把压缩包通过放到/data或者/cache目录传递给recovery小系统进行升级,因为recovery小系统无法成功挂载。虽然没有cache分区,但是不代表根目录没有cache文件夹,因为odm厂商可能还是会在主系统文件系统创建这个目录
-
vendor.img,文件的生成跟是否A/B系统无关,主要有厂家决定
3.1.3 分区表的差异
3.2 升级原理
3.2.1 传统系统
- 传统系统有一个Android主系统和一个Recovery小系统。他们是同等级的平行世界,即他们分别对应于拥有自己的一套文件系统,init进程等,区别就是Android主系统更强大拥有了android虚拟机和整个framework以上的世界,而Recovery系统只拥有很小的一部分进程,他只能做恢复出厂设置、升级等一些工作,因此又把它叫做小系统。
- Android主系统运行时检测到需要升级,就讲升级的数据包下载并存放到cache分区,并通过向misc分区写入一些命令来通知bootloader下次启动进入recovery小系统(即传统系统通过misc来向bootloader或者recovery小系统来进行通信),做好这些事情后就主动重启系统。
- 重启系统过程中,bootloader通过misc分区的命令来决定是启动Android主系统还是启动Recovery小系统。PS:Android主系统有自己一套kernel,kernel启动init,init启动zygote,zygote启动安卓世界,recovery小系统有自己一套min-kernel,min-kernel启动init,init启动recovery进程。
- Recovery小系统被启动后,可以通过misc分区的命令来决定接下来干那些事情,例如升级分区、恢复出厂设置、进入Android主系统。如果更新分区失败,设备重启就不能正常使用,唯一的办法就是重新升级,直到成功为止。
3.2.2 AB系统
- AB系统有两套分区slot A和slot B。按照系统启动的时候,bootloader从其中一套分区启动,另外一套分区被留着备份,通过几个标记来管理他们的状态。
- Android主系统运行的时候,通过update_engine后台服务进程来接收应用端的升级指令接口。当update_engine对升级数据包下载校验都准备好之后,就会通过boot_control HAL层服务来和bootloader设置标记状态。做好这些事情后就主动重启系统。
- 重启系统过程中,bootloader通过前一步标记的一些状态来决定激活进入哪一个分区槽,即slot A还是slot B。如果需要升级的槽进入激活失败者会回到之前的槽(根据经验可以判断此现象会导致黑屏一次),无论成功还是失败都会设置相应的状态,详情参考后文。
- bootloader无论上一步激活了哪一个槽,如果能进一步启动对应槽的kernel,然后通过init启动update_verifier进程来标记设置分区槽的状态。
- 下一步还会通过init启动的update_engine进程来判断当前槽和升级信息来决定此次升级是否成功。
3.3 AB系统分区槽如何切换?
这里借鉴一下大佬的文章:https://blog.csdn.net/guyongqiangx/article/details/140759462
3.3.1 分区槽的标记
对于A/B
系统的slot A
和slot B
分区,其都存在以下三个属性:
- active:系统的活动分区标识,这是一个排他属性,系统只能有一个分区设置为active属性,启动时bootloader选取设置为active的分区进行启动。
- bootable:分区可启动标识,设置为bootable的分区表明该分区包含了一个完整的可以启动的系统。
- successful:分区成功运行标识,设置为successful的分区表明该分区在上一次启动或当前启动中可以正确运行。
3.3.2 分区槽的状态切换
典型的应用场景有以下4个(假定当前从B分区启动):
说明1:当前运行的系统(current)用绿色方框表示,当前没有用的系统(unused)用灰色方框表示
说明2:属性标识为红色,表示该状态下相应属性被设置,标识为灰色标识该状态下属性没有设置或设置为相反属性,如:
- active表示已经设置active属性当前为活动分区;active 表示没有设置active属性
- bootable表示已经设置bootable属性;bootable表示设置为没有设置bootable属性
- successful表示已经设置successful属性,successful表示没有设置successful属性
状态1:普通场景
最常见的情形,例如设备出厂时,A分区和B分区都可以成功启动并正确运行,所以两个分区都设置为bootable和successful,但由于是从B分区启动,所以只有B分区设置为active。
状态2:升级中
B分区检测到升级数据,在A分区进行升级,此时将A分区标识为unbootable,另外清除successful标识;B分区仍然为active,bootable和successful。
状态3:更新完成等待重启
B分区将A分区成功更新后,将A分区标识为bootable。另外,由于重启后需要从A分区启动,所以也需要将A分区设置为active,但是由于还没有验证过A分区是否能成功运行,所以不设置successful;B分区的状态变为bootable和successful,但没有active。
状态4:从新系统成功启
设备重启后,bootloader检测到A分区为active,所以加载A分区系统。进入A系统后如果能正确运行,需要将A分区标识为successful。对比第1个普通场景,A和B系统都设置为bootable和successful,但active从B分区切换到A分区。至此,B分区成功更新并切换到A分区,设备重新进入普通场景。
3.4 bootloader交互的方式
3.4.1 传统系统
请参考链接:https://blog.csdn.net/qq_27672101/article/details/144148071
3.4.2 AB系统
请参考链接:https://blog.csdn.net/qq_27672101/article/details/144148002