系统从bootable/recovery/updater中构建updater二进制程序,并且在OTA更新包里面使用他.
更新包本身是一个.zip压缩包文件(ota_update.zip,incremental_ota_update.zip),压缩包内包含可执行二进制程序 META-INF/com.google/android/update-binary .
Updater包含几个内置函数和用于可扩展脚本语言(edify)的解释器,可扩展脚本语言支持与更新任务相关的命令.Updater从.zip中的 META-INF/com/google/android/updater-script查找脚本.
注意:使用edify脚本和内置的函数不是命令,但是如果你需要调试这个更新文件的话是非常有用的
Edify语法
一个edify脚本是一个单一的语句,在脚本中,所有的值都是字符串.空字符串在一个Boolean上下文中是false,其他所有的字符串都是true.Edify支持下面的操作(都是通常的含义)
(expr )
expr + expr # string concatenation, not integer addition
expr == expr
expr != expr
expr && expr
expr || expr
! expr
if expr then expr endif
if expr then expr else expr endif
function_name(expr, expr,...)
expr; expr
任何由a-z,A-Z,0-9,_,:,/,.这些字符组成的,凡不是保留字的,都看作是字符串.(保留字是if else then endif).字符串使用双引号因起来,下面是如何创建空格和其他不在上面集合中的自负值.\n,\t,\”,和\为转义字符.
&&和||操作符是和我们平时编程的时候一样的,下面两个是等价的:
e1 && e2
if e1 then e2 endif
;操作符号是一个序列点;他的意思是,首先分析左边的表达式,然后分析右边的表达式.他的值是右边表达式的值.分号也用于一个表达式的后面,像是C语言风格的模拟.
prepare();
do_other_thing("argument");
finish_up();
内置函数
大多数更新功能都被包含在可用的函数中被脚本执行.(在Lisp意义上,严格的说这些是宏而不是函数,他们需要分析所有的参数).除非另有说明,函数返回true表示执行成功,返回false表示执行出错.如果你想在出错的时候停止脚本的执行,使用abort()或者是asset()函数.在updater中可用的函数集合也可以被扩展提供设备特定功能(后面会讲到).
abort([msg])
立即结束脚本的执行,带有可选信息.如果用户已经切换到文本显示,msg会出现在recovery log中和屏幕上.-
assert(expr[, expr, ...])
依次分析每一个expr.如果任何一个是false,立即终止脚本的执行,并且显示信息”assert failed”和错误语句的源文本.-
apply_patch(src_file, tgt_file, tgt_sha1, tgt_size, patch1_sha1, patch1_blob, [...])
将一个二进制补丁应用到src_file中来生成tgt_file.如果期望的目标和源码是一样的,向tgt_file传递”-“.tgt_sha1和tgt_size是期望的最终的SHA1哈希和目标文件的大小.剩下的参数必须是成对出现的:一个SHA1哈希(40位字符的16进制字符串)和一个块.当源码当前的内容已经有了SHA1,块就是要被应用的补丁.打补丁以一个安全的方式完成了,这保证了目标文件拥有期望的SHA1哈希和大小,或者是他是不变的.如果打包过程被中断,目标文件可能会在一个中间状态;位于cache分区的一个复制品,所以重启更新就能成功更新那个文件.
把存储技术设备(MTD)分区当作文件的特殊语法也是支持的,允许对raw分区的打包,例如boot.为了读取一个MTD分区,既然该分区没有文件末尾的概念,你一定要知道你想要读取多少数据.你一定要使用字符串”MTD:partition:size_1:sha1_1:size_2: sha1_2”作为文件名称来读取给定的分区.你至少指定一个(size,sha-1)对;如果有多个可能性,但是对你想要读取的内容至少指定一个.
apply_patch_check(filename, sha1[, sha1, ...])
如果文件名称或者是在cache分区中的临时复制品有一个和给定的sha1值相同的SHA1的话,就会返回true.sha1值被指定为40位的16进制数字.这个函数和sha1_check(read_file(filename), sha1 [, …]) 不同的是,他知道去检查cache分区中的copy,所以,apply_patch_check()将会成功即使文件被一个中断的apply_patch() 更新损坏了.apply_patch_space(bytes)
如果至少暂存空间对应用的二进制补丁可用的话返回trueconcat(expr[, expr, ...])
分析每一个语句并且连接他们.+操作在两个参数的特殊情况下是这个函数的语法糖(但是该函数能够接受任意数量的语句).语句必须是字符串,他不能连接块.delete([filename, ...])
删除列出文件名的所有.返回成功删除的文件的数量.delete_recursive([dirname, ...])
递归删除dirnames和其所有的内容.返回成功删除的目录的个数.file_getprop(filename, key)
读取给定的文件名的文件,作为属性文件解释(例如,/system/build.prop),返回给定的key的值,或者是如果key不存在,返回空字符串.-
format(fs_type, partition_type, location, fs_size, mount_point)
重新格式化给定的分区.支持的分区类型:fs_type = “yaffs2”并且partition_type=”MTD”.位置必须是MTD分区的名称;一个空的yaffs2文件系统在那里被构建.其他的参数就不用了.
fs_type=”ext4”并且partition_type=”EMMC”.位置一定是分区的设备文件.一个空的ext4文件系统在那里被构建.如果fs_size为0,文件系统采用整个分区.如果fs_size为一个正数,文件系统采用分区的fs_size大小位.如果fs_size是一个负数,文件系统采用除了|fs_size|大小的区域.
fs_type=”f2fs”并且partition_type=”EMMC”.位置一定要是分区设备文件.fs_size必须是非负数.如果fs_size为0,文件系统采用整个分区.如果fs_size为正数,文件系统采用这些大小位的分区.
mount_point(挂载点)一定要是后来文件系统挂载的点.
getprop(key)
返回系统属性key的值(如果没有定义,返回空字符串).被recovery分区定义的系统属性值不必和主系统定义的一样.这个函数在recovery模式中返回值.greater_than_int(a, b)
如果a比b大的话,返回trueifelse(cond, e1[, e2])
分析条件,如果条件为真,返回e1的值,如果条件为假,返回e2的值(如果存在的话).”if…else..then..endif”是该函数的一个语法糖.is_mounted(mount_point)
如果文件系统在挂载点被挂载了返回trueis_substring(needle, haystack)
如果needle是haystack的子串的话返回trueless_than_int(a, b)
如果a小于b的话返回true-
mount(fs_type, partition_type, name, mount_point)
挂载fs_type在mount_point的文件系统.partition_type一定是下面的一个:MTD:名称是MTD分区的名称(例如,system,userdata;查看设备上的/proc/mtd获取完整的列表).
EMMC:Recovery默认情况下不会挂载任何文件系统(如果用户从SD卡中手动安装更新的话,SD开需要挂载);你的脚本必须挂载你需要修改的分区.
package_extract_dir(package_dir, dest_dir)
从package_dir下提取所有的文件,并且将他们写到相应的dest_dir下面.任何存在的文件都要被重写.package_extract_file(package_file[, dest_file])
从更新包中提取一个单一的package_file,并且将他们写入到dest_file中,如果必要的话,重写存在的文件.如果没有dest_dir参数的话,将包文件的内容作为二进制块返回.read_file(filename)
读取文件并且返回他的内容作为一个二进制块.rename(src_filename, tgt_filename)
重命名src_filename为tgt_filename.他自动为tgt_filename创建一个必要的目录.例如:rename("system/app/Hangouts/Hangouts.apk", "system/priv-app/Hangouts/Hangouts.apk").
run_program(path[, arg, ...])
在path中,通过传递args执行二进制程序.返回程序的退出状态.-
set_metadata(filename, key1, value1[, key2 , value2, ...])
给给定的文件名称的键赋值.举个例子:set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0).
-
set_metadata_recursive(dirname, key1, value1[, key2, value2, ...])
递归设置给定目录和他的子文件的键和值.举个例子:set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0)
set_progress(frac)
在由show_progress()调用创建的块中设置进度条的位置.frac必须在[0.0,1.0]区间内.进度条不会后退.尝试这样的做法被忽略了.sha1_check(blob[, sha1])
blob参数是有read_file()返回的类型的块或者是package_extract_file()的一个参数形式.如果没有sha1参数,该函数返回块的SHA1哈希.如果存在一个或多个sha1参数,如果SHA1和一个参数相等的话,返回SHA1哈希,如果没有相等的,则返回空字符串.show_progress(frac, secs)
推进进度条在sec秒中到达frac长度.secs可能为0,在这种情况下,进度条不会被自动推进.sleep(secs)
睡眠secs秒(必须是整数)stdout(expr[, expr, ...])
分析每一个表达式并且将他的值转储到stdout.在调试的时候有用.symlink(target[, source, ...])
创建所有源码为链接到目标中.tune2fs(device[, arg, …])
在设备上调整可调参数argsui_print([text, ...])
连接所有的文本参数并且将结果打印到UI中(如果用户转换到文本显示的话结果就是可视的)unmount(mount_point)
卸载在挂载点挂载的文件系统wipe_block_device(block_dev, len)
清楚给定块设备block_dev的len位的数据.wipe_cache()
在成功安装之后,清空cache分区-
write_raw_image(filename_or_blob, partition)
在filename_or_blob中把镜像写入到MTD分区中. filename_or_blob可以是一个字符串名称是本地文件或者是一个包含数据的块值参数.为了能够从OTA包中复制一个文件到分区中,使用:write_raw_image(package_extract_file("zip_filename"), "partition_name")
注意:之前Android 4.1,仅仅接收文件名称,所以,为了能够完成这个工作,数据需要首先去解压缩到一个临时本地文件中.