入口函数: main    (sbin/recovery)
load_volume_table();    //创建分区表
ensure_path_mounted(LAST_LOG_FILE);   //挂载cache分区
ui->Init();
     ev_init(input_callback, NULL);        //初始化input_event事件
    pthread_create(&input_t, NULL, input_thread, NULL);    //监听input_event事件
if (show_text) ui->ShowText(true);      //如果show_text不为真,则recovery界面不能显示文字
if (update_package) {   //如果不为null,获取升级包的位置
   if (strncmp(update_package, "CACHE:", 6) == 0) { 
int status = INSTALL_SUCCESS;       //初始化status的默认状态为成功,如果后面的操作失败,重新更改status的状态
if (update_package != NULL) {
            struct bootloader_message boot;
            memset(&boot, 0, sizeof(boot));
            strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
            strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
            strlcat(boot.recovery, "--update_package=", sizeof(boot.recovery));
            strlcat(boot.recovery, update_package, sizeof(boot.recovery));
            if (stage) {
                snprintf(boot.stage, sizeof(boot.stage), "%s", stage);
            }
            set_bootloader_message(&boot);    //往misc分区写指令,表明当前的状态,万一升级一半突然掉电,重新启动还可以重新进入升级模式
            sync();

            status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);
            if (status == INSTALL_SUCCESS) {        //升级成功的话,往data分区写个文件表明当前状态。
                update_modem_property();
            }
            prompt_error_message(status);       //在屏幕打印安装信息
        if (status != INSTALL_SUCCESS) {
            ui->Print("Installation aborted.\n");

            // If this is an eng or userdebug build, then automatically
            // turn the text display on if the script fails so the error
            // message is visible.
            char buffer[PROPERTY_VALUE_MAX+1];
            property_get("ro.build.fingerprint", buffer, "");
            if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) {
                ui->ShowText(true);                     //为true后屏幕才能显示文字
            }
        }
else if (wipe_data) {           //清空data分区
    ........
else if (wipe_cache){        //清空cache分区
    .......
else if (fota_delta_path) {        //ota升级
    ......
else if (restore_data_path) {    //备份用户数据到sd卡
    ......
else if (!just_exit) {       //如果没发来什mecommand命令,执行这个else
    status = INSTALL_NONE;  // No command specified             
     ui->SetBackground(RecoveryUI::NO_COMMAND);
if (update_package    
    write_result_file(MOTA_RESULT_FILE, status);
    remove_mota_file(update_package);    //删除本地升级包
    remove_mota_file(DEFAULT_MOTA_FILE);     //删除默认升级包   
if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {       //如果升级失败或者没有什么操作命令或者之前ui->ShowText设置为true,进入prompt_and_wait
        Device::BuiltinAction temp = prompt_and_wait(device, status);    //进入用户手动操作界面,等待键盘按键事件
        if (temp != Device::NO_ACTION) after = temp;
    }
if (update_package != NULL) {      //如果升级成功,才会接下来跑到这里,不然会阻塞在之前的prompt_and_wait函数。
    perform_update = 1;
}
finish_recovery(send_intent);        //关键函数,主要是擦除 misc分区,拷贝log到cache分区。如果recovery中没执行过这个函数,开机检测misc有
                                                      //进入recovery的命令,就会再次进入recovery,并执行相关操作
switch (after) {                                   //最后根据after的值进行退出recovery的操作
        case Device::SHUTDOWN:
                .......
        case Device::REBOOT_BOOTLOADER:
                .......
        default:                                 //reboot
                .......
    }
    sleep(5); // should reboot before this finishes
    return EXIT_SUCCESS;

调试过程关键点:
1.cat /etc/recovery.fstab //mtk并没有使用这个配置文件,而是在recovery中写死
boot /boot emmc defaults defaults
/dev/block/mmcblk0p2 /cache ext4 defaults defaults
/dev/block/mmcblk0p3 /data ext4 defaults defaults
misc /misc emmc defaults defaults
recovery /recovery emmc defaults defaults
/dev/block/mmcblk0p4 /sdcard vfat defaults defaults
/dev/block/mmcblk0p6 /system ext4 defaults defaults
/dev/block/mmcblk0p12 /custom ext4 defaults defaults

2.当时所用代码中recovery写死的分区表为 //可以通过/tmp/recovery.log查看
0 /tmp ramdisk (null) (null) 0
1 /boot emmc /dev/block/platform/mtk-msdc.0/by-name/boot (null) 0
2 /cache ext4 /dev/block/platform/mtk-msdc.0/by-name/cache (null) 0
3 /data ext4 /dev/block/platform/mtk-msdc.0/by-name/userdata (null) 0
4 /misc emmc /dev/block/platform/mtk-msdc.0/by-name/para (null) 0
5 /recovery emmc /dev/block/platform/mtk-msdc.0/by-name/recovery (null) 0
6 /sdcard vfat /dev/block/mmcblk1p1 /dev/block/mmcblk1 0
7 /system ext4 /dev/block/platform/mtk-msdc.0/by-name/system (null) 0
8 /nvdata ext4 /dev/block/platform/mtk-msdc.0/by-name/nvdata (null) 0
这个写死的分区表不是每个都会mount上,在recover的main函数中,调用ensure_path_mounted来实现实际上需要挂载的分区,但是如果要挂载的分区不在这个分区表,调用ensure_path_mounted函数会报错

3.recovery实现adb shell和串口控制台的最简单的方式
a.mount安卓的system分区到recovery上,这样就可以实现adb shell;
b.在recovery/etc/init.rc实现这样的语句
service console /system/bin/sh (实现方式是照抄kernel的init.rc文件,recovery的kernel跟android的kernel的差别其实就是文件系统的差别,用的内核是一样的)
console
disabled
user shell
group shell log
seclabel u:r:shell:s0
start console
这样就可以实现串口控制台

4.关键函数
set_bootloader_message //设置bootloader的启动信息,在finish_recovery函数调用这个函数,重新设置boot.mode为正常启动模式而不是recovery模式set_bootloader_message,其实就是往misc分区写东西

5.对于recovery这个进程的selinux策略一律放行。修改external/sepolicy/recovery.te 里面 recovery_only(`这一句后面加一条 permissive recovery; (