Chinaunix首页 | 论坛 | 博客
  • 博客访问: 409250
  • 博文数量: 101
  • 博客积分: 2247
  • 博客等级: 大尉
  • 技术积分: 979
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-15 22:39
文章分类

全部博文(101)

文章存档

2012年(1)

2011年(100)

分类: 嵌入式

2011-06-19 15:28:35

前言
    从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
120     //            fill up the next part of of the progress bar
121     //            over seconds.  If is zero, use
122     //            set_progress commands to manually control the
123     //            progress of this segment of the bar
124     //
125     //        set_progress
126     //             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">
131     //            arrange to install the contents of in the
132     //            given partition on reboot.
133     //
134     //            (API v2: 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
140     //            display 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
阅读(1427) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~