1.OTA
所谓OTA(Over-the-AirTechnology)是指手机终端通过无线网下载远程服务器上的升级包,对系统或应用进行升级的技术。进一步说,就是将升级包(update.zip压缩包)写入到(手机)系统存储区。
2.OTA 升级包(update.zip)
OTA 升级包有整包与差分包之分。
整包:包含整个system分区中的数据文件;利用整包升级好比对电脑进行重作系统,格式分系统分区,并将新系统数据写入分区;
差分包:仅包含新旧两个版本之间改动的部分。而利用差分包升级不会格式化system分区,只是对其中部分存储段的内容进行重写。
3.OTA 升级包(update.zip)制作
(1) 整包制作与说明
系统经过整编后,执行make otapackage命令,即可完成整包的制作。该命令执行分为两个阶段:
1) 将系统资源(包括system、recovery、boot等目录)重新打包,生成差分资源包(target-files zipfile)
2) 制作升级包
第一阶段,makfile文件(./build/core/Makefile)中,otapackage相关代码段:
# -----------------------------------------------------------------
# OTA update package
name := $(TARGET_PRODUCT)
ifeq ($(TARGET_BUILD_TYPE),debug)
name := $(name)_debug
endif
name := $(name)-ota-$(FILE_NAME_TAG)
INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
$(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS)
@echo "Package OTA: $@"
$(hide) ./build/tools/releasetools/ota_from_target_files -v \
-n \
-p $(HOST_OUT) \
-k $(KEY_CERT_PAIR) \
$(ota_extra_flag) \
$(BUILT_TARGET_FILES_PACKAGE) $@
.PHONY: otapackage
otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
# -----------------------------------------------------------------
首先,otapackage目标的执行只依赖于$(INTERNAL_OTA_PACKAGE_TARGET),而不存在任何规则(根据Makefile语法,规则必须以TAB键开始,而目标otapackage的定义之后却是变量name的声明,因此不存在规则),因此只需要关注目标$(INTERNAL_OTA_PACKAGE_TARGET)的生成。显然,此目标的生成依赖于目标文件:$(BUILT_TARGET_FILES_PACKAGE)和$(OTATOOLS),且其执行的命令为./build/tools/releasetools/ota_from_target_files。也就是说,make otapackage所完成的功能全是通过这两个目标文件和执行的命令完成的,我们将分别对这三个关键点进行分析。
1)$(OTATOOLS)
目标文件OTATOOLS的编译规则如下所示:
OTATOOLS := $(HOST_OUT_EXECUTABLES)/minigzip \
$(HOST_OUT_EXECUTABLES)/mkbootfs \
$(HOST_OUT_EXECUTABLES)/mkbootimg \
$(HOST_OUT_EXECUTABLES)/fs_config \
$(HOST_OUT_EXECUTABLES)/mkyaffs2image \
$(HOST_OUT_EXECUTABLES)/zipalign \
$(HOST_OUT_EXECUTABLES)/aapt \
$(HOST_OUT_EXECUTABLES)/bsdiff \
$(HOST_OUT_EXECUTABLES)/imgdiff \
$(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar \
$(HOST_OUT_JAVA_LIBRARIES)/signapk.jar \
$(HOST_OUT_EXECUTABLES)/mkuserimg.sh \
$(HOST_OUT_EXECUTABLES)/genext2fs \
$(HOST_OUT_EXECUTABLES)/tune2fs \
$(HOST_OUT_EXECUTABLES)/e2fsck \
$(HOST_OUT_EXECUTABLES)/make_ext4fs
.PHONY: otatools
otatools: $(OTATOOLS)
变量OTATOOLS为系统中一系列文件的集合。这些文件用于压缩(minigzip:用于gzip文件;make_ext4fs:将文件转换为ext4类型;mkyaffs2image:用于yaffs文件系统;......)、解压缩、差分(bsdiff,imgdiff)、签名(singapk.jar)等功能。目标$(INTERNAL_OTA_PACKAGE_TARGET)的执行依赖于这一系列系统工具。也就是说,目标文件$(OTATOOLS)仅仅指定了命令执行所需要的工具,并未执行任何操作。
注:变量$(HOST_OUT_EXECUTABLES)指代的是out/host/linux-x86/bin目录,而变量$(HOST_OUT_JAVA_LIBRARIES)/表示的是out/host/linux-x86/framework目录,这意味着我们可以从此目录下找到上述工具,并为我们所用。
2)$(BUILT_TARGET_FILES_PACKAGE)
目标OTATOOLS指明了执行makeotapackage命令所需要的系统工具,而目标$(BUILT_TARGE_FILES_PACKAGE)的生成则完成了两件事:重新打包system.img文件和生成差分资源包。$(BUILT_TARGE_FILES_PACKAGE)的编译规则如下所示:
1. # -----------------------------------------------------------------
2. # A zip of the directories that map to the target filesystem.
3. # This zip can be used to create an OTA package or filesystem image
4. # as a post-build step.
5. #
6. name := $(TARGET_PRODUCT)
7. ifeq ($(TARGET_BUILD_TYPE),debug)
8. name := $(name)_debug
9. endif
10.name := $(name)-target_files-$(FILE_NAME_TAG)
11.
12.intermediates := $(call intermediates-dir-for,PACKAGING,target_files)
13.BUILT_TARGET_FILES_PACKAGE := $(intermediates)/$(name).zip
14.$(BUILT_TARGET_FILES_PACKAGE): intermediates := $(intermediates)
15.$(BUILT_TARGET_FILES_PACKAGE): \
16. zip_root := $(intermediates)/$(name)
17.
18.# $(1): Directory to copy
19.# $(2): Location to copy it to
20.# The "ls -A" is to prevent "acp s/* d" from failing if s is empty.
21.define package_files-copy-root
22. if [ -d "$(strip $(1))" -a "$$(ls -A $(1))" ]; then \
23. mkdir -p $(2) && \
24. $(ACP) -rd $(strip $(1))/* $(2); \
25. fi
26.endef
27.
28.built_ota_tools := \
29. $(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \
30. $(call intermediates-dir-for,EXECUTABLES,applypatch_static)/applypatch_static \
31. $(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq \
32. $(call intermediates-dir-for,EXECUTABLES,sqlite3)/sqlite3 \
33. $(call intermediates-dir-for,EXECUTABLES,updater)/updater
34.$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools)
35.
36.$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_API_VERSION := $(RECOVERY_API_VERSION)
37.
38.ifeq ($(TARGET_RELEASETOOLS_EXTENSIONS),)
39.# default to common dir for device vendor
40.$(BUILT_TARGET_FILES_PACKAGE): tool_extensions := $(TARGET_DEVICE_DIR)/../common
41.else
42.$(BUILT_TARGET_FILES_PACKAGE): tool_extensions := $(TARGET_RELEASETOOLS_EXTENSIONS)
43.endif
44.
45.# Depending on the various images guarantees that the underlying
46.# directories are up-to-date.
47.
48.ifeq ($(TARGET_USERIMAGES_USE_EXT4),true)
49.$(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_CACHEIMAGE_TARGET)
50.endif
51.
52.$(BUILT_TARGET_FILES_PACKAGE): \
53. $(INSTALLED_BOOTIMAGE_TARGET) \
54. $(INSTALLED_RADIOIMAGE_TARGET) \
55. $(INSTALLED_RECOVERYIMAGE_TARGET) \
56. $(INSTALLED_FACTORYIMAGE_TARGET) \
57. $(INSTALLED_SYSTEMIMAGE) \
58. $(INSTALLED_CACHEIMAGE_TARGET) \
59. $(INSTALLED_USERDATAIMAGE_TARGET) \
60. $(INSTALLED_SECROIMAGE_TARGET) \
61. $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
62. $(built_ota_tools) \
63. $(APKCERTS_FILE) \
64. $(HOST_OUT_EXECUTABLES)/fs_config \
65. | $(ACP)
66. @echo "Package target files: $@"
67. $(hide) rm -rf $@ $(zip_root)
68. $(hide) mkdir -p $(dir $@) $(zip_root)
69. @# Components of the recovery image
70. $(hide) mkdir -p $(zip_root)/RECOVERY
71. $(hide) $(call package_files-copy-root, \
72. $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/RECOVERY/RAMDISK)
73.ifdef INSTALLED_KERNEL_TARGET
74. $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/RECOVERY/kernel
75. $(hide) $(ACP) $(recovery_ramdisk) $(zip_root)/RECOVERY/ramdisk
76.endif
77.ifdef INSTALLED_2NDBOOTLOADER_TARGET
78. $(hide) $(ACP) \
79. $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/RECOVERY/second
80.endif
81.ifdef BOARD_KERNEL_CMDLINE
82. $(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/RECOVERY/cmdline
83.endif
84.ifdef BOARD_KERNEL_BASE
85. $(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/RECOVERY/base
86.endif
87. @# Components of the factory image
88. $(hide) mkdir -p $(zip_root)/FACTORY
89. $(hide) $(call package_files-copy-root, \
90. $(TARGET_FACTORY_ROOT_OUT),$(zip_root)/FACTORY/RAMDISK)
91.ifdef INSTALLED_KERNEL_TARGET
92. $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/FACTORY/kernel
93.endif
94.ifdef BOARD_KERNEL_PAGESIZE
95. $(hide) echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/RECOVERY/pagesize
96.endif
97.ifdef INSTALLED_2NDBOOTLOADER_TARGET
98. $(hide) $(ACP) \
99. $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/FACTORY/second
100.endif
101.ifdef BOARD_KERNEL_CMDLINE
102. $(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/FACTORY/cmdline
103.endif
104.ifdef BOARD_KERNEL_BASE
105. $(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/FACTORY/base
106.endif
107. @# Components of the boot image
108. $(hide) mkdir -p $(zip_root)/BOOT
109. $(hide) $(call package_files-copy-root, \
110. $(TARGET_ROOT_OUT),$(zip_root)/BOOT/RAMDISK)
111.ifdef INSTALLED_KERNEL_TARGET
112. $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/BOOT/kernel
113. $(hide) $(ACP) $(INSTALLED_RAMDISK_TARGET) $(zip_root)/BOOT/ramdisk
114.endif
115.ifdef INSTALLED_2NDBOOTLOADER_TARGET
116. $(hide) $(ACP) \
117. $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/BOOT/second
118.endif
119.ifdef BOARD_KERNEL_CMDLINE
120. $(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
121.endif
122.ifdef BOARD_KERNEL_BASE
123. $(hide) echo "$(BOARD_KERNEL_BASE)" > $(zip_root)/BOOT/base
124.endif
125.ifdef BOARD_KERNEL_PAGESIZE
126. $(hide) echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/BOOT/pagesize
127.endif
128.#wschen
129.ifneq "" "$(CUSTOM_BUILD_VERNO)"
130. $(hide) echo "$(CUSTOM_BUILD_VERNO)" > $(zip_root)/BOOT/board
131.endif
132.
133.#[eton begin]: added by LiuDekuan for u-boot update
134. $(hide) $(ACP) $(PRODUCT_OUT)/uboot_eyang77_ics2.bin $(zip_root)/uboot.bin
135.#[eton end]
136.
137. $(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\
138. mkdir -p $(zip_root)/RADIO; \
139. $(ACP) $(t) $(zip_root)/RADIO/$(notdir $(t));)
140. @# Contents of the system image
141. $(hide) $(call package_files-copy-root, \
142. $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
143. @# Contents of the data image
144. $(hide) $(call package_files-copy-root, \
145. $(TARGET_OUT_DATA),$(zip_root)/DATA)
146. @# Extra contents of the OTA package
147. $(hide) mkdir -p $(zip_root)/OTA/bin
148. $(hide) $(ACP) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/
149. $(hide) $(ACP) $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/
150. @# Security information of the OTA package
151. @echo "[SEC OTA] Adding Security information to OTA package"
152. @echo "[SEC OTA] path : mediatek/custom/$(MTK_PROJECT)/security/recovery/SEC_VER.txt"
153. $(hide) $(ACP) mediatek/custom/$(MTK_PROJECT)/security/recovery/SEC_VER.txt $(zip_root)/OTA/
154. @# Files that do not end up in any images, but are necessary to
155. @# build them.
156. $(hide) mkdir -p $(zip_root)/META
157. $(hide) $(ACP) $(APKCERTS_FILE) $(zip_root)/META/apkcerts.txt
158. $(hide) echo "$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt
159. $(hide) echo "recovery_api_version=$(PRIVATE_RECOVERY_API_VERSION)" > $(zip_root)/META/misc_info.txt
160.ifdef BOARD_FLASH_BLOCK_SIZE
161. $(hide) echo "blocksize=$(BOARD_FLASH_BLOCK_SIZE)" >> $(zip_root)/META/misc_info.txt
162.endif
163.ifdef BOARD_BOOTIMAGE_PARTITION_SIZE
164. $(hide) echo "boot_size=$(BOARD_BOOTIMAGE_PARTITION_SIZE)" >> $(zip_root)/META/misc_info.txt
165.endif
166.ifdef BOARD_RECOVERYIMAGE_PARTITION_SIZE
167. $(hide) echo "recovery_size=$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)" >> $(zip_root)/META/misc_info.txt
168.endif
169.ifdef BOARD_SYSTEMIMAGE_PARTITION_SIZE
170. $(hide) echo "system_size=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)" >> $(zip_root)/META/misc_info.txt
171.endif
172.ifdef BOARD_SECROIMAGE_PARTITION_SIZE
173. $(hide) echo "secro_size=$(BOARD_SECROIMAGE_PARTITION_SIZE)" >> $(zip_root)/META/misc_info.txt
174.endif
175.ifdef BOARD_CACHEIMAGE_PARTITION_SIZE
176. $(hide) echo "cache_size=$(BOARD_CACHEIMAGE_PARTITION_SIZE)" >> $(zip_root)/META/misc_info.txt
177.endif
178.ifdef BOARD_USERDATAIMAGE_PARTITION_SIZE
179. $(hide) echo "userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)" >> $(zip_root)/META/misc_info.txt
180.endif
181. $(hide) echo "tool_extensions=$(tool_extensions)" >> $(zip_root)/META/misc_info.txt
182.ifdef mkyaffs2_extra_flags
183. $(hide) echo "mkyaffs2_extra_flags=$(mkyaffs2_extra_flags)" >> $(zip_root)/META/misc_info.txt
184.endif
185.ifdef INTERNAL_USERIMAGES_SPARSE_EXT_FLAG
186. $(hide) echo "extfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG)" >> $(zip_root)/META/misc_info.txt
187.endif
188. $(hide) echo "default_system_dev_certificate=$(DEFAULT_KEY_CERT_PAIR)" >> $(zip_root)/META/misc_info.txt
189.ifdef PRODUCT_EXTRA_RECOVERY_KEYS
190. $(hide) echo "extra_recovery_keys=$(PRODUCT_EXTRA_RECOVERY_KEYS)" >> $(zip_root)/META/misc_info.txt
191.endif
192. @# Zip everything up, preserving symlinks
193. $(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
194. @# Run fs_config on all the system, boot ramdisk, and recovery ramdisk files in the zip, and save the output
195. $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/filesystem_config.txt
196. $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/boot_filesystem_config.txt
197. $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/recovery_filesystem_config.txt
198. $(hide) (cd $(zip_root) && zip -q ../$(notdir $@) META/*filesystem_config.txt)
199.target-files-package: $(BUILT_TARGET_FILES_PACKAGE)
200.ifneq ($(TARGET_PRODUCT),sdk)
201.ifeq ($(filter generic%,$(TARGET_DEVICE)),)
202.ifneq ($(TARGET_NO_KERNEL),true)
203.ifneq ($(recovery_fstab),)
system.img文件的重新打包是通过$(BUILT_TARGE_FILES_PACKAGE)的依赖条件$(INSTALLED_SYSTEMIMAGE)目标文件的编译来完成的,而$(BUILT_TARGE_FILES_PACKAGE)所有的执行命令(代码第66行至最后)都只为完成一件事,生成差分资源包所对应的目录并将其打包为ZIP包。具体的操作包括:
创建$(zip_root)目录(代码第66~68行);
创建/$(zip_root)/RECOVERY目录并将COPY相关文件(代码69~86);
创建/$(zip_root)/FACTORY目录并将COPY相关文件(代码87~106);
创建/$(zip_root)/BOOT目录并将COPY相关文件(代码107~131);
创建其他目录并COPY文件(代码132~191);
将$(zip_root)目录压缩为资源差分包(代码192~198)等。
$(zip_root)即out/target/product/<product-name>/obj/PACKAGING/target_files_from_intermedias/<product-name>-target_files-<version-name>;
经过目标文件$(BUILT_TARGE_FILES_PACKAGE)的执行后,system.img已经被重新打包,且差分资源包也已经生成,剩下的工作就是将差分资源包(target-files zipfile)传递给ota_ from _target _files代码,由它来生成OTA整包。
我们可以看下差分资源包(target-files zipfile)中的文件结构,如下:
其中,OTA目录值得关注,因为在此目录下存在着一个至关重要的文件:OTA/bin/updater(后文会有详述)。生成的差分资源包被传递给./build/tools/releasetools/
第二阶段,制作升级包。
./build/tools/releasetools目录下的文件如下:
这组文件是Google提供的用来制作升级包的代码工具,核心文件为:ota_from_target_files和img_from_target_files。其中,前者用来制作recovery模式下的升级包;后者则用来制作fastboot下的升级包(fastboot貌似是一种更底层的刷机操作,未过多研究,不再详述)。其他文件则是为此二者提供服务的,比如,common.py中包含有制作升级包所需操作的代码;edify_generator.py则用于生成recovery模式下升级的脚本文件:<升级包>.zip/ META-INF/com/google/android/updater-script。
文件ota_from_target_files是本文所关注的重点,其中定义了两个主要的方法:
WriteFullOTAPackage 、WriteIncrementalOTAPackage。
前者用于生成整包,后者用来生成差分包。接着上文,当Makefile调用ota_from_target_files,并将差分资源包传递进来时,会执行WriteFullOTAPackage。此方法完成的工作包括:(1)将system目录,boot.img等文件添加到整包中;(2)生成升级包中的脚本文件:<升级包>.zip/META-INF/com/google/android/updater-script;(3)将上文提到的可执行文件:OTA/bin/updater添加到升级包中:META-INF/com/google/android/updater-script。
WriteFullOTAPackage代码片段:
1. script.FormatPartition("/system")
2. script. FormatPartition ("/system")
3. script.UnpackPackageDir("recovery", "/system")
4. script.UnpackPackageDir("system", "/system")
5. (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
6. script.MakeSymlinks(symlinks)
7. if OPTIONS.aslr_mode:
8. script.RetouchBinaries(retouch_files)
9. else:
10. script.UndoRetouchBinaries(retouch_files)
其中的script为edify_generator对象,其FormatPartition、UnpackPackageDir等方法分别是向脚本文件update-script中写入格式化分区、解压包等指令。
edify_generator中的AddToZip方法:
1. def AddToZip(self, input_zip, output_zip, input_path=None):
2. """Write the accumulated script to the output_zip file. input_zip
3. is used as the source for the 'updater' binary needed to run
4. script. If input_path is not None, it will be used as a local
5. path for the binary instead of input_zip."""
6.
7. self.UnmountAll()
8. common.ZipWriteStr(output_zip, "META-INF/com/google/android/updater-script",
9. "\n".join(self.script) + "\n")
10. if input_path is None:
11. data = input_zip.read("OTA/bin/updater")
12. else:
13. data = open(os.path.join(input_path, "updater")).read()
14. common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-binary",
15. data, perms=0755)
WriteFullOTAPackage执行的最后会调用此方法。将资源差分包中OTA/bin/updater文件copy到升级包中META-INF/com/google/android/update-binary。此文件是OTA升级的关键,其将在recovery模式下被执行,用来将代码段2中生成的指令转换为相应的函数去执行,从而完成对系统数据的重写。
(2) 差分包制作与说明
生成差分包调用的是文件./build/tools/releasetools/ota_from_target_files中的WriteIncrementalOTA方法,调用时需要将两个版本的差分资源包作为参数传进来,形如:
./build/tools/releasetools/ota_from_target_files –n –i ota_v1.zip ota_v2.zip update.zip
其中,参数n表示忽略时间戳;i表示生成增量包(即差分包);ota_v1.zip与ota_v2.zip分别代表前后两个版本的差分资源包;而update.zip则表示最终生成的差分包。
WriteIncrementalOTA函数会计算输入的两个差分资源包中版本的差异,并将其写入到差分包中;同时,将updater及生成脚本文件udpate-script添加到升级包中。
4. 将升级包写入系统存储区
制作完升级包后,之后便是将其写入到相应存储区中,这部分工作是在recovery模式下完成的。之前的几篇笔记亦有描述,recovery模式下通过创建一个新的进程读取并执行脚本文件META-INF/com/google/android/updater-script。见如下代码:
1. const char** args = (const char**)malloc(sizeof(char*) * 5);
2. args[0] = binary;
3. args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk
4. char* temp = (char*)malloc(10);
5. sprintf(temp, "%d", pipefd[1]);
6. args[2] = temp;
7. args[3] = (char*)path;
8. args[4] = NULL;
9.
10. pid_t pid = fork();
11. if (pid == 0) {
12. close(pipefd[0]);
13. execv(binary, (char* const*)args);
14. _exit(-1);
15. }
16. close(pipefd[1]);
分析代码之前,首先介绍linux中函数fork与execv的用法。
pid_t fork( void)
创建新的进程,fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程(http://os.chinaunix.net/a2012/0203/1306/000001306508.shtml)。
int execv(const char *progname, char *const argv[])
execv会停止执行当前的进程,并且以progname应用进程替换被停止执行的进程,进程ID没有改变。
progname: 被执行的应用程序。
argv: 传递给应用程序的参数列表, 注意,这个数组的第一个参数应该是应用程序名字本身,并且最后一个参数应该为NULL,不参将多个参数合并为一个参数放入数组。
代码于bootable/recovery/install.c的try_update_binary函数中,是OTA升级的核心代码之一。通过对fork及execv函数的介绍可知,创建了一个新的进程并在新进程中运行升级包中的META-INF/com/google/android/updater-binary文件(参数binary已在此前赋值),此文件将按照META-INF/com/google/android/updater-script中的指令将升级包里的数据写入到存储区中。OK,我们来看下META-INF/com/google/android/updater-binary文件的来历。
在源代码的./bootable/recovery/updater目录下,存在着如下几个文件:
通过查看Android.mk代码可知,文件install.c、updater.c将会被编译为可执行文件updater存放到目录out/target/product/<product-name>/obj/EXECUTABLES/
updater_intermediates/中;而在生成差分资源包(target-files zipfile)时,会将此文件添加到压缩包中。
Makefile中定义的变量built_ota_tools:
1. built_ota_tools := \
2. $(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \
3. $(call intermediates-dir-for,EXECUTABLES,applypatch_static)/applypatch_static \
4. $(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq \
5. $(call intermediates-dir-for,EXECUTABLES,sqlite3)/sqlite3 \
6. $(call intermediates-dir-for,EXECUTABLES,updater)/updater
复制built_ota_tools工具到差分资源包:
7. $(hide) mkdir -p $(zip_root)/OTA/bin
8. $(hide) $(ACP) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/
9. $(hide) $(ACP) $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/
如代码段5,Makefile中定义了执行OTA所需要的一组工具(built_ota_tools),其中便包括由图4中文件编译而成的文件updater;而在生成差分资源包时,会将这组工具拷贝到差分资源包的OTA/bin目录中(见代码段6);在生成升级包时(无论是执行WriteFullOTAPackage还是WriteIncrementalOTAPackage),最后都会调用edify_generator的AddToZip方法,将updater添加到升级包中(更名为"META-INF/com/google/android/update-binary");最终在recovery模式下被执行,这便是其来龙去脉。而关于updater的执行,也大致的描述下吧。
由前文可知,updater主要由bootable/recovery/updater目录下的install.c和updater.c编译而成,主函数位于updater.c。其中,在install.c中定义了读写系统存储区的操作函数(这才是重写系统数据的真正代码)并将这些函数与updater-script中的指令映射起来。而在updater.c会首先装载install.c定义的函数,之后便解析升级脚本updater-script,执行其对应的操作命令。与此同时,执行updater的进程还会与父进程通信,通知父进程进行UI的相关操作(代码见bootable/recovery/install.c中的try_update_binary函数)。