Android OTA 升级之四:进入根文件系统
作者: 宋立新
Email:zjujoe@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 使用了一个简单的基于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;