天天看點

Android OTA更新原理和流程分析(六)---Recovery服務流程細節

轉載自:http://blog.chinaunix.net/uid-22028566-id-3533855.html

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

Android OTA更新原理和流程分析(六)---Recovery服務流程細節

一、 Recovery的三類服務:

         先看一下在這個源碼檔案中開始部分的一大段注釋,這将對我們了解Recovery服務的主要功能有很大幫助。代碼如下:

/*

  • The recovery tool communicates with the main system through /cache files.
  • /cache/recovery/command - INPUT - command line for tool, one arg per line
  • /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
  • /cache/recovery/intent - OUTPUT - intent that was passed in
  • The arguments which may be supplied in the recovery.command file:
  • –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
  • After completing, we remove /cache/recovery/command and reboot.
  • Arguments may also be supplied in the bootloader control block (BCB).
  • These important scenarios must be safely restartable at any point:
  • FACTORY RESET
    1. user selects “factory reset”
    1. main system writes “–wipe_data” to /cache/recovery/command
    1. main system reboots into recovery
    1. get_args() writes BCB with “boot-recovery” and “–wipe_data”
  • – after this, rebooting will restart the erase –
    1. erase_volume() reformats /data
    1. erase_volume() reformats /cache
    1. finish_recovery() erases BCB
  • – after this, rebooting will restart the main system –
    1. main() calls reboot() to boot main system
  • OTA INSTALL
    1. main system downloads OTA package to /cache/some-filename.zip
    1. main system writes “–update_package=/cache/some-filename.zip”
    1. main system reboots into recovery
    1. get_args() writes BCB with “boot-recovery” and “–update_package=…”
  • – after this, rebooting will attempt to reinstall the update –
    1. install_package() attempts to install the update
  • NOTE: the package install must itself be restartable from any point
    1. finish_recovery() erases BCB
  • – after this, rebooting will (try to) restart the main system –
    1. ** if install failed **
  • 7a. prompt_and_wait() shows an error icon and waits for the user
  • 7b; the user reboots (pulling the battery, etc) into the main system
    1. main() calls maybe_install_firmware_update()
  • ** if the update contained radio/hboot firmware **:
  • 8a. m_i_f_u() writes BCB with “boot-recovery” and “–wipe_cache”
  • -- after this, rebooting will reformat cache & restart main system -- 
               
  • 8b. m_i_f_u() writes firmware image into raw cache partition
  • 8c. m_i_f_u() writes BCB with “update-radio/hboot” and “–wipe_cache”
  • -- after this, rebooting will attempt to reinstall firmware -- 
               
  • 8d. bootloader tries to flash firmware
  • 8e. bootloader writes BCB with “boot-recovery” (keeping “–wipe_cache”)
  • -- after this, rebooting will reformat cache & restart main system -- 
               
  • 8f. erase_volume() reformats /cache
  • 8g. finish_recovery() erases BCB
  • -- after this, rebooting will (try to) restart the main system -- 
               
    1. main() calls reboot() to boot main system
  • SECURE FILE SYSTEMS ENABLE/DISABLE
    1. user selects “enable encrypted file systems”
    1. main system writes “–set_encrypted_filesystems=on|off” to
  • /cache/recovery/command
    1. main system reboots into recovery
    1. get_args() writes BCB with “boot-recovery” and
  • “–set_encrypted_filesystems=on|off”
  • – after this, rebooting will restart the transition –
    1. read_encrypted_fs_info() retrieves encrypted file systems settings from /data
  • Settings include: property to specify the Encrypted FS istatus and
  • FS encryption key if enabled (not yet implemented)
    1. erase_volume() reformats /data
    1. erase_volume() reformats /cache
    1. restore_encrypted_fs_info() writes required encrypted file systems settings to /data
  • Settings include: property to specify the Encrypted FS status and
  • FS encryption key if enabled (not yet implemented)
    1. finish_recovery() erases BCB
  • – after this, rebooting will restart the main system –
    1. main() calls reboot() to boot main system

      */

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

         ①FACTORY RESET,恢複出廠設定。

         ②OTA INSTALL,即我們的update.zip包更新。

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

二、Recovery服務的通用流程:

        在這裡我們以OTA INSTALL的流程為例具體分析。并從相關函數的調用過程圖開始,如下圖:
Android OTA更新原理和流程分析(六)---Recovery服務流程細節
          我們順着流程圖分析,從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塊中。

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

Android OTA更新原理和流程分析(六)---Recovery服務流程細節

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的必經之路。其大體流程如下:
Android OTA更新原理和流程分析(六)---Recovery服務流程細節

               ① 将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函數。

上一篇:Android OTA更新原理和流程分析(五)---update.zip包從上層進入Recovery服務

下一篇:Android OTA更新原理和流程分析(七)---Recovery服務的核心install_package函數

轉載自:http://blog.chinaunix.net/uid-22028566-id-3533855.html