recovery模式的代码存在于源码目录下的bootable目录中,主函数位于./bootable/recovery/recovery.c文件中。我们将主函数的代码复制如下,并对其中核心代码进行分析。
int main(int argc, char **argv) { time_t start = time(NULL); // If these fail, there's not really anywhere to complain... freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL); freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL); printf("Starting recovery on %s", ctime(&start)); device_ui_init(&ui_parameters); ui_init(); ui_set_background(BACKGROUND_ICON_INSTALLING); load_volume_table(); //加载系统分区表 get_args(&argc, &argv); //获取参数信息,包括需要执行的动作(升级、清除/data、清除cache等)及升级包路径 int previous_runs = 0; const char *send_intent = NULL; //通过finish_recovery函数传递到main system下的信息 const char *update_package = NULL; //升级包路径 int wipe_data = 0, wipe_cache = 0; int arg; while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) { switch (arg) { case 'p': previous_runs = atoi(optarg); break; 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': ui_show_text(1); break; case '?': LOGE("Invalid command argument\n"); continue; } } #ifdef HAVE_SELINUX struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } }; sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); if (!sehandle) { fprintf(stderr, "Warning: No file_contexts\n"); ui_print("Warning: No file_contexts\n"); } #endif device_recovery_start(); printf("Command:"); for (arg = 0; arg < argc; arg++) { printf(" \"%s\"", argv[arg]); } printf("\n"); 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". if (strncmp(update_package, "CACHE:", 6) == 0) { int len = strlen(update_package) + 10; char* modified_path = 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; } } printf("\n"); property_list(print_property, NULL); printf("\n"); int status = INSTALL_SUCCESS; if (update_package != NULL) { status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE); 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_wipe_data()) 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 { status = INSTALL_ERROR; // No command specified } if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR); if (status != INSTALL_SUCCESS || ui_text_visible()) { prompt_and_wait(); } // Otherwise, get ready to boot the main system... finish_recovery(send_intent); ui_print("Rebooting...\n"); android_reboot(ANDROID_RB_RESTART, 0, 0); return EXIT_SUCCESS; }
代码段1 ./bootable/recovery/recovery.c文件中的main函数,即系统进入到recovery模式执行的主流程
代码分析如下:
代码06-08:打开临时日志文件TEMPORARY_LOG_FILE(定义在recovery.c的开始部分),并写入信息。注意:此文件里的信息最终会被写入到日志文件LAST_LOG_FILE
中,LAST_LOG_FILE的定义如下:static const char *LAST_LOG_FILE = "/cache/recovery/last_log";这就意味着可以获得系统升级时详细的LOG信息,这对于
OTA项目的开发具有相当重要的作用;
代码10-12:UI相关的操作,未研究,略去;
代码 13:加载recovery模式下系统分区信息,由于需要对某些分区进行读写操作,故需此步。此分区信息以文件形式存在,路径为:./device/<生产厂商>/<产品型号>/
recovery.fstab;比如三星crespo4g系列此文件在源码下的全路径为:./device/samsung/crespo4g/recovery.fstab,其内容如下所示:
# mount point fstype device /sdcard vfat /dev/block/platform/s3c-sdhci.0/by-name/media /system ext4 /dev/block/platform/s3c-sdhci.0/by-name/system /cache yaffs2 cache /data ext4 /dev/block/platform/s3c-sdhci.0/by-name/userdata /misc mtd misc /boot mtd boot /recovery mtd recovery /bootloader mtd bootloader /radio mtd radio
代码段2 recovery.fstab示例
代码 14:调用get_args()获取由常规模式下传入到recovery模式下的参数,这些参数包括执行动作(系统升级、清空data分区、清空cache分区等)及升级包路径,获取来源为
BCB(结构体bootloader_message)或COMMAND_FILE(/cache/recovery/command),详细信息将在后文介绍;
代码21-34:解析get_args所获得的参数,如果是在进行OTA操作,我们将通过第26行获取升级包的路径update_package;
代码57-93中,系统将根据不同的指令进行不同的操作,包括升级、清除/data分区、清除/cache分区等。
代码57-70:对update_package的预处理,本人所遇到的情况是:在main system传入的路径为/mnt/sdcard/update.zip,而在recovery下需将其转换为/sdcard/update.zip,相
关的操作便在此处进行(当然本文使用的为android源码,并未加入自己改动的代码);
代码78-85:升级操作。其实核心代码只有一句,第79行调用install_package函数将升级包中的数据写入到系统相应分区中,我们将在后文中详细介绍此函数;
代码86-90:清空/data分区(貌似就是恢复出厂设置吧?我没详细研究,不敢妄言);
代码91-93:清空/cache分区;
代码99-101:当升级失败或者我们是手动进入recovery模式时,将调用prompt_and_wait(),显示recovery模式下的交互界面,我们在后文中再详细介绍;
代码 104: 调用finish_recovery函数清空BCB块(防止重复进入recovery模式)、将send_intent写入文件(传递到main system)、将日志写入到LAST_LOG_FILE中等;
代码 106: 系统重启。
以上即系统进入recovery模式下的执行流程,其中涉及到的关键步骤:如何获取参数(get_args()),系统升级(install_package())及交互界面的显示与操作(prompt_and_w-
ait())等将在下一篇文章中进行分析。