Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1306809
  • 博文数量: 478
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4833
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-28 11:12
文章分类

全部博文(478)

文章存档

2019年(1)

2018年(27)

2017年(21)

2016年(171)

2015年(258)

我的朋友

分类: Android平台

2016-04-19 13:35:55

作者: 宋立新

Email

前言

       前面介绍了ota package 的编译过程,其中最核心的部分就是一个  脚本:ota_from_target_files. 现在我们分析这个脚本。

先看一下帮助

不带任何参数,先看一下它的帮助:

  

简单翻译一下:

-b 过时,不再使用。

-k 签名用的密钥

-i 生成增量OTA包时用于定义对比包

-w 是否清除 userdata 分区

-n 是否在升级时检查时间戳,缺省情况下只能基于老的版本升级。

-e 定义额外运行的脚本

-m 定义采用的脚本格式,目前有两种,amend & edify, 其中amend为较老的格式。对应的,升级时会采用不同的解释器。缺省情况下,ota_from_target_files 会同时生成两个脚本。这提供了最大灵活性。

-p 定义脚本用到的一些可执行文件的路径

-s 定义额外运行的脚本的路径

-x 定义额外运行的脚本可能用到的键/值对

-v 老朋友,冗余模式,让脚本打印出执行的命令

-h 老朋友,这个就不用说了吧。

我们调用如下命令生成我们的升级包:

 

./build/tools/releasetools/ota_from_target_files /

  -m auto /

  -p out/host/linux-x86 /

  -k build/target/product/security/testkey -n /

out/target/product/{product-name}/obj/PACKAGING/target_files_intermediates/{product-name}-target_files-eng.{uid}.zip {output_zip}

再看内容

ota_from_target_filespython 脚本,所以如果懂 python 会更顺利一点。

文件有1000行。分析过程中,我们只是贴代码片段。 完整文件见:

build/tools/releasetools/ota_from_target_files from Android 2.2

 

入口:main

按照python惯例,单独执行的代码执行从__main__开始:

 if __name__ == '__main__':
   try:
     main(sys.argv[1:])
   except common.ExternalError, e:
     print
     print "   ERROR: %s" % (e,)
     print
     sys.exit(1)

 它调用 main 函数:

   

将用户设定的 Option 存入 OPTIONS 变量中。它是一个Python Class, 我们将其理解为一个C Struct 即可。
    if OPTIONS.script_mode not in ("amend", "edify", "auto"):
     raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
 Script_mode 只能是amend/edify/auto之一, auto 目前是选择两者都支持。
可以理解是为了向前兼容,(早期 Android 使用 amend)
    if OPTIONS.extra_script is not None:
     OPTIONS.extra_script = open(OPTIONS.extra_script).read()
 读入 额外脚本的内容。(如果有)
    print "unzipping target target-files..."
   OPTIONS.input_tmp = common.UnzipTemp(args[0])
 解开输入包。
  
处理 device-specific extensions, 没用到。
    common.LoadMaxSizes()
   if not OPTIONS.max_image_size:
     print
     print "  WARNING:  Failed to load max image sizes; will not enforce"
     print "  image size limits."
     print
 读入设定image大小的参数,没用到。
    OPTIONS.target_tmp = OPTIONS.input_tmp
   input_zip = zipfile.ZipFile(args[0], "r")
   if OPTIONS.package_key:
     temp_zip_file = tempfile.NamedTemporaryFile()
     output_zip = zipfile.ZipFile(temp_zip_file, "w",
                                  compression=zipfile.ZIP_DEFLATED)
   else:
     output_zip = zipfile.ZipFile(args[1], "w",
                  compression=zipfile.ZIP_DEFLATED)
 设定输出文件,如果要签名(our case,则还需要一个临时输出文件。
    if OPTIONS.incremental_source is None:
     WriteFullOTAPackage(input_zip, output_zip)
   else:
     print "unzipping source target-files..."
     OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
     source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
     WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
 根据参数,调用增量和非增量创建 ZIP 创建函数,我们采用非增量模式。
    output_zip.close()
   if OPTIONS.package_key:
     SignOutput(temp_zip_file.name, args[1])
     temp_zip_file.close()
   common.Cleanup()
   print "done."

 签名(如果需要的话),处理完毕。

 

下面我们看主要功能函数:WriteFullOTAPackage

主功能:WriteFullOTAPackage

  def WriteFullOTAPackage(input_zip, output_zip):

   if OPTIONS.script_mode == "auto":
     script = both_generator.BothGenerator(2)
   elif OPTIONS.script_mode == "amend":
     script = amend_generator.AmendGenerator()
   else:
     # TODO: how to determine this?  We don't know what version it will
     # be installed on top of.  For now, we expect the API just won't
     # change very often.
     script = edify_generator.EdifyGenerator(2)
 首先,我们获得脚本生成器,他们的实现见脚本:edify_generator.py 等。
    metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
               "pre-device": GetBuildProp("ro.product.device", input_zip),
               "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
               }
 获得一些环境变量,来自android 环境变量。 Google 一下即知其义。
    device_specific = common.DeviceSpecificParams(
       input_zip=input_zip,
       input_version=GetRecoveryAPIVersion(input_zip),
       output_zip=output_zip,
       script=script,
       input_tmp=OPTIONS.input_tmp,
       metadata=metadata)
 设备相关参数,不深究。
    if not OPTIONS.omit_prereq:
     ts = GetBuildProp("ro.build.date.utc", input_zip)
     script.AssertOlderBuild(ts)
 如果需要,在脚本中增加一个Assert语句,要求update zip包只能用于升级老的系统。
    AppendAssertions(script, input_zip)
 如果需要,在脚本中增加一个Assert语句,要求update zip包只能用于同一设备,即目标设备的 ro.product.device 必须跟update.zip中的相同。

 

   device_specific.FullOTA_Assertions()
 Callback, 用于调用设备相关代码。调用时机为即将开始升级。类似还有:
