系统从 bootable/recovery/updater
构建更新程序二进制文件,并在 OTA 软件包中使用它。
ota_update.zip
、incremental_ota_update.zip
),其中包含可执行二进制文件 META-INF/com/google/android/update-binary
。更新程序包含多个内置函数和一个可扩展脚本语言 (edify) 的解释器,该语言支持用于典型更新相关任务的命令。更新程序在软件包 .zip 文件中查找文件 META-INF/com/google/android/updater-script
中的脚本。
注意:使用 edify 脚本和/或内置函数不是常见的操作,但如果您需要调试更新文件,则可能会有所帮助。
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()
函数。updater 中可用的函数集也可以扩展以提供 设备特定功能。
abort([msg])
- 立即中止脚本的执行,并带有可选的 msg。如果用户已开启文本显示,msg 将出现在恢复日志和屏幕上。
-
assert(expr[, expr, ...])
- 依次评估每个 expr。如果任何一个为假,则立即中止执行,并显示消息“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 个字符的十六进制字符串)和一个 blob。blob 是当源文件的当前内容具有给定 SHA1 时要应用的补丁。
修补以安全的方式完成,以保证目标文件要么具有所需的 SHA1 哈希值和大小,要么保持原样——它不会处于不可恢复的中间状态。如果在修补过程中中断,目标文件可能处于中间状态;缓存分区中存在一个副本,因此重新启动更新可以成功更新文件。
支持特殊语法将内存技术设备 (MTD) 分区的内容视为文件,从而允许修补原始分区(如 boot)。要读取 MTD 分区,您必须知道要读取多少数据,因为分区没有文件结束概念。您可以使用字符串“MTD:partition:size_1:sha1_1:size_2: sha1_2”作为文件名来读取给定的分区。您必须至少指定一个 (size, sha-1) 对;如果对于您期望读取的内容存在多种可能性,您可以指定多个。
-
apply_patch_check(filename, sha1[, sha1, ...])
- 如果 filename 的内容或缓存分区中的临时副本(如果存在)的 SHA1 校验和等于给定的 sha1 值之一,则返回 true。sha1 值指定为 40 个十六进制数字。此函数与
sha1_check(read_file(filename), sha1 [, ...])
的不同之处在于,它知道检查缓存分区副本,因此即使文件被中断的apply_patch()
更新损坏,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_type="f2fs" 和 partition_type="EMMC"。Location 必须是分区的设备文件。fs_size 必须是非负数。如果 fs_size 为零,则文件系统占用整个分区。如果 fs_size 是正数,则文件系统占用分区的前 fs_size 个字节。
- mount_point 应该是文件系统的未来挂载点。
getprop(key)
- 返回系统属性 key 的值(如果未定义,则返回空字符串)。恢复分区定义的系统属性值不一定与主系统的属性值相同。此函数返回恢复模式下的值。
-
greater_than_int(a, b)
- 当且仅当 (iff) a(解释为整数)大于 b(解释为整数)时,返回 true。
-
ifelse(cond, e1[, e2])
- 评估 cond,如果为真,则评估并返回 e1 的值,否则评估并返回 e2(如果存在)。“if ... else ... then ... endif”结构只是此函数的语法糖。
is_mounted(mount_point)
- 当且仅当 mount_point 处挂载了文件系统时,返回 true。
-
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。
恢复模式默认情况下不挂载任何文件系统(除非用户正在从 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 返回。
read_file(filename)
- 读取 filename 并将其内容作为二进制 blob 返回。
-
run_program(path[, arg, ...])
- 执行 path 处的二进制文件,并传递 arg。返回程序的退出状态。
set_progress(frac)
- 在最近一次
show_progress()
调用定义的块内设置进度条的位置。frac 必须在 [0.0, 1.0] 范围内。进度条永远不会后退;尝试使其后退的操作将被忽略。 -
sha1_check(blob[, sha1])
- blob 参数是由
read_file()
或package_extract_file()
的单参数形式返回的 blob 类型。如果没有 sha1 参数,此函数返回 blob 的 SHA1 哈希值(作为 40 位十六进制字符串)。如果有一个或多个 sha1 参数,如果 SHA1 哈希值等于其中一个参数,则此函数返回 SHA1 哈希值,如果与任何参数都不相等,则返回空字符串。 -
show_progress(frac, secs)
- 在 secs 秒内(必须是整数)将进度条推进其长度的下一个 frac。secs 可以为 0,在这种情况下,仪表不会自动前进,而是通过使用上面定义的
set_progress()
函数前进。 sleep(secs)
- 休眠 secs 秒(必须是整数)。
-
stdout(expr[, expr, ...])
- 评估每个表达式并将其值转储到 stdout。用于调试很有用。
-
tune2fs(device[, arg, …])
- 调整 device 上的可调参数 args。
ui_print([text, ...])
- 连接所有 text 参数并将结果打印到 UI(如果用户已开启文本显示,则将在 UI 中可见)。
unmount(mount_point)
- 卸载在 mount_point 挂载的文件系统。
-
wipe_block_device(block_dev, len)
- 擦除给定块设备 block_dev 的 len 个字节。
wipe_cache()
- 导致在成功安装结束时擦除缓存分区。
-
write_raw_image(filename_or_blob, partition)
- 将 filename_or_blob 中的映像写入 MTD partition。filename_or_blob 可以是命名本地文件的字符串,也可以是包含要写入的数据的 blob 值参数。要将文件从 OTA 软件包复制到分区,请使用:
write_raw_image(package_extract_file("zip_filename"), "partition_name");
注意: 在 Android 4.1 之前,仅接受文件名,因此为了完成此操作,必须首先将数据解压缩到临时本地文件中。