田海立@CSDN
2013-3-23
Android原生系統中就已經支援OTA升級。所謂OTA升級就是通過空中介面擷取升級包,然後更新系統韌體。一般地,升級包無論如何擷取,哪怕是直接TCard本地升級,也被稱為OTA升級。
OTA升級首要是產生OTA升級包,升級包又分為升級全包和升級差分包(或要增量包)。升級全包是編譯當前系統得到的軟體包,這個包很大,有上百兆,但是不依賴與當前手機裡的軟體版本;升級差分包是對手機兩個軟體版本做差分,在第一個版本上打patch,得到第二個升級包,所以差分包只能對第一個版本的機器進行升級。
本文主要講述升級全包的產生過程。
編譯升級全包,用下面指令:
#make otapackage
最終產生INTERNAL_OTA_PACKAGE_TARGET,也就是$(PRODUCT_OUT)/$(name).zip。
一、升級全包($(PRODUCT_OUT)/$(name).zip)的產生
INTERNAL_OTA_PACKAGE_TARGET依賴於BUILT_TARGET_FILES_PACKAGE
建置規則【在build/core/Makefile中】:
$(INTERNAL_OTA_PACKAGE_TARGET):$(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS) @echo"Package OTA: $@" $(hide)./build/tools/releasetools/ota_from_target_files -v \ -p $(HOST_OUT) \ -k $(KEY_CERT_PAIR) \ $(BUILT_TARGET_FILES_PACKAGE) $@
ota_from_target_files是python指令碼,需要中間檔案$(BUILT_TARGET_FILES_PACKAGE)。所以,在分析完中間檔案的產生之後再看最終升級全包的產生過程。
二、中間檔案包($(TARGET_PRODUCT)-target_files-$(FILE_NAME_TAG).zip)的產生
BUILT_TARGET_FILES_PACKAGE是$(intermediates)/$( TARGET_PRODUCT)-target_files-$(FILE_NAME_TAG).zip
形如:out/target/product/<product>/obj/PACKAGING/target_files_intermediates/<product>-target_files-<eng>.haili.tian
建置規則【在build/core/Makefile中】:
$(BUILT_TARGET_FILES_PACKAGE):\ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_RADIOIMAGE_TARGET) \ $(INSTALLED_RECOVERYIMAGE_TARGET)\ $(INSTALLED_SYSTEMIMAGE) \ $(INSTALLED_USERDATAIMAGE_TARGET)\ $(INSTALLED_ANDROID_INFO_TXT_TARGET)\ $(built_ota_tools) \ $(APKCERTS_FILE) \ $(HOST_OUT_EXECUTABLES)/fs_config\ | $(ACP) @echo "Package target files:$@" $(hide) rm -rf $@ $(zip_root) $(hide) mkdir -p $(dir $@) $(zip_root) @# Components of the recovery image $(hide) mkdir -p $(zip_root)/RECOVERY $(hide) $(call package_files-copy-root, \ $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/RECOVERY/RAMDISK)ifdef INSTALLED_KERNEL_TARGET $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET)$(zip_root)/RECOVERY/kernelendififdef INSTALLED_2NDBOOTLOADER_TARGET $(hide) $(ACP) \ $(INSTALLED_2NDBOOTLOADER_TARGET)$(zip_root)/RECOVERY/secondendififdef BOARD_KERNEL_CMDLINE $(hide) echo"$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/RECOVERY/cmdlineendififdef BOARD_KERNEL_BASE $(hide) echo"$(BOARD_KERNEL_BASE)" > $(zip_root)/RECOVERY/baseendififdef BOARD_KERNEL_PAGESIZE $(hide) echo "$(BOARD_KERNEL_PAGESIZE)"> $(zip_root)/RECOVERY/pagesizeendif @# Components of the boot image $(hide) mkdir -p $(zip_root)/BOOT $(hide) $(call package_files-copy-root, \ $(TARGET_ROOT_OUT),$(zip_root)/BOOT/RAMDISK)ifdef INSTALLED_KERNEL_TARGET $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET)$(zip_root)/BOOT/kernelendififdef INSTALLED_2NDBOOTLOADER_TARGET $(hide) $(ACP) \ $(INSTALLED_2NDBOOTLOADER_TARGET)$(zip_root)/BOOT/secondendififdef BOARD_KERNEL_CMDLINE $(hide) echo"$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdlineendififdef BOARD_KERNEL_BASE $(hide) echo"$(BOARD_KERNEL_BASE)" > $(zip_root)/BOOT/baseendififdef BOARD_KERNEL_PAGESIZE $(hide) echo"$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/BOOT/pagesizeendif $(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\ mkdir -p $(zip_root)/RADIO; \ $(ACP) $(t)$(zip_root)/RADIO/$(notdir $(t));) @# Contents of the system image $(hide) $(call package_files-copy-root, \ $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM) @# Contents of the data image $(hide) $(call package_files-copy-root, \ $(TARGET_OUT_DATA),$(zip_root)/DATA) @# Extra contents of the OTA package $(hide) mkdir -p $(zip_root)/OTA/bin $(hide) $(ACP)$(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/ $(hide) $(ACP) $(PRIVATE_OTA_TOOLS)$(zip_root)/OTA/bin/ @# Files that do not end up in anyimages, but are necessary to @# build them. $(hide) mkdir -p $(zip_root)/META $(hide) $(ACP) $(APKCERTS_FILE)$(zip_root)/META/apkcerts.txt $(hide) echo"$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt $(hide) echo"recovery_api_version=$(PRIVATE_RECOVERY_API_VERSION)" >$(zip_root)/META/misc_info.txtifdef BOARD_FLASH_BLOCK_SIZE $(hide) echo"blocksize=$(BOARD_FLASH_BLOCK_SIZE)" >> $(zip_root)/META/misc_info.txtendififdef BOARD_BOOTIMAGE_PARTITION_SIZE $(hide) echo"boot_size=$(BOARD_BOOTIMAGE_PARTITION_SIZE)" >>$(zip_root)/META/misc_info.txtendififdef BOARD_RECOVERYIMAGE_PARTITION_SIZE $(hide) echo"recovery_size=$(BOARD_RECOVERYIMAGE_PARTITION_SIZE)" >>$(zip_root)/META/misc_info.txtendififdef BOARD_SYSTEMIMAGE_PARTITION_SIZE $(hide) echo"system_size=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE)" >>$(zip_root)/META/misc_info.txtendififdef BOARD_USERDATAIMAGE_PARTITION_SIZE $(hide) echo "userdata_size=$(BOARD_USERDATAIMAGE_PARTITION_SIZE)">> $(zip_root)/META/misc_info.txtendif $(hide) echo"tool_extensions=$(tool_extensions)" >>$(zip_root)/META/misc_info.txtifdef mkyaffs2_extra_flags $(hide) echo"mkyaffs2_extra_flags=$(mkyaffs2_extra_flags)" >>$(zip_root)/META/misc_info.txtendif @# Zip everything up, preserving symlinks $(hide) (cd $(zip_root) && zip-qry ../$(notdir $@) .) @# Run fs_config on all the system filesin the zip, and save the output $(hide) zipinfo -1 $@ | awk -F/ 'BEGIN {OFS="/" } /^SYSTEM\// {$$1 = "system"; print}' |$(HOST_OUT_EXECUTABLES)/fs_config > $(zip_root)/META/filesystem_config.txt $(hide) (cd $(zip_root) && zip -q../$(notdir $@) META/filesystem_config.txt)
其中,變數定義:
- TARGET_RECOVERY_ROOT_OUT也就是$(TARGET_RECOVERY_OUT)/root;TARGET_RECOVERY_OUT是$(PRODUCT_OUT)/recovery;PRODUCT_OUT是out/target/product/<product>。所以,TARGET_RECOVERY_ROOT_OUT也就是out/target/product/<product>/recovery/root;
- zip_root是$(intermediates)/$( TARGET_PRODUCT)-target_files-$(FILE_NAME_TAG)路徑,也就是out/target/product/<product>/obj/PACKAGING/target_files_intermediates/<product>-target_files-<eng>.haili.tian;
- TARGET_ROOT_OUT是$(PRODUCT_OUT)/root,也就是out/target/product/<product>/root;
- INSTALLED_RADIOIMAGE_TARGET;
- SYSTEMIMAGE_SOURCE_DIR是$(TARGET_OUT),也就是out/target/product/<product>/system/;
- TARGET_OUT_DATA是$(PRODUCT_OUT)/data,也就是out/target/product/<product>/data;
- INSTALLED_ANDROID_INFO_TXT_TARGET是$(PRODUCT_OUT)/android-info.txt,也就是out/target/product/<product>/android-info.txt;
- PRIVATE_OTA_TOOLS是$(built_ota_tools),也就是包含了:
out/target/product/<product>/obj/EXECUTABLES/applypatch_intermediates/applypatch
out/target/product/<product>/obj/EXECUTABLES/applypatch_static_intermediates/applypatch_static
out/target/product/<product>/obj/EXECUTABLES/check_prereq_intermediates/check_prereq
out/target/product/<product>/obj/EXECUTABLES/updater_intermediates/updater
- APKCERTS_FILE是out/target/product/<product>/obj/PACKAGING/apkcerts_intermediates/<product>-apkcerts-<eng>.haili.tian.txt
- PRODUCT_OTA_PUBLIC_KEYS目前為空白;
- PRIVATE_RECOVERY_API_VERSION也就是RECOVERY_API_VERSION,RecoveryAPI的版本號碼;
- tool_extensions如果定義了TARGET_RELEASETOOLS_EXTENSIONS,tool_extensions也就是TARGET_RELEASETOOLS_EXTENSIONS;
- DEFAULT_SYSTEM_DEV_CERTIFICATE目前為空白;
- HOST_OUT_EXECUTABLES是$(HOST_OUT)/bin,也就是out/host/linux-x86/bin;
指令碼的執行:
1. #清理以前殘存的目錄和檔案,並建立$(zip_root)目錄(也就是out/target/product/<product>/obj/PACKAGING/target_files_intermediates/<product>-target_files-<eng>.haili.tian);
2. 建立$(zip_root)/RECOVERY目錄,把$(TARGET_RECOVERY_ROOT_OUT)(也就是out/target/product/<product>/recovery/root)下的所有檔案和目錄都拷貝到$(zip_root)/RECOVERY/RAMDISK/下;
3. 建立$(zip_root)/BOOT目錄,然後把$(TARGET_ROOT_OUT)(也就是out/target/product/<product>/root)下的所有檔案和目錄都拷貝到$(zip_root)/BOOT/RAMDISK/下;
4. 建立$(zip_root)/RADIO目錄,然後把$(INSTALLED_RADIOIMAGE_TARGET)下的所有檔案和目錄都拷貝到$(zip_root)/ RADIO/下;
5. 把$(SYSTEMIMAGE_SOURCE_DIR)【也就是out/target/product/<product>/system/】下的所有檔案和目錄都拷貝到$(zip_root)/SYSTEM/下;
6. 把$(TARGET_OUT_DATA)【也就是out/target/product/<product>/data】下的所有檔案和目錄都拷貝到$(zip_root)/DATA/下;
7. 建立$(zip_root)/OTA/bin目錄,然後把INSTALLED_ANDROID_INFO_TXT_TARGET【也就是out/target/product/<product>/android-info.txt】放到$(zip_root)/OTA/下;
8. 把PRIVATE_OTA_TOOLS【也就是applypatch、applypatch_static、check_prereq、updater】放到$(zip_root)/OTA/bin下。
9. 建立$(zip_root)/META目錄,搜集META資訊並儲存到檔案裡:
把APKCERTS_FILE【也就是out/target/product/<product>/obj/PACKAGING/apkcerts_intermediates/<product>-apkcerts-<eng>.haili.tian.txt】放到$(zip_root)/META/apkcerts.txt;
把$(PRODUCT_OTA_PUBLIC_KEYS)追加到$(zip_root)/META/otakeys.txt;【】
把recovery_api_version=$(PRIVATE_RECOVERY_API_VERSION)這行追加到$(zip_root)/META/misc_info.txt;
把tool_extensions=$(tool_extensions)這行追加到$(zip_root)/META/misc_info.txt;
把default_system_dev_certificate=$(DEFAULT_SYSTEM_DEV_CERTIFICATE)這行追加到$(zip_root)/META/misc_info.txt;
10. 拷貝Image檔案
把$(PRODUCT_OUT)/ramdisk.img放到$(zip_root)/下;
把$(PRODUCT_OUT)/ramdisk-recovery.img放到(zip_root)/
把$(PRODUCT_OUT)/../../../../boot/out/u-boot.bin放到$(zip_root)/
把$(PRODUCT_OUT)/../../../../kernel/out/uImage放到(zip_root)/
11. 進入$(zip_root),把$(zip_root)下所有的內容打進zip包<product>-target_files-<eng>.haili.tian.zip;
12. 用$(HOST_OUT_EXECUTABLES)/fs_config擷取檔案系統資訊並儲存在檔案裡
“SYSTEM/”下的檔案系統資訊儲存在$(zip_root)/META/filesystem_config.txt中;
“BOOT/RAMDISK/”下的檔案系統資訊儲存在$(zip_root)/META/boot_filesystem_config.txt中;
“RECOVERY/RAMDISK/”下的檔案系統資訊儲存在$(zip_root)/META/recovery_filesystem_config.txt中;
13. 把META/*filesystem_config.txt檔案打到zip包<product>-target_files-<eng>.haili.tian.zip裡。
三、ota_from_target_file分析
Ota_from_target_file是python指令碼,從main()開始分析。
3.1 產生總過程 – main()
Main()裡主要做下列工作:
1. 從參數和環境中解析OPTIONS;
2. 從中間zip包裡的META/misc_info.txt中解析出{<key>=<value>}OPTIONS對,比如recovery_api_version=3;tool_extensions=device/<company>/<product>/recovery;
3. 從OPTIONS對裡找到tool_extensions,賦值給OPTIONS.device_specific;
4. input_zip為上文的中間zip包;output_zip為最終產生的臨時zip包;
5. 對於全包升級包,調用WriteFullOTAPackage(input_zip,output_zip);
6. 簽名臨時包,產生最終簽過名的最終全包升級包。
上述過程中,我們最關注的是WriteFullOTAPackage(input_zip,output_zip)做的工作。
3.2 產生全包 – WriteFullOTAPackage()
WriteFullOTAPackage的定義如下:
def WriteFullOTAPackage(input_zip, output_zip): # TODO:how to determine this? We don't knowwhat version it will # beinstalled on top of. For now, we expectthe API just won't # changevery often. script =edify_generator.EdifyGenerator(3, OPTIONS.info_dict) 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), } device_specific = common.DeviceSpecificParams( input_zip=input_zip, input_version=OPTIONS.info_dict["recovery_api_version"], output_zip=output_zip, script=script, input_tmp=OPTIONS.input_tmp, metadata=metadata, info_dict=OPTIONS.info_dict, trusted_boot=OPTIONS.trusted_boot) # if not OPTIONS.omit_prereq:# ts =GetBuildProp("ro.build.date.utc", input_zip)# script.AssertOlderBuild(ts) # AppendAssertions(script, input_zip)# device_specific.FullOTA_Assertions() script.ShowProgress(0.9, 16) if OPTIONS.wipe_user_data: script.FormatPartition("/data") script.FormatPartition("/system") script.Mount("/system")# script.UnpackPackageDir("recovery", "/system") script.UnpackPackageDir("system", "/system") symlinks= CopySystemFiles(input_zip, output_zip) script.MakeSymlinks(symlinks) # boot_img = common.File("boot.img", common.BuildBootableImage(# os.path.join(OPTIONS.input_tmp, "BOOT")))# recovery_img = common.File("recovery.img",common.BuildBootableImage(# os.path.join(OPTIONS.input_tmp, "RECOVERY")))# MakeRecoveryPatch(output_zip, recovery_img, boot_img) Item.GetMetadata(input_zip) Item.Get("system").SetPermissions(script) # common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)# common.ZipWriteStr(output_zip, "boot.img", boot_img.data)# script.ShowProgress(0.2, 0) # script.ShowProgress(0.2, 10)# script.WriteRawImage("/boot", "boot.img") # script.ShowProgress(0.1, 0) device_specific.FullOTA_InstallEnd() if OPTIONS.extra_script is not None: script.AppendExtra(OPTIONS.extra_script) script.UnmountAll() script.AddToZip(input_zip, output_zip) WriteMetadata(metadata,output_zip)
OTA全包產生的過程:
1. script用來產生Edify指令碼,在edify_generator.py中實現;
2. script中增加語句:顯示進度;
3. script中增加語句:擦除“/system”分區;
4. script中增加語句:安裝system分區到“/system”;
5. script中增加語句:把system中的內容複寫到/system下;
6. 把input_zip包/system中的內容,複製到output_zip包中,不包含其中的link檔案;
7. 對於link檔案,script中增加指向連結的語句;
8. 從input_zip包的META/filesystem_config.txt中擷取其中描述的/system下各個檔案的許可權資訊;
9. script中增加語句:設定/system下檔案的許可權和屬主資訊;
10. 調用具體device特定的函數FullOTA_InstallEnd。
main()函數中已經擷取OPTIONS.device_specific為/device/<company>/<product>/recovery;這裡的device_specific是common.DeviceSpecificParams。
common實現在common.py中,在判斷出OPTIONS.device_specific為目錄時,用該目錄下的releasetools.py指令碼。所以這裡執行releasetools.py中的FullOTA_InstallEnd。
11. 如果特別指定了其他指令碼,script中加入;
12. 通過script.AddToZip()寫META-INF
把前面所有的指令碼,寫入到output_zip裡的META-INF/com/google/android/updater-script這個edify指令碼中;
把input_zip裡的OTA/bin/updater寫入到output_zip裡的META-INF/com/google/android/update-binary
13. 把metadata的內容寫入到output_zip裡的META-INF/com/android/metadata
總結
本文分析了Android OTA全包的編譯產生過程。首先產生中間包,這個包是後續全包產生的基礎,也是差分包產生的基礎,接著解析了產生最終升級全包的過程。通過分析知道升級包中update-binary和updater-script的來源,對於後續分析OTA升級的過程至關重要。
差分包的產生在介紹完整個OTA的過程之後,再進一步闡述。