一、概述
目前Android系统终端的升级主要是通过无线进行的(FOTA,Firmware Over-The-Air),主要流程是通过无线方式将升级包下载到终端,而后调用系统的升级接口进行升级。本文主要分析升级包下载后,调用系统升级接口之后的流程。
1.1 升级包结构
升级包是用make otapackage命令生成的,对于差分包,需要生成两个ota整包,再用系统的编译工具利用这两个整包生成一个差分包。下面是某终端Android9.0版本的ota升级包结构。
1 | ├─firmware-update |
firmware-update:目录下主要是一些固件的升级,如mbn、dtbo、vbmeta等
boot.img:更新boot分区所需要的文件,包括kernel+ramdisk
system.new.dat.br:Android8.1镜像后新的压缩格式,为数据部分,里面是system.img数据。
system.transfer.list:数据转换的描述列表。
system.patch.dat:升级时用到,ota包中大小为0。
vendor和system类似。
update-binary:二进制文件,相当于一个脚本解释器,能够识别updater-script中描述的操作,文件的命名由bootable/recovery/install.cpp 中的UPDATE_BINARY_NAME值而定。
updater-script:升级包的升级脚本,文件的命名由bootable/recovery/updater/updater.cpp中的SCRIPT_NAME值而定。
metadata:描述设备信息及环境变量的元数据,主要包括一些编译选项、签名公钥,时间戳以及设备型号等。
otacert:ota签名。
1.2 系统启动模式
Android系统启动主要有两种:
1.组合键
用户通过按下组合键,不同的终端可以定义组合键,进入不同的工作模式,在bootable/bootloader/lk/app/aboot/aboot.c文件中判断,具体有两种:
bootloader模式,可进一步进入fastboot(快速刷机模式)。
Recovery模式,进入这种模式系统会出现一个简单的ui界面,用来提示用户的进一步操作。
2.正常启动
没有按下任何组合键,正常开机,bootloader会读取启动控制信息块BCB(bootloader control block),它是一个结构体,存放着启动命令Command。根据不同的命令,系统可以进入三种不同的启动模式。下面是Bootloader Message的定义,通过注释可以了解具体结构体中成员变量的含义。
[->bootloader_message.h]
1 | /* Bootloader Message (2-KiB) |
二、OTA升级重启前
前面介绍完OTA升级包和系统启动模式,现在看下真正的ota升级的步骤。无论ota包是通过无线下载还是导入到SD卡,最后都会调用到RecoverySystem.installPackage方法,下面分析下这个详细的流程。
2.1 RS.installPackage
[->RecoverySystem.java]
1 | public static void installPackage(Context context, File packageFile, boolean processed) |
private static final File RECOVERY_DIR = new File(“/cache/recovery”);
public static final File UNCRYPT_PACKAGE_FILE = new File(RECOVERY_DIR, “uncrypt_file”);
这里主要是对ota升级包的处理,由于进入recovery模式后,data分区将不能加载,因此需要将其通过block方式,把ota升级包生成稀疏的描述文件,保存在/cache/recovery/block.map中。升级的命令写入到BCB,重启之后bootloader会读取BCB中的command从而进入升级模式。
2.2 RS.setupBcb
[->RecoverySystem.java]
1 | /** |
2.3 RSS.setupBcb
[->RecoverySystemService.java]
1 | @Override // Binder call |
2.4 RSS.setupOrClearBcb
[->RecoverySystemService.java]
1 | private boolean setupOrClearBcb(boolean isSetup, String command) { |
SystemProperties.set(“ctl.start”, “setup-bcb”),通过这种方式来启动服务(SystemProperties设置属性的原理详细见文章Android SystemProperties系统属性分析),而后连接服务,向其中发送BCB command。
2.5 PM.reboot
[->PowerManager.java]
1 | public void reboot(String reason) { |
2.6 PMS.reboot
[->PowerManagerService.java]
1 | /** |
这里传入的参数confirm:false , reason:REBOOT_RECOVERY_UPDATE = “recovery-update”,wait:true
2.7 PMS.shutdownOrRebootInternal
[->PowerManagerService.java]
1 | private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm, |
2.8 ST.reboot
[->ShutdownThread.java]
1 | public static void reboot(final Context context, String reason, boolean confirm) { |
2.9 ST.shutdownInner
[->ShutdownThread.java]
1 | private static void shutdownInner(final Context context, boolean confirm) { |
2.10 ST.beginShutdownSequence
[->ShutdownThread.java]
1 | private static void beginShutdownSequence(Context context) { |
2.10.1 ST.showShutdownDialog
[->ShutdownThread.java]
1 | private static ProgressDialog showShutdownDialog(Context context) { |
2.11 ST.run
[->ShutdownThread.java]
1 | /** |
2.12 ST.uncrypt
[->ShutdownThread.java]
1 | private void uncrypt() { |
2.13 RS.processPackage
[->RecoverySystem.java]
1 | public static void processPackage(Context context, |
2.14 RS.uncrypt
[->RecoverySystem.java]
1 | /** |
2.15 RSS.uncrypt
[->RecoverySystemService.java]
1 | public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) { |
SystemProperties.set(“ctl.start”, “uncrypt”);这个操作主要是通过init(启动的Properties服务)进行,而后启动uncrypt服务,通过socket方式systemserver和uncrypt进行通信。
[->uncrypt.rc]
1 | service uncrypt /system/bin/uncrypt |
2.16 uncrypt.main
[->uncrypt.cpp]
1 | // |
通过文件头的注释,可以知道具体的通信方式。
2.17 uncrypt.uncrypt_wrapper
[->uncrypt.cpp]
1 | static bool uncrypt_wrapper(const char* input_path, const char* map_file, const int socket) { |
2.18 uncrypt.uncrypt
[->uncrypt.cpp]
1 | static int uncrypt(const char* input_path, const char* map_file, const int socket) { |
2.19 uncrypt.produce_block_map
[->uncrypt.cpp]
1 | static int produce_block_map(const char* path, const char* map_file, const char* blk_dev, |
这里将ota升级包生成block.map,如果升级包在的分区data是加密,那么每次获得每个block实际索引时,读取解密后的block数据到buffer,每当有5个block数据时,然后把buffer数据写入到实际的对应索引block中。
执行完成uncrypt操作,接着2.11节,执行重启的操作。
如果要测试uncrypt的功能,可以在adb shell环境下测试,将ota包推到data目录下,直接执行uncrypt命名。
1 | # uncrypt /data/ota.zip /cache/recovery/block.map |
2.20 ST.rebootOrShutdown
接着2.11中run方法继续。
[->ShutdownThread.java]
1 | public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { |
2.20.1 PWS.lowLevelReboot
[->PowerManagerService.java]
1 | /** |
2.20.2 PWS.lowLevelReboot
1 | public static void lowLevelShutdown(String reason) { |
2.21 小结
OTA升级重启前,主要的操作是对升级包进行处理的过程,对升级包提前处理的原因是因为在进入recovery模式进行升级时无法加载升级包所在的分区。在重启前的主要操作如下:
1.通过SystemProperties设置属性(setup-bcb),向BCB中设置升级的Command;
2.通过SystemProperties设置属性(uncrypt),开启uncrypt服务,将升级包生成一系列的block块,recovery可以读取block.map文件并获取这个文件的数据作为升级包;
3.uncrypt完成后,重启。
三、OTA升级重启后
终端重启后,加载bootloader过程中,由于之前写入的recovery的command,则将进入recovery模式.
3.1 aboot.aboot_init
[->aboot.c]
1 | void aboot_init(const struct app_descriptor *app) |
aboot执行后读取bootloader中command命令,执行recovery_init.
3.2 recovery.recovery_init
[->recovery.c]
1 | /* Bootloader / Recovery Flow |
参照代码前注释boot recovery.img之后,将会执行recovery.cpp中的main方法。
3.3 recovery.main
[->recovery.cpp]
1 | /* |
获取参数后,根据相应的参数执行相应的操作,这里是执行OTA INSTALL的流程,install_package完成之执行finish_recovery操作,之后正式完成升级的操作。
3.4 install.install_package
[->install.cpp]
1 | int install_package(const std::string& path, bool* wipe_cache, const std::string& install_file, |
3.5 install.really_install_package
[->install.cpp]
1 | static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount, |
3.6 install.try_update_binary
[->install.cpp]
1 | static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache, |
3.7 install.update_binary_command
[->install.cpp]
1 | int update_binary_command(const std::string& package, ZipArchiveHandle zip, |
前面介绍到update-binary相当于一个脚本解释器,能够识别updater-script中描述的操作。来看下updater-script中的内容:
1 | ... |
调用的是block_image_update,传入的是升级包里面的system.transfer.list和system.new.dat.br来实现升级。
block_image_update在bootable/recovery/updater/blockimg.cpp中,具体的实现PerformBlockImageUpdate函数中,这里不再详细展开。
1 | void RegisterBlockImageFunctions() { |
来system.transfer.list中的内容:
1 | 4 |
其中
4:为transfer的版本,目前支持1-4版本
583603:为总共new的block数量
0:stash slots没有使用,所以这里两个都是0
erase:需要擦除的block块范围数
new: 需要写入的block块范围数
zero: 需要填充0的block块范围数
3.8 recovery.finish_recovery
[->recovery.cpp]
1 | static void finish_recovery() { |
完成升级后,清除BCB操作。
3.9 小结
ota升级重启后,主要的操作如下:
1.加载bootloader,读取bootloader中的command命令
2.读取到升级的命令后,boot recovery.img,recovery.cpp中的main函数执行。
3.执行install_package操作,这里会解析ota包中的内容(block.map的形式),执行相应的升级包中脚本操作,同时会同步进行一些ui的显示操作。
4.install_package操作完成后,最后finish_recovery,完成升级的操作。
四、总结
本文分析Android终端ota升级的全过程。从介绍ota升级包的格式和系统启动模式开始作为基础,后面详细分析了从升级重启前到重启后的详细流程,其中升级重启前的流程如下:
OTA升级重启前,主要的操作是对升级包进行处理的过程,对升级包提前处理的原因是因为在进入recovery模式进行升级时无法加载升级包所在的分区。在重启前的主要操作如下:
1.通过SystemProperties设置属性(setup-bcb),向BCB中设置升级的Command;
2.通过SystemProperties设置属性(uncrypt),开启uncrypt服务,将升级包生成一系列的block块,recovery可以读取block.map文件并获取这个文件的数据作为升级包;
3.uncrypt完成后,重启。
ota升级重启后,主要的操作如下:
1.加载bootloader,读取bootloader中的command命令
2.读取到升级的命令后,boot recovery.img,recovery.cpp中的main函数执行。
3.执行install_package操作,这里会解析ota包中的内容(block.map的形式),执行相应的升级包中脚本操作,同时会同步进行一些ui的显示操作。
4.install_package操作完成后,最后finish_recovery,完成升级的操作。
附录
源码路径
1 | frameworks/base/core/java/android/os/RecoverySystem.java |