系统从 bootable/recovery/updater
构建更新程序二进制文件并将其用在 OTA 软件包中。
META-INF/com/google/android/update-binary
的 .zip 文件(ota_update.zip
、incremental_ota_update.zip
)。
更新程序包含多个内建函数和一个可扩展脚本语言 (Edify) 解释器,该脚本语言支持用于执行更新相关的典型任务的命令。更新程序会从软件包 .zip 文件内的 META-INF/com/google/android/updater-script
文件中查找脚本。
注意:Edify 脚本和/或内建函数并非常用 activity,但当您需要调试更新文件时会很有用。
Edify 语法
Edify 脚本是一个单一的表达式,其中的所有值都是字符串。 从布尔值角度考虑,空字符串为 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、\" 和 \\ 充当带英文引号的字符串中的转义字符,\x## 亦如此。
&& 和 || 为短路运算符;如果逻辑结果由左侧确定,则不评估右侧。下面两个表达式是对等的:
e1 && e2 if e1 then e2 endif
分号“;”运算符是序列点;表示要先评估左侧,再评估右侧。它的值是右侧表达式的值。分号也可以出现在表达式之后,其效果与 C 风格语句相仿:
prepare(); do_other_thing("argument"); finish_up();
内建函数
大部分更新功能都包含在可供脚本执行的函数中。(严格来说,这些是“宏”,而不是 Lisp 意义上的函数,因为它们不需要评估所有参数。)除非另有说明,函数在成功时返回 true,出错时返回 false。如果您希望在出错时中止脚本执行,请使用 abort()
和/或 assert()
函数。您也可以扩展更新程序中的可用函数集,以提供设备特定的功能。
abort([msg])
- 立即中止脚本执行,并提供一条可选的 msg。msg如果用户开启了文本显示功能,msg 将出现在恢复日志和屏幕上。msg
-
assert(expr[, 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_file tgt_sha1 和 tgt_size 分别是目标文件的预期最终 SHA1 哈希值和文件大小。其余参数必须成对出现:一个 SHA1 哈希值(40 个字符的十六进制字符串)和一个 blob。如果源文件当前的内容具有给定的 SHA1,该 blob 即为要应用的补丁。
打补丁以一种安全的方式完成,该方式可保证目标文件要么具有所需的 SHA1 哈希值和大小,要么不发生变化,因此不会处于不可恢复的中间状态。如果在打补丁期间进程中断,目标文件可能会处于中间状态;这时缓存分区中会存在一个副本,重新开始更新便可成功更新该文件。
支持特殊语法将内存技术设备 (MTD) 分区的内容作为文件处理,以便为引导分区这样的原始分区打补丁。由于 MTD 分区没有文件结束标记,因此要读取该分区,您必须知道您要读取的数据量。您可以使用字符串“MTD:partition:size_1:sha1_1:size_2:sha1_2”作为文件名来读取给定的分区。您必须至少指定一个 (size, sha-1) 对;如果您希望读取的内容有多种可能,则可以指定多个对。
-
apply_patch_check(filename, sha1[, sha1, ...])
-
如果 filename 的内容或 cache 分区中临时副本(如果存在)的内容具有与指定的 sha1 值之一相等的 SHA1 校验和,则返回 true。sha1 值指定为 40 位十六进制数字。sha1该函数不同于
sha1_check(read_file(filename), sha1 [, ...])
,因为它知道要检查 cache 分区副本,因此即使文件因apply_patch() update
中断而损坏,apply_patch_check()
也会成功。 apply_patch_space(bytes)
- 如果至少有 bytes 个字节的暂存空间可用于应用二进制补丁,则返回 true。
-
concat(expr[, expr, ...])
- 评估每个表达式并将它们串联起来。在只有两个参数的特殊情况下,+ 运算符就是该函数的语法糖(但该函数形式可以包含任意数量的表达式)。表达式必须为字符串;该函数不能串联 blob。
-
file_getprop(filename, key)
- 读取给定的 filename,将其解释为属性文件(例如:
/system/build.prop
),并返回给定 key 的值,如果 key 不存在,则返回空字符串。 -
format(fs_type, partition_type, location, fs_size, mount_point)
- 重新格式化给定的分区。支持的分区类型如下:
- fs_type =“yaffs2”和 partition_type =“MTD”。Location 必须为 MTD 分区的名称;将在该位置构建一个空的 yaffs2 文件系统。其余参数未使用。
- fs_type =“ext4”和 partition_type =“EMMC”。Location 必须为该分区的设备文件。将在该位置构建一个空的 ext4 文件系统。fs_size如果 fs_size 为零,文件系统将占用整个分区。如果 fs_size 为正数,文件系统将占用分区的前 fs_size 个字节。如果 fs_size 为负数,文件系统将占用分区中除最后 |fs_size| 个字节以外的所有字节。
- fs_type =“f2fs” 和 partition_type =“EMMC”。Location 必须为该分区的设备文件。fs_sizefs_size 必须为非负数。fs_size如果 fs_size 为零,文件系统将占用整个分区。如果 fs_size 为正数,文件系统将占用分区的前 fs_size 个字节。
- mount_point 应为文件系统的未来挂载点。
getprop(key)
- 返回系统属性 key 的值(或者,如果未定义,则返回空字符串)。由 recovery 分区定义的系统属性值未必与主系统属性值相同。此函数返回 recovery 分区中的值。
-
greater_than_int(a, b)
- 当且仅当 (iff) a(解析为整数)大于 b(解析为整数)时,才返回 true。
-
ifelse(cond, e1[, e2])
- 评估 cond,如果为 true,则评估并返回 e1 的值,否则评估并返回 e2(如果存在)。“if ... else ... then ... endif”结构就是此函数的语法糖。
is_mounted(mount_point)
- 当且仅当 mount_point 挂载了文件系统时,才返回 True。mount_point
-
is_substring(needle, haystack)
- 当且仅当 needle 是 haystack 的子字符串时,才返回 true。
-
less_than_int(a, b)
- 当且仅当 a(解析为整数)小于 b(解析为整数)时,才返回 true。
-
mount(fs_type, partition_type, name, mount_point)
- 在 mount_point 挂载 fs_type 的文件系统。partition_type 必须为以下类型之一:
-
MTD:name 是 MTD 分区的名称(例如:system、userdata;如需获取完整列表,请参见设备上的
/proc/mtd
)。 - EMMC。
默认情况下,recovery 分区不挂载任何文件系统(如果用户正在手动从 SD 卡安装软件包,则 SD 卡除外);您的脚本必须挂载需要修改的所有分区。
-
MTD:name 是 MTD 分区的名称(例如:system、userdata;如需获取完整列表,请参见设备上的
-
package_extract_dir(package_dir, dest_dir)
- 从 package_dir 下的文件包中提取所有文件,并将它们写入 dest_dir 下相应的树中。所有现有文件都将被覆盖。
-
package_extract_file(package_file[, dest_file])
- 从更新软件包中提取单个 package_file 并将其写入 dest_file,如有必要,覆盖现有文件。在没有 dest_file 参数的情况下,将软件包文件的内容作为二进制 blob 返回。dest_file
read_file(filename)
- 读取 filename 并将其内容作为二进制 blob 返回。filename
-
run_program(path[, arg, ...])
- 在 path 上执行二进制文件并传入 arg。返回程序的退出状态。
set_progress(frac)
- 在最近的
show_progress()
调用所定义的块内设置进度条的位置。frac 必须在 [0.0, 1.0] 范围内。frac进度条从不向后移动;向后移动的尝试会被忽略。 -
sha1_check(blob[, sha1])
- blob 参数是
read_file()
返回的 blob 类型或package_extract_file()
的单参数形式 blob。blob在没有 sha1 参数的情况下,此函数返回 blob 的 SHA1 哈希值(作为 40 位十六进制字符串)。sha1在提供了一个或多个 sha1 参数的情况下,如果 SHA1 哈希值等于其中一个参数,该函数返回此 SHA1 哈希值;如果与任何一个参数都不相等,则返回空字符串。sha1 -
show_progress(frac, secs)
- 在 secs 秒(必须为整数)内推动进度条向前移动下一个 frac 的长度。secs 可为 0,在这种情况下,进度条不会自动向前移动,而是通过使用上面所定义的
set_progress()
函数实现进度递增。 sleep(secs)
- 休眠 secs 秒(必须为整数)。
-
stdout(expr[, expr, ...])
- 评估每个表达式并将其值转储到 stdout。这在调试时很有用。
-
tune2fs(device[, arg, …])
- 调整 device 上的可调参数 arg。
ui_print([text, ...])
- 串联所有 text 参数并将结果输出到界面(如果用户已开启文本显示功能,界面上将显示该结果)。
unmount(mount_point)
- 将挂载在 mount_point 上的文件系统卸载。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 可以是一个指代本地文件的字符串或一个包含要写入的数据的 blob 值参数。filename_or_blob如需将文件从 OTA 软件包复制到分区,请使用:
write_raw_image(package_extract_file("zip_filename"), "partition_name");
注意:对于 Android 4.1 之前的版本,仅接受 filename。因此,如需完成该操作,必须先将数据解压缩到临时本地文件中。