文章開頭咱們就提到update.zip包來源有兩種,一個是OTA在線下載(通常下載到/CACHE分區),一個是手動拷貝到SD卡中。不管是哪一種方式得到update.zip包,在進入Recovery模式前,都未對這個zip包作處理。只是在重啓以前將zip包的路徑告訴了Recovery服務(經過將--update_package=CACHE:some_filename.zip或--update_package=SDCARD:update.zip命令寫入到/cache/recovery/command中)。在這裏咱們假設update.zip包已經制做好並拷貝到了SD卡中,並以Settings-->About Phone-->System Update-->Installed From SDCARD方式升級。java
咱們的測試開發板是TCC8800,使用的Android源碼是gingerbread0919,在這種方式下升級的源碼位於gingerbread/device/telechips/common/apps/TelechipsSystemUpdater/src/com/telechips/android/systemupdater/下。 下面咱們具體分析這種升級方式下,咱們的update.zip是怎樣從上層一步步進入到Recovery模式的。android
1、從System Update到Rebootsql
當咱們依次選擇Settings-->About Phone-->System Update-->Installed From SDCARD後會彈出一個對話框,提示已有update.zip包是否如今更新,咱們從這個地方跟蹤。這個對話框的源碼是SystemUpdateInstallDialog.java。數據庫
①在mNowButton按鈕的監聽事件裏,會調用mService.rebootAndUpdate(new File(mFile))。這個mService就是SystemUpdateService的實例。 這 個類所在的源碼文件是SystemUpdateService.java。這個函數的參數是一個文件。它確定就是咱們的update.zip包了。咱們能夠證明一下這個猜測。app
②mFile的值:在SystemUpdateInstallDialog.java中的ServiceConnection中咱們能夠看到這個mFile的值有兩個來源。ionic
來源一:函數
mFile的一個來源是這個是否當即更新提示框接受的上一個Activity以「file」標記傳來的值。這個Activity就是SystemUpdate.java。它是一個PreferenceActivity類型的。在其onPreferenceChange函數中定義了向下一個Activity傳送的值,這個值是根據咱們不一樣的選擇而定的。若是咱們在以前選擇了從SD卡安裝,則這個傳下去的「file」值爲「/sdcard/update.zip」。若是選擇了從NAND安裝,則對應的值爲「/nand/update.zip」。測試
來源二:spa
另個一來源是從mService.getInstallFile()得到。咱們進一步跟蹤就可發現上面這個函數得到的值就是「/cache」+ mUpdateFileURL.getFile();這就是OTA在線下載後對應的文件路徑。不論參數mFile的來源如何,咱們能夠發如今mNowButton按鈕的監聽事件裏是將整個文件,也就是咱們的update.zip包做爲參數往rebootAndUpdate()中傳遞的。線程
③rebootAndUpdate:在這個函數中Main System作了重啓前的準備。繼續跟蹤下去會發現,在SystemUpdateService.java中的rebootAndUpdate函數中新建了一個線程,在這個線程中最後調用的就是RecoverySystem.installPackage(mContext,mFile),咱們的update.zip包也被傳遞進來了。
④RecoverySystem類:RecoverySystem類的源碼所在文件路徑爲:gingerbread0919/frameworks/base/core/java/android/os/RecoverySystem.java。咱們關心的是installPackage(Context context,FilepackageFile)函數。這個函數首先根據咱們傳過來的包文件,獲取這個包文件的絕對路徑filename。而後將其拼成arg=「--update_package=」+filename。它最終會被寫入到BCB中。這個就是重啓進入Recovery模式後,Recovery服務要進行的操做。它被傳遞到函數bootCommand(context,arg)。
⑤bootCommand():在這個函數中才是Main System在重啓前真正作的準備。主要作了如下事情,首先建立/cache/recovery/目錄,刪除這個目錄下的command和log(可能不存在)文件在sqlite數據庫中的備份。而後將上面④步中的arg命令寫入到/cache/recovery/command文件中。下一步就是真正重啓了。接下來看一下在重啓函數reboot中所作的事情。
⑥pm.reboot():重啓以前先得到了PowerManager(電源管理)並進一步得到其系統服務。而後調用了pm.reboot(「recovery」)函數。他就是/gingerbread0919/bionic/libc/unistd/reboot.c中的reboot函數。這個函數其實是一個系統調用,即__reboot(LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,mode,NULL);從這個函數咱們能夠看出前兩個參數就表明了咱們的組合鍵,mode就是咱們傳過來的「recovery」。再進一步跟蹤就到了彙編代碼了,咱們沒法直接查看它的具體實現細節。但能夠確定的是 這個函數只將「recovery」參數傳遞過去了,以後將「boot-recovery」寫入到了MISC分區的BCB數據塊的command域中。這樣在重啓以後Bootloader才知道要進入Recovery模式。
在這裏咱們沒法確定Main System在重啓以前對BCB的recovery域是否進行了操做。其實在重啓前是否更新BCB的recovery域是不重要的,由於進入Recovery服務後,Recovery會自動去/cache/recovery/command中讀取要進行的操做而後寫入到BCB的recovery域中。
至此,Main System就開始重啓並進入Recovery模式。在這以前Main System作的最實質的就是兩件事,一是將「boot-recovery」寫入BCB的command域,二是將--update_package=/cache/update.zip」或則「--update_package=/sdcard/update.zip」寫入/cache/recovery/command文件中。下面的部分就開始重啓並進入Recovery服務了。
2、從reboot到Recovery服務
這個過程咱們在上文(對照第一個圖)已經講過了。從Bootloader開始若是沒有組合鍵按下,就從MISC分區讀取BCB塊的command域(在主系統時已經將「boot-recovery」寫入)。而後就以Recovery模式開始啓動。與正常啓動不一樣的是Recovery模式下加載的鏡像是recovery.img。這個鏡像同boot.img相似,也包含了標準的內核和根文件系統。其後就與正常的啓動系統相似,也是啓動內核,而後啓動文件系統。在進入文件系統後會執行/init,init的配置文件就是/init.rc。這個配置文件來自bootable/recovery/etc/init.rc。查看這個文件咱們能夠看到它作的事情很簡單:
①設置環境變量。
②創建etc鏈接。
③新建目錄,備用。
④掛載/tmp爲內存文件系統tmpfs
⑤啓動recovery(/sbin/recovery)服務。
⑥啓動adbd服務(用於調試)。
這裏最重要的就是固然就recovery服務了。在Recovery服務中將要完成咱們的升級工做。
咱們將在下一篇詳細分析Recovery服務的流程細節。