Android 9.0 A/B升级原理分析

本文详细介绍了Android 9.0 A/B系统更新的原理,包括无缝更新的目的、系统分区和更新流程。重点讲解了update升级包的构成、updater-script语法以及OTA更新过程中的关键步骤,如mount、unmount、format等操作。同时,讨论了JAVA层的调用流程,涉及UpdateEngine和UpdateEngineCallback等相关类。
摘要由CSDN通过智能技术生成

概述:

A/B 系统更新,也称为无缝更新,用于确保可运行的启动系统在无线 (OTA) 更新期间能够保留在磁盘上。这样可以降低更新之后设备无法启动的可能性,也就是说,用户需要将设备送到维修/保修中心进行更换和刷机的情况将有所减少。

用户在 OTA 期间可以继续使用设备。在更新过程中,仅当设备重新启动到更新后的磁盘分区时,会发生一次宕机情况。即使 OTA 失败,设备也仍然可以使用,因为它会启动到 OTA 之前的磁盘分区。您可以再次尝试下载 OTA。建议仅针对新设备通过 OTA 实现 A/B 系统更新。

A/B 系统更新使用称为 update_engine 的后台守护进程以及两组分区。这两组分区称为插槽,通常为插槽 A 和插槽 B。系统从其中一个插槽(“当前插槽”)运行,但运行的系统不会访问“未使用的”插槽中的分区(用于正常操作)。

 

传统升级方式与ab升级方式采用的设备分区如下:

ab升级方式中boot和system均保留了两份,所以对设备的存储容量要求会比传统升级方式要大的多。

典型应用场景如下,假定当前从slot B中启动

  • 正常情况:系统正在从其当前插槽(插槽 B)运行。目前为止尚未应用任何更新。系统的当前插槽是可启动、成功且活动的插槽。
  • 正在更新:系统正在从插槽 B 运行,因此,插槽 B 是可启动、成功且活动的插槽。由于插槽 A 中的内容正在更新,但是尚未完成,因此插槽 A 标记为不可启动。在此状态下,应继续从插槽 B 重新启动。
  • 已应用更新,正在等待重新启动:系统正在从插槽 B 运行,插槽 B 的状态为可启动且成功,但是插槽 A 过去标记为活动(因此现在标记为可启动)。插槽 A 尚未被标记为成功,引导加载程序应该尝试从插槽 A 启动几次。
  • 系统重新启动到新的更新:系统首次从插槽 A 运行,插槽 B 的状态仍为可启动且成功,而插槽 A 仅可启动,且仍然处于活动但不成功的状态。在进行几次检查之后,用户空间守护进程应将插槽 A 标记为成功。

 

update升级包分析

一、目录结构
update.zip包的目录结构,如下图所示:

二、目录结构分析
下面分析以全量包升级为准。

1、META文件夹
bootargs.txt    bootargs启动参数

filesystem_config.txt    system目录文件权限

recovery.fstab    分区表

2、META-INF目录
目录结构如下:|---META-INF/
       `|CERT.RSA
       `|CERT.SF
       `|MANIFEST.MF
       `|----com/
              `|----android/
                      `|----metadata
              `|----google/
                      `|----android/
                             `|----update-binary
                             `|----updater-script
CERT.RSA:与签名文件相关联的签名程序块文件,它存储了用于签名JAR文件的公共签名。

CERT.SF:这是JAR文件的签名文件,其中前缀CERT代表签名者。

MANIFEST.MF:这个manifest文件定义了与包的组成结构相关的数据。类似Android应用的mainfest.xml文件。

metadata文件:是描述设备信息及环境变量的元数据。主要包括一些编译选项,签名公钥,时间戳以及设备型号等。

updater-script:此文件是一个脚本文件,具体描述了更新过程。我们可以根据具体情况编写该脚本来适应我们的具体需求。

update-binary:是一个二进制文件,相当于一个脚本解释器,能够识别updater-script中描述的操作。

文件怎么来的:

1、CERT.RSA、CERT.SF、MANIFEST.MF、metadata文件是自动生成的(怎么生成详见下文签名部分)

2、update-binary一般是系统编译过程中自动生成的升级脚本,但是这部分是可以通过手动编辑(详见后文update-binary脚本语言详解)

3、update-binary在sdk中哪个部分

./device/hisilicon/bigfish/build/emmc.mk
cp -a $(PRODUCT_OUT)/system/bin/updater $(EMMC_PRODUCT_OUT)/update/file/META-INF/com/google/android/update-binary
又上面脚本部分可知update-binary其实就是updater,updater部分是通过源码编译生成的,源码路径在:

bootable/recovery/updater/

3、system目录
system/目录的内容在升级后会放在系统的system分区。主要用来更新系统的一些应用或则应用会用到的一些库等等。

有的时候会以打包的形式(system.img)存在。

4、userdata目录
userdata目录,用来更新系统中的用户数据部分。这部分内容在更新后会存放在系统的/data目录下。

有的时候会以打包的形式(userdata.img)存在。

5、其他文件
*.img是更新各个分区分区所需要的文件。

三、如何制作一个update升级包
update升级包一般有两种方式得到:

一种是通过编译系统得到update.zip包(make ota-package)
另一种是通过自己手动创建的方式得到update升级包

 


 

updater-script语法详解

update升级包主要的启动入口为updater-script,因此先介绍一下updater-script的基础语法。
1、mount
语法:

mount(type, location, mount_point);

说明:

type="MTD"   location="<partition>" 挂载yaffs2文件系统分区;

type="vfat"  location="/dev/block/<whatever>"  挂载设备。

例如:

 

mount("MTD", "system", "/system");

挂载system分区,设置返回指针"/system”

mount("vfat", "/dev/block/mmcblk1p2", "/system");

