Recovery流程分析之恢复出厂设置分析:
/bootable/recovery/目录下:
一、首先recovery.cpp文件分析,main()函数
#define LAST_LOG_FILE "/cache/recovery/last_log"
#if defined(CACHE_MERGE_SUPPORT)
static const char *DATA_CACHE_ROOT = "/data/.cache";
#endif
static const char *CACHE_LOG_DIR = "/cache/recovery";
static const char *COMMAND_FILE = "/cache/recovery/command";
static const char *INTENT_FILE = "/cache/recovery/intent";
static const char *LOG_FILE = "/cache/recovery/log";
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
static const char *LOCALE_FILE = "/cache/recovery/last_locale";
static const char *CACHE_ROOT = "/cache";
static const char *SDCARD_ROOT = "/sdcard";
#if defined(SUPPORT_SDCARD2) && !defined(MTK_SHARED_SDCARD) //wschen 2012-11-15
static const char *SDCARD2_ROOT = "/sdcard2";
#endif
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
#define KLOG_DEFAULT_LEN (64 * 1024)
#if 1 //wschen 2012-07-10
static const char *DEFAULT_MOTA_FILE = "/data/data/com.mediatek.GoogleOta/files/update.zip";
static const char *DEFAULT_FOTA_FOLDER = "/cache/data/com.mediatek.dm/files";
static const char *MOTA_RESULT_FILE = "/data/data/com.mediatek.systemupdate/files/updateResult";
static const char *FOTA_RESULT_FILE = "/data/data/com.mediatek.dm/files/updateResult";
#endif
1、这上面的宏,字符的指针宏;没有什么好说的;下面分析main()函数
int
main(int argc, char **argv) {
time_t start = time(NULL);
redirect_stdio(TEMPORARY_LOG_FILE);
static void redirect_stdio(const char* filename) {
// If these fail, there's not really anywhere to complain...
freopen(filename, "a", stdout); setbuf(stdout, NULL);
freopen(filename, "a", stderr); setbuf(stderr, NULL);
}
文件中,如果是eng的版本,通过adb pull /tmp/recovery.log, 查看log日志。
// If this binary is started with the single argument "--adbd",
// instead of being the normal recovery binary, it turns into kind
// of a stripped-down version of adbd that only supports the
// 'sideload' command. Note this must be a real argument, not
// anything in the command file or bootloader control block; the
// only way recovery should be run with this argument is when it
// starts a copy of itself from the apply_from_adb() function.
if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
adb_main();
return 0;
}
3、继续main()函数,遇到一个if判断,首先是判断main函数的第一个参数:argc是否等于2,且比较argv[1]与“--adbd”是否相关;
如果都满足条件,则直接进入adb_main()函数中,该方法在bootable/recovery/minadbd/adb.c中,普及一下strcmp这个函数:
在c++ 中,strcmp(s1,s2):第一:当s1<s2时,返回为负数 注意不是-1;第二、当s1==s2时,返回值等于0;第三、当s1>s2时,返回正数 注意不是1;
即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止;如:"A"<"B" "a">"A" "computer">"compare";
特别注意:strcmp(const char *s1,const char * s2)这里面只能比较字符串,不能比较数字等其他形式的参数
printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
4、main函数中,将本身的pid和系统时间打印出来;
load_volume_table();
#if defined(CACHE_MERGE_SUPPORT)
// create symlink from CACHE_ROOT to DATA_CACHE_ROOT
if (ensure_path_mounted(DATA_CACHE_ROOT) == 0) {
if (mkdir(DATA_CACHE_ROOT, 0770) != 0) {
if (errno != EEXIST) {
LOGE("Can't mkdir %s (%s)\n", DATA_CACHE_ROOT, strerror(errno));
return NULL;
}
}
rmdir(CACHE_ROOT); // created in init.rc
if (symlink(DATA_CACHE_ROOT, CACHE_ROOT)) {
if (errno != EEXIST) {
LOGE("create symlink from %s to %s failed(%s)\n",
DATA_CACHE_ROOT, CACHE_ROOT, strerror(errno));
return NULL;
}
}
} else {
LOGE("mount %s error\n", DATA_CACHE_ROOT);
}
#endif
5、加载分区表;创建链接,从CACHE_ROOT到DATA_CACHE_ROOT,即:"/cache"-> "/data/.cache"
ensure_path_mounted(LAST_LOG_FILE);
rotate_last_logs(KEEP_LOG_COUNT);
get_args(&argc, &argv);
const char *send_intent = NULL;
const char *update_package = NULL;
int wipe_data = 0, wipe_cache = 0, show_text = 0;
bool just_exit = false;
bool shutdown_after = false;
6、挂载LAST_LOG_FILE文件,即"/cache/recovery/last_log";保留10个last_log日志文件;获取参数;声明变量
#if 1 //wschen 2012-07-10
#ifdef SUPPORT_FOTA
const char *fota_delta_path = NULL;
#endif
#ifdef SUPPORT_DATA_BACKUP_RESTORE //wschen 2011-05-16
const char *restore_data_path = NULL;
#endif //SUPPORT_DATA_BACKUP_RESTORE
#endif
7、判断是否支持FOTA升级,和是否支持数据备份
8、从misc 分区以及 CACHE:recovery/command 文件中读入参数,写入到argc, argv (get_bootloader_message) 并有可能写回 misc 分区(set_bootloader_message)
int arg;
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
switch (arg) {
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': show_text = 1; break;
case 'x': just_exit = true; break;
case 'l': locale = optarg; break;
#if 1 //wschen 2012-07-10
#ifdef SUPPORT_FOTA
case 'f': fota_delta_path = optarg; break;
#endif
#ifdef SUPPORT_DATA_BACKUP_RESTORE //wschen 2011-05-16
case 'd': restore_data_path = optarg; break;
#endif //SUPPORT_DATA_BACKUP_RESTORE
#endif
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;
}
}
9、以上是开始解析具体参数:
if (locale == NULL) {
load_locale_from_cache();
}
printf("locale is [%s]\n", locale);
printf("stage is [%s]\n", stage);
printf("reason is [%s]\n", reason);
Device* device = make_device();
ui = device->GetUI();
gCurrentUI = ui;
ui->SetLocale(locale);
ui->Init();
int st_cur, st_max;
if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) {
ui->SetStage(st_cur, st_max);
}
ui->SetBackground(RecoveryUI::NONE);
if (show_text) ui->ShowText(true);
struct selinux_opt seopts[] = {
{ SELABEL_OPT_PATH, "/file_contexts" }
};
sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
if (!sehandle) {
ui->Print("Warning: No file_contexts\n");
}
device->StartRecovery();
printf("Command:");
for (arg = 0; arg < argc; arg++) {
printf(" \"%s\"", argv[arg]);
}
printf("\n");
/* ----------------------------- */
/* SECURE BOOT INIT */
/* ----------------------------- */
#ifdef SUPPORT_SBOOT_UPDATE
sec_init(false);
#endif
10、接下来就是具体执行显示ui的操作了信息了
if (update_package) {
// For backwards compatibility on the cache partition only, if
// we're given an old 'root' path "CACHE:foo", change it to
// "/cache/foo".
#ifdef SUPPORT_DATA_BACKUP_RESTORE
set_force_upgrade();
#endif
if (strncmp(update_package, "CACHE:", 6) == 0) {
int len = strlen(update_package) + 10;
char* modified_path = (char*)malloc(len);
strlcpy(modified_path, "/cache/", len);
strlcat(modified_path, update_package+6, len);
printf("(replacing path \"%s\" with \"%s\")\n",
update_package, modified_path);
update_package = modified_path;
}
#ifdef MTK_SYS_FW_UPGRADE
else if (strncmp(update_package, "/storage/emulated/0/", 20) == 0) {
int len = strlen(update_package) + 13;
char* modified_path = (char*)malloc(len);
strlcpy(modified_path, "/sdcard/", len);
strlcat(modified_path, update_package+20, len);
printf("(replacing path \"%s\" with \"%s\")\n",
update_package, modified_path);
update_package = modified_path;
}else if (strncmp(update_package, "/storage/sdcard0/", 17) == 0) {
int len = strlen(update_package) + 13;
char* modified_path = (char*)malloc(len);
strlcpy(modified_path, "/sdcard/", len);
strlcat(modified_path, update_package+17, len);
printf("(replacing path \"%s\" with \"%s\")\n",
update_package, modified_path);
update_package = modified_path;
} else if (strncmp(update_package, "/storage/sdcard1/", 17) == 0) {
int len = strlen(update_package) + 13;
char* modified_path = (char*)malloc(len);
strlcpy(modified_path, "/sdcard/", len);
strlcat(modified_path, update_package+17, len);
printf("(replacing path \"%s\" with \"%s\")\n",
update_package, modified_path);
update_package = modified_path;
} else if (strncmp(update_package, "/mnt/sdcard/", 12) == 0) {
int len = strlen(update_package) + 12;
char* modified_path = (char*)malloc(len);
strlcpy(modified_path, "/sdcard/", len);
strlcat(modified_path, update_package+12, len);
printf("(replacing path \"%s\" with \"%s\")\n",
update_package, modified_path);
update_package = modified_path;
} else if (strncmp(update_package, "/mnt/sdcard2/", 13) == 0) {
int len = strlen(update_package) + 13;
char* modified_path = (char*)malloc(len);
strlcpy(modified_path, "/sdcard/", len);
strlcat(modified_path, update_package+13, len);
printf("(replacing path \"%s\" with \"%s\")\n",
update_package, modified_path);
update_package = modified_path;
}
#endif
}
11、有update_package有数据,说明是可以升级的;
printf("\n");
property_list(print_property, NULL);
property_get("ro.build.display.id", recovery_version, "");
printf("\n");
12、获取recovery的属性版本
int status = INSTALL_SUCCESS;
if (update_package != NULL) {
status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);
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->WipeData()) 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");
} else if (wipe_cache) {
if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n");
} else if (!just_exit) {
status = INSTALL_NONE; // No command specified
ui->SetBackground(RecoveryUI::NO_COMMAND);
}
if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
ui->SetBackground(RecoveryUI::ERROR);
}
if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
prompt_and_wait(device, status);
}
13、这里就开始干活了,install_package方法就是具体的安装升级包;wipe_data就是擦除/data分区的数据,wipe_cache擦除/
cache分区的数据;just_exit执行没有命令,最后显示根据状态显示成功和失败;不同时的显示;
14、prompt_and_wait方法;如果前面做的操作成功则进入重启流程,否则由用户操作,可选操作为: reboot, 安装update.zip,除cache分区, 擦除user data分区
// Save logs and clean up before rebooting or shutting down.
finish_recovery(send_intent);
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;
}
15、最后的执行操作,finish_recovery方法,关机-重启bootloader或者重启操作;
四、对于上面的代码总结:
它的功能如下:
1、将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command
2、将 /tmp/recovery.log 复制到 "CACHE:recovery/log";
3、清空 misc 分区,这样重启就不会进入recovery模式
4、删除command 文件:CACHE:recovery/command;
最后重启机器
ui_print("Rebooting...\n");
android_reboot(ANDROID_RB_RESTART, 0, 0);
五、factory reset 重要代码实现方式操作步骤
* FACTORY RESET
* 1. user selects "factory reset"---用户选择恢复出厂设置选项
* 2. main system writes "--wipe_data" to /cache/recovery/command--应用将--wipe_data写到 /cache/recovery/command文件中
* 3. main system reboots into recovery---应用通过reboot进入recovery模式
* 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"---获取参数"boot-recovery" 和"--wipe_data"写入BCB中
* -- after this, rebooting will restart the erase -- 在获取参数之后,重启,将恢复擦除数据
* 5. erase_volume() reformats /data---擦除/data分区下的数据
* 6. erase_volume() reformats /cache---擦除/cache分区下的数据
* 7. finish_recovery() erases BCB ---擦除BCB中的参数
* -- after this, rebooting will restart the main system --重启恢复系统
* 8. main() calls reboot() to boot main system--主系统调用reboot开始启动主系统