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开始启动主系统