挂载/dev/block/mmcblk1p2,返回指针"/system”

2、Unmount
语法:

unmount(mount_point);

说明:

mount_point是mount所设置产生的指针。其作用与挂载相对应,卸载分区或设备。此函数与mount配套使用。

例如:

unmount("/system");

卸载/system分区

3、Format
语法:

format(type, location);

说明:

type="MTD"  location=partition(分区),格式化location参数所代表的分区。

例如:

format("MTD", "system");

格式化system分区

4、Delete
语法:

delete(<path>);

说明:

删除文件<path>

例如:

delete("/data/zipalign.log");

删除文件/data/zipalign.log

5、delete_recursive
语法:

delete_recursive(<path>);

说明:

删除文件夹<path>

例如:

delete_recursive("/data/dalvik-cache");

删除文件夹/data/dalvik-cache

6、show_progress
语法:

show_progress(<fraction>,<duration>);

说明:

为下面进行的程序操作显示进度条,进度条会根据<duration>进行前进<fraction>

例如:

show_progress(0.1, 10);

show_progress下面的操作可能进行10s,完成后进度条前进0.1(也就是10%)

7、package_extract_dir
语法:

package_extract_dir(package_path, destination_path);

说明:

释放文件夹package_path至destination_path

例如:

package_extract_dir("system", "/system");

释放ROM包里system文件夹下所有文件和子文件夹至/system

8、package_extract_file
语法:

package_extract_file(package_path, destination_path);

说明:

解压package_path文件至destination_path

例如:

package_extract_dir("my.zip", "/system");

解压ROM包里的my.zip文件至/system

9、Symlink
语法:

symlink(<target>, <src1>, <src2>,...);

说明:

建立指向target符号链接src1,src2,……

例如:

symlink("toolbox", "/system/bin/ps");

建立指向toolbox的符号链接/system/bin/ps

10、set_perm
语法:

set_perm(<uid>, <gid>,<mode>, <path>);

说明:

设置<path>文件的用户为uid,用户组为gid,权限为mode

例如:

set_perm(1002, 1002, 0440, "/system/etc/dbus.conf");

设置文件/system/etc/dbus.conf的所有者为1002,所属用户组为1002,权限为:所有者有读权限,所属用户组有读权限,其他无任何权限。

11、set_perm_recursive
语法:

set_perm_recursive(<uid>,<gid>,<dir-mode>,<file-mode>,<path>);

说明:

设置文件夹和文件夹内文件的权限

例如:

set_perm_recursive(1000, 1000, 0771, 0644, "/data/app");

设置/data/app的所有者和所属用户组为1000,app文件夹的权限是:所有者和所属组拥有全部权限,其他有执行权限;app文件夹下的文件权限是:所有者有读写权限,所属组有读权限,其他有读权限。

12、ui_print
语法:

ui_print("str");

说明:

屏幕打印输出"str"

例如:

ui_print("It's ready!");

屏幕打印It’s ready!

13、run_program
语法:

run_program(<path>);

说明:

运行<path>脚本

例如:

run_program("/system/xbin/installbusybox.sh");

运行installbusybox.sh脚本文件

14、write_raw_image
语法:

write_raw_image(<path>, partition);

说明:

写入<path>至partition分区

例如:

write_raw_image("/tmp/boot.img", "boot")

将yaffs2格式的boot包直接写入boot分区

15、assert
语法:

assert(<sub1>,<sub2>,<sub3>);

说明:

如果执行sub1不返回错误则执行sub2,如果sub2不返回错误则执行sub3一次类推。

例如:

assert(package_extract_file("boot.img", "/tmp/boot.img"), 
write_raw_image("/tmp/boot.img", "boot"), 
delete("/tmp/boot.img"));

执行package_extract_file,如果不返回错误则执行write_raw_image,如果write_raw_image不出错则执行delete

16、getprop
语法:

getprop("key")

说明:

通过指定key的值来获取对应的属性信息。

例如:

getprop(“ro.product.device”)

获取ro.product.device的属性值。

17、ifelse
语法:

ifelse(condition, truecondition, falsecondition)

说明:

condition----------------要运算的表达式

Truecondition-----------当值为True时执行的 Edify脚本块

Falsecodnition-----------当值为False时执行的 Edify脚本块

列如:

ifelse(isuserversion(),
    ui_print(" ----user version----- "),
    ui_print(" --------- ");
    set_perm(0, 2000, 04750, "/system/xbin/su");
);

根据isuserversion()返回值判断,如果true,打印" ----user version----- ";如果false,打印" --------- ",并获取su权限。

注意:值得注意的是false分支,执行了两个语句,只需通过‘;’来分割开就可以了。

18、其他

向上一个例子中isuserversion()不是常见的函数,这个是什么呢,怎么识别,这就需要特有的update-binary。

update-binary相当于一个脚本解释器,能够识别updater-script中描述的操作。
 

ota_from_target_files

OTA包的制作是通过执行ota_from_target_files来实现的。ota_from_target_files文件内容较多,下面分析一下几个主要的方法:

看下参数简介:

Given a target-files zipfile, produces an OTA package that installs that build.  An incremental OTA is produced if -i is given, otherwise a full OTA is produced.

Usage:  ota_from_target_files [flags] input_target_files output_ota_package

-k (--package_key):指定OTA包的签名

-i  (--incremental_from)  <file>:生成增量包

--full_radio:包含全射频img的包,只对增量包使用,因为全包一定会包含。

--full_bootloader:和--full_radio功能类似

--verify:对system和vendor分区文件进行校验,只对非A/B升级的增量包使用

--wipe_user_data:对data分区数据进行擦除

--downgrade:指定降级增量包(如从P降级到O

  • 0
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值