下面是我从网上学习和recovery代码中大致总结出来的流程,可能不是很详细到位,大家可参考,不对之处,请指正,技术无边,多多交流。

recovery大概流程总结:

1、首先bootloader(有些平台是在uboot有些是在kernel,有些平台则在uboot和kernel都可以)通过reboot_mode传递一个参数给recovery那边作为启动参数。
2、bootloader和recovery通过misc分区传递启动参数:
对应的参数数据结构体为bootloader_message;
参照源码中bootloader_message 的注释

struct bootloader_message {
char command[32];//bootloader 启动时读取改数据,决定是否进入recovery模式
char status[32];//由bootloader进行更新,标识升级的结果;
char recovery[768];//由Android系统进行写入,recovery从中读取信息;
char stage[32];
char reserved[224];
};

3、recovery 根据命令行参数,再从/misc分区中解析出对应的参数,然后进行后续的操作,具体的调用函数为get_args(&argc, &argv);

在get_args函数中,通过调用get_bootloader_message来获取从bootloader传过来的参数信息。

get_bootloader_message(&boot);  // 具体的读取信息的函数,可能为空的情况

如果为空,则从/cache/recovery/command中获取参数。再把从/cache/recovery/command获取参数重新写回到/misc分区。

注意,此函数会先把struct bootloader_message boot写入到misc分区,目的是防止断电等原因导致关机,开机后bootloader会从misc分区中读取相关信息,如果发现是"boot-recovery"会再次进入recovery模式,misc分区会在退出recovery时被清除,以至于可以正常开机,如果手机每次都是进入recovery而不能正常开机,可以分析是否跟没有清除misc分区有关。

4、然后解析command或者传入的参数,并把对应的功能设置为true或给相应的变量赋值。

while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {         //while循环解析command或者传入的参数,并把对应的功能设置为true或给相应的变量赋值
    switch (arg) {
    case 'i': send_intent = optarg; break;
    case 'u': update_package = optarg; break;
    case 'w': should_wipe_data = true; break;
    case 'c': should_wipe_cache = true; break;
    case 't': show_text = true; break;
    case 's': sideload = true; break;
    case 'a': sideload = true; sideload_auto_reboot = true; break;
    case 'x': just_exit = true; break;
    case 'l': locale = optarg; break;
    case 'g': {
        if (stage == NULL || *stage == '\0') {
            char buffer[20] = "1/";
            strncat(buffer, optarg, sizeof(buffer)-3);
            stage = strdup(buffer);
        }
        break;
    }
    case 'p': shutdown_after = true; break;
    case 'r': reason = optarg; break;
    case '?':
        LOGE("Invalid command argument\n");
        continue;
    }
}

5、执行完上面的操作之后,会进入prompt_and_wait()函数

Device::BuiltinAction temp = prompt_and_wait(device, status);

//prompt_and_wait()函数是个死循环 开始显示recovery选项 并处理用户通过按键或者触摸屏的选项,如Reboot system等。

6、prompt_and_wait函数里面会执行按键的检测函数,如果按键选择对应菜单,则进行对应的操作,例如擦除数据等。

int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), 0, 0, device);
Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);

static Device::BuiltinAction
prompt_and_wait(Device* device, int status) {
for (;;) {
    finish_recovery(NULL);
    switch (status) {
        case INSTALL_SUCCESS:
            ui->SetBackground(RecoveryUI::NONE);
            break;
        case INSTALL_NONE:
            ui->SetBackground(RecoveryUI::NO_COMMAND);
            break;

        case INSTALL_ERROR:
        case INSTALL_CORRUPT:
            ui->SetBackground(RecoveryUI::ERROR);
            break;
    }
    ui->SetProgressType(RecoveryUI::EMPTY);

    int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), 0, 0, device);

    // device-specific code may take some action here.  It may
    // return one of the core actions handled in the switch
    // statement below.
    Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);

    bool should_wipe_cache = false;
    switch (chosen_action) {
        case Device::NO_ACTION:
            break;

        case Device::REBOOT:
        case Device::SHUTDOWN:
        case Device::REBOOT_BOOTLOADER:
            return chosen_action;

        case Device::WIPE_DATA:
            wipe_data(ui->IsTextVisible(), device);
            if (!ui->IsTextVisible()) return Device::NO_ACTION;
            break;

        case Device::WIPE_CACHE:
            wipe_cache(ui->IsTextVisible(), device);
            if (!ui->IsTextVisible()) return Device::NO_ACTION;
            break;

下面是摘抄来自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
 *   --just_exit - do nothing; exit and reboot
 *
 * 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() calls 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() 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 --
 *    9. main() calls reboot() to boot main system
 */