Android recovery main system code analysis

Source: Internet
Author: User

We have already understood how to enter the normal mode and Recovery mode. If we enter the Recovery mode, how does the core code work?

 


The code path is in the root path of the android source code: bootable \ recovery its entry file is the main function in recovery. c.

 


The following describes the Recovery design concept:

Static const char * COMMAND_FILE = "/cache/recovery/command ";
Static const char * INTENT_FILE = "/cache/recovery/intent ";
Static const char * LOG_FILE = "/cache/recovery/log ";

 


The description in the annotation is quite clear:

* 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

 

 

Static const char * LAST_LOG_FILE = "/cache/recovery/last_log ";

Static const char * LAST_INSTALL_FILE = "/cache/recovery/last_install ";
Static const char * CACHE_ROOT = "/cache ";
Static const char * SDCARD_ROOT = "/sdcard ";

 

 

The following describes the write command in a rough way:

* 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

 

 

Two upgrade modes:

* 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"
* 2. main system writes "-- wipe_data" to/cache/recovery/command
* 3. main system reboots into recovery
* 4. get_args () writes BCB with "boot-recovery" and "-- wipe_data"
* -- After this, rebooting will restart the erase --
* 5. erase_volume () reformats/data
* 6. erase_volume () reformats/cache
* 7. finish_recovery () erases BCB
* -- After this, rebooting will restart the main system --
* 8. main () CILS reboot () to boot main system
*
* OTA INSTALL
* 1. main system downloads OTA package to/cache/some-filename.zip
* 2. main system writes "-- update_package =/cache/some-filename.zip"
* 3. main system reboots into recovery
* 4. get_args () writes BCB with "boot-recovery" and "-- update_package = ..."
* -- After this, rebooting will attempt to reinstall the update --
* 5. install_package () attempts to install the update
* NOTE: the package install must itself be restartable from any point
* 6. finish_recovery () erases BCB
* -- After this, rebooting will (try to) restart the main system --
* 7. ** 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
* 8. main () callmaybe_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 --
* 9. main () CILS reboot () to boot main system


From the above annotations, we can basically understand how Recovery works. Next we will analyze the code step by step.

 


