前言 从bootloader 进入Recovery 模式后,首先也是运行Linux内核,该内核跟普通模式没有区别(减轻了BSP开发者的任务)。区别从执行文件系统开始。 Recovery 模式的细节就隐藏在其根文件系统中。
======================================================================== 进入Recovery 根文件系统都干些啥。 init.rc 和正常启动一样,内核进入文件系统会执行/init, init 的配置文件就是 /init.rc, 前面文章讲过,这个文件来自:bootable/recovery/etc/init.rc,下面,我们看看它的内容。 1 2 on init 3 export PATH /sbin 4 export ANDROID_ROOT /system 5 export ANDROID_DATA /data 6 export EXTERNAL_STORAGE /sdcard 7 8 symlink /system/etc /etc 9 10 mkdir /sdcard 11 mkdir /system 12 mkdir /data 13 mkdir /cache 14 mount /tmp /tmp tmpfs 15 16 on boot 17 18 ifup lo 19 hostname localhost 20 domainname localdomain 21 22 class_start default 23 24 25 service recovery /sbin/recovery 26 27 service adbd /sbin/adbd recovery 28 disabled 29 30 on property:persist.service.adb.enable=1 31 start adbd 32 33 on property:persist.service.adb.enable=0 34 stop adbd 可以看到,它很非常简单: 1) 设置几个环境变量。备用。 2) 建立 etc 链接。 3) 造几个目录。备用。 4) Mount /tmp 目录为内存文件系统 tmpfs,后面会用到。 5) Trival 设置,不必关心。 6) 启动 recovery主程序。 7) 如果是eng模式(此时persist.service.adb.enable),启动adb 当然,init主程序还会装载属性配置文件 /default.prop, 它包含了很多系统属性设置,比如,ro.build.*, 等等。 很明显,这里最重要的就是recovery主程序,下面,我们分析它。 先看一段注释
Recovery.c 中,作者写了一段注释,对我们理解recovery的实现很有帮助,下面看一下:(我就不翻译了) 89 /* 90 * The recovery tool communicates with the main system through /cache files. 91 * /cache/recovery/command - INPUT - command line for tool, one arg per line 92 * /cache/recovery/log - OUTPUT - combined log file from recovery run(s) 93 * /cache/recovery/intent - OUTPUT - intent that was passed in 94 * 95 * The arguments which may be supplied in the recovery.command file: 96 * --send_intent=anystring - write the text out to recovery.intent 97 * --update_package=root:path - verify install an OTA package file 98 * --wipe_data - erase user data (and cache), then reboot 99 * --wipe_cache - wipe cache (but not user data), then reboot 100 * --set_encrypted_filesystem=on|off - enables / diasables encrypted fs 101 * 102 * After completing, we remove /cache/recovery/command and reboot. 103 * Arguments may also be supplied in the bootloader control block (BCB). 104 * These important scenarios must be safely restartable at any point: 105 * 106 * FACTORY RESET 107 * 1. user selects "factory reset" 108 * 2. main system writes "--wipe_data" to /cache/recovery/command 109 * 3. main system reboots into recovery 110 * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data" 111 * -- after this, rebooting will restart the erase -- 112 * 5. erase_root() reformats /data 113 * 6. erase_root() reformats /cache 114 * 7. finish_recovery() erases BCB 115 * -- after this, rebooting will restart the main system -- 116 * 8. main() calls reboot() to boot main system 117 * 118 * OTA INSTALL 119 * 1. main system downloads OTA package to /cache/some-filename.zip 120 * 2. main system writes "--update_package=CACHE:some-filename.zip" 121 * 3. main system reboots into recovery 122 * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..." 123 * -- after this, rebooting will attempt to reinstall the update -- 124 * 5. install_package() attempts to install the update 125 * NOTE: the package install must itself be restartable from any point 126 * 6. finish_recovery() erases BCB 127 * -- after this, rebooting will (try to) restart the main system -- 128 * 7. ** if install failed ** 129 * 7a. prompt_and_wait() shows an error icon and waits for the user 130 * 7b; the user reboots (pulling the battery, etc) into the main system 131 * 8. main() calls maybe_install_firmware_update() 132 * ** if the update contained radio/hboot firmware **: 133 * 8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache" 134 * -- after this, rebooting will reformat cache & restart main system -- 135 * 8b. m_i_f_u() writes firmware image into raw cache partition 136 * 8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache" 137 * -- after this, rebooting will attempt to reinstall firmware -- 138 * 8d. bootloader tries to flash firmware 139 * 8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache") 140 * -- after this, rebooting will reformat cache & restart main system -- 141 * 8f. erase_root() reformats /cache 142 * 8g. finish_recovery() erases BCB 143 * -- after this, rebooting will (try to) restart the main system -- 144 * 9. main() calls reboot() to boot main system 145 * 146 * ENCRYPTED FILE SYSTEMS ENABLE/DISABLE 147 * 1. user selects "enable encrypted file systems" 148 * 2. main system writes "--set_encrypted_filesystem=on|off" to 149 * /cache/recovery/command 150 * 3. main system reboots into recovery 151 * 4. get_args() writes BCB with "boot-recovery" and 152 * "--set_encrypted_filesystems=on|off" 153 * -- after this, rebooting will restart the transition -- 154 * 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data 155 * Settings include: property to specify the Encrypted FS istatus and 156 * FS encryption key if enabled (not yet implemented) 157 * 6. erase_root() reformats /data 158 * 7. erase_root() reformats /cache 159 * 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data 160 * Settings include: property to specify the Encrypted FS status and 161 * FS encryption key if enabled (not yet implemented) 162 * 9. finish_recovery() erases BCB 163 * -- after this, rebooting will restart the main system -- 164 * 10. main() calls reboot() to boot main system 165 */ recovery 主程序
559 int 560 main(int argc, char **argv) 561 { 562 time_t start = time(NULL); 563 564 // If these fail, there's not really anywhere to complain... 565 freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL); 566 freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL); 567 fprintf(stderr, "Starting recovery on %s", ctime(&start)); 568 将标准输出和标准错误输出重定位到"/tmp/recovery.log",如果是eng模式,就可以通过adb pull /tmp/recovery.log, 看到当前的log信息,这为我们提供了有效的调试手段。后面还会看到,recovery模式运行完毕后,会将其拷贝到cache分区,以便后续分析。 569 ui_init(); Recovery 使用了一个简单的基于framebuffer的ui系统,叫miniui,这里,进行了简单的初始化(主要是图形部分以及事件部分),并启动了一个 event 线程用于响应用户按键。 570 get_args(&argc, &argv); 从misc 分区以及 CACHE:recovery/command 文件中读入参数,写入到argc, argv ,并且,如果有必要,回写入misc分区。这样,如果recovery没有操作成功(比如,升级还没有结束,就拔电池),系统会一直进入recovery模式。提醒用户继续升级,直到成功。 572 int previous_runs = 0; 573 const char *send_intent = NULL; 574 const char *update_package = NULL; 575 int wipe_data = 0, wipe_cache = 0; 576 577 int arg; 578 while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) { 579 switch (arg) { 580 case 'p': previous_runs = atoi(optarg); break; 581 case 's': send_intent = optarg; break; 582 case 'u': update_package = optarg; break; 583 case 'w': wipe_data = wipe_cache = 1; break; 584 case 'c': wipe_cache = 1; break; 585 case '?': 586 LOGE("Invalid command argument\n"); 587 continue; 588 } 589 } 590 解析参数,p: previous_runs没有用到,其它含义见前面注释。 591 device_recovery_start(); 这个函数没干什么。看名字,它給设备制造商提供了一个调用机会,可写入设备相关初始化代码。 592 593 fprintf(stderr, "Command:"); 594 for (arg = 0; arg < argc; arg++) { 595 fprintf(stderr, " \"%s\"", argv[arg]); 596 } 597 fprintf(stderr, "\n\n"); 598 打印出命令,比如,正常启动进入recovery模式,会打印:Command: "/sbin/recovery" 599 property_list(print_property, NULL); 600 fprintf(stderr, "\n"); 601 打印出所有的系统属性(from default.prop)到log文件。 602 int status = INSTALL_SUCCESS; 603 604 if (update_package != NULL) { 605 status = install_package(update_package); 606 if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); 607 } else if (wipe_data) { 608 if (device_wipe_data()) status = INSTALL_ERROR; 609 if (erase_root("DATA:")) status = INSTALL_ERROR; 610 if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR; 611 if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n"); 612 } else if (wipe_cache) { 613 if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR; 614 if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n"); 615 } else { 616 status = INSTALL_ERROR; // No command specified 617 } 根据用户提供参数,调用各项功能,比如,安装一个升级包,擦除cache分区, 擦除user data分区,install_package比较复杂,后面专门分析,其它都很简单。忽略。 618 619 if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR); 622 if (status != INSTALL_SUCCESS) prompt_and_wait(); 如果前面已经做了某项操作并且成功,则进入重启流程。否则,等待用户选择具体操作。 而用户可选操作为: reboot, 安装update.zip,除cache分区, 擦除user data分区,如前所述,只有安装package 比较复杂,其它简单。 623 624 // Otherwise, get ready to boot the main system... 625 finish_recovery(send_intent); 它的功能如下: 1)将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command 2)将 /tmp/recovery.log 复制到 "CACHE:recovery/log"; 3)清空 misc 分区,这样重启就不会进入recovery模式 4)删除command 文件:CACHE:recovery/command; 626 ui_print("Rebooting...\n"); 627 sync(); 628 reboot(RB_AUTOBOOT); 629 return EXIT_SUCCESS; 630 } 重启。
======================================================================= 下面我们分析核心函数 install_package install_package
289 int 290 install_package(const char *root_path) 291 { 292 ui_set_background(BACKGROUND_ICON_INSTALLING); 294 ui_print("Finding update package...\n"); 295 LOGI("Finding update package...\n"); 296 ui_show_indeterminate_progress(); 297 LOGI("Update location: %s\n", root_path); 298 更新 UI 显示 299 if (ensure_root_path_mounted(root_path) != 0) { 300 LOGE("Can't mount %s\n", root_path); 301 reset_mark_block(); 302 return INSTALL_CORRUPT; 303 } 304 确保升级包所在分区已经mount,通常为 cache 分区或者 SD 分区 305 char path[PATH_MAX] = ""; 306 if (translate_root_path(root_path, path, sizeof(path)) == NULL) { 307 LOGE("Bad path %s\n", root_path); 308 reset_mark_block(); 309 return INSTALL_CORRUPT; 310 } 将根分区转化为具体分区信息.这些信息来自:全局数组:g_roots 313 ui_print("Opening update package...\n"); 314 LOGI("Opening update package...\n"); 315 LOGI("Update file path: %s\n", path); 316 317 int numKeys; 318 RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); 319 if (loadedKeys == NULL) { 320 LOGE("Failed to load keys\n"); 321 reset_mark_block(); 322 return INSTALL_CORRUPT; 323 } 324 LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); 从/res/keys中装载公钥。 326 // Give verification half the progress bar... 328 ui_print("Verifying update package...\n"); 329 LOGI("Verifying update package...\n"); 330 ui_show_progress( 331 VERIFICATION_PROGRESS_FRACTION, 332 VERIFICATION_PROGRESS_TIME); 333 334 int err; 335 err = verify_file(path, loadedKeys, numKeys); 336 free(loadedKeys); 337 LOGI("verify_file returned %d\n", err); 338 if (err != VERIFY_SUCCESS) { 339 LOGE("signature verification failed\n"); 340 reset_mark_block(); 341 return INSTALL_CORRUPT; 342 } 根据公钥验证升级包verify_file的注释说的很明白: // Look for an RSA signature embedded in the .ZIP file comment given // the path to the zip. Verify it matches one of the given public // keys. 344 /* Try to open the package. 345 */ 346 ZipArchive zip; 347 err = mzOpenZipArchive(path, &zip); 348 if (err != 0) { 349 LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad"); 350 reset_mark_block(); 351 return INSTALL_CORRUPT; 352 } 打开升级包,将相关信息存到ZuoArchive数据机构中,便于后面处理。 354 /* Verify and install the contents of the package. 355 */ 356 int status = handle_update_package(path, &zip); 处理函数,我们后面继续分析。 357 mzCloseZipArchive(&zip); 358 return status; 359 } 关闭zip包,结束处理。 handle_update_package
204 static int 205 handle_update_package(const char *path, ZipArchive *zip) 206 { 207 // Update should take the rest of the progress bar. 208 ui_print("Installing update...\n"); 209 210 int result = try_update_binary(path, zip); 211 register_package_root(NULL, NULL); // Unregister package root 212 return result; 213 } 它主要调用函数try_update_binary完成功能。 try_update_binary
84 // If the package contains an update binary, extract it and run it. 85 static int 86 try_update_binary(const char *path, ZipArchive *zip) { 87 const ZipEntry* binary_entry = 88 mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); 89 if (binary_entry == NULL) { 90 return INSTALL_CORRUPT; 91 } 92 93 char* binary = "/tmp/update_binary"; 94 unlink(binary); 95 int fd = creat(binary, 0755); 96 if (fd < 0) { 97 LOGE("Can't make %s\n", binary); 98 return 1; 99 } 100 bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); 101 close(fd); 102 103 if (!ok) { 104 LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); 105 return 1; 106 } 将升级包内文件META-INF/com/google/android/update-binary 复制为/tmp/update_binary 108 int pipefd[2]; 109 pipe(pipefd); 110 111 // When executing the update binary contained in the package, the 112 // arguments passed are: 113 // 114 // - the version number for this interface 115 // 116 // - an fd to which the program can write in order to update the 117 // progress bar. The program can write single-line commands: 118 // 119 // progress <frac> <secs> 120 // fill up the next <frac> part of of the progress bar 121 // over <secs> seconds. If <secs> is zero, use 122 // set_progress commands to manually control the 123 // progress of this segment of the bar 124 // 125 // set_progress <frac> 126 // <frac> should be between 0.0 and 1.0; sets the 127 // progress bar within the segment defined by the most 128 // recent progress command. 129 // 130 // firmware <"hboot"|"radio"> <filename> 131 // arrange to install the contents of <filename> in the 132 // given partition on reboot. 133 // 134 // (API v2: <filename> may start with "PACKAGE:" to 135 // indicate taking a file from the OTA package.) 136 // 137 // (API v3: this command no longer exists.) 138 // 139 // ui_print <string> 140 // display <string> on the screen. 141 // 142 // - the name of the package zip file. 143 // 144 注意看这段注释,它解释了以下代码的行为。结合代码,可知: 1) 将会创建新的进程,执行:/tmp/update_binary 2) 同时,会给该进程传入一些参数,其中最重要的就是一个管道fd,供新进程与原进程通信。 3) 新进程诞生后,原进程就变成了一个服务进程,它提供若干UI更新服务: a) progress b) set_progress c) ui_print 这样,新进程就可以通过老进程的UI系统完成显示任务。而其他功能就靠它自己了。 145 char** args = malloc(sizeof(char*) * 5); 146 args[0] = binary; 147 args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk 148 args[2] = malloc(10); 149 sprintf(args[2], "%d", pipefd[1]); 150 args[3] = (char*)path; 151 args[4] = NULL; 152 153 pid_t pid = fork(); 154 if (pid == 0) { 155 close(pipefd[0]); 156 execv(binary, args); 157 fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno)); 158 _exit(-1); 159 } 160 close(pipefd[1]); 161 162 char buffer[1024]; 163 FILE* from_child = fdopen(pipefd[0], "r"); 164 while (fgets(buffer, sizeof(buffer), from_child) != NULL) { 165 char* command = strtok(buffer, " \n"); 166 if (command == NULL) { 167 continue; 168 } else if (strcmp(command, "progress") == 0) { 169 char* fraction_s = strtok(NULL, " \n"); 170 char* seconds_s = strtok(NULL, " \n"); 171 172 float fraction = strtof(fraction_s, NULL); 173 int seconds = strtol(seconds_s, NULL, 10); 174 175 ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), 176 seconds); 177 } else if (strcmp(command, "set_progress") == 0) { 178 char* fraction_s = strtok(NULL, " \n"); 179 float fraction = strtof(fraction_s, NULL); 180 ui_set_progress(fraction); 181 } else if (strcmp(command, "ui_print") == 0) { 182 char* str = strtok(NULL, "\n"); 183 if (str) { 184 ui_print(str); 185 } else { 186 ui_print("\n"); 187 } 188 } else { 189 LOGE("unknown command [%s]\n", command); 190 } 191 } 192 fclose(from_child); 193 194 int status; 195 waitpid(pid, &status, 0); 196 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 197 LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); 198 return INSTALL_ERROR; 199 } 200 201 return INSTALL_SUCCESS; 202 } 这样,我们又回到了升级包中的文件:META-INF/com/google/android/update-binary,下回继续研究。
======================================================================== 转载: http://blog.csdn.net/zjujoe/archive/2011/03/13/6245988.aspx |