一、简介
从bootloader 进入Recovery 模式后,首先也是运行Linux内核,该内核跟普通模式没有区别(减轻了BSP开发者的任务)。区别从执行文件系统开始。 Recovery 模式的细节就隐藏在其根文件系统中。
下面,我们就看看进入Recovery 根文件系统都干些啥。
二、init.rc
和正常启动一样,内核进入文件系统会执行/init, init 的配置文件就是 /init.rc, 前面文章讲过,这个文件来自:bootable/recovery/etc/init.rc,下面,我们看看它的内容。
-
on early-init
-
start ueventd
-
-
on init
-
export PATH /sbin
-
export ANDROID_ROOT /system
-
export ANDROID_DATA /data
-
export EXTERNAL_STORAGE /sdcard
-
-
symlink /system/etc /etc
-
-
mkdir /sdcard
-
mkdir /system
-
mkdir /data
-
mkdir /cache
-
mount /tmp /tmp tmpfs
-
-
on boot
-
ifup lo
-
hostname localhost
-
domainname localdomain
-
-
class_start default
-
-
service ueventd /sbin/ueventd
-
critical
-
-
service recovery /sbin/recovery
-
-
service adbd /sbin/adbd recovery
-
disabled
-
-
-
on property:ro.debuggable=1
-
write /sys/class/android_usb/android0/enable 0
-
write /sys/class/android_usb/android0/idVendor 18D1
-
write /sys/class/android_usb/android0/idProduct D001
-
write /sys/class/android_usb/android0/functions adb
-
write /sys/class/android_usb/android0/enable 1
-
write /sys/class/android_usb/android0/iManufacturer $ro.product.manufacturer
-
write /sys/class/android_usb/android0/iProduct $ro.product.model
-
write /sys/class/android_usb/android0/iSerial $ro.serialno
-
start adbd
-
-
-
on property:service.adb.root=1
-
write /sys/class/android_usb/android0/enable 0
-
restart adbd
-
write /sys/class/android_usb/android0/enable 1
可以看到,它很非常简单:
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 分析
bootable/recovery/recovery.c
Recovery.c: main->
install_package->
really_install_package->
try_update_binary->
execv(META-INF/com/google/android/update-binary, args);
/*
* The recovery tool communicates with the main system through /cache files.
* /cache/recovery/command - INPUT - command line for tool, one arg per line
* /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
* /cache/recovery/intent - OUTPUT - intent that was passed in
*
* The arguments which may be supplied in the recovery.command file:
* --send_intent=anystring - write the text out to recovery.intent
* --update_package=path - verify install an OTA package file
* --wipe_data - erase user data (and cache), then reboot
* --wipe_cache - wipe cache (but not user data), then reboot
* --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
*
* After completing, we remove /cache/recovery/command and reboot.
* Arguments may also be supplied in the bootloader control block (BCB).
* These important scenarios must be safely restartable at any point:
*
* FACTORY RESET
* 1. user selects "factory reset"
* 2. main system writes "--wipe_data" to /cache/recovery/command
* 3. main system reboots into recovery
* 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
* -- after this, rebooting will restart the erase --
* 5. erase_volume() reformats /data
* 6. erase_volume() reformats /cache
* 7. finish_recovery() erases BCB
* -- after this, rebooting will restart the main system --
* 8. main() calls reboot() to boot main system
*
* OTA INSTALL
* 1. main system downloads OTA package to /cache/some-filename.zip
* 2. main system writes "--update_package=/cache/some-filename.zip"
* 3. main system reboots into recovery
* 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
* -- after this, rebooting will attempt to reinstall the update --
* 5. install_package() attempts to install the update
* NOTE: the package install must itself be restartable from any point
* 6. finish_recovery() erases BCB
* -- after this, rebooting will (try to) restart the main system --
* 7. ** if install failed **
* 7a. prompt_and_wait() shows an error icon and waits for the user
* 7b; the user reboots (pulling the battery, etc) into the main system
* 8. main() calls maybe_install_firmware_update()
* ** if the update contained radio/hboot firmware **:
* 8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
* -- after this, rebooting will reformat cache & restart main system --
* 8b. m_i_f_u() writes firmware image into raw cache partition
* 8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
* -- after this, rebooting will attempt to reinstall firmware --
* 8d. bootloader tries to flash firmware
* 8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
* -- after this, rebooting will reformat cache & restart main system --
* 8f. erase_volume() reformats /cache
* 8g. finish_recovery() erases BCB
* -- after this, rebooting will (try to) restart the main system --
* 9. main() calls reboot() to boot main system
*/
代码及注释如下:
-
int
-
main(int argc, char **argv) {
-
time_t start = time(NULL);
-
-
-
-
-
-
-
freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
-
freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
-
-
freopen("/dev/ttyS1", "a", stdout); setbuf(stdout, NULL);
-
freopen("/dev/ttyS1", "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();
-
SetSdcardRootPath();
-
SureCacheMount();
-
ui_print("Recovery system v4.00 \n\n");
-
-
-
-
-
-
get_args(&argc, &argv);
-
-
-
int previous_runs = 0;
-
const char *send_intent = NULL;
-
const char *update_package = NULL;
-
const char *update_rkimage = NULL;
-
int wipe_data = 0, wipe_cache = 0, wipe_all = 0;
-
int factory_test_en = 0;
-
char prop[64] = {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 'r': update_rkimage = optarg; break;
-
case 'w': wipe_data = wipe_cache = 1; break;
-
case 'c': wipe_cache = 1; break;
-
case 'f'+'t':{ factory_test_en = 1;ui_show_text(1);} break;
-
case 't': ui_show_text(1); break;
-
case 'w'+'a':{ wipe_all = wipe_data = wipe_cache = 1;ui_show_text(1);
-
} break;
-
case '?':
-
LOGE("Invalid command argument\n");
-
continue;
-
}
-
}
-
-
device_recovery_start();
-
-
-
printf("Command:");
-
for (arg = 0; arg < argc; arg++) {
-
printf(" \"%s\"", argv[arg]);
-
}
-
printf("\n");
-
-
if (update_package) {
-
-
-
-
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;
-
}
-
strcpy(updatepath,update_package);
-
}
-
printf("\n");
-
-
if (update_rkimage) {
-
-
-
-
if (strncmp(update_rkimage, "CACHE:", 6) == 0) {
-
int len = strlen(update_rkimage) + 10;
-
char* modified_path = malloc(len);
-
strlcpy(modified_path, "/cache/", len);
-
strlcat(modified_path, update_rkimage+6, len);
-
printf("(replacing path \"%s\" with \"%s\")\n",
-
update_rkimage, modified_path);
-
update_rkimage = modified_path;
-
}
-
strcpy(updatepath,update_rkimage);
-
}
-
printf("\n");
-
-
-
property_list(print_property, NULL);
-
printf("\n");
-
-
int status = INSTALL_SUCCESS;
-
if(factory_test_en)
-
{
-
ui_print("\nwait factory test tool connect........\n");
-
char factory_test_finish = 0;
-
while(1)
-
{
-
property_get("FACTORY_TEST_FINISH", prop, "0");
-
factory_test_finish = (unsigned long)strtoumax(prop, 0, 16);
-
if(factory_test_finish != 0)break;
-
sleep(1);
-
}
-
ui_print("\n factory test finish........\n");
-
-
}
-
-
-
-
if (update_package != NULL) {
-
printf("update_package = %s", update_package);
-
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
-
bAutoUpdateComplete=true;
-
} else if (update_rkimage != NULL) {
-
status = install_rkimage(update_rkimage);
-
if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
-
else
-
bAutoUpdateComplete=true;
-
} 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 (wipe_all && erase_volume(IN_SDCARD_ROOT)) 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;
-
}
-
-
-
-
-
if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
-
if (status != INSTALL_SUCCESS) {
-
bClearbootmessage = false;
-
prompt_and_wait();
-
}
-
-
-
-
-
-
-
-
finish_recovery(send_intent);
-
ui_print("Rebooting...\n");
-
android_reboot(ANDROID_RB_RESTART, 0, 0);
-
return EXIT_SUCCESS;
-
}
四、install_package分析
bootable/recovery/install.c
-
int
-
install_package(const char* path, int* wipe_cache, const char* install_file)
-
{
-
FILE* install_log = fopen_path(install_file, "w");
-
if (install_log) {
-
fputs(path, install_log);
-
fputc('\n', install_log);
-
} else {
-
LOGE("failed to open last_install: %s\n", strerror(errno));
-
}
-
int result = really_install_package(path, wipe_cache);
-
if (install_log) {
-
fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);
-
fputc('\n', install_log);
-
fclose(install_log);
-
}
-
return result;
-
}
-
-
static int
-
really_install_package(const char *path, int* wipe_cache)
-
{
-
-
ui_set_background(BACKGROUND_ICON_INSTALLING);
-
ui_print("Finding update package...\n");
-
ui_show_indeterminate_progress();
-
LOGI("Update location: %s\n", path);
-
-
-
if (ensure_path_mounted(path) != 0) {
-
LOGE("Can't mount %s\n", path);
-
return INSTALL_CORRUPT;
-
}
-
-
ui_print("Opening update package...\n");
-
-
-
int numKeys;
-
RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
-
if (loadedKeys == NULL) {
-
LOGE("Failed to load keys\n");
-
return INSTALL_CORRUPT;
-
}
-
LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
-
-
-
ui_print("Verifying update package...\n");
-
ui_show_progress(
-
VERIFICATION_PROGRESS_FRACTION,
-
VERIFICATION_PROGRESS_TIME);
-
-
-
int err;
-
err = verify_file(path, loadedKeys, numKeys);
-
free(loadedKeys);
-
LOGI("verify_file returned %d\n", err);
-
if (err != VERIFY_SUCCESS) {
-
LOGE("signature verification failed\n");
-
return INSTALL_CORRUPT;
-
}
-
-
-
-
-
ZipArchive zip;
-
err = mzOpenZipArchive(path, &zip);
-
if (err != 0) {
-
LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
-
return INSTALL_CORRUPT;
-
}
-
-
-
-
ui_print("Installing update...\n");
-
return try_update_binary(path, &zip, wipe_cache);
-
}
五、try_update_binary分析
bootable/recovery/install.c
-
-
static int
-
try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
-
const ZipEntry* binary_entry =
-
mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
-
if (binary_entry == NULL) {
-
mzCloseZipArchive(zip);
-
return INSTALL_CORRUPT;
-
}
-
-
-
-
char* binary = "/tmp/update_binary";
-
unlink(binary);
-
int fd = creat(binary, 0755);
-
if (fd < 0) {
-
mzCloseZipArchive(zip);
-
LOGE("Can't make %s\n", binary);
-
return INSTALL_ERROR;
-
}
-
bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
-
close(fd);
-
mzCloseZipArchive(zip);
-
-
if (!ok) {
-
LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
-
return INSTALL_ERROR;
-
}
-
-
int pipefd[2];
-
pipe(pipefd);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
char** args = malloc(sizeof(char*) * 5);
-
args[0] = binary;
-
args[1] = EXPAND(RECOVERY_API_VERSION);
-
args[2] = malloc(10);
-
sprintf(args[2], "%d", pipefd[1]);
-
args[3] = (char*)path;
-
args[4] = NULL;
-
-
-
-
-
-
pid_t pid = fork();
-
if (pid == 0) {
-
close(pipefd[0]);
-
execv(binary, args);
-
fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
-
_exit(-1);
-
}
-
close(pipefd[1]);
-
-
*wipe_cache = 0;
-
-
-
char buffer[1024];
-
FILE* from_child = fdopen(pipefd[0], "r");
-
while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
-
char* command = strtok(buffer, " \n");
-
if (command == NULL) {
-
continue;
-
} else if (strcmp(command, "progress") == 0) {
-
char* fraction_s = strtok(NULL, " \n");
-
char* seconds_s = strtok(NULL, " \n");
-
-
float fraction = strtof(fraction_s, NULL);
-
int seconds = strtol(seconds_s, NULL, 10);
-
-
ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION),
-
seconds);
-
} else if (strcmp(command, "set_progress") == 0) {
-
char* fraction_s = strtok(NULL, " \n");
-
float fraction = strtof(fraction_s, NULL);
-
ui_set_progress(fraction);
-
} else if (strcmp(command, "ui_print") == 0) {
-
char* str = strtok(NULL, "\n");
-
if (str) {
-
ui_print("%s", str);
-
} else {
-
ui_print("\n");
-
}
-
} else if (strcmp(command, "wipe_cache") == 0) {
-
*wipe_cache = 1;
-
} else {
-
LOGE("unknown command [%s]\n", command);
-
}
-
}
-
fclose(from_child);
-
-
int status;
-
waitpid(pid, &status, 0);
-
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-
LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
-
return INSTALL_ERROR;
-
}
-
-
return INSTALL_SUCCESS;
-
}
这样,我们又回到了升级包中的文件:META-INF/com/google/android/update-binary,下回继续研究。