一: Android 升级包介绍:
OTA(On The Air)升级是Android提供的标准升级方式。其原理是将需要升级的部分制作成OTA Package,由用户手动或者通过网络下载后,在recovery mode下执行Package中的升级脚本,将需要升级的文件更新到系统中,从而完成系统的升级。
OTA Package可以是完整升级包,也可以是增量升级包。完整升级包包含了全部的系统文件以及images,而增量升级包只包含相对于指定版本有差异的部分,其中有差异的部分是以patch文件的方式进行更新。
OTA package是一个zip格式的压缩包,下表是一个升级包组成范例:
update.zip
|-- boot.img
|-- logo.img
|-- META-INF
| |-- CERT.RSA
| |-- CERT.SF
| |-- com
| | |-- android
| | | `-- metadata
| | `-- android
| | |-- update-binary
| | `-- updater-script
| `-- MANIFEST.MF
|-- META
`-- system
`-- app
`-- xxx.apk
主要组成部分如下:
(1) updater-script是升级脚本。在recovery mode下,通过执行此脚本中的语句来完成指定的动作。update-binary是升级程序,从\bootable\recovery\updater中的代码编译得到,此程序用来解析并执行升级脚本。
(2) CERT.RSA、CERT.SF、MANIFEST.MF是经过签名得到的校验文件,Android系统进行升级包校验时需要用到;
(3) 其它部分,如boot.img、logo.img、和system等都是具体需要升级的内容。其中,recovery部分是通过patch方式更新的。
综上所述,OTA Package的核心是updater-script。
二: 手动制作升级包
升级包也可以手动创建,只要按照规定的格式打包并签名即可。具体步骤如下:
(1) 准备好升级所需的文件和脚本,并按照规定的目录结构创建并放置。假设需要更新/system/app里的某个apk,目录结构应该如下所示:
|-- META-INF
| `-- com
| `-- android
| |-- update-binary
| `-- updater-script
`-- system
`-- app
`-- xxx.apk
如前所述,update-binary是从 bootable/recovery/updater/编译得到的,生成out/target/product/godbox/system/bin/updater 文件,把此文件拷贝过来并命名成update-binary。
(2) 打包上述文件成update.zip,指令大致如下:
zip -r update.zip META-INF system ……
(3) 给升级包添加签名。指令如下:
java –jar out/host/linux-x86/framework/signapk.jar -w build/target/product/security/testkey.x509.pem build/target/product/security/testkey.pk8 update.zip update_signed.zip
这里的签名工具和安全证书是以Android自带的为例,可以修改指令指定自己的证书。
(4) 生成的update_signed.zip即是所需的升级包。
三:定制化升级脚本
升级脚本update-script是一个纯文本的文件,需要按照Android规定的语法规则编写,下面是一个范例:
set_progress("1");
show_progress("1","100");
ui_print("update fastboot......");
package_extract_file("fastboot.bin", "/dev/block/platform/hi_mci.1/by-name/fastboot");
ui_print("update baseparam......");
package_extract_file("baseparam.img", "/dev/block/platform/hi_mci.1/by-name/baseparam");
ui_print("update recovery......");
package_extract_file("recovery.img", "/dev/block/platform/hi_mci.1/by-name/recovery");
ui_print("update logo......");
package_extract_file("logo.img", "/dev/block/platform/hi_mci.1/by-name/logo");
ui_print("update boot......");
package_extract_file("boot.img", "/dev/block/platform/hi_mci.1/by-name/kernel");
ui_print("update system......");
format("ext4", "EMMC", "/dev/block/platform/hi_mci.1/by-name/system", "0", "/system");
mount("ext4", "EMMC", "/dev/block/platform/hi_mci.1/by-name/system", "/system");
package_extract_dir("system", "/system");
symlink("Roboto-Bold.ttf", "/system/fonts/DroidSans-Bold.ttf");
symlink("Roboto-Regular.ttf", "/system/fonts/DroidSans.ttf");
symlink("mksh", "/system/bin/sh");
symlink("toolbox", "/system/bin/cat", "/system/bin/chmod",
"/system/bin/chown", "/system/bin/cmp", "/system/bin/cp",
"/system/bin/date", "/system/bin/dd", "/system/bin/df",
"/system/bin/dmesg", "/system/bin/du", "/system/bin/getevent",
"/system/bin/getprop", "/system/bin/grep", "/system/bin/hd",
"/system/bin/id", "/system/bin/ifconfig", "/system/bin/iftop",
"/system/bin/insmod", "/system/bin/ioctl", "/system/bin/ionice",
"/system/bin/kill", "/system/bin/ln", "/system/bin/log",
"/system/bin/ls", "/system/bin/lsmod", "/system/bin/lsof",
"/system/bin/md5", "/system/bin/mkdir", "/system/bin/mount",
"/system/bin/mv", "/system/bin/nandread", "/system/bin/netstat",
"/system/bin/newfs_msdos", "/system/bin/notify", "/system/bin/printenv",
"/system/bin/ps", "/system/bin/r", "/system/bin/reboot",
"/system/bin/renice", "/system/bin/rm", "/system/bin/rmdir",
"/system/bin/rmmod", "/system/bin/route", "/system/bin/schedtop",
"/system/bin/sendevent", "/system/bin/setconsole",
"/system/bin/setprop", "/system/bin/sleep", "/system/bin/smd",
"/system/bin/start", "/system/bin/stop", "/system/bin/sync",
"/system/bin/top", "/system/bin/touch", "/system/bin/umount",
"/system/bin/uptime", "/system/bin/vmstat", "/system/bin/watchprops",
"/system/bin/wipe");
set_perm_recursive(0, 2000, 0755, 0755, "/system/bin");
set_perm_recursive(0, 2000, 0755, 0755, "/system/xbin");
set_perm_recursive(0, 2000, 0755, 0755, "/system/etc");
unmount("/system");
ui_print("update ok ...");
升级脚本的生成根据升级包的制程也作过分两种情况,自动和手动。
3.1 自动生成升级脚本
自动模式下,执行ota_from_target_files时,制作升级包的函数会调用edify_generator.py脚本里的函数去自动编写updater-script。而edify_generator.py里的函数和updater-
script的语法规则是一一对应的。例如:Mount函数对应于mount(fs_type, partition_type, location, mount_point);Print函数对应于ui_print (“str”);
如果想定制updater-script,只需要修改ota_from_target_files(ota_from_target_files1)里自动编写脚本的那些代码,具体见WriteFullOTAPackage 或
WriteIncrementalOTAPackage里的script类,这两个方法分别为制作作全量包和差分包使用。
3.2 手动定制化升级脚本
手动模式就需要客户自行编写updater-script。由于是纯文本文件,所以可以用任意的文本编辑器去编写,只需要遵照Android里规定好的语法规则即可。在ICS里,updater-
script的编写使用的是被称为Edify的一套语法规则,其支持的指令见下表(只列举部分):
序号 | 命令 | 功能说明 |
1 | mount(fs_type, partition_type, location, mount_point) | 挂载指定的分区或设备 |
2 | unmount (mount_point) | 卸载指定的分区或设备 |
3 | format(fs_type, partition_type, location, fs_size) | 格式化指定的分区 |
4 | show_progress (fraction, duration) | 进度显示 |
5 | set_progress (fraction) | 设置进度条步进长度 |
6 | delete (paths) | 删除指定路径的文件 |
7 | package_extract_file(zip_path, dest_path) | 将升级包内特定的文件释放到指定的目录。如果是emmc版本,可以直接将image文件更新到指定partition。 |
8 | symlink(target src1 src2 ...) | 建立指向target的符号链接src1,src2,…… |
9 | package_extract_dir(zip_path, dest_path) | 将升级包内特定的目录释放到指定的目录 |
10 | set_perm_recursive (uid, gid, dir-mode, file-mode, path) | 设置文件夹和文件夹内文件的权限 |
11 | ui_print (“str”) | 用于在recovery界面中输出指定信息 |
如前所述,updater-script的解释执行是由update-binary来完成的。如果需要扩充语法规则,则可以修改update-binary的source code,涉及的具体文件如下:
\bootable\recovery\updater\intall.h
\bootable\recovery\updater\intall.c