下面是我从网上学习和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
*/