Chinaunix首页 | 论坛 | 博客
  • 博客访问: 353451
  • 博文数量: 107
  • 博客积分: 927
  • 博客等级: 大尉
  • 技术积分: 865
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-13 17:50
文章分类

全部博文(107)

文章存档

2014年(2)

2013年(13)

2012年(16)

2011年(76)

分类: Java

2011-12-24 15:06:27

Android OTA 升级之四:进入根文件系统

作者: 宋立新

Emailzjujoe@yahoo.com

前言

       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 使用了一个简单的基于framebufferui系统,叫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 *pathZipArchive *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(binaryargs);

157         fprintf(stderr"E:Can't run %s (%s)/n"binarystrerror(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"pathWEXITSTATUS(status));

198         return INSTALL_ERROR;

199     }

200

201     return INSTALL_SUCCESS;

202 }

阅读(1061) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~