1. recovery main Function

 int main(int argc, char **argv) {     time_t start = time(NULL);      // If these fail, there's not really anywhere to complain...      freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);     freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);     printf("Starting recovery on %s", ctime(&start)); 

Int
Main (int argc, char ** argv ){
Time_t start = time (NULL );

// If these fail, there's not really anywhere to complain...
Freopen (TEMPORARY_LOG_FILE, "a", stdout); setbuf (stdout, NULL );
Freopen (TEMPORARY_LOG_FILE, "a", stderr); setbuf (stderr, NULL );
Printf ("Starting recovery on % s", ctime (& start ));

Relocate the standard output and standard error output to "/tmp/recovery. log ", if the mode is eng, you can use adb pull/tmp/recovery. log to view the current log information, which provides an effective debugging method.


Ui_init ();


A simple framebuffer-based ui system called miniui mainly establishes the image part (gglInit, gr_init_font, framebuffer), progress bar, and event processing (input_callback)
Load_volume_table ();


Create a partition table based on/etc/recovery. fstab

// Command line args come from, in decreasing precedence:
//-The actual command line
//-The bootloader control block (one per line, after "recovery ")
//-The contents of COMMAND_FILE (one per line)


Get_args (& argc, & argv );


Read parameters from misc partition and CACHE: recovery/command file, write them to argc, argv (get_bootloader_message), and possibly write them back to misc partition (set_bootloader_message)

 

 

After completing the preceding steps, parse the specific parameters:

While (arg = getopt_long (argc, argv, "", OPTIONS, NULL ))! =-1 ){
Switch (arg ){
Case 'p': previous_runs = atoi (optarg); break;
Case's ': send_intent = optarg; break;
Case 'U': update_package = optarg; break;
Case 'W': wipe_data = wipe_cache = 1; break;
Case 'C': wipe_cache = 1; break;
Case 'T': ui_show_text (1); break;
Case '? ':
LOGE ("Invalid command argument \ n ");
Continue;
}
}

 

 

Printf ("Command :");
For (arg = 0; arg <argc; arg ++ ){
Printf ("\" % s \ "", argv [arg]);
}
Printf ("\ n ");


The above is just a print to indicate the step to take, to facilitate the grasp of the debugging situation

 


The following code is specific:

If (update_package! = NULL ){
Status = install_package (update_package, & wipe_cache, TEMPORARY_INSTALL_FILE );
If (status = INSTALL_SUCCESS & wipe_cache ){
If (erase_volume ("/cache ")){
LOGE ("Cache wipe (requested by package) failed .");
}
}
If (status! = INSTALL_SUCCESS) ui_print ("Installation aborted. \ n ");
} Else if (wipe_data ){
If (device_wipe_data () status = INSTALL_ERROR;
If (erase_volume ("/data") status = INSTALL_ERROR;
If (wipe_cache & erase_volume ("/cache") status = INSTALL_ERROR;
If (status! = INSTALL_SUCCESS) ui_print ("Data wipe failed. \ n ");
Clear_sdcard_update_bootloader_message ();
} Else if (wipe_cache ){
If (wipe_cache & erase_volume ("/cache") status = INSTALL_ERROR;
If (status! = INSTALL_SUCCESS) ui_print ("Cache wipe failed. \ n ");
Clear_sdcard_update_bootloader_message ();
} Else {
Status = update_by_key (); // No command specified
}

 

 


Call various functions based on the parameters provided by the user, such as installing an upgrade package, erasing the cache partition, and erasing the user data Partition. After that, the detailed breakdown will continue.
If (status! = INSTALL_SUCCESS) prompt_and_wait (); if the previous operation is successful, the restart process is started. Otherwise, the user can perform the following operations: reboot, install update.zip, remove the cache partition, and erase the user data partition.


// Otherwise, get ready to boot the main system...
Finish_recovery (send_intent );

First look at the function annotation:

// Clear the recovery command and prepare to boot a (hopefully working) system,
// Copy our log file to cache as well (for the system to read), and
// Record any intent we were asked to communicate back to the system.
// This function is idempotent: call it as your times as you like.

 

 

In fact, the main function operations are as follows:

// Remove the command file, so recovery won't repeat indefinitely.
If (ensure_path_mounted (COMMAND_FILE )! = 0 |
(Unlink (COMMAND_FILE) & errno! = ENOENT )){
LOGW ("Can't unlink % s \ n", COMMAND_FILE );
}

The specified partition mounted is successfully deleted and unlink is used to delete the directory items of a file and reduce the number of links to it.

Ensure_path_unmounted (CACHE_ROOT );

Unmounted
Sync (); // For good measure.

 

 

Summary of the above Code:


Its functions are as follows: 1. Write the previously defined intent string (if any): CACHE: recovery/command2, And/tmp/recovery. copy logs to "CACHE: recovery/log"; 3. Clear the misc partition, so that the restart will not enter recovery mode. 4. Delete the command file: CACHE: recovery/command;

 

Restart the machine

Ui_print ("Rebooting... \ n ");
Android_reboot (ANDROID_RB_RESTART, 0, 0 );

 

 

2. factory reset core code implementation

 


Follow the eight steps listed above. The steps 1-6 and 7-8 are the same as the general process of main and will not be repeated.

* 5. erase_volume () reformats/data
* 6. erase_volume () reformats/cache


How are these two operations performed?

If (erase_volume ("/data") status = INSTALL_ERROR;


If (erase_volume ("/cache") status = INSTALL_ERROR;


Finally

Clear_sdcard_update_bootloader_message ();

 

 

Take a look at the erase_volume () function first:

 static int erase_volume(const char *volume) {     ui_set_background(BACKGROUND_ICON_INSTALLING);     ui_show_indeterminate_progress();     ui_print("Formatting %s...\n", volume);      ensure_path_unmounted(volume);      if (strcmp(volume, "/cache") == 0) {         // Any part of the log we'd copied to cache is now gone.          // Reset the pointer so we copy from the beginning of the temp          // log.          tmplog_offset = 0;     }      return format_volume(volume); } static interase_volume(const char *volume) {    ui_set_background(BACKGROUND_ICON_INSTALLING);    ui_show_indeterminate_progress();    ui_print("Formatting %s...\n", volume);    ensure_path_unmounted(volume);    if (strcmp(volume, "/cache") == 0) {        // Any part of the log we'd copied to cache is now gone.        // Reset the pointer so we copy from the beginning of the temp        // log.        tmplog_offset = 0;    }    return format_volume(volume);}

The red text above indicates an important function call.

Int ensure_path_unmounted (const char * path ){


Volume * v = volume_for_path (path );

Result = scan_mounted_volumes ();

Return unmount_mounted_volume (mv );

}

It is to uninstall the specified path's medium path mount point, and the main function of format_volume is:

MtdWriteContext * write = mtd_write_partition (partition );


Mtd_erase_blocks (write,-1 );


Mtd_write_close (write );


Don't elaborate on it, that is, clear all the data in the partition.

Last function:

Void
Clear_sdcard_update_bootloader_message (){
Struct bootloader_message boot;
Memset (& boot, 0, sizeof (boot ));
Set_bootloader_message (& boot );
}


It is to reset the misc partition data to 0.

In this way, the factory settings are restored. Just erase the data/cache partition erase.

 


3. OTA core installation code implementation

The main function is to install Package:

* 5. install_package () attempts to install the update
* NOTE: the package install must itself be restartable from any point

 

 

Int
Install_package (const char * path, int * wipe_cache, const char * install_file)


-->

Static int
Really_install_package (const char * path, int * wipe_cache ){


Clear_sdcard_update_bootloader_message ();

Ui_set_background (BACKGROUND_ICON_INSTALLING );
Ui_print ("Finding update package... \ n ");
Ui_show_indeterminate_progress ();
LOGI ("Update location: % s \ n", path );

Update ui display

 


For (; (I <5) & (ensure_path_mounted (path )! = 0); I ++ ){
LOGE ("Can't mount % s \ n", path );
Sleep (1 );
}
If (I> = 5) & (ensure_path_mounted (path )! = 0 )){
Return INSTALL_CORRUPT;
}

Make sure that the partition where the upgrade package is located has been mounted, usually the cache partition or SD partition RSAPublicKey * loadedKeys = load_keys (PUBLIC_KEYS_FILE, & numKeys); // Look for an RSA signature embedded in. ZIP file comment given
// The path to the zip. Verify it matches one of the given public
// Keys.
//
// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
// Or no key matches the signature ).
Err = verify_file (path, loadedKeys, numKeys); load the public key from/res/keys and confirm the validity of the file
/* Try to open the package.
*/
ZipArchive zip;
Err = mzOpenZipArchive (path, & zip); open the upgrade package and save the information to the ZipArchive Data Organization for later processing.
/* Verify and install the contents of the package.
*/
Ui_print ("Installing update... \ n ");
Return try_update_binary (path, & zip, wipe_cache); perform the final installation package file
}

 


// If the package contains an update binary, extract it and run it.
Static int
Try_update_binary (const char * path, ZipArchive * zip, int * wipe_cache ){
Const ZipEntry * binary_entry =
MzFindZipEntry (zip, ASSUMED_UPDATE_BINARY_NAME );

 

 

Char * binary = "/tmp/update_binary ";
Unlink (binary );
Int fd = creat (binary, 0755 );

 


Bool OK = mzExtractZipEntryToFile (zip, binary_entry, fd );
Close (fd );
MzCloseZipArchive (zip );

Copy files in the upgrade package META-INF/com/google/android/update-binary to/tmp/update_binary

 

// When executing the update binary contained in the package,
// Arguments passed are:
//
//-The version number for this interface
//
//-An fd to which the program can write in order to update
// Progress bar. The program can write single-line commands:


Int pipefd [2];
Pipe (pipefd );

 

 

Char ** args = malloc (sizeof (char *) * 5 );

Args [0] = binary;
Args [1] = EXPAND (RECOVERY_API_VERSION); // defined in Android. mk
Args [2] = malloc (10 );
Sprintf (args [2], "% d", pipefd [1]);
Args [3] = (char *) path;
Args [4] = buf_uuid;
Args [5] = NULL;


Assemble New Process Parameters

Pid_t pid = fork ();
If (pid = 0) {// child process
Close (pipefd [0]);
Execv (binary, args );

}

// Parent process
Close (pipefd [1]);

Ui_show_progress

Ui_set_progress

Ui_print

 


Summarize the main code behavior functions:

1. A new process will be created and executed:/tmp/update_binary


2. At the same time, some parameters will be input to the process, the most important of which is a pipeline fd for the new process to communicate with the original process.

3. After the birth of a new process, the original process becomes a service process, which provides several UI Update Services:

A) SS

B) set_progress

C) ui_print

In this way, the new process can complete the display task through the UI system of the old process. Other functions depend on itself.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.