免费网站模板无需注册,国际知名设计公司收入,外贸网络推广高手何在,最新注册公司流程及费用Android OTA 升级之一#xff1a;编译升级包 
作者#xff1a; 宋立新 
Email#xff1a;zjujoeyahoo.com 
前言 OTA 升级是 Android 系统提供的标准软件升级方式。 它功能强大#xff0c;提供了完全升级、增量升级模式#xff0c;可以通过 SD 卡升级#xff0c;也可以通… Android OTA 升级之一编译升级包 
作者 宋立新 
Emailzjujoeyahoo.com 
前言 OTA 升级是 Android 系统提供的标准软件升级方式。 它功能强大提供了完全升级、增量升级模式可以通过 SD 卡升级也可以通过网络升级。 这里我们先研究最简单的情况通过 SD 卡进行完全升级。 如何执行升级就不多说了网上有很多资料。比如介绍HTC手机如何升级。我们感兴趣的是它是如何实现的作为开发者如何修改它以符合我们的定制化需求。 首先我们研究一下 ota 升级包的编译过程。 
Quick start 首先编译出android, 然后执行 
make otapackage 即可获得out/target/product/{product_name}/ {product_name}-ota-eng.{uid}.zip 将该文件改名为update.zip放到T卡根目录, 即可开始recovery模式下的 OTA 升级。 
编译过程研究 主要分两步第一步 会准备一个包其中包含升级需要的内容(原材料)比如system 目录。 
第二步运行python 脚本 ./build/tools/releasetools/ota_from_target_files以步骤一准备的ZIP包作为输入最终生成需要的升级包。 步骤一 
编译脚本如下: 
(From: build/core/Makefile) 1073 # Depending on the various images guarantees that the underlying  1074 # directories are up-to-date.  1075 $(BUILT_TARGET_FILES_PACKAGE): /  1076                 $(INSTALLED_BOOTIMAGE_TARGET) /  1077                 $(INSTALLED_RADIOIMAGE_TARGET) /  1078                 $(INSTALLED_RECOVERYIMAGE_TARGET) /  1079                 $(INSTALLED_FACTORYIMAGE_TARGET) /  1080                 $(INSTALLED_SYSTEMIMAGE) /  1081                 $(INSTALLED_USERDATAIMAGE_TARGET) /  1082                 $(INSTALLED_SECROIMAGE_TARGET) /  1083                 $(INSTALLED_ANDROID_INFO_TXT_TARGET) /  1084                 $(built_ota_tools) /  1085                 $(APKCERTS_FILE) /  1086                 $(HOST_OUT_EXECUTABLES)/fs_config /  1087                 | $(ACP)  1088         echo Package target files: $  1089         $(hide) rm -rf $ $(zip_root)  1090         $(hide) mkdir -p $(dir $) $(zip_root)  1091         # Components of the recovery image  1092         $(hide) mkdir -p $(zip_root)/RECOVERY  1093         $(hide) $(call package_files-copy-root, /  1094                 $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/RECOVERY/RAMDISK)  1095 ifdef INSTALLED_KERNEL_TARGET  1096         $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/RECOVERY/kernel  1097         $(hide) $(ACP) $(recovery_ramdisk) $(zip_root)/RECOVERY/ramdisk  1098 endif  1099 ifdef INSTALLED_2NDBOOTLOADER_TARGET  1100         $(hide) $(ACP) /  1101                 $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/RECOVERY/second  1102 endif  1103 ifdef BOARD_KERNEL_CMDLINE  1104         $(hide) echo $(BOARD_KERNEL_CMDLINE)  $(zip_root)/RECOVERY/cmdline  1105 endif  1106 ifdef BOARD_KERNEL_BASE  1107         $(hide) echo $(BOARD_KERNEL_BASE)  $(zip_root)/RECOVERY/base  1108 endif  1109         # Components of the factory image  1110         $(hide) mkdir -p $(zip_root)/FACTORY  1111         $(hide) $(call package_files-copy-root, /  1112                 $(TARGET_FACTORY_ROOT_OUT),$(zip_root)/FACTORY/RAMDISK)  1113 ifdef INSTALLED_KERNEL_TARGET  1114         $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/FACTORY/kernel  1115 endif  1116 ifdef INSTALLED_2NDBOOTLOADER_TARGET  1117         $(hide) $(ACP) /  1118                 $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/FACTORY/second  1119 endif  1120 ifdef BOARD_KERNEL_CMDLINE  1121         $(hide) echo $(BOARD_KERNEL_CMDLINE)  $(zip_root)/FACTORY/cmdline  1122 endif  1123 ifdef BOARD_KERNEL_BASE  1124         $(hide) echo $(BOARD_KERNEL_BASE)  $(zip_root)/FACTORY/base  1125 endif  1126         # Components of the boot image  1127         $(hide) mkdir -p $(zip_root)/BOOT  1128         $(hide) $(call package_files-copy-root, /  1129                 $(TARGET_ROOT_OUT),$(zip_root)/BOOT/RAMDISK)  1130 ifdef INSTALLED_KERNEL_TARGET  1131         $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/BOOT/kernel  1132         $(hide) $(ACP) $(INSTALLED_RAMDISK_TARGET) $(zip_root)/BOOT/ramdisk  1133 endif  1134 ifdef INSTALLED_2NDBOOTLOADER_TARGET  1135         $(hide) $(ACP) /  1136                 $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/BOOT/second  1137 endif  1138 ifdef BOARD_KERNEL_CMDLINE  1139         $(hide) echo $(BOARD_KERNEL_CMDLINE)  $(zip_root)/BOOT/cmdline  1140 endif  1141 ifdef BOARD_KERNEL_BASE  1142         $(hide) echo $(BOARD_KERNEL_BASE)  $(zip_root)/BOOT/base  1143 endif  1144         $(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),/  1145                     mkdir -p $(zip_root)/RADIO; /  1146                     $(ACP) $(t) $(zip_root)/RADIO/$(notdir $(t));)  1147         # Contents of the system image  1148         $(hide) $(call package_files-copy-root, /  1149                 $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)  1150         # Contents of the data image  1151         $(hide) $(call package_files-copy-root, /  1152                 $(TARGET_OUT_DATA),$(zip_root)/DATA)  1153         # Extra contents of the OTA package  1154         $(hide) mkdir -p $(zip_root)/OTA/bin  1155         $(hide) $(ACP) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/  1156         $(hide) $(ACP) $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/  1157         # Files that do not end up in any images, but are necessary to  1158         # build them.  1159         $(hide) mkdir -p $(zip_root)/META  1160         $(hide) $(ACP) $(APKCERTS_FILE) $(zip_root)/META/apkcerts.txt  1161         $(hide) echo $(PRODUCT_OTA_PUBLIC_KEYS)  $(zip_root)/META/otakeys.txt  1162         $(hide) echo $(PRIVATE_RECOVERY_API_VERSION)  $(zip_root)/META/recovery-api-version.txt  1163         $(hide) echo blocksize $(BOARD_FLASH_BLOCK_SIZE)  $(zip_root)/META/imagesizes.txt  1164         $(hide) echo boot $(call image-size-from-data-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))  $(zip_root)/META/imagesizes.txt  1165         $(hide) echo recovery $(call image-size-from-data-size,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE))  $(zip_root)/META/imagesizes.txt  1166         $(hide) echo system $(call image-size-from-data-size,$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))  $(zip_root)/META/imagesizes.txt  1167         $(hide) echo secro $(call image-size-from-data-size,$(BOARD_SECROIMAGE_PARTITION_SIZE))  $(zip_root)/META/imagesizes.txt  1168         $(hide) echo userdata $(call image-size-from-data-size,$(BOARD_USERDATAIMAGE_PARTITION_SIZE))  $(zip_root)/META/imagesizes.txt  1169         $(hide) echo $(tool_extensions)  $(zip_root)/META/tool-extensions.txt  1170         # Zip everything up, preserving symlinks  1171         $(hide) (cd $(zip_root)  zip -qry ../$(notdir $) .)  1172         # Run fs_config on all the system files in the zip, and save the output  1173         $(hide) zipinfo -1 $ | awk -F/ BEGIN { OFS/ } /^SYSTEM/// {$$1  system; print} | $(HOST_OUT_EXECUTABLES)/fs_config  $(zip_root)/META/filesystem_config.txt  1174         $(hide) (cd $(zip_root)  zip -q ../$(notdir $) META/filesystem_config.txt)   可见往里面添加了很多内容。 
L1089-1090 , 造一个目录。 
L1091-1108填充 RECOVERY 子目录的内容。用于生成recovery.img。包括kernel 的image, recovery 根文件系统的 image, recovery 根文件系统的内容 
RECOVERY$ tree -L 2 
├── kernel 
├── ramdisk 
└── RAMDISK ├── advanced_meta_init.rc ├── data ├── default.prop ├── dev ├── etc ├── init ├── init.factory.rc ├── init.goldfish.rc ├── init.mt6516.rc ├── init.rc ├── meta_init.rc ├── proc ├── res ├── sbin ├── sys ├── system └── tmp 
L1109-1125, 填充 FACTORY 子目录的内容 没有用到包括kernel 的image  
L1126-1143, 填充 BOOT子目录的内容用于生成boot.img。和 RECOVERY目录类似包括kernel 的image,根文件系统的 image,根文件系统的内容 
BOOT$ tree -L 2 
. 
├── kernel 
├── ramdisk 
└── RAMDISK ├── advanced_meta_init.rc ├── data ├── default.prop ├── dev ├── init ├── init.factory.rc ├── init.goldfish.rc ├── init.mt6516.rc ├── init.rc ├── meta_init.rc ├── proc ├── res - /system/res ├── sbin ├── sys └── system L1144-1146, 填充 RADIO子目录的内容 没有用到。 
L1147-1149, 填充 SYSTEM子目录的内容。 这是升级的主要内容。 
L1150-1152, 填充 DATA子目录的内容。缺省没有用到。 
L1153-1156, 填充 OTA/bin子目录的内容,这是OTA升级自己使用的程序。后面会遇到。 
OTA/bin$ tree 
. 
├── applypatch 
├── applypatch_static 
├── check_prereq 
└── updater 
L1159-1169, 填充 META子目录的内容,这里包含了OTA脚本需要的一些附加信息。 
L1170-1171,将所有内容打包。供下一阶段使用。 
L1173-1174,生成 META/filesystem_config.txt 并将其加入到 zip 包中。该文件保存了 system 目录下各目录、文件的权限及 owner. 
$ head META/filesystem_config.txt  
system 0 0 755 
system/usr 0 0 755 
system/usr/srec 0 0 755 
system/usr/srec/config 0 0 755 
system/usr/srec/config/en.us 0 0 755 
system/usr/srec/config/en.us/grammars 0 0 755 
system/usr/srec/config/en.us/grammars/phone_type_choice.g2g 0 0 644 
system/usr/srec/config/en.us/grammars/VoiceDialer.g2g 0 0 644 
system/usr/srec/config/en.us/grammars/boolean.g2g 0 0 644 
system/usr/srec/config/en.us/g2p 0 0 755 这里目录由 zipinfo –l 提供 而权限则由 fs_config 设定。此程序的源码位于build/tools/fs_config, 其中fs_config 包含了一个头文件 
54 #include private/android_filesystem_config.h 
这个文件(system/core/include/private/android_filesystem_config.h)以hardcoding 的方式设定了 system 下各目录、文件的权限、属主。比如 
152     { 00440, AID_ROOT,      AID_SHELL,     system/etc/init.goldfish.rc }, 
153     { 00550, AID_ROOT,      AID_SHELL,     system/etc/init.goldfish.sh }, 
154     { 00440, AID_ROOT,      AID_SHELL,     system/etc/init.trout.rc }, 
155     { 00550, AID_ROOT,      AID_SHELL,     system/etc/init.ril }, 如果需要升级其它内容比如 bootloader, 则可以在这里加入。 步骤二 
编译脚本如下: 
(From: build/core/Makefile) 1186 name : $(TARGET_PRODUCT)  1187 ifeq ($(TARGET_BUILD_TYPE),debug)  1188   name : $(name)_debug  1189 endif  1190 name : $(name)-ota-$(FILE_NAME_TAG)  1191   1192 INTERNAL_OTA_PACKAGE_TARGET : $(PRODUCT_OUT)/$(name).zip  1193   1194 $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR : $(DEFAULT_KEY_CERT_PAIR)  1195   1196 ifeq ($(TARGET_OTA_SCRIPT_MODE),)  1197 # default to auto  1198 $(INTERNAL_OTA_PACKAGE_TARGET): scriptmode : auto  1199 else  1200 $(INTERNAL_OTA_PACKAGE_TARGET): scriptmode : $(TARGET_OTA_SCRIPT_MODE)  1201 endif  1202   1203 $(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS)  1204         echo Package OTA: $  1205         $(hide) ./build/tools/releasetools/ota_from_target_files /  1206            -m $(scriptmode) /  1207            -p $(HOST_OUT) /  1208            -k $(KEY_CERT_PAIR) /  1209            $(BUILT_TARGET_FILES_PACKAGE) $   核心是一个python脚本: ota_from_target_files, 它以前一步骤生成的ZIP包作为输入生成可用于OTA升级的zip包。 具体内容我们后文继续分析。 Android OTA 升级之二脚本 ota_from_target_files 
作者 宋立新 
Emailzjujoeyahoo.com 
前言 前面介绍了ota package 的编译过程其中最核心的部分就是一个 python 脚本ota_from_target_files. 现在我们分析这个脚本。 
先看一下帮助 
不带任何参数先看一下它的帮助 $ ./ota_from_target_files     Given a target-files zipfile, produces an OTA package that installs    that build.  An incremental OTA is produced if -i is given, otherwise    a full OTA is produced.         Usage:  ota_from_target_files [flags] input_target_files output_ota_package      -b  (--board_config)  file          Deprecated.      -k  (--package_key)  key          Key to use to sign the package (default is          build/target/product/security/testkey).      -i  (--incremental_from)  file          Generate an incremental OTA using the given target-files zip as          the starting build.      -w  (--wipe_user_data)          Generate an OTA package that will wipe the user data partition          when installed.      -n  (--no_prereq)          Omit the timestamp prereq check normally included at the top of          the build scripts (used for developer OTA packages which          legitimately need to go back and forth).      -e  (--extra_script)  file          Insert the contents of file at the end of the update script.      -m  (--script_mode)  mode          Specify amend or edify scripts, or auto to pick          automatically (this is the default).      -p  (--path)  dir          Prepend dir/bin to the list of places to search for binaries          run by this script, and expect to find jars in dir/framework.      -s  (--device_specific) file          Path to the python module containing device-specific          releasetools code.      -x  (--extra)  keyvalue          Add a key/value pair to the extras dict, which device-specific          extension code may look at.      -v  (--verbose)          Show command lines being executed.      -h  (--help)          Display this usage message and exit.   简单翻译一下 
-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_files为python 脚本所以如果懂 python 会更顺利一点。 
文件有1000行。分析过程中我们只是贴代码片段。 完整文件见 
build/tools/releasetools/ota_from_target_files from Android 2.2 入口main 
按照python惯例单独执行的代码执行从__main__开始 
944 if __name__  __main__: 
945   try: 
946     main(sys.argv[1:]) 
947   except common.ExternalError, e: 
948     print 
949     print    ERROR: %s % (e,) 
950     print 
951     sys.exit(1) 它调用 main 函数 844 def main(argv):  845   846   def option_handler(o, a):  847     if o in (-b, --board_config):  848       pass   # deprecated  849     elif o in (-k, --package_key):  850       OPTIONS.package_key  a  851     elif o in (-i, --incremental_from):  852       OPTIONS.incremental_source  a  853     elif o in (-w, --wipe_user_data):  854       OPTIONS.wipe_user_data  True  855     elif o in (-n, --no_prereq):  856       OPTIONS.omit_prereq  True  857     elif o in (-e, --extra_script):  858       OPTIONS.extra_script  a  859     elif o in (-m, --script_mode):  860       OPTIONS.script_mode  a  861     elif o in (--worker_threads):  862       OPTIONS.worker_threads  int(a)  863     else:  864       return False  865     return True  866   867   args  common.ParseOptions(argv, __doc__,  868                              extra_optsb:k:i:d:wne:m:,  869                              extra_long_opts[board_config,  870                                               package_key,  871                                               incremental_from,  872                                               wipe_user_data,  873                                               no_prereq,  874                                               extra_script,  875                                               script_mode,  876                                               worker_threads],  877                              extra_option_handleroption_handler)  878   879   if len(args) ! 2:  880     common.Usage(__doc__)  881     sys.exit(1)   将用户设定的 Option 存入 OPTIONS 变量中。它是一个Python Class, 我们将其理解为一个C Struct 即可。 883   if OPTIONS.script_mode not in (amend, edify, auto): 
884     raise ValueError(unknown script mode %s % (OPTIONS.script_mode,)) Script_mode 只能是amend/edify/auto之一 auto 目前是选择两者都支持。 
可以理解是为了向前兼容,(早期 Android 使用 amend) 886   if OPTIONS.extra_script is not None: 
887     OPTIONS.extra_script  open(OPTIONS.extra_script).read() 读入 额外脚本的内容。如果有 889   print unzipping target target-files... 
890   OPTIONS.input_tmp  common.UnzipTemp(args[0]) 解开输入包。 892   if OPTIONS.device_specific is None:  893     # look for the device-specific tools extension location in the input  894     try:  895       f  open(os.path.join(OPTIONS.input_tmp, META, tool-extensions.txt))  896       ds  f.read().strip()  897       f.close()  898       if ds:  899         ds  os.path.normpath(ds)  900         print using device-specific extensions in, ds  901         OPTIONS.device_specific  ds  902     except IOError, e:  903       if e.errno  errno.ENOENT:  904         # nothing specified in the file  905         pass  906       else:  907         raise     
处理 device-specific extensions 没用到。 909   common.LoadMaxSizes() 
910   if not OPTIONS.max_image_size: 
911     print 
912     print   WARNING:  Failed to load max image sizes; will not enforce 
913     print   image size limits. 
914     print 读入设定image大小的参数没用到。 916   OPTIONS.target_tmp  OPTIONS.input_tmp 
917   input_zip  zipfile.ZipFile(args[0], r) 
918   if OPTIONS.package_key: 
919     temp_zip_file  tempfile.NamedTemporaryFile() 
920     output_zip  zipfile.ZipFile(temp_zip_file, w, 
921                                  compressionzipfile.ZIP_DEFLATED) 
922   else: 
923     output_zip  zipfile.ZipFile(args[1], w, 
924                  compressionzipfile.ZIP_DEFLATED) 设定输出文件如果要签名our case,则还需要一个临时输出文件。 926   if OPTIONS.incremental_source is None: 
927     WriteFullOTAPackage(input_zip, output_zip) 
928   else: 
929     print unzipping source target-files... 
930     OPTIONS.source_tmp  common.UnzipTemp(OPTIONS.incremental_source) 
931     source_zip  zipfile.ZipFile(OPTIONS.incremental_source, r) 
932     WriteIncrementalOTAPackage(input_zip, source_zip, output_zip) 根据参数调用增量和非增量创建 ZIP 创建函数我们采用非增量模式。 934   output_zip.close() 
935   if OPTIONS.package_key: 
936     SignOutput(temp_zip_file.name, args[1]) 
937     temp_zip_file.close() 
939   common.Cleanup() 
941   print done. 签名如果需要的话处理完毕。 下面我们看主要功能函数WriteFullOTAPackage。 
主功能WriteFullOTAPackage 345 def WriteFullOTAPackage(input_zip, output_zip): 
346   if OPTIONS.script_mode  auto: 
347     script  both_generator.BothGenerator(2) 
348   elif OPTIONS.script_mode  amend: 
349     script  amend_generator.AmendGenerator() 
350   else: 
351     # TODO: how to determine this?  We dont know what version it will 
352     # be installed on top of.  For now, we expect the API just wont 
353     # change very often. 
354     script  edify_generator.EdifyGenerator(2) 首先我们获得脚本生成器他们的实现见脚本edify_generator.py 等。 356   metadata  {post-build: GetBuildProp(ro.build.fingerprint, input_zip), 
357               pre-device: GetBuildProp(ro.product.device, input_zip), 
358               post-timestamp: GetBuildProp(ro.build.date.utc, input_zip), 
359               } 获得一些环境变量来自android 环境变量。 Google 一下即知其义。 361   device_specific  common.DeviceSpecificParams( 
362       input_zipinput_zip, 
363       input_versionGetRecoveryAPIVersion(input_zip), 
364       output_zipoutput_zip, 
365       scriptscript, 
366       input_tmpOPTIONS.input_tmp, 
367       metadatametadata) 设备相关参数不深究。 369   if not OPTIONS.omit_prereq: 
370     ts  GetBuildProp(ro.build.date.utc, input_zip) 
371     script.AssertOlderBuild(ts) 如果需要在脚本中增加一个Assert语句要求update zip包只能用于升级老的系统。 373   AppendAssertions(script, input_zip) 如果需要在脚本中增加一个Assert语句要求update zip包只能用于同一设备即目标设备的 ro.product.device 必须跟update.zip中的相同。 374   device_specific.FullOTA_Assertions() Callback, 用于调用设备相关代码。调用时机为即将开始升级。类似还有 
FullOTA_InstallEnd IncrementalOTA_Assertions IncrementalOTA_VerifyEnd。 不深究。 376   script.ShowProgress(0.5, 0) 在升级脚本中加入显示进度的语句 参数一表示底下的操作到下一条同类语句或者到末尾将暂用的时间在总体时间的比例。参数二用于控制显示的速度。比如50 则表示底下的操作估计50秒内完成要求进度条显示线程用50秒显示这一部分的进度。0 表示不自动更新手动控制使用SetProgress) 378   if OPTIONS.wipe_user_data: 
379     script.FormatPartition(userdata) 如果需要在脚本中增加语句擦除 userdata 分区。 381   script.FormatPartition(system) 在脚本中增加语句擦除 system分区。 382   script.Mount(MTD, system, /system) 在脚本中增加语句安装 system分区到 /system 目录。 
383   script.UnpackPackageDir(recovery, /system) 
384   script.UnpackPackageDir(system, /system) 
在脚本中增加语句将recovery以及system中的内容拷贝到 /system目录。其中recovery 目录包含一个patch 以及应用该patch 的脚本。 386   symlinks  CopySystemFiles(input_zip, output_zip) 
387   script.MakeSymlinks(symlinks) 386 行从输入 ZIP 包 /system 拷贝文件到输出 ZIP 包 /system。由于这个过程不支持链接文件所以它将这些文件返回。 于 387 行做继续处理。该行建立这些link 文件。所有的link文件都指向 toolbox 389   boot_img  File(boot.img, common.BuildBootableImage( 
390       os.path.join(OPTIONS.input_tmp, BOOT))) 
391   recovery_img  File(recovery.img, common.BuildBootableImage( 
392       os.path.join(OPTIONS.input_tmp, RECOVERY))) 
393   MakeRecoveryPatch(output_zip, recovery_img, boot_img) 这个复杂MakeRecoveryPatch 做了两件事: 
1.在输出 ZIP包中生成一个patch: recovery/recovery-from-boot.p(boot.img和 recovery.img的patch), 它最后会位于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 395   Item.GetMetadata(input_zip) 从 META/filesystem_config.txt 中获得 system 目录下的各文件权限信息。  396   Item.Get(system).SetPermissions(script) 在脚本中增加语句设置 system 目录下文件的权限及属主等。 398   common.CheckSize(boot_img.data, boot.img) 检查 boot.img 文件大小是否超标. 399   common.ZipWriteStr(output_zip, boot.img, boot_img.data) 将boot.img 放到输出 ZIP 包中。 400   script.ShowProgress(0.2, 0) 
402   script.ShowProgress(0.2, 10) 更行进度条。 403   script.WriteRawImage(boot, boot.img) 在脚本中增加语句将 boot.img 写到 boot 分区。 405   script.ShowProgress(0.1, 0) 更行进度条。 406   device_specific.FullOTA_InstallEnd() Callback, 同前。 408   if OPTIONS.extra_script is not None: 
409     script.AppendExtra(OPTIONS.extra_script) 如果有额外脚本加入。 411   script.UnmountAll() 在脚本中增加语句umount 所有分区。 412   script.AddToZip(input_zip, output_zip) 1将前面生成的脚本输出到META-INF/com/google/android/updater-script 对于edify assert(getprop(ro.product.device)  thedevicename ||           getprop(ro.build.product)  theproductname);    show_progress(0.500000, 0);    format(MTD, system);    mount(MTD, system, /system);    package_extract_dir(recovery, /system);    package_extract_dir(system, /system);    symlink(dumpstate, /system/bin/dumpcrash);    symlink(toolbox, /system/bin/cat, /system/bin/chmod,            /system/bin/chown, /system/bin/cmp, /system/bin/date,            /system/bin/dd, /system/bin/df, /system/bin/dmesg,            /system/bin/fb2bmp, /system/bin/getevent, /system/bin/getprop,            /system/bin/hd, /system/bin/id, /system/bin/ifconfig,            /system/bin/iftop, /system/bin/insmod, /system/bin/ioctl,            /system/bin/kill, /system/bin/ln, /system/bin/log,            /system/bin/ls, /system/bin/lsmod, /system/bin/mkdir,            /system/bin/mount, /system/bin/mv, /system/bin/netstat,            /system/bin/newfs_msdos, /system/bin/notify, /system/bin/printenv,            /system/bin/ps, /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/umount,            /system/bin/vmstat, /system/bin/watchprops,            /system/bin/wipe);    set_perm_recursive(0, 0, 0755, 0644, /system);    set_perm_recursive(0, 2000, 0755, 0755, /system/bin);    set_perm(0, 3003, 02755, /system/bin/netcfg);    set_perm(0, 3004, 02755, /system/bin/ping);    set_perm_recursive(1002, 1002, 0755, 0440, /system/etc/bluez);    set_perm(0, 0, 0755, /system/etc/bluez);    set_perm(1002, 1002, 0440, /system/etc/dbus.conf);    set_perm(1014, 2000, 0550, /system/etc/dhcpcd/dhcpcd-run-hooks);    set_perm(0, 2000, 0550, /system/etc/init.goldfish.sh);    set_perm(0, 0, 0544, /system/etc/install-recovery.sh);    set_perm_recursive(0, 0, 0755, 0555, /system/etc/ppp);    set_perm_recursive(0, 2000, 0755, 0755, /system/xbin);    show_progress(0.200000, 0);    show_progress(0.200000, 10);    assert(package_extract_file(boot.img, /tmp/boot.img),           write_raw_image(/tmp/boot.img, boot),           delete(/tmp/boot.img));    show_progress(0.100000, 0);    unmount(/system);     
2将升级程序OTA/bin/updater 从输入ZIP包中拷贝到输出ZIP包中的META-INF/com/google/android/update-binary 413   WriteMetadata(metadata, output_zip) 
将前面获取的metadata 写入输出包的文件中 META-INF/com/android/metadata 
至此我们就得到了一个update.zip包。可以开始升级了。 
思考 
1 虽然提供了更新recovery分区的机制但是没有看到触发该更新的语句。所以缺省的情况是不会更新recovery分区的。大概是为了安全的原因吧。 但是有时确实需要更新recovery 分区比如设备的硬件配置、分区表等发生改变这该如何操作呢 Android OTA 升级之三生成recovery.img 前言 得到了ota升级包后我们就可以用它来升级系统了。Android 手机开机后会先运行 bootloader。 Bootloader 会根据某些判定条件比如按某个特殊键决定是否进入 recovery 模式。Recovery 模式会装载 recovery 分区 该分区包含recovery.img。recovery.img 包含了标准内核和boot.img中的内核相同以及recovery 根文件系统。下面我们看一下它是如何生成的。 recovery.img生成过程 
L630-L637 依赖关系 
(From: build/core/Makefile) 630 $(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) / 
631                 $(INSTALLED_RAMDISK_TARGET) / 
632                 $(INSTALLED_BOOTIMAGE_TARGET) / 
633                 $(recovery_binary) / 
634                 $(recovery_initrc) $(recovery_kernel) / 
635                 $(INSTALLED_2NDBOOTLOADER_TARGET) / 
636                 $(recovery_build_prop) $(recovery_resource_deps) / 
637                 $(RECOVERY_INSTALL_OTA_KEYS) INSTALLED_RECOVERYIMAGE_TARGET 为我们的编译目标 
584 INSTALLED_RECOVERYIMAGE_TARGET : $(PRODUCT_OUT)/recovery.img 它依赖很多其它目标 
1MKBOOTFS, MINIGZIP, MKBOOTIMGPC端工具软件 
From build/core/config.mk 
265 MKBOOTFS : $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX) 
266 MINIGZIP : $(HOST_OUT_EXECUTABLES)/minigzip$(HOST_EXECUTABLE_SUFFIX) 
267 MKBOOTIMG : $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX) 2INSTALLED_RAMDISK_TARGET标准根文件系统 ramdisk.img 
326 BUILT_RAMDISK_TARGET : $(PRODUCT_OUT)/ramdisk.img 
328 # We just build this directly to the install location. 
329 INSTALLED_RAMDISK_TARGET : $(BUILT_RAMDISK_TARGET) 3INSTALLED_BOOTIMAGE_TARGET 即boot.img,标准内核及标准根文件系统 
362 INSTALLED_BOOTIMAGE_TARGET : $(PRODUCT_OUT)/boot.img 4. recovery_binary, Recovery可执行程序源码位于bootable/recovery 
590 recovery_binary : $(call intermediates-dir-for,EXECUTABLES,recovery)/recovery 5. recovery_initrcrecovery模式的init.rc, 位于 bootable/recovery/etc/init.rc 
586 recovery_initrc : $(call include-path-for, recovery)/etc/init.rc 6. recovery_kernel, recovery 模式的kernel, 同标准内核 
587 recovery_kernel : $(INSTALLED_KERNEL_TARGET) # same as a non-recovery system 7.INSTALLED_2NDBOOTLOADER_TARGET我们不用。 8. recovery_build_prop recovery 模式的build.prop, 同标准模式。 
589 recovery_build_prop : $(INSTALLED_BUILD_PROP_TARGET) 9. recovery_resource_deps recovery 模式使用的res, 位于recovery/custom/{product_name}/res, 以及设备自定义部分我们没用到 
591 recovery_resources_common : $(call include-path-for, recovery)/custom/$(TARGET_PRODUCT)/res 
592 recovery_resources_private : $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery/res)) 
593 recovery_resource_deps : $(shell find $(recovery_resources_common)  
594   $(recovery_resources_private) -type f) 10.  RECOVERY_INSTALL_OTA_KEYS, ota 密钥 
618 # Generate a file containing the keys that will be read by the 
619 # recovery binary. 
620 RECOVERY_INSTALL_OTA_KEYS : / 
621         $(call intermediates-dir-for,PACKAGING,ota_keys)/keys 
L638-L655 准备内容 
638         echo ----- Making recovery image ------ 
639         rm -rf $(TARGET_RECOVERY_OUT) 
640         mkdir -p $(TARGET_RECOVERY_OUT) 
641         mkdir -p $(TARGET_RECOVERY_ROOT_OUT) 
642         mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/etc 
643         mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/tmp 准备recovery目录out/target/product/{product_name}/recovery 及其子目录 
./root 
./root/etc 
./root/tmp 644         echo Copying baseline ramdisk... 
645         cp -R $(TARGET_ROOT_OUT) $(TARGET_RECOVERY_OUT) 
646         echo Modifying ramdisk contents... 
647         rm -rf $(TARGET_RECOVERY_ROOT_OUT)/res 从标准根文件系统拷贝所有文件 删除其res 目录。 648         cp -f $(recovery_initrc) $(TARGET_RECOVERY_ROOT_OUT)/ 
649         cp -f $(recovery_binary) $(TARGET_RECOVERY_ROOT_OUT)/sbin/ 拷贝recovery 模式的核心文件 init.rc 及 recovery 650         cp -rf $(recovery_resources_common) $(TARGET_RECOVERY_ROOT_OUT)/ 
651         $(foreach item,$(recovery_resources_private), / 
652           cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/) 
653         cp $(RECOVERY_INSTALL_OTA_KEYS) $(TARGET_RECOVERY_ROOT_OUT)/res/keys 拷贝资源文件及密钥文件。 654         cat $(INSTALLED_DEFAULT_PROP_TARGET) $(recovery_build_prop) / 
655                  $(TARGET_RECOVERY_ROOT_OUT)/default.prop 生成属性文件 default.prop, 它包含了标准根文件系统的default.prop out/target/product/{product_name}/root/default.prop以及system分区的build.prop (out/target/product/{product_name}/system/build.prop) L656-L661 最终生成recovery.img 
656         $(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | $(MINIGZIP)  $(recovery_ramdisk) 压缩recovery根文件系统 657         build/quacomm/mkimage $(PRODUCT_OUT)/ramdisk-recovery.img RECOVERY  $(PRODUCT_OUT)/ramdisk_recovery.img 加一个标识头RECOVERY 658         mv $(PRODUCT_OUT)/ramdisk_recovery.img $(PRODUCT_OUT)/ramdisk-recovery.img 
659         $(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) --output $ 
660         echo ----- Made recovery image -------- $ 
661         $(hide) $(call assert-max-image-size,$,$(BOARD_RECOVERYIMAGE_PARTITION_SIZE),raw) 和内核一起生成recovery.img 附Recovery 根文件系统目录结构 $ tree 
. 
├── advanced_meta_init.rc 
├── data 
├── default.prop 
├── dev 
├── etc 
├── init 
├── init.factory.rc 
├── init.goldfish.rc 
├── init.quacomm.rc 
├── init.rc 
├── meta_init.rc 
├── proc 
├── res 
│   ├── images 
│   │   ├── icon_error.png 
│   │   ├── icon_installing.png 
│   │   ├── indeterminate1.png 
│   │   ├── indeterminate2.png 
│   │   ├── indeterminate3.png 
│   │   ├── indeterminate4.png 
│   │   ├── indeterminate5.png 
│   │   ├── indeterminate6.png 
│   │   ├── progress_empty.png 
│   │   └── progress_fill.png 
│   └── keys 
├── sbin 
│   ├── adbd 
│   ├── advanced_meta_init 
│   ├── meta_init 
│   ├── meta_tst 
│   └── recovery 
├── sys 
├── system 
└── tmp Android OTA 升级之四进入根文件系统 
作者 宋立新 
Emailzjujoeyahoo.com 
前言 从bootloader 进入Recovery 模式后首先也是运行Linux内核该内核跟普通模式没有区别(减轻了BSP开发者的任务)。区别从执行文件系统开始。 Recovery 模式的细节就隐藏在其根文件系统中。 下面我们就看看进入Recovery 根文件系统都干些啥。 init.rc 和正常启动一样内核进入文件系统会执行/init, init 的配置文件就是 /init.rc, 前面文章讲过这个文件来自bootable/recovery/etc/init.rc下面我们看看它的内容。 1  2 on init 3     export PATH /sbin 4     export ANDROID_ROOT /system 5     export ANDROID_DATA /data 6     export EXTERNAL_STORAGE /sdcard 7  8     symlink /system/etc /etc 9  10     mkdir /sdcard 11     mkdir /system 12     mkdir /data 13     mkdir /cache 14     mount /tmp /tmp tmpfs 15  16 on boot 17  18     ifup lo 19     hostname localhost 20     domainname localdomain 21  22     class_start default 23  24  25 service recovery /sbin/recovery 26  27 service adbd /sbin/adbd recovery 28     disabled 29  30 on property:persist.service.adb.enable1 31     start adbd 32  33 on property:persist.service.adb.enable0 34     stop adbd 可以看到它很非常简单 
1   设置几个环境变量。备用。 
2   建立 etc 链接。 
3   造几个目录。备用。 
4   Mount /tmp 目录为内存文件系统 tmpfs后面会用到。 
5   Trival 设置,不必关心。 
6   启动 recovery主程序。 
7   如果是eng模式此时persist.service.adb.enable启动adb 
当然init主程序还会装载属性配置文件 /default.prop, 它包含了很多系统属性设置比如ro.build.* 等等。 很明显这里最重要的就是recovery主程序下面我们分析它。 
先看一段注释 
Recovery.c 中作者写了一段注释对我们理解recovery的实现很有帮助下面看一下我就不翻译了 
89 /* 90  * The recovery tool communicates with the main system through /cache files. 91  *   /cache/recovery/command - INPUT - command line for tool, one arg per line 92  *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s) 93  *   /cache/recovery/intent - OUTPUT - intent that was passed in 94  * 95  * The arguments which may be supplied in the recovery.command file: 96  *   --send_intentanystring - write the text out to recovery.intent 97  *   --update_packageroot:path - verify install an OTA package file 98  *   --wipe_data - erase user data (and cache), then reboot 99  *   --wipe_cache - wipe cache (but not user data), then reboot 
100  *   --set_encrypted_filesystemon|off - enables / diasables encrypted fs 
101  * 
102  * After completing, we remove /cache/recovery/command and reboot. 
103  * Arguments may also be supplied in the bootloader control block (BCB). 
104  * These important scenarios must be safely restartable at any point: 
105  * 
106  * FACTORY RESET 
107  * 1. user selects factory reset 
108  * 2. main system writes --wipe_data to /cache/recovery/command 
109  * 3. main system reboots into recovery 
110  * 4. get_args() writes BCB with boot-recovery and --wipe_data 
111  *    -- after this, rebooting will restart the erase -- 
112  * 5. erase_root() reformats /data 
113  * 6. erase_root() reformats /cache 
114  * 7. finish_recovery() erases BCB 
115  *    -- after this, rebooting will restart the main system -- 
116  * 8. main() calls reboot() to boot main system 
117  * 
118  * OTA INSTALL 
119  * 1. main system downloads OTA package to /cache/some-filename.zip 
120  * 2. main system writes --update_packageCACHE:some-filename.zip 
121  * 3. main system reboots into recovery 
122  * 4. get_args() writes BCB with boot-recovery and --update_package... 
123  *    -- after this, rebooting will attempt to reinstall the update -- 
124  * 5. install_package() attempts to install the update 
125  *    NOTE: the package install must itself be restartable from any point 
126  * 6. finish_recovery() erases BCB 
127  *    -- after this, rebooting will (try to) restart the main system -- 
128  * 7. ** if install failed ** 
129  *    7a. prompt_and_wait() shows an error icon and waits for the user 
130  *    7b; the user reboots (pulling the battery, etc) into the main system 
131  * 8. main() calls maybe_install_firmware_update() 
132  *    ** if the update contained radio/hboot firmware **: 
133  *    8a. m_i_f_u() writes BCB with boot-recovery and --wipe_cache 
134  *        -- after this, rebooting will reformat cache  restart main system -- 
135  *    8b. m_i_f_u() writes firmware image into raw cache partition 
136  *    8c. m_i_f_u() writes BCB with update-radio/hboot and --wipe_cache 
137  *        -- after this, rebooting will attempt to reinstall firmware -- 
138  *    8d. bootloader tries to flash firmware 
139  *    8e. bootloader writes BCB with boot-recovery (keeping --wipe_cache) 
140  *        -- after this, rebooting will reformat cache  restart main system -- 
141  *    8f. erase_root() reformats /cache 
142  *    8g. finish_recovery() erases BCB 
143  *        -- after this, rebooting will (try to) restart the main system -- 
144  * 9. main() calls reboot() to boot main system 
145  * 
146  * ENCRYPTED FILE SYSTEMS ENABLE/DISABLE 
147  * 1. user selects enable encrypted file systems 
148  * 2. main system writes --set_encrypted_filesystemon|off to 
149  *    /cache/recovery/command 
150  * 3. main system reboots into recovery 
151  * 4. get_args() writes BCB with boot-recovery and 
152  *    --set_encrypted_filesystemson|off 
153  *    -- after this, rebooting will restart the transition -- 
154  * 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data 
155  *    Settings include: property to specify the Encrypted FS istatus and 
156  *    FS encryption key if enabled (not yet implemented) 
157  * 6. erase_root() reformats /data 
158  * 7. erase_root() reformats /cache 
159  * 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data 
160  *    Settings include: property to specify the Encrypted FS status and 
161  *    FS encryption key if enabled (not yet implemented) 
162  * 9. finish_recovery() erases BCB 
163  *    -- after this, rebooting will restart the main system -- 
164  * 10. main() calls reboot() to boot main system 
165  */ recovery 主程序 
559 int 
560 main(int argc, char **argv) 
561 { 
562     time_t start  time(NULL); 
563  
564     // If these fail, theres not really anywhere to complain... 
565     freopen(TEMPORARY_LOG_FILE, a, stdout); setbuf(stdout, NULL); 
566     freopen(TEMPORARY_LOG_FILE, a, stderr); setbuf(stderr, NULL); 
567     fprintf(stderr, Starting recovery on %s, ctime(start)); 
568  将标准输出和标准错误输出重定位到/tmp/recovery.log,如果是eng模式就可以通过adb pull /tmp/recovery.log, 看到当前的log信息这为我们提供了有效的调试手段。后面还会看到recovery模式运行完毕后会将其拷贝到cache分区以便后续分析。 569     ui_init(); Recovery 使用了一个简单的基于framebuffer的ui系统叫miniui,这里进行了简单的初始化主要是图形部分以及事件部分并启动了一个 event 线程用于响应用户按键。 570     get_args(argc, argv); 
从misc 分区以及 CACHE:recovery/command 文件中读入参数写入到argc, argv ,并且如果有必要回写入misc分区。这样如果recovery没有操作成功比如升级还没有结束就拔电池系统会一直进入recovery模式。提醒用户继续升级直到成功。 572     int previous_runs  0; 
573     const char *send_intent  NULL; 
574     const char *update_package  NULL; 
575     int wipe_data  0, wipe_cache  0; 
576  
577     int arg; 
578     while ((arg  getopt_long(argc, argv, , OPTIONS, NULL)) ! -1) { 
579         switch (arg) { 
580         case p: previous_runs  atoi(optarg); break; 
581         case s: send_intent  optarg; break; 
582         case u: update_package  optarg; break; 
583         case w: wipe_data  wipe_cache  1; break; 
584         case c: wipe_cache  1; break; 
585         case ?: 
586             LOGE(Invalid command argument/n); 
587             continue; 
588         } 
589     } 
590  
解析参数p: previous_runs没有用到其它含义见前面注释。 591     device_recovery_start(); 这个函数没干什么。看名字它給设备制造商提供了一个调用机会可写入设备相关初始化代码。 
592  
593     fprintf(stderr, Command:); 
594     for (arg  0; arg  argc; arg) { 
595         fprintf(stderr,  /%s/, argv[arg]); 
596     } 
597     fprintf(stderr, /n/n); 
598  
打印出命令比如正常启动进入recovery模式会打印Command: /sbin/recovery 
599     property_list(print_property, NULL); 
600     fprintf(stderr, /n); 
601  
打印出所有的系统属性from default.prop到log文件。 602     int status  INSTALL_SUCCESS; 
603  
604     if (update_package ! NULL) { 
605         status  install_package(update_package); 
606         if (status ! INSTALL_SUCCESS) ui_print(Installation aborted./n); 
607     } else if (wipe_data) { 
608         if (device_wipe_data()) status  INSTALL_ERROR; 
609         if (erase_root(DATA:)) status  INSTALL_ERROR; 
610         if (wipe_cache  erase_root(CACHE:)) status  INSTALL_ERROR; 
611         if (status ! INSTALL_SUCCESS) ui_print(Data wipe failed./n); 
612     } else if (wipe_cache) { 
613         if (wipe_cache  erase_root(CACHE:)) status  INSTALL_ERROR; 
614         if (status ! INSTALL_SUCCESS) ui_print(Cache wipe failed./n); 
615     } else { 
616         status  INSTALL_ERROR;  // No command specified 
617     } 根据用户提供参数调用各项功能比如安装一个升级包擦除cache分区, 擦除user data分区install_package比较复杂后面专门分析其它都很简单。忽略。 618  
619     if (status ! INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR); 
622     if (status ! INSTALL_SUCCESS) prompt_and_wait(); 如果前面已经做了某项操作并且成功则进入重启流程。否则等待用户选择具体操作。 
而用户可选操作为 reboot, 安装update.zip除cache分区, 擦除user data分区,如前所述只有安装package 比较复杂其它简单。 623  
624     // Otherwise, get ready to boot the main system... 
625     finish_recovery(send_intent); 它的功能如下 
1将前面定义的intent字符串写入如果有的话CACHE:recovery/command 
2将 /tmp/recovery.log 复制到 CACHE:recovery/log; 
3清空 misc 分区这样重启就不会进入recovery模式 
4)删除command 文件CACHE:recovery/command; 626     ui_print(Rebooting.../n); 
627     sync(); 
628     reboot(RB_AUTOBOOT); 
629     return EXIT_SUCCESS; 
630 } 重启。 
下面我们分析核心函数 install_package install_package 
289 int 
290 install_package(const char *root_path) 
291 { 
292     ui_set_background(BACKGROUND_ICON_INSTALLING); 
294     ui_print(Finding update package.../n); 
295     LOGI(Finding update package.../n); 
296     ui_show_indeterminate_progress(); 
297     LOGI(Update location: %s/n, root_path); 
298  
更新 UI 显示 
299     if (ensure_root_path_mounted(root_path) ! 0) { 
300         LOGE(Cant mount %s/n, root_path); 
301         reset_mark_block(); 
302         return INSTALL_CORRUPT; 
303     } 
304  确保升级包所在分区已经mount,通常为 cache 分区或者 SD 分区 305     char path[PATH_MAX]  ; 
306     if (translate_root_path(root_path, path, sizeof(path))  NULL) { 
307         LOGE(Bad path %s/n, root_path); 
308         reset_mark_block(); 
309         return INSTALL_CORRUPT; 
310     } 将根分区转化为具体分区信息.这些信息来自全局数组g_roots 313     ui_print(Opening update package.../n); 
314     LOGI(Opening update package.../n); 
315     LOGI(Update file path: %s/n, path); 
316  
317     int numKeys; 
318     RSAPublicKey* loadedKeys  load_keys(PUBLIC_KEYS_FILE, numKeys); 
319     if (loadedKeys  NULL) { 
320         LOGE(Failed to load keys/n); 
321         reset_mark_block(); 
322         return INSTALL_CORRUPT; 
323     } 
324     LOGI(%d key(s) loaded from %s/n, numKeys, PUBLIC_KEYS_FILE); 从/res/keys中装载公钥。 326     // Give verification half the progress bar... 
328     ui_print(Verifying update package.../n); 
329     LOGI(Verifying update package.../n); 
330     ui_show_progress( 
331             VERIFICATION_PROGRESS_FRACTION, 
332             VERIFICATION_PROGRESS_TIME); 
333  
334     int err; 
335     err  verify_file(path, loadedKeys, numKeys); 
336     free(loadedKeys); 
337     LOGI(verify_file returned %d/n, err); 
338     if (err ! VERIFY_SUCCESS) { 
339         LOGE(signature verification failed/n); 
340         reset_mark_block(); 
341         return INSTALL_CORRUPT; 
342     } 根据公钥验证升级包verify_file的注释说的很明白 // Look for an RSA signature embedded in the .ZIP file comment given // the path to the zip.  Verify it matches one of the given public // keys. 344     /* Try to open the package. 
345      */ 
346     ZipArchive zip; 
347     err  mzOpenZipArchive(path, zip); 
348     if (err ! 0) { 
349         LOGE(Cant open %s/n(%s)/n, path, err ! -1 ? strerror(err) : bad); 
350         reset_mark_block(); 
351         return INSTALL_CORRUPT; 
352     } 打开升级包将相关信息存到ZuoArchive数据机构中便于后面处理。 354     /* Verify and install the contents of the package. 
355      */ 
356     int status  handle_update_package(path, zip); 处理函数我们后面继续分析。 357     mzCloseZipArchive(zip); 
358     return status; 
359 } 关闭zip包结束处理。 
handle_update_package 
204 static int 
205 handle_update_package(const char *path, ZipArchive *zip) 
206 { 
207     // Update should take the rest of the progress bar. 
208     ui_print(Installing update.../n); 
209  
210     int result  try_update_binary(path, zip); 
211     register_package_root(NULL, NULL);  // Unregister package root 
212     return result; 
213 } 它主要调用函数try_update_binary完成功能。 
try_update_binary 
84 // If the package contains an update binary, extract it and run it. 85 static int 86 try_update_binary(const char *path, ZipArchive *zip) { 87     const ZipEntry* binary_entry  88             mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); 89     if (binary_entry  NULL) { 90         return INSTALL_CORRUPT; 91     } 92 93     char* binary  /tmp/update_binary; 94     unlink(binary); 95     int fd  creat(binary, 0755); 96     if (fd  0) { 97         LOGE(Cant make %s/n, binary); 98         return 1; 99     } 
100     bool ok  mzExtractZipEntryToFile(zip, binary_entry, fd); 
101     close(fd); 
102 
103     if (!ok) { 
104         LOGE(Cant copy %s/n, ASSUMED_UPDATE_BINARY_NAME); 
105         return 1; 
106     } 将升级包内文件META-INF/com/google/android/update-binary 复制为/tmp/update_binary 108     int pipefd[2]; 
109     pipe(pipefd); 
110 
111     // When executing the update binary contained in the package, the 
112     // arguments passed are: 
113     // 
114     //   - the version number for this interface 
115     // 
116     //   - an fd to which the program can write in order to update the 
117     //     progress bar.  The program can write single-line commands: 
118     // 
119     //        progress frac secs 
120     //            fill up the next frac part of of the progress bar 
121     //            over secs seconds.  If secs is zero, use 
122     //            set_progress commands to manually control the 
123     //            progress of this segment of the bar 
124     // 
125     //        set_progress frac 
126     //            frac should be between 0.0 and 1.0; sets the 
127     //            progress bar within the segment defined by the most 
128     //            recent progress command. 
129     // 
130     //        firmware hboot|radio filename 
131     //            arrange to install the contents of filename in the 
132     //            given partition on reboot. 
133     // 
134     //            (API v2: filename may start with PACKAGE: to 
135     //            indicate taking a file from the OTA package.) 
136     // 
137     //            (API v3: this command no longer exists.) 
138     // 
139     //        ui_print string 
140     //            display string on the screen. 
141     // 
142     //   - the name of the package zip file. 
143     // 
144 注意看这段注释它解释了以下代码的行为。结合代码可知 
1  将会创建新的进程执行/tmp/update_binary 
2  同时会给该进程传入一些参数其中最重要的就是一个管道fd供新进程与原进程通信。 
3  新进程诞生后原进程就变成了一个服务进程它提供若干UI更新服务 
a)   progress 
b)   set_progress 
c)   ui_print 
这样新进程就可以通过老进程的UI系统完成显示任务。而其他功能就靠它自己了。 145     char** args  malloc(sizeof(char*) * 5); 
146     args[0]  binary; 
147     args[1]  EXPAND(RECOVERY_API_VERSION);   // defined in Android.mk 
148     args[2]  malloc(10); 
149     sprintf(args[2], %d, pipefd[1]); 
150     args[3]  (char*)path; 
151     args[4]  NULL; 
152 
153     pid_t pid  fork(); 
154     if (pid  0) { 
155         close(pipefd[0]); 
156         execv(binary, args); 
157         fprintf(stderr, E:Cant run %s (%s)/n, binary, strerror(errno)); 
158         _exit(-1); 
159     } 
160     close(pipefd[1]); 
161 
162     char buffer[1024]; 
163     FILE* from_child  fdopen(pipefd[0], r); 
164     while (fgets(buffer, sizeof(buffer), from_child) ! NULL) { 
165         char* command  strtok(buffer,  /n); 
166         if (command  NULL) { 
167             continue; 
168         } else if (strcmp(command, progress)  0) { 
169             char* fraction_s  strtok(NULL,  /n); 
170             char* seconds_s  strtok(NULL,  /n); 
171 
172             float fraction  strtof(fraction_s, NULL); 
173             int seconds  strtol(seconds_s, NULL, 10); 
174 
175             ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), 
176                              seconds); 
177         } else if (strcmp(command, set_progress)  0) { 
178             char* fraction_s  strtok(NULL,  /n); 
179             float fraction  strtof(fraction_s, NULL); 
180             ui_set_progress(fraction); 
181         } else if (strcmp(command, ui_print)  0) { 
182             char* str  strtok(NULL, /n); 
183             if (str) { 
184                 ui_print(str); 
185             } else { 
186                 ui_print(/n); 
187             } 
188         } else { 
189             LOGE(unknown command [%s]/n, command); 
190         } 
191     } 
192     fclose(from_child); 
193 
194     int status; 
195     waitpid(pid, status, 0); 
196     if (!WIFEXITED(status) || WEXITSTATUS(status) ! 0) { 
197         LOGE(Error in %s/n(Status %d)/n, path, WEXITSTATUS(status)); 
198         return INSTALL_ERROR; 
199     } 
200 
201     return INSTALL_SUCCESS; 
202 } 这样我们又回到了升级包中的文件META-INF/com/google/android/update-binary下回继续研究。 Android OTA 升级之五updater 
作者 宋立新 
Emailzjujoeyahoo.com 
前言 可以说前面分析的OTA升级的各部分代码都是在搭一个舞台而主角现在终于登场它就是updater. Google的代码架构设计非常好各部分尽量松耦合。前面介绍升级脚本时可知有两种类型的脚本amend  edify. 他们各自对应一个updater. 这里我们主要关注新的edify的updater. Updater可以作为学习解释器/编译器的同学一个很好的实例但是我们只关心产品化相关的内容所以并不去深究lex/yacc相关的东西。 入口函数 main 
(from: bootable/recovery/updater/updater.c) 
62 // Where in the package we expect to find the edify script to execute. 63 // (Note its updateR-script, not the older update-script.) 64 #define SCRIPT_NAME META-INF/com/google/android/updater-script 65 这里定义脚本的位置注释说明本updater支持edify格式的脚本。 66 int main(int argc, char** argv) { 67     // Various things log information to stdout or stderr more or less 68     // at random.  The log file makes more sense if buffering is 69     // turned off so things appear in the right order. 70     setbuf(stdout, NULL); 71     setbuf(stderr, NULL); 72 73     if (argc ! 4) { 74         fprintf(stderr, unexpected number of arguments (%d)/n, argc); 75         return 1; 76     } 77 78     char* version  argv[1]; 79     if ((version[0] ! 1  version[0] ! 2  version[0] ! 3) || 80         version[1] ! /0) { 81         // We support version 1, 2, or 3. 82         fprintf(stderr, wrong updater binary API; expected 1, 2, or 3;  83                         got %s/n, 84                 argv[1]); 85         return 2; 86     } 87 
获取 version 参数。 88     // Set up the pipe for sending commands back to the parent process. 89 90     int fd  atoi(argv[2]); 91     FILE* cmd_pipe  fdopen(fd, wb); 92     setlinebuf(cmd_pipe); 93 获取命令管道用于图形显示等见前篇 94     // Extract the script from the package. 95 96     char* package_data  argv[3]; 97     ZipArchive za; 98     int err; 99     err  mzOpenZipArchive(package_data, za); 
100     if (err ! 0) { 
101         fprintf(stderr, failed to open package %s: %s/n, 
102                 package_data, strerror(err)); 
103         return 3; 
104     } 
105 
106     const ZipEntry* script_entry  mzFindZipEntry(za, SCRIPT_NAME); 
107     if (script_entry  NULL) { 
108         fprintf(stderr, failed to find %s in %s/n, SCRIPT_NAME, package_data); 
109         return 4; 
110     } 
111 
112     char* script  malloc(script_entry-uncompLen1); 
113     if (!mzReadZipEntry(za, script_entry, script, script_entry-uncompLen)) { 
114         fprintf(stderr, failed to read script from package/n); 
115         return 5; 
116     } 
117     script[script_entry-uncompLen]  /0; 
118 读入脚本 META-INF/com/google/android/updater-script 119     // Configure edifys functions. 
120 
121     RegisterBuiltins(); 
122     RegisterInstallFunctions(); 
123     RegisterDeviceExtensions(); 
124     FinishRegistration(); 
125 
注册语句处理函数 
126     // Parse the script. 
127 
128     Expr* root; 
129     int error_count  0; 
130     yy_scan_string(script); 
131     int error  yyparse(root, error_count); 
132     if (error ! 0 || error_count  0) { 
133         fprintf(stderr, %d parse errors/n, error_count); 
134         return 6; 
135     } 
136 
调用yy* 库函数解析脚本。 
137     // Evaluate the parsed script. 
138 
139     UpdaterInfo updater_info; 
140     updater_info.cmd_pipe  cmd_pipe; 
141     updater_info.package_zip  za; 
142     updater_info.version  atoi(version); 
143 
144     State state; 
145     state.cookie  updater_info; 
146     state.script  script; 
147     state.errmsg  NULL; 
148 
149     char* result  Evaluate(state, root); 
150     if (result  NULL) { 
151         if (state.errmsg  NULL) { 
152             fprintf(stderr, script aborted (no error message)/n); 
153             fprintf(cmd_pipe, ui_print script aborted (no error message)/n); 
154         } else { 
155             fprintf(stderr, script aborted: %s/n, state.errmsg); 
156             char* line  strtok(state.errmsg, /n); 
157             while (line) { 
158                 fprintf(cmd_pipe, ui_print %s/n, line); 
159                 line  strtok(NULL, /n); 
160             } 
161             fprintf(cmd_pipe, ui_print/n); 
162         } 
163         free(state.errmsg); 
164         return 7; 
165     } else { 
166         fprintf(stderr, script result was [%s]/n, result); 
167         free(result); 
168     } 
解释执行脚本。 核心函数是 Evaluate。它会调用其他callback函数而这些callback函数又会调用Evaluate去解析不同的脚本片段。从而实现一个简单的解释器。 
169 
170     mzCloseZipArchive(za); 
171     free(script); 
172 
173     return 0; 
174 } 还没开始就结束了。代码非常简单因为细节隐藏在那些callback函数里。我们看一下。 
RegisterBuiltins 
415 void RegisterBuiltins() { 
416     RegisterFunction(ifelse, IfElseFn); 
417     RegisterFunction(abort, AbortFn); 
418     RegisterFunction(assert, AssertFn); 
419     RegisterFunction(concat, ConcatFn); 
420     RegisterFunction(is_substring, SubstringFn); 
421     RegisterFunction(stdout, StdoutFn); 
422     RegisterFunction(sleep, SleepFn); 
423  
424     RegisterFunction(less_than_int, LessThanIntFn); 
425     RegisterFunction(greater_than_int, GreaterThanIntFn); 
426 } 
这些语句控制执行流程。 
RegisterInstallFunctions 
1036  
1037 void RegisterInstallFunctions() { 
1038     RegisterFunction(mount, MountFn); 
1039     RegisterFunction(is_mounted, IsMountedFn); 
1040     RegisterFunction(unmount, UnmountFn); 
1041     RegisterFunction(format, FormatFn); 
1042     RegisterFunction(show_progress, ShowProgressFn); 
1043     RegisterFunction(set_progress, SetProgressFn); 
1044     RegisterFunction(delete, DeleteFn); 
1045     RegisterFunction(delete_recursive, DeleteFn); 
1046     RegisterFunction(package_extract_dir, PackageExtractDirFn); 
1047     RegisterFunction(package_extract_file, PackageExtractFileFn); 
1048     RegisterFunction(symlink, SymlinkFn); 
1049     RegisterFunction(set_perm, SetPermFn); 
1050     RegisterFunction(set_perm_recursive, SetPermFn); 
1051  
1052     RegisterFunction(getprop, GetPropFn); 
1053     RegisterFunction(file_getprop, FileGetPropFn); 
1054     RegisterFunction(write_raw_image, WriteRawImageFn); 
1055  
1056     RegisterFunction(apply_patch, ApplyPatchFn); 
1057     RegisterFunction(apply_patch_check, ApplyPatchCheckFn); 
1058     RegisterFunction(apply_patch_space, ApplyPatchSpaceFn); 
1059  
1060     RegisterFunction(read_file, ReadFileFn); 
1061     RegisterFunction(sha1_check, Sha1CheckFn); 
1062  
1063     RegisterFunction(ui_print, UIPrintFn); 
1064  
1065     RegisterFunction(run_program, RunProgramFn); 
1066 } 
这些语句执行各种功能。基本上我们只需要知道用法就可以了。值得注意的是run_program原语允许我们去执行自定义程序这应该足够满足我们的个性化需求了。