一、MTD分區:
BOOT: boot.img,Linux kernel (within normal ramdisk)
MISC: bootloader message struct
RECOVERY: recovery.img,Linux kernel (within recovery ramdisk)
SYSTEM: system.img
DATA: userdata.img
CACHE: some cache files
二、 三個部分、兩個接口
Recovery的工作需要整個軟件平臺的配合,從架構角度看,有三個部分:
1) Main system:用boot.img啓動的Linux系統,Android的正常工作模式。
2) Recovery:用recovery.img啓動的Linux系統,主要是運行Recovery程序。
3) Bootloader:除了加載、啓動系統,還會通過讀取flash的MISC分區獲得來自Main system和Recovery的消息,並以此決定做何種操作。
在Recovery的工作流程中,上述三個實體的通信必不可少。通信的接口有以下兩個:
1. 接口1: CACHE分區中的三個文件:/cache/recovery/…
Recovery通過/cache/recovery裏的文件與main system通信,有三個文件:
1) /cache/recovery/command (Main System-->Recovery)
Main system傳給Recovery的命令行,每一行有一個命令,支持以下幾種:
--send_intent=anystring - write the text out to recovery.intent
--update_package=path - verify install an OTA package file
--wipe_data - erase user data (and cache), then reboot
--wipe_cache - wipe cache (but not user data), then reboot
--set_encrypted_filesystem=on|off - enables / diasables encrypted fs
2) /cache/recovery/log(Recovery寫的log文件)
Recovery的log輸出,在recovery運行過程中,stdout及stderr會重定位到/tmp/recovery.log文件,Recovery退出之前會將其轉儲到/cache/recovery/log中,也就是cache分區的recovery/log。
3) /cache/recovery/intent(Recovery-->Main system)
Recovery傳給Main system的信息
2. 接口2: BCB (bootloader control block)即bootloader_message
- struct bootloader_message {
- char command[32];
- char status[32];
- char recovery[1024];
- };
BCB是Bootloader與Recovery的通信接口,也是Bootloader與Main system的通信接口,存儲在flash中的MISC分區,佔用三個page,各成員意義如下:
• command:
當Main system(Linux)想要重啓進入recovery模式,或升級radio/bootloader firmware時,會更新這個域。當firmware更新完畢,爲了啓動後進入recovery做最終的清除,bootloader還會修改它。
•status:
update-radio或update-hboot完成後,bootloader會寫入相應的信息,一般是一些狀態或執行結果。
• recovery:
僅被Main system(Linux)寫入,用於向Recovery發送消息,必須以“recovery\n”開頭,否則這個域的所有內容會被忽略。這一項的內容中“recovery/\n”以後的部分,是/cache/recovery/command支持的命令,可以認爲這是在Recovery操作過程中,對命令操作的備份。Recovery也會更新這個域的信息,執行某操作前把該操作命令寫到recovery域,並更新command域,操作完成後再清空recovery域及command域,這樣在進入Main
system之前,就能確保操作被執行。
如下圖所示,Main system、Recovery與Bootloader通過上述接口通信,通信邏輯依不同的目的而不同,在後面介紹具體工作流程中還會詳細介紹。
三、從Main system進入Recovery的方法
從Main system進入到Recovery,要修改misc分區的數據並重啓,從而告訴Bootloader是用boot.img還是用recovery.img啓動。
/system/core/init/signal_handler.c裏的wait_for_one_process函數中有如下代碼:
android_reboot(ANDROID_RB_RESTART2, 0, "recovery")->
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, “recovery”);
一些關鍵的進程運行異常,會重啓進入recovery模式,這裏用__reboot函數進入recovery。跟蹤這個函數,由系統調用處理函數,到kernel_restart(char *cmd),最終調用machine_restart使用體系結構相關的代碼完成重啓。
Android中沒有給出如何處理“recovery”重啓。不過可以斷定,在重啓之前會向BCB中寫入信息,以告知bootloader如何啓動,具體操作是這樣的:
• 向command域中寫入“boot-recovery” // 此操作必做
• 向recovery域寫入“recovery\n” // 此操作也可不做
這些操作很可能在kernel_restart(char *cmd)中完成,因爲這一部分與體系結構無關,如果要實現完整的Recovery,這部分工作是必須做的。
Bootloader得到進入Recovery模式的指示,用recovery.img啓動,進入Recovery模式,init.rc (bootable/recovery/etc/init.rc)的內容比Main system的要短的多,最重要的是把recovery程序作爲服務啓動:
service recovery /sbin/recovery
四、Android Recovery: 總體流程
根據Recovery的initrc,kernel啓動完成後,啓動recovery服務,這是一個C程序,入口在/bootable/recovery/recovery.c中,main函數結構清晰,主要流程如圖:
1. get_args
首先調用get_args獲取參數,主要流程如下:
get_args不僅傳回獲取到的參數,還會將其寫入misc分區中的BCB,這樣,一旦升級或擦除數據的過程中出現錯誤,重啓之後依然進入Recovery並做相同操作。
2. install_package
或install_rkimage
或wipe_data
或wipe_cache
3. finish_recovery
離開Recovery進入Main system的必經之路,流程如下:
• intent內容作爲參數傳進來,如果有intent需要告知Main system,將其寫入/cache/recovery/intent;
• 將所有log信息轉儲到/cache/recovery/log文件,以供Main system讀取;
• 清除BCB,也就是告知Bootloader啓動進入Main system;
• 刪除/cache/recovery/command;
以上是整體流程中的幾個函數,關於安裝升級包、升級firmware等操作將在具體流程中介紹。
五、Android Recovery: Factory data reset流程
如果系統不穩定,可以嘗試恢復出廠設置,該操作會擦除DATA分區及CACHE分區,有兩種恢復方式,下面分別介紹:
1. 通過Setting程序發起Facory data reset
1) 在應用程序Setting中選擇factory data reset
2) Main system向/cache/recovery/command寫入”–wipe_data”
3) Main system重啓進入recovery模式(方法:修改BCB)
4) Recovery向BCB寫入”boot-recevory”和”recovery\n–wipe_data\n”
5) 擦除DATA分區,裏面是用戶數據,擦除CACHE分區
6) finish_recovery函數
7) 重啓,回到Main system
第4) 步驟,Recovery向BCB寫入boot-recovery和–wipe_data,這是爲了保證後面幾個步驟的完整執行。如,在擦除DATA分區或CACHE分區過程中,如果發生了重啓、關機等操作,導致沒有擦除成功,那麼再次用常規方式開機後,Bootloader會依據misc分區中BCB的指示,引導進入Recovery,並重新擦除這兩個分區。擦完DATA分區與CACHE分區後,調用finish_recovery,做返回Main system前最後的工作,最終要的是擦除BCB,即MISC分區。此後,用常規方式重新開機,系統會進入Main system。
閱讀Android的代碼,發現Setting通過RPC調用Checkin Service的masterClear()啓動這個過程,然而在Android中並沒有找到masterClear()的實現,相關代碼需要在產品化的過程中加入。從ICheckinService.aidl的註釋可以瞭解到這個函數的作用:
/** Reboot into the recovery system and wipe all user data. */
代碼位置:
/packages/apps/Settings/src/com/android/settings/MasterClear.java
/frameworks/base/core/java/android/os/ICheckinService.aidl
2. 通過HOME+POWER組合鍵進入Recovery,再按ALT+W啓動Factory data reset
過程比較簡單,而且與上一種方式類似,結合總體流程,步驟如下:
1) 捕獲按鍵Alt + W。
2) 擦除DATA分區、擦除CACHE分區。
3a) 若激活了log顯示(ALT+L:toggle log text display),調用finish_recovery函數重啓,回到Main system。
3b) 若沒有激活log顯示,繼續接收按鍵,可用HOME+BACK重啓回到Main system。
六、Android Recovery: Update流程
1. update.zip
update操作需要升級包,該升級包是文件名是*.zip,但觀察包內結構會發現其實就是JAR包,JAR包是具有特定目錄和文件結構的ZIP壓縮包,因此可以作爲ZIP包解開。
• MANIFEST.MF:這個manifest文件定義了與包相關數據。
• XXX.SF:這是JAR文件的簽名文件,佔位符xxx標識簽名者,如CERT。
• XXX.DSA:與簽名文件相關聯的簽名程序塊文件,它存儲了用於簽名JAR文件的公共簽名。
• 在META-INF/com/google/android目錄下有updater-script文件,內容就是update要做的操作,也就是前面提到過的command序列。
出於安全性及版本控制的考慮,JAR包要求必須有完整性以及合法性簽名。可以看出這是Android確保安全的策略。JAR相關內容參見http://www.ibm.com/developerworks/cn/java/j-jar/,這裏就不再詳細介紹。
2. Main system部分
通過Android系統下載升級包並啓動升級操作,需要上層應用Updater的支持,它是Java程序,代碼位置android/packages/apps/Updater。大致流程:
• 系統啓動後,如果存在網絡連接,則檢查是否存在升級包;
• 如果存在升級包,則下載至/cache目錄;
• 調用Updater程序來提示是否升級;
• 如果Updater程序進程不存在,則自動啓動此程序;
• 沒有在代碼中找到開始升級後執行哪些操作。不過由recovery.c的註釋部分可以肯定一定需要重啓進入Recovery,重啓前要更新/cache/recovery/command,以告知Recovery進行升級:
–update_package=root:path
3. update流程
update有兩種方式,第一種是上面提到的由Android啓動的自動update過程,升級包在cache/下,升級包的名字在/cache/recovery/command文件中指定。第二種是手動進入Recovery模式,然後輸入Alt + S,安裝/sdcard/update.zip升級包。兩種方式不同的只是安裝包的位置以及傳遞參數給Recovery的方法,update過程都是一樣的,工作流程如下圖所示:
1) install_package @ android/bootable/recovery/install.c
得到安裝包信息,如“–update_package=CACHE:update.zip”,進入install_package函數,流程如下左圖。mount安裝包所在的分區,然後打開zip壓縮包,進入handle_update_package(Android4.0: try_update_binary,它從zip包中提取並運行update-binary程序,update-binary由/bootable/recovery/updater/updater.c編譯生成,update-binary負責解釋updater-script腳本並執行腳本中的命令)開始升級:
4. Bootloader
每次啓動,Bootloader都會讀取位於misc分區的bootloader_message,並檢查command區域以\0結尾,還要考慮flash存在壞塊的情況。然後根據讀取的命令,啓動系統或者更新firmware。工作流程如下:
升級之後,無論升級成功是否,Bootloader都會進入recovery完成最後的收尾工作,並帶着status以告知是否成功。如果更新hboot(尚不知道爲什麼叫這個名字,不過可以確定它就是bootloader firmware),一旦失敗,若原有的bootloader遭到破壞,那麼系統將不能boot。