Android系統Recovery工作原理之使用update.zip升級過程分析(六)---Recovery服務流程細節

Recovery服務毫無疑問是Recovery啓動模式中最核心的部分。它完成Recovery模式所有的工作。Recovery程序對應的源碼文件位於:/gingerbread0919/bootable/recovery/recovery.c。


一、 Recovery的三類服務:

         先看一下在這個源碼文件中開始部分的一大段註釋,這將對我們理解Recovery服務的主要功能有很大幫助。代碼如下:

         

  1. /* 
  2.  * The recovery tool communicates with the main system through /cache files. 
  3.  *   /cache/recovery/command - INPUT - command line for tool, one arg per line 
  4.  *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s) 
  5.  *   /cache/recovery/intent - OUTPUT - intent that was passed in 
  6.  * 
  7.  * The arguments which may be supplied in the recovery.command file: 
  8.  *   --send_intent=anystring - write the text out to recovery.intent 
  9.  *   --update_package=path - verify install an OTA package file 
  10.  *   --wipe_data - erase user data (and cache), then reboot 
  11.  *   --wipe_cache - wipe cache (but not user data), then reboot 
  12.  *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs 
  13.  * 
  14.  * After completing, we remove /cache/recovery/command and reboot. 
  15.  * Arguments may also be supplied in the bootloader control block (BCB). 
  16.  * These important scenarios must be safely restartable at any point: 
  17.  * 
  18.  * FACTORY RESET 
  19.  * 1. user selects "factory reset" 
  20.  * 2. main system writes "--wipe_data" to /cache/recovery/command 
  21.  * 3. main system reboots into recovery 
  22.  * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data" 
  23.  *    -- after this, rebooting will restart the erase -- 
  24.  * 5. erase_volume() reformats /data 
  25.  * 6. erase_volume() reformats /cache 
  26.  * 7. finish_recovery() erases BCB 
  27.  *    -- after this, rebooting will restart the main system -- 
  28.  * 8. main() calls reboot() to boot main system 
  29.  * 
  30.  * OTA INSTALL 
  31.  * 1. main system downloads OTA package to /cache/some-filename.zip 
  32.  * 2. main system writes "--update_package=/cache/some-filename.zip" 
  33.  * 3. main system reboots into recovery 
  34.  * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..." 
  35.  *    -- after this, rebooting will attempt to reinstall the update -- 
  36.  * 5. install_package() attempts to install the update 
  37.  *    NOTE: the package install must itself be restartable from any point 
  38.  * 6. finish_recovery() erases BCB 
  39.  *    -- after this, rebooting will (try to) restart the main system -- 
  40.  * 7. ** if install failed ** 
  41.  *    7a. prompt_and_wait() shows an error icon and waits for the user 
  42.  *    7b; the user reboots (pulling the battery, etc) into the main system 
  43.  * 8. main() calls maybe_install_firmware_update() 
  44.  *    ** if the update contained radio/hboot firmware **: 
  45.  *    8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache" 
  46.  *        -- after this, rebooting will reformat cache & restart main system -- 
  47.  *    8b. m_i_f_u() writes firmware image into raw cache partition 
  48.  *    8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache" 
  49.  *        -- after this, rebooting will attempt to reinstall firmware -- 
  50.  *    8d. bootloader tries to flash firmware 
  51.  *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache") 
  52.  *        -- after this, rebooting will reformat cache & restart main system -- 
  53.  *    8f. erase_volume() reformats /cache 
  54.  *    8g. finish_recovery() erases BCB 
  55.  *        -- after this, rebooting will (try to) restart the main system -- 
  56.  * 9. main() calls reboot() to boot main system 
  57.  * 
  58.  * SECURE FILE SYSTEMS ENABLE/DISABLE 
  59.  * 1. user selects "enable encrypted file systems" 
  60.  * 2. main system writes "--set_encrypted_filesystems=on|off" to 
  61.  *    /cache/recovery/command 
  62.  * 3. main system reboots into recovery 
  63.  * 4. get_args() writes BCB with "boot-recovery" and 
  64.  *    "--set_encrypted_filesystems=on|off" 
  65.  *    -- after this, rebooting will restart the transition -- 
  66.  * 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data 
  67.  *    Settings include: property to specify the Encrypted FS istatus and 
  68.  *    FS encryption key if enabled (not yet implemented) 
  69.  * 6. erase_volume() reformats /data 
  70.  * 7. erase_volume() reformats /cache 
  71.  * 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data 
  72.  *    Settings include: property to specify the Encrypted FS status and 
  73.  *    FS encryption key if enabled (not yet implemented) 
  74.  * 9. finish_recovery() erases BCB 
  75.  *    -- after this, rebooting will restart the main system -- 
  76.  * 10. main() calls reboot() to boot main system 
  77.  */  

          從註釋中我們可以看到Recovery的服務內容主要有三類:

         FACTORY RESET,恢復出廠設置。

         OTA INSTALL,即我們的update.zip包升級。

         ENCRYPTED FILE SYSTEM ENABLE/DISABLE,使能/關閉加密文件系統。具體的每一類服務的大概工作流程,註釋中都有,我們在下文中會詳細講解OTA INSTALL的工作流程。這三類服務的大概的流程都是通用的,只是不同操作體現與不同的操作細節。下面我們看Recovery服務的通用流程。