FullOTA_InstallEnd IncrementalOTA_Assertions IncrementalOTA_VerifyEnd。 不深究。
    script.ShowProgress(0.5, 0)
 在升级脚本中加入显示进度的语句, 参数一表示底下的操作(到下一条同类语句或者到末尾)将暂用的时间在总体时间的比例。参数二用于控制显示的速度。比如,50 则表示底下的操作估计50秒内完成,要求进度条显示线程用50秒显示这一部分的进度。0 表示不自动更新,手动控制(使用SetProgress)
    if OPTIONS.wipe_user_data:
     script.FormatPartition("userdata")
 如果需要,在脚本中增加语句,擦除 userdata 分区。
    script.FormatPartition("system")
 在脚本中增加语句,擦除 system分区。
    script.Mount("MTD", "system", "/system")
 在脚本中增加语句,安装 system分区到 /system 目录。
   script.UnpackPackageDir("recovery", "/system")
   script.UnpackPackageDir("system", "/system")
在脚本中增加语句,将recovery以及system中的内容拷贝到 /system目录。其中recovery 目录包含一个patch 以及应用该patch 的脚本。
    symlinks = CopySystemFiles(input_zip, output_zip)
   script.MakeSymlinks(symlinks)
 386 行从输入 ZIP  /system 拷贝文件到输出 ZIP  /system。由于这个过程不支持链接文件,所以它将这些文件返回。 于 387 行做继续处理。该行建立这些link 文件。所有的link文件都指向 toolbox
    boot_img = File("boot.img", common.BuildBootableImage(
       os.path.join(OPTIONS.input_tmp, "BOOT")))
   recovery_img = File("recovery.img", common.BuildBootableImage(
       os.path.join(OPTIONS.input_tmp, "RECOVERY")))
   MakeRecoveryPatch(output_zip, recovery_img, boot_img)
 这个复杂,MakeRecoveryPatch 做了两件事:
1.在输出 ZIP包中生成一个patch: recovery/recovery-from-boot.p(boot.img recovery.imgpatch), 它最后会位于:system/recovery-from-boot.p
2.在输出 ZIP包中生成一个脚本:recovery/etc/install-recovery.sh , 它最后会位于system/etc/install-recovery.sh.
该脚本的内容为:
#!/system/bin/sh
if ! applypatch -c MTD:recovery:2048:6a167ffb86a4a16cb993473ce0726a3067163fc1; then
  log -t recovery "Installing new recovery image"
  applypatch MTD:boot:2324480:9a72a20a9c2f958ba586a840ed773cf8f5244183 MTD:recovery f6c2a70c5f2b02b6a49c9f5c5507a45a42e2d389 2564096 9a72a20a9c2f958ba586a840ed773cf8f5244183:/system/recovery-from-boot.p
else
  log -t recovery "Recovery image already installed"
fi
    Item.GetMetadata(input_zip)
 META/filesystem_config.txt 中获得 system 目录下的各文件权限信息。 
    Item.Get("system").SetPermissions(script)
 在脚本中增加语句,设置 system 目录下文件的权限及属主等。
    common.CheckSize(boot_img.data, "boot.img")
 检查 boot.img 文件大小是否超标.
    common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
 boot.img 放到输出 ZIP 包中。
    script.ShowProgress(0.2, 0)
   script.ShowProgress(0.2, 10)
 更行进度条。
    script.WriteRawImage("boot", "boot.img")
 在脚本中增加语句,将 boot.img 写到 boot 分区。
    script.ShowProgress(0.1, 0)
 更行进度条。
    device_specific.FullOTA_InstallEnd()
 Callback, 同前。
    if OPTIONS.extra_script is not None:
     script.AppendExtra(OPTIONS.extra_script)
 如果有额外脚本,加入。
    script.UnmountAll()
 在脚本中增加语句,umount 所有分区。
    script.AddToZip(input_zip, output_zip)
 1)将前面生成的脚本输出到:META-INF/com/google/android/updater-script (对于edify
   
2)将升级程序:OTA/bin/updater 从输入ZIP包中拷贝到输出ZIP包中的:META-INF/com/google/android/update-binary
    WriteMetadata(metadata, output_zip)

将前面获取的metadata 写入输出包的文件中: META-INF/com/android/metadata

至此,我们就得到了一个update.zip包。可以开始升级了。

思考

1 虽然提供了更新recovery分区的机制,但是没有看到触发该更新的语句。所以,缺省的情况是不会更新recovery分区的。大概是为了安全的原因吧。 但是,有时确实需要更新recovery 分区(比如,设备的硬件配置、分区表等发生改变),这该如何操作呢?

阅读(3508) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~