android恢复出厂设置后重启进入recovery模式,recovery模式也从uboot启动进入kernel,kernel启动结束后和正常启动就有所不同,此时第一个进程init启动精简模式,运行recovery bin文件。此文件是bootable/recovery/recovery.cpp文件生成,我们就从recovery.cpp文件开始分析。
下面的代码位于bootable/recovery/etc/init.rc,由此可知,进入recovery模式后会执行sbin /recovery,此文件是bootable/recovery/recovery.cpp生成(可查看对应目录的Android.mk查看),所以recovery.cpp是recovery模式的入口。
service recovery /sbin/recovery seclabel u:r:recovery:s0
bootable/recovery/recovery.cpp
// text display during the installation. This is to enable automated
// testing.
if (!sideload_auto_reboot) {
ui->ShowText(true);
}
status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
if (status == INSTALL_SUCCESS && should_wipe_cache) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
}
}
ui->Print("\nInstall from ADB complete (status: %d).\n", status);
if (sideload_auto_reboot) {
ui->Print("Rebooting automatically.\n");
}
} else if (!just_exit) {
status = INSTALL_NONE; // No command specified
ui->SetBackground(RecoveryUI::NO_COMMAND);
// http://b/17489952
// If this is an eng or userdebug build, automatically turn on the
// text display if no command is specified.
if (is_ro_debuggable()) {
ui->ShowText(true);
}
}
if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {
copy_logs();
ui->SetBackground(RecoveryUI::ERROR);
}
Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) {
Device::BuiltinAction temp = prompt_and_wait(device, status);
if (temp != Device::NO_ACTION) {
after = temp;
}
}
// Save logs and clean up before rebooting or shutting down.
finish_recovery(send_intent);
// MStar Android Patch Begin
char reload_env_flag = (char)(0x00);
if (get_reload_env_enable()) {
reload_env_flag = reload_env_flag | BOOT_STATUS_ACTION_RELOADENV_BIT;
}
if (get_reload_panel_enable()) {
reload_env_flag = reload_env_flag | BOOT_STATUS_ACTION_RELOADPANEL_BIT;
}
if ((int)(reload_env_flag) > 0) {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
strlcpy(boot.command, BOOT_STATUS_CUSTOMER_ACTIONS_STR, sizeof(boot.command));
boot.status[0] = reload_env_flag;
set_bootloader_message(&boot);
}
// MStar Android Patch End
switch (after) {
case Device::SHUTDOWN:
ui->Print("Shutting down...\n");
property_set(ANDROID_RB_PROPERTY, "shutdown,");
break;
case Device::REBOOT_BOOTLOADER:
ui->Print("Rebooting to bootloader...\n");
property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
break;
default:
ui->Print("Rebooting...\n");
property_set(ANDROID_RB_PROPERTY, "reboot,");
break;
}
sleep(5); // should reboot before this finishes
return EXIT_SUCCESS;
}
先获取参数 get_args(&argc, &argv); ,没有获取到参数将展示用户菜单。
static void
get_args(int *argc, char ***argv) {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
stage = strndup(boot.stage, sizeof(boot.stage));
if (boot.command[0] != 0 && boot.command[0] != 255) {
LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
}
if (boot.status[0] != 0 && boot.status[0] != 255) {
LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
}
// --- if arguments weren't supplied, look in the bootloader control block
if (*argc <= 1) {
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
const char *arg = strtok(boot.recovery, "\n");
if (arg != NULL && !strcmp(arg, "recovery")) {
*argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
(*argv)[0] = strdup(arg);
for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
if ((arg = strtok(NULL, "\n")) == NULL) break;
(*argv)[*argc] = strdup(arg);
}
LOGI("Got arguments from boot message\n");
} else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
}
}
// --- if that doesn't work, try the command file
if (*argc <= 1) {
FILE *fp = fopen_path(COMMAND_FILE, "r");
if (fp != NULL) {
char *token;
char *argv0 = (*argv)[0];
*argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
(*argv)[0] = argv0; // use the same program name
char buf[MAX_ARG_LENGTH];
for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
if (!fgets(buf, sizeof(buf), fp)) break;
token = strtok(buf, "\r\n");
if (token != NULL) {
(*argv)[*argc] = strdup(token); // Strip newline.
} else {
--*argc;
}
}
check_and_fclose(fp, COMMAND_FILE);
LOGI("Got arguments from %s\n", COMMAND_FILE);
}
}
// --> write the arguments we have back into the bootloader control block
// always boot into recovery after this (until finish_recovery() is called)
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
int i;
for (i = 1; i < *argc; ++i) {
strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
set_bootloader_message(&boot);
}
get_bootloader_message(&boot); 获取misc分区,
int get_bootloader_message(struct bootloader_message *out) {
Volume* v = volume_for_path("/misc");
if (v == NULL) {
LOGE("Cannot load volume /misc!\n");
return -1;
}
if (strcmp(v->fs_type, "mtd") == 0) {
return get_bootloader_message_mtd(out, v);
} else if (strcmp(v->fs_type, "emmc") == 0) {
return get_bootloader_message_block(out, v);
}
LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
return -1;
}
此处将获取到I:Boot command: boot-recovery,之前在关机流程中值发现有擦除/misc分区数据,一直没有找到写/misc分区数据的代码,但是此处确实获取到了/misc分区里boot-recovery
get_args(int *argc, char ***argv) 在/misc里没有获取到参数的话 check_and_fclose(fp, COMMAND_FILE);
static const char *COMMAND_FILE = "/cache/recovery/command";
将从/cache/recovery/command读取参数,然后会将此参数重新写入/misc,set_bootloader_message(&boot);防止中途断电,重新开机将重新执行。
/cache/recovery/command参数如下:
#cat command
--wipe_data
--reason=MasterClearConfirm
--locale=zh_CN
经过参数判断,将执行should_wipe_data = tru
if (!wipe_data(false, device)) {
status = INSTALL_ERROR;
}
// Return true on success.
static bool wipe_data(int should_confirm, Device* device) {
if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) {
return false;
}
modified_flash = true;
ui->Print("\n-- Wiping data...\n");
bool success =
device->PreWipeData() &&
erase_volume("/data") &&
erase_volume("/cache") &&
device->PostWipeData();
// MStar Android Patch Begin
ui->Print("\n-- Resting tv database & reproduce rate...\n");
success = success &&
reset_tv_database() &&
reset_reproduce_rate();
// MStar Android Patch End
ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
return success;
}
擦除完毕后执行:
// Save logs and clean up before rebooting or shutting down.
finish_recovery(send_intent);
// 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 many times as you like.
static void
finish_recovery(const char *send_intent) {
// By this point, we're ready to return to the main system...
if (send_intent != NULL) {
FILE *fp = fopen_path(INTENT_FILE, "w");
if (fp == NULL) {
LOGE("Can't open %s\n", INTENT_FILE);
} else {
fputs(send_intent, fp);
check_and_fclose(fp, INTENT_FILE);
}
}
// Save the locale to cache, so if recovery is next started up
// without a --locale argument (eg, directly from the bootloader)
// it will use the last-known locale.
if (locale != NULL) {
LOGI("Saving locale \"%s\"\n", locale);
FILE* fp = fopen_path(LOCALE_FILE, "w");
fwrite(locale, 1, strlen(locale), fp);
fflush(fp);
fsync(fileno(fp));
check_and_fclose(fp, LOCALE_FILE);
}
copy_logs();
// Reset to normal system boot so recovery won't cycle indefinitely.
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
set_bootloader_message(&boot);
// 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);
}
ensure_path_unmounted(CACHE_ROOT);
sync(); // For good measure.
}