OTA本质与实现流程分析
接觸OTA也有段時間了,是時候總結下了。所謂OTA(Over-the-AirTechnology)是指手機終端通過無線網下載遠程服務器上的升級包,對系統或應用進行升級的技術。有關網絡部分不做過多討論,本文重點放在系統升級這一概念上。
一 OTA本質
先以PC機進行類比。假設計算機操作系統裝在C盤,當加電啟動時,引導程序會將C盤的系統程序裝入內存并運行,而系統升級或重裝系統,則是將C盤中原來的系統文件部分或全部重寫。對于手機及其上的ANDROID系統而言,同樣如此,需要一個存儲系統文件的“硬盤”。
圖1 某android手機存儲設備結構圖
?
圖1 是某款手機的存儲設備結構圖,其存儲區(紅色框圖部分)分為四部分:SRAM、Nand Flash、SDRAM及外設地址空間。其中Nand Flash中存儲著全部系統數據(通過專門的燒寫工具將編譯后的映象文件download到Nand Flash中,工具由IC廠商提供),包括boot.img、system.img、recovery.img等,因此Nand Flash即是上文所說的手機上的“硬盤”。圖1最右部分(圖中綠色框圖部分)是Nand Flash存儲區更詳細的劃分,我們將主要關注system分區(藍色框圖),因為OTA升級主要是對這部分系統數據的重寫(當然boot分區也可升級)。除此之外,藍黑色區域標示的misc分區也應值得注意,它在OTA升級中發揮著重要的作用。
OK,一言以蔽之,所謂OTA就是將升級包(zip壓縮包)寫入到系統存儲區,因此我們需要考慮兩個問題,1.升級包是如何生成的?2.升級包是如何寫入到system分區的?
二 問題一:升級包的制作
1.整包的制作
升級包有整包與差分包之分。顧名思義,所謂整包即包含整個system分區中的數據文件;而差分包則僅包含兩個版本之間改動的部分。利用整包升級好比對電腦進行重作系統,格式分系統分區,并將新系統數據寫入分區;而利用差分包升級不會格式化system分區,只是對其中部分存儲段的內容進行重寫。除升級包之外,制作過程中還會涉及到另一種zip包,代碼中稱之為target-files zipfile,我稱之為差分資源包。首先闡述下整包的制作過程。
系統經過整編后,執行make otapackage命令,即可完成整包的制作,而此命令可分為兩個階段進行。首先執行./build/core/Makefile中的代碼:
# ----------------------------------------------------------------- # OTA update packagename := $(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) # -----------------------------------------------------------------代碼段1 make otapackage目標代碼
圖2 target-files zipfile目錄結構
其中,OTA目錄值得關注,因為在此目錄下存在著一個至關重要的文件:OTA/bin/updater(后文會有詳述)。生成的差分資源包被傳遞給./build/tools/releasetools/
ota_from_target_files執行第二階段的操作:制作升級包。
圖3 ./build/tools/releasetools目錄下的文件
圖3是./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。摘取部分代碼片段如下:
代碼2 WriteFullOTAPackage代碼片段
其中的script為edify_generator對象,其FormatPartition、UnpackPackageDir等方法分別是向腳本文件update-script中寫入格式化分區、解壓包等指令。
代碼段3 ?edify_generator中的AddToZip方法
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添加到升級包中。
三 問題二:將升級包寫入系統存儲區
制作完升級包后,之后便是將其寫入到相應存儲區中,這部分工作是在recovery模式下完成的。之前的幾篇筆記亦有描述,recovery模式下通過創建一個新的進程讀取并執行腳本文件META-INF/com/google/android/updater-script。見如下代碼:
const char** args = (const char**)malloc(sizeof(char*) * 5);args[0] = binary;args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mkchar* temp = (char*)malloc(10);sprintf(temp, "%d", pipefd[1]);args[2] = temp;args[3] = (char*)path;args[4] = NULL;pid_t pid = fork();if (pid == 0) {close(pipefd[0]);execv(binary, (char* const*)args);_exit(-1);}close(pipefd[1]);代碼段4 創建新進程安裝升級包
分析代碼之前,首先介紹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,不參將多個參數合并為一個參數放入數組。
?代碼4見于bootable/recovery/install.c的try_update_binary函數中,是OTA升級的核心代碼之一。通過對fork及execv函數的介紹可知,代碼4創建了一個新的進程并在新進程中運行升級包中的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目錄下,存在著如下幾個文件:
圖4 ./bootable/recovery/updater目錄
?
通過查看Android.mk代碼可知,文件install.c、updater.c將會被編譯為可執行文件updater存放到目錄out/target/product/<product-name>/obj/EXECUTABLES/
updater_intermediates/中;而在生成差分資源包(target-files zipfile)時,會將此文件添加到壓縮包中。
built_ota_tools := \$(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \$(call intermediates-dir-for,EXECUTABLES,applypatch_static)/applypatch_static \$(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq \$(call intermediates-dir-for,EXECUTABLES,sqlite3)/sqlite3 \$(call intermediates-dir-for,EXECUTABLES,updater)/updater代碼段5 Makefile中定義的變量built_ota_tools
代碼段6 復制built_ota_tools工具到差分資源包
如代碼段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函數)。
貌似差不多了,就此打住。
PS:唉,發現現在的表達能力真TMD差,寫個總結都TM憋屈得要死。。。
總結
以上是生活随笔為你收集整理的OTA本质与实现流程分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【华为云技术分享】一文看懂什么是汽车OT
- 下一篇: OTA全称为Over-The-Air t