二、Recovery服務的通用流程:

        在這裏我們以OTA INSTALL的流程爲例具體分析。並從相關函數的調用過程圖開始,如下圖:


          

          我們順着流程圖分析,從recovery.c的main函數開始:

          1.    ui_init():Recovery服務使用了一個基於framebuffer的簡單ui(miniui)系統。這個函數對其進行了簡單的初始化。在Recovery服務的過程中主要用於顯示一個背景圖片(正在安裝或安裝失敗)和一個進度條(用於顯示進度)。另外還啓動了兩個線程,一個用於處理進度條的顯示(progress_thread),另一個用於響應用戶的按鍵(input_thread)。

          2.    get_arg():這個函數主要做了上圖中get_arg()往右往下直到parse arg/v的工作。我們對照着流程一個一個看。

                get_bootloader_message():主要工作是根據分區的文件格式類型(mtd或emmc)從MISC分區中讀取BCB數據塊到一個臨時的變量中。

                然後開始判斷Recovery服務是否有帶命令行的參數(/sbin/recovery,根據現有的邏輯是沒有的),若沒有就從BCB中讀取recovery域。如果讀取失敗則從/cache/recovery/command中讀取然後。這樣這個BCB的臨時變量中的recovery域就被更新了。在將這個BCB的臨時變量寫回真實的BCB之前,又更新的這個BCB臨時變量的command域爲“boot-recovery”。這樣做的目的是如果在升級失敗(比如升級還未結束就斷電了)時,系統在重啓之後還會進入Recovery模式,直到升級完成。

                在這個BCB臨時變量的各個域都更新完成後使用set_bootloader_message()寫回到真正的BCB塊中。

                這個過程可以用一個簡單的圖來概括,這樣更清晰:

                                 


          3.     parserargc/argv:解析我們獲得參數。註冊所解析的命令(register_update_command),在下面的操作中會根據這一步解析的值進行一步步的判斷,然後進行相應的操作。

          4.    if(update_package):判斷update_package是否有值,若有就表示需要升級更新包,此時就會調用install_package()(即圖中紅色的第二個階段)。在這一步中將要完成安裝實際的升級包。這是最爲複雜,也是升級update.zip包最爲核心的部分。我們在下一節詳細分析這一過程。爲從宏觀上理解Recovery服務的框架,我們將這一步先略過,假設已經安裝完成了。我們接着往下走,看安裝完成後Recovery怎樣一步步結束服務,並重啓到新的主系統的。

          5.    if(wipe_data/wipe_cache):這一步判斷實際是兩步,在源碼中是先判斷是否擦除data分區(用戶數據部分)的,然後再判斷是否擦除cache分區。值得注意的是在擦除data分區的時候必須連帶擦除cache分區。在只擦除cache分區的情形下可以不擦除data分區。

          6.    maybe_install_firmware_update():如果升級包中包含/radio/hboot firmware的更新,則會調用這個函數。查看源碼發現,在註釋中(OTA INSTALL)有這一個流程。但是main函數中並沒有顯示調用這個函數。目前尚未發現到底是在什麼地方處理。但是其流程還是向上面的圖示一樣。即,① 先向BCB中寫入“boot-recovery”和“—wipe_cache”之後將cache分區格式化,然後將firmware image 寫入原始的cache分區中。②將命令“update-radio/hboot”和“—wipe_cache”寫入BCB中,然後開始重新安裝firmware並刷新firmware。③之後又會進入圖示中的末尾,即finish_recovery()。

          7.    prompt_and_wait():這個函數是在一個判斷中被調用的。其意義是如果安裝失敗(update.zip包錯誤或驗證簽名失敗),則等待用戶的輸入處理(如通過組合鍵reboot等)。

          8.    finish_recovery():這是Recovery關閉並進入Main System的必經之路。其大體流程如下:

                                               

                將intent(字符串)的內容作爲參數傳進finish_recovery中。如果有intent需要告知Main System,則將其寫入/cache/recovery/intent中。這個intent的作用尚不知有何用。

                將內存文件系統中的Recovery服務的日誌(/tmp/recovery.log)拷貝到cache(/cache/recovery/log)分區中,以便告知重啓後的Main System發生過什麼。

                擦除MISC分區中的BCB數據塊的內容,以便系統重啓後不在進入Recovery模式而是進入更新後的主系統。

                刪除/cache/recovery/command文件。這一步也是很重要的,因爲重啓後Bootloader會自動檢索這個文件,如果未刪除的話又會進入Recovery模式。原理在上面已經講的很清楚了。


          9.    reboot():這是一個系統調用。在這一步Recovery完成其服務重啓並進入Main System。這次重啓和在主系統中重啓進入Recovery模式調用的函數是一樣的,但是其方向是不一樣的。所以參數也就不一樣。查看源碼發現,其重啓模式是RB_AUTOBOOT。這是一個系統的宏。

 

            至此,我們對Recovery服務的整個流程框架已有了大概的認識。下面就是升級update.zip包時特有的也是Recovery服務中關於安裝升級包最核心的第二個階段。即我們圖例中的紅色2的那個分支。


           我們將在下一篇詳細講解這一部分,即Recovery服務的核心部分install_package函數

發佈了11 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章