本文转载自:http://blog.csdn.net/morixinguan/article/details/72858346

版权声明:本文为博主原创文章,如有需要,请注明转载地址:http://blog.csdn.net/morixinguan。若是侵权用于商业用途,请联系博主,否则将追究责任

在Tiny4412的Android5.0源代码中:

bootable/recovery/recovery.cpp是recovery程序的主文件。

仔细一看,对比了其它平台的recovery源代码,除了MTK对Recovery做了相应的定制外,其它的平台几乎没有看到,关于MTK平台,后续再分析。

关于Android5.0的recovery,有什么功能,在recovery.cpp中开头就已经做了详细的说明,我们来看看:

  1. /*
  2. * The recovery tool communicates with the main system through /cache files.
  3. *   /cache/recovery/command - INPUT - command line for tool, one arg per line
  4. *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
  5. *   /cache/recovery/intent - OUTPUT - intent that was passed in
  6. *
  7. * The arguments which may be supplied in the recovery.command file:
  8. *   --send_intent=anystring - write the text out to recovery.intent
  9. *   --update_package=path - verify install an OTA package file
  10. *   --wipe_data - erase user data (and cache), then reboot
  11. *   --wipe_cache - wipe cache (but not user data), then reboot
  12. *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
  13. *   --just_exit - do nothing; exit and reboot
  14. *
  15. * After completing, we remove /cache/recovery/command and reboot.
  16. * Arguments may also be supplied in the bootloader control block (BCB).
  17. * These important scenarios must be safely restartable at any point:
  18. *
  19. * FACTORY RESET
  20. * 1. user selects "factory reset"
  21. * 2. main system writes "--wipe_data" to /cache/recovery/command
  22. * 3. main system reboots into recovery
  23. * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
  24. *    -- after this, rebooting will restart the erase --
  25. * 5. erase_volume() reformats /data
  26. * 6. erase_volume() reformats /cache
  27. * 7. finish_recovery() erases BCB
  28. *    -- after this, rebooting will restart the main system --
  29. * 8. main() calls reboot() to boot main system
  30. *
  31. * OTA INSTALL
  32. * 1. main system downloads OTA package to /cache/some-filename.zip
  33. * 2. main system writes "--update_package=/cache/some-filename.zip"
  34. * 3. main system reboots into recovery
  35. * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
  36. *    -- after this, rebooting will attempt to reinstall the update --
  37. * 5. install_package() attempts to install the update
  38. *    NOTE: the package install must itself be restartable from any point
  39. * 6. finish_recovery() erases BCB
  40. *    -- after this, rebooting will (try to) restart the main system --
  41. * 7. ** if install failed **
  42. *    7a. prompt_and_wait() shows an error icon and waits for the user
  43. *    7b; the user reboots (pulling the battery, etc) into the main system
  44. * 8. main() calls maybe_install_firmware_update()
  45. *    ** if the update contained radio/hboot firmware **:
  46. *    8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
  47. *        -- after this, rebooting will reformat cache & restart main system --
  48. *    8b. m_i_f_u() writes firmware image into raw cache partition
  49. *    8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
  50. *        -- after this, rebooting will attempt to reinstall firmware --
  51. *    8d. bootloader tries to flash firmware
  52. *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
  53. *        -- after this, rebooting will reformat cache & restart main system --
  54. *    8f. erase_volume() reformats /cache
  55. *    8g. finish_recovery() erases BCB
  56. *        -- after this, rebooting will (try to) restart the main system --
  57. * 9. main() calls reboot() to boot main system
  58. */

在这段英文注释里,详细的说明了factory_reset(Android的恢复出厂设置功能)的流程以及OTA系统更新的流程。

在这段注释得最前面说得很明白,我们只要往/cache/recovery/command中写入相应的命令:

  1. * The arguments which may be supplied in the recovery.command file:
  2. *   --send_intent=anystring - write the text out to recovery.intent
  3. *   --update_package=path - verify install an OTA package file
  4. *   --wipe_data - erase user data (and cache), then reboot
  5. *   --wipe_cache - wipe cache (but not user data), then reboot
  6. *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
  7. *   --just_exit - do nothing; exit and reboot

比如写入:

--update_package=path(对应的OTA更新的路径)

例如:

--update_package=/mnt/external_sd/xxx.zip

将这条命令写入后,再重启Android系统,recovery检测到有这个命令存在,就会去搜索这个路径,然后将这个路径做路径转换,接下来获取转换后的路径后,就挂载这个路径,然后挂载这个路径,获取OTA包,解包,校验,然后最后实现真正的更新。

如果我们往这个文件写入: --wipe_data

那么就会做出厂设置,格式化/data分区的内容。

接下来,我们来看看代码,从main函数开始分析:

进入main函数后,会将recovery产生的log信息重定向到/tmp/recovery.log这个文件里,具体代码实现如下:

  1. //重定向标准输出和标准出错到/tmp/recovery.log 这个文件里
  2. //static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
  3. redirect_stdio(TEMPORARY_LOG_FILE);

redirect_stdio函数源代码:

  1. static void redirect_stdio(const char* filename) {
  2. // If these fail, there's not really anywhere to complain...
  3. freopen(filename, "a", stdout); setbuf(stdout, NULL);
  4. freopen(filename, "a", stderr); setbuf(stderr, NULL);
  5. }

我们看到,所有产生来自stdout和stderr的信息会使用freopen这个函数重定向到/tmp/recovery.log这个文件里。

stdout就是标准输出,stdout就是标准出错。标准输出就是我们平时使用的printf输出的信息。

当然也可以使用fprintf(stdout,"hello world\n");也是一样的

标准出错就是fprintf(stderr,"hello world!\n");类似的代码。

接下下来,将会判断是否使用adb的sideload来传入,通过参数--adbd来判断:

  1. // If this binary is started with the single argument "--adbd",
  2. // instead of being the normal recovery binary, it turns into kind
  3. // of a stripped-down version of adbd that only supports the
  4. // 'sideload' command.  Note this must be a real argument, not
  5. // anything in the command file or bootloader control block; the
  6. // only way recovery should be run with this argument is when it
  7. // starts a copy of itself from the apply_from_adb() function.
  8. if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
  9. adb_main();
  10. return 0;
  11. }

做完这些步骤以后,会初始化并装载recovery的分区表recovery.fstab,然后挂载/cache/recovery/last_log这个文件,用来输出log。

  1. printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
  2. //装载recovery的分区表recovery.fstab
  3. load_volume_table();
  4. //在recovery中挂载/cache/recovery/last_log这个文件
  5. //#define LAST_LOG_FILE "/cache/recovery/last_log"
  6. ensure_path_mounted(LAST_LOG_FILE);
  7. rotate_last_logs(KEEP_LOG_COUNT);

这里主要看如何装载分区表的流程,先来看看recovery.fstab

  1. /dev/block/by-name/boot         /boot         emmc     defaults                                                                defaults
  2. /dev/block/by-name/recovery     /recovery     emmc     defaults                                                                defaults
  3. /dev/block/by-name/splashscreen /splashscreen emmc     defaults                                                                defaults
  4. /dev/block/by-name/fastboot     /fastboot     emmc     defaults                                                                defaults
  5. /dev/block/by-name/misc         /misc         emmc     defaults                                                                defaults
  6. /dev/block/by-name/system       /system       ext4     ro,noatime                                                              wait
  7. /dev/block/by-name/cache        /cache        ext4     nosuid,nodev,noatime,barrier=1,data=ordered                             wait,check
  8. /dev/block/by-name/userdata     /data         ext4     nosuid,nodev,noatime,discard,barrier=1,data=ordered,noauto_da_alloc     wait,check
  9. /dev/block/by-name/factory      /factory      ext4     nosuid,nodev,noatime,barrier=1,data=ordered                             wait

接下来看是如果挂载的:

  1. void load_volume_table()
  2. {
  3. int i;
  4. int ret;
  5. //读recovery.fstab 这个分区表
  6. fstab = fs_mgr_read_fstab("/etc/recovery.fstab");
  7. if (!fstab) {
  8. LOGE("failed to read /etc/recovery.fstab\n");
  9. return;
  10. }
  11. //将对应的信息加入到一条链表中
  12. ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk");
  13. //如果load到的分区表为空,后面做释放操作
  14. if (ret < 0 ) {
  15. LOGE("failed to add /tmp entry to fstab\n");
  16. fs_mgr_free_fstab(fstab);
  17. fstab = NULL;
  18. return;
  19. }
  20. printf("recovery filesystem table\n");
  21. printf("=========================\n");
  22. //到这一步,打印分区表信息,这类信息在
  23. //recovery启动的时候的log可以看到
  24. //分别是以下
  25. //编号|   挂载节点|  文件系统类型|  块设备|   长度
  26. for (i = 0; i < fstab->num_entries; ++i) {
  27. Volume* v = &fstab->recs[i];
  28. printf("  %d %s %s %s %lld\n", i, v->mount_point, v->fs_type,
  29. v->blk_device, v->length);
  30. }
  31. printf("\n");
  32. }

挂载完相应的分区以后,就需要获取命令参数,因为只有挂载了对应的分区,才能访问到前面要写入command的这个文件,这样我们才能正确的打开文件,如果分区都没找到,那么当然就找不到分区上的文件,上面这个步骤是至关重要的。

  1. //获取参数
  2. //这个参数也可能是从/cache/recovery/command文件中得到相应的命令
  3. //也就是可以往command这个文件写入对应的格式的命令即可
  4. get_args(&argc, &argv);
  5. const char *send_intent = NULL;
  6. const char *update_package = NULL;
  7. int wipe_data = 0, wipe_cache = 0, show_text = 0;
  8. bool just_exit = false;
  9. bool shutdown_after = false;
  10. int arg;
  11. //参数有擦除分区,OTA更新等
  12. while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
  13. switch (arg) {
  14. case 's': send_intent = optarg; break;
  15. case 'u': update_package = optarg; break;
  16. case 'w': wipe_data = wipe_cache = 1; break;
  17. case 'c': wipe_cache = 1; break;
  18. case 't': show_text = 1; break;
  19. case 'x': just_exit = true; break;
  20. case 'l': locale = optarg; break;
  21. case 'g': {
  22. if (stage == NULL || *stage == '\0') {
  23. char buffer[20] = "1/";
  24. strncat(buffer, optarg, sizeof(buffer)-3);
  25. stage = strdup(buffer);
  26. }
  27. break;
  28. }
  29. case 'p': shutdown_after = true; break;
  30. case 'r': reason = optarg; break;
  31. case '?':
  32. LOGE("Invalid command argument\n");
  33. continue;
  34. }
  35. }

获取到对应的命令,就会执行对应的标志,后面会根据标志来执行对应的操作。

做完以上的流程后,下面就是创建设备,设置语言信息,初始化recovery的UI界面,设置Selinux权限,代码如下:

  1. //设置语言
  2. if (locale == NULL) {
  3. load_locale_from_cache();
  4. }
  5. printf("locale is [%s]\n", locale);
  6. printf("stage is [%s]\n", stage);
  7. printf("reason is [%s]\n", reason);
  8. //创建设备
  9. Device* device = make_device();
  10. //获取UI
  11. ui = device->GetUI();
  12. //设置当前的UI
  13. gCurrentUI = ui;
  14. //设置UI的语言信息
  15. ui->SetLocale(locale);
  16. //UI初始化
  17. ui->Init();
  18. int st_cur, st_max;
  19. if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) {
  20. ui->SetStage(st_cur, st_max);
  21. }
  22. //设置recovery的背景图
  23. ui->SetBackground(RecoveryUI::NONE);
  24. //设置界面上是否能够显示字符,使能ui->print函数开关
  25. if (show_text) ui->ShowText(true);
  26. //设置selinux权限,一般我会把selinux 给disabled
  27. struct selinux_opt seopts[] = {
  28. { SELABEL_OPT_PATH, "/file_contexts" }
  29. };
  30. sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
  31. if (!sehandle) {
  32. ui->Print("Warning: No file_contexts\n");
  33. }
  34. //虚函数,没有做什么流程
  35. device->StartRecovery();
  36. printf("Command:");
  37. for (arg = 0; arg < argc; arg++) {
  38. printf(" \"%s\"", argv[arg]);
  39. }
  40. printf("\n");

接下来是重要的环节,这个环节将会根据上面命令参数来做真正的事情了,比如恢复出厂设置,OTA更新等。

  1. //如果update_package(也就是要升级的OTA包)不为空的情况下
  2. //这里要对升级包的路径做一下路径转换,这里可以自由定制自己升级包的路径
  3. if (update_package) {
  4. // For backwards compatibility on the cache partition only, if
  5. // we're given an old 'root' path "CACHE:foo", change it to
  6. // "/cache/foo".
  7. //这里就是做转换的方法
  8. //先比较传进来的recovery参数的前6个byte是否是CACHE
  9. //如果是将其路径转化为/cache/CACHE: ......
  10. if (strncmp(update_package, "CACHE:", 6) == 0) {
  11. int len = strlen(update_package) + 10;
  12. char* modified_path = (char*)malloc(len);
  13. strlcpy(modified_path, "/cache/", len);
  14. strlcat(modified_path, update_package+6, len);
  15. printf("(replacing path \"%s\" with \"%s\")\n",
  16. update_package, modified_path);
  17. //这个update_package就是转换后的路径
  18. update_package = modified_path;
  19. }
  20. }
  21. printf("\n");
  22. property_list(print_property, NULL);
  23. //获取属性,这里应该是从一个文件中找到ro.build.display.id
  24. //获取recovery的版本信息
  25. property_get("ro.build.display.id", recovery_version, "");
  26. printf("\n");
  27. //定义一个安装成功的标志位INSTALL_SUCCESS  ----> 其实是个枚举,值为0
  28. int status = INSTALL_SUCCESS;
  29. //判断转换后的OTA升级包的路径是否不为空,如果不为空
  30. //执行install_package 函数进行升级
  31. if (update_package != NULL) {
  32. status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);
  33. //判断是否升级成功
  34. if (status == INSTALL_SUCCESS && wipe_cache) {
  35. //擦除这个路径,相当于删除了这个路径下的OTA升级包
  36. if (erase_volume("/cache")) {
  37. LOGE("Cache wipe (requested by package) failed.");
  38. }
  39. }
  40. //如果安装不成功
  41. if (status != INSTALL_SUCCESS) {
  42. ui->Print("Installation aborted.\n");
  43. // If this is an eng or userdebug build, then automatically
  44. // turn the text display on if the script fails so the error
  45. // message is visible.
  46. char buffer[PROPERTY_VALUE_MAX+1];
  47. property_get("ro.build.fingerprint", buffer, "");
  48. if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) {
  49. ui->ShowText(true);
  50. }
  51. }
  52. }
  53. //如果跑的是格式化数据区,那么就走这个流程
  54. else if (wipe_data) {
  55. if (device->WipeData()) status = INSTALL_ERROR;
  56. //格式化/data分区
  57. if (erase_volume("/data")) status = INSTALL_ERROR;
  58. if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
  59. if (erase_persistent_partition() == -1 ) status = INSTALL_ERROR;
  60. if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
  61. }
  62. //格式化cache分区
  63. else if (wipe_cache) {
  64. if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
  65. if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n");
  66. }
  67. else if (!just_exit) {
  68. status = INSTALL_NONE;  // No command specified
  69. ui->SetBackground(RecoveryUI::NO_COMMAND);
  70. }
  71. //如果安装失败或者。。。
  72. if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
  73. copy_logs();
  74. //显示错误的LOGO
  75. ui->SetBackground(RecoveryUI::ERROR);
  76. }
  77. Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
  78. if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
  79. Device::BuiltinAction temp = prompt_and_wait(device, status);
  80. if (temp != Device::NO_ACTION) after = temp;
  81. }
  82. // Save logs and clean up before rebooting or shutting down.
  83. //完成recovery升级
  84. finish_recovery(send_intent);
  85. switch (after) {
  86. case Device::SHUTDOWN:
  87. ui->Print("Shutting down...\n");
  88. property_set(ANDROID_RB_PROPERTY, "shutdown,");
  89. break;
  90. case Device::REBOOT_BOOTLOADER:
  91. ui->Print("Rebooting to bootloader...\n");
  92. property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
  93. break;
  94. default:
  95. ui->Print("Rebooting...\n");
  96. property_set(ANDROID_RB_PROPERTY, "reboot,");
  97. break;
  98. }
  99. sleep(5); // should reboot before this finishes
  100. return EXIT_SUCCESS;

在这里面,我们最常用的即是OTA更新和恢复出厂设置,先来说说恢复出厂设置,这个功能就是所谓的手机双清,众所周知,Android手机在使用很久后,由于垃圾数据,以及其它的因素会导致手机的反应越来越慢,这让人烦恼不已,所以就需要双清,双清一般就是清除

/data分区和/cache分区,代码流程很详细,有兴趣可以自己去分析。

接下来看看OTA是如何实现更新的,我们看到install_ota_package这个函数,执行到这个函数,看到源码:

  1. //安装更新包
  2. int
  3. install_package(const char* path, int* wipe_cache, const char* install_file,
  4. bool needs_mount)
  5. {
  6. FILE* install_log = fopen_path(install_file, "w");
  7. if (install_log) {
  8. fputs(path, install_log);
  9. fputc('\n', install_log);
  10. } else {
  11. LOGE("failed to open last_install: %s\n", strerror(errno));
  12. }
  13. int result;
  14. //设置安装挂载对应的节点
  15. //这一步是关键
  16. if (setup_install_mounts() != 0) {
  17. LOGE("failed to set up expected mounts for install; aborting\n");
  18. result = INSTALL_ERROR;
  19. } else {
  20. //到这里才是真正的去安装OTA包
  21. result = really_install_package(path, wipe_cache, needs_mount);
  22. }
  23. //如果返回结果为0,那么安装就成功了
  24. if (install_log) {
  25. fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);
  26. fputc('\n', install_log);
  27. fclose(install_log);
  28. }
  29. return result;
  30. }

其实到了really_install_package这一步,才是真正做到OTA更新,但是在OTA更新之前至关重要的一步就是设置安装挂载对应的节点了,我曾经掉入此坑,现在拿出来分析一下,我们来看看setup_install_mounts这个函数:

  1. //设置安装挂载的节点
  2. int setup_install_mounts() {
  3. if (fstab == NULL) {
  4. LOGE("can't set up install mounts: no fstab loaded\n");
  5. return -1;
  6. }
  7. for (int i = 0; i < fstab->num_entries; ++i) {
  8. Volume* v = fstab->recs + i;
  9. //如果判断挂载的路径是/tmp 或者/cache
  10. //那么就挂载对应的节点,而其它的节点都不会去挂载
  11. if (strcmp(v->mount_point, "/tmp") == 0 ||
  12. strcmp(v->mount_point, "/cache") == 0) {
  13. if (ensure_path_mounted(v->mount_point) != 0) {
  14. LOGE("failed to mount %s\n", v->mount_point);
  15. return -1;
  16. }
  17. }
  18. //如果不是/tmp或者/cache这两个节点,则默认就会卸载所有的挂载节点
  19. else {
  20. //卸载所有的挂载节点
  21. if (ensure_path_unmounted(v->mount_point) != 0) {
  22. LOGE("failed to unmount %s\n", v->mount_point);
  23. return -1;
  24. }
  25. }
  26. }
  27. return 0;
  28. }

如果在安装更新的时候,OTA包经过路径转换后不是放在/tmp和/cache这个路径下的时候,那么就会走else分支,从而卸载所有的挂载节点,这样就会导致,传的路径正确,却OTA更新不成功,如果是做自己定制的路径,这一步一定要小心,我们可以在这里继续添加定制的挂载点。

那么,执行完设置挂载节点的函数后,接下来就是执行真正的OTA更新了,我们来看看:

  1. static int
  2. really_install_package(const char *path, int* wipe_cache, bool needs_mount)
  3. {
  4. //设置更新时的背景
  5. ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
  6. ui->Print("Finding update package...\n");
  7. // Give verification half the progress bar...
  8. //设置进度条的类型
  9. ui->SetProgressType(RecoveryUI::DETERMINATE);
  10. //显示进度条
  11. ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
  12. LOGI("Update location: %s\n", path);
  13. //在屏幕上打印 Opening update package..
  14. // Map the update package into memory.
  15. ui->Print("Opening update package...\n");
  16. //patch是OTA的路径,need_mount参数表示是否需要挂载,1挂载,0,不挂载
  17. if (path && needs_mount) {
  18. if (path[0] == '@') {
  19. ensure_path_mounted(path+1);
  20. } else {
  21. //挂载OTA升级包的路径------> 一般是执行这个流程
  22. ensure_path_mounted(path);
  23. }
  24. }
  25. MemMapping map;
  26. if (sysMapFile(path, &map) != 0) {
  27. LOGE("failed to map file\n");
  28. return INSTALL_CORRUPT;
  29. }
  30. int numKeys;
  31. //获取校验公钥文件
  32. Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
  33. if (loadedKeys == NULL) {
  34. LOGE("Failed to load keys\n");
  35. return INSTALL_CORRUPT;
  36. }
  37. LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
  38. ui->Print("Verifying update package...\n");
  39. int err;
  40. //校验文件
  41. err = verify_file(map.addr, map.length, loadedKeys, numKeys);
  42. free(loadedKeys);
  43. LOGI("verify_file returned %d\n", err);
  44. //如果校验不成功
  45. if (err != VERIFY_SUCCESS) {
  46. //打印签名失败
  47. LOGE("signature verification failed\n");
  48. sysReleaseMap(&map);
  49. return INSTALL_CORRUPT;
  50. }
  51. /* Try to open the package.
  52. */
  53. //尝试去打开ota压缩包
  54. ZipArchive zip;
  55. err = mzOpenZipArchive(map.addr, map.length, &zip);
  56. if (err != 0) {
  57. LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
  58. sysReleaseMap(&map);
  59. return INSTALL_CORRUPT;
  60. }
  61. /* Verify and install the contents of the package.
  62. */
  63. //开始安装升级包
  64. ui->Print("Installing update...\n");
  65. ui->SetEnableReboot(false);
  66. int result = try_update_binary(path, &zip, wipe_cache);
  67. //安装成功后自动重启
  68. ui->SetEnableReboot(true);
  69. ui->Print("\n");
  70. sysReleaseMap(&map);
  71. //返回结果
  72. return result;
  73. }

关于recovery的大致流程,我们分析至此,关于如何像MTK平台一样,定制recovery,这就需要读者能够读懂recovery的流程,然后加入自己的代码进行定制,当然我们也会看到,一些recovery花样百出,很多UI做了自己的,而不是用安卓系统原生态的,安卓系统recovery原生态的UI如下:

如何定制相应的UI,后续我们会对recovery源代码中的UI显示做进一步的分析。。。。

接下来,贴出Android5.0的recovery.cpp代码和注释:

  1. /*
  2. * Copyright (C) 2007 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. *      http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <ctype.h>
  17. #include <dirent.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <getopt.h>
  21. #include <limits.h>
  22. #include <linux/input.h>
  23. #include <stdarg.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <sys/stat.h>
  28. #include <sys/types.h>
  29. #include <time.h>
  30. #include <unistd.h>
  31. #include "bootloader.h"
  32. #include "common.h"
  33. #include "cutils/properties.h"
  34. #include "cutils/android_reboot.h"
  35. #include "install.h"
  36. #include "minui/minui.h"
  37. #include "minzip/DirUtil.h"
  38. #include "roots.h"
  39. #include "ui.h"
  40. #include "screen_ui.h"
  41. #include "device.h"
  42. #include "adb_install.h"
  43. extern "C" {
  44. #include "minadbd/adb.h"
  45. #include "fuse_sideload.h"
  46. #include "fuse_sdcard_provider.h"
  47. }
  48. struct selabel_handle *sehandle;
  49. static const struct option OPTIONS[] = {
  50. { "send_intent", required_argument, NULL, 's' },
  51. { "update_package", required_argument, NULL, 'u' },
  52. { "wipe_data", no_argument, NULL, 'w' },
  53. { "wipe_cache", no_argument, NULL, 'c' },
  54. { "show_text", no_argument, NULL, 't' },
  55. { "just_exit", no_argument, NULL, 'x' },
  56. { "locale", required_argument, NULL, 'l' },
  57. { "stages", required_argument, NULL, 'g' },
  58. { "shutdown_after", no_argument, NULL, 'p' },
  59. { "reason", required_argument, NULL, 'r' },
  60. { NULL, 0, NULL, 0 },
  61. };
  62. #define LAST_LOG_FILE "/cache/recovery/last_log"
  63. static const char *CACHE_LOG_DIR = "/cache/recovery";
  64. static const char *COMMAND_FILE = "/cache/recovery/command";
  65. static const char *INTENT_FILE = "/cache/recovery/intent";
  66. static const char *LOG_FILE = "/cache/recovery/log";
  67. static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
  68. static const char *LOCALE_FILE = "/cache/recovery/last_locale";
  69. static const char *CACHE_ROOT = "/cache";
  70. static const char *SDCARD_ROOT = "/sdcard";
  71. static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
  72. static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
  73. #define KEEP_LOG_COUNT 10
  74. RecoveryUI* ui = NULL;
  75. char* locale = NULL;
  76. char recovery_version[PROPERTY_VALUE_MAX+1];
  77. char* stage = NULL;
  78. char* reason = NULL;
  79. /*
  80. * The recovery tool communicates with the main system through /cache files.
  81. *   /cache/recovery/command - INPUT - command line for tool, one arg per line
  82. *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
  83. *   /cache/recovery/intent - OUTPUT - intent that was passed in
  84. *
  85. * The arguments which may be supplied in the recovery.command file:
  86. *   --send_intent=anystring - write the text out to recovery.intent
  87. *   --update_package=path - verify install an OTA package file
  88. *   --wipe_data - erase user data (and cache), then reboot
  89. *   --wipe_cache - wipe cache (but not user data), then reboot
  90. *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
  91. *   --just_exit - do nothing; exit and reboot
  92. *
  93. * After completing, we remove /cache/recovery/command and reboot.
  94. * Arguments may also be supplied in the bootloader control block (BCB).
  95. * These important scenarios must be safely restartable at any point:
  96. *
  97. * FACTORY RESET
  98. * 1. user selects "factory reset"
  99. * 2. main system writes "--wipe_data" to /cache/recovery/command
  100. * 3. main system reboots into recovery
  101. * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
  102. *    -- after this, rebooting will restart the erase --
  103. * 5. erase_volume() reformats /data
  104. * 6. erase_volume() reformats /cache
  105. * 7. finish_recovery() erases BCB
  106. *    -- after this, rebooting will restart the main system --
  107. * 8. main() calls reboot() to boot main system
  108. *
  109. * OTA INSTALL
  110. * 1. main system downloads OTA package to /cache/some-filename.zip
  111. * 2. main system writes "--update_package=/cache/some-filename.zip"
  112. * 3. main system reboots into recovery
  113. * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
  114. *    -- after this, rebooting will attempt to reinstall the update --
  115. * 5. install_package() attempts to install the update
  116. *    NOTE: the package install must itself be restartable from any point
  117. * 6. finish_recovery() erases BCB
  118. *    -- after this, rebooting will (try to) restart the main system --
  119. * 7. ** if install failed **
  120. *    7a. prompt_and_wait() shows an error icon and waits for the user
  121. *    7b; the user reboots (pulling the battery, etc) into the main system
  122. * 8. main() calls maybe_install_firmware_update()
  123. *    ** if the update contained radio/hboot firmware **:
  124. *    8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
  125. *        -- after this, rebooting will reformat cache & restart main system --
  126. *    8b. m_i_f_u() writes firmware image into raw cache partition
  127. *    8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
  128. *        -- after this, rebooting will attempt to reinstall firmware --
  129. *    8d. bootloader tries to flash firmware
  130. *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
  131. *        -- after this, rebooting will reformat cache & restart main system --
  132. *    8f. erase_volume() reformats /cache
  133. *    8g. finish_recovery() erases BCB
  134. *        -- after this, rebooting will (try to) restart the main system --
  135. * 9. main() calls reboot() to boot main system
  136. */
  137. static const int MAX_ARG_LENGTH = 4096;
  138. static const int MAX_ARGS = 100;
  139. // open a given path, mounting partitions as necessary
  140. FILE*
  141. fopen_path(const char *path, const char *mode) {
  142. if (ensure_path_mounted(path) != 0) {
  143. LOGE("Can't mount %s\n", path);
  144. return NULL;
  145. }
  146. // When writing, try to create the containing directory, if necessary.
  147. // Use generous permissions, the system (init.rc) will reset them.
  148. if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1, sehandle);
  149. FILE *fp = fopen(path, mode);
  150. return fp;
  151. }
  152. static void redirect_stdio(const char* filename) {
  153. // If these fail, there's not really anywhere to complain...
  154. freopen(filename, "a", stdout); setbuf(stdout, NULL);
  155. freopen(filename, "a", stderr); setbuf(stderr, NULL);
  156. }
  157. // close a file, log an error if the error indicator is set
  158. static void
  159. check_and_fclose(FILE *fp, const char *name) {
  160. fflush(fp);
  161. if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno));
  162. fclose(fp);
  163. }
  164. // command line args come from, in decreasing precedence:
  165. //   - the actual command line
  166. //   - the bootloader control block (one per line, after "recovery")
  167. //   - the contents of COMMAND_FILE (one per line)
  168. static void
  169. get_args(int *argc, char ***argv) {
  170. struct bootloader_message boot;
  171. memset(&boot, 0, sizeof(boot));
  172. get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure
  173. stage = strndup(boot.stage, sizeof(boot.stage));
  174. if (boot.command[0] != 0 && boot.command[0] != 255) {
  175. LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
  176. }
  177. if (boot.status[0] != 0 && boot.status[0] != 255) {
  178. LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
  179. }
  180. // --- if arguments weren't supplied, look in the bootloader control block
  181. if (*argc <= 1) {
  182. boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
  183. const char *arg = strtok(boot.recovery, "\n");
  184. if (arg != NULL && !strcmp(arg, "recovery")) {
  185. *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
  186. (*argv)[0] = strdup(arg);
  187. for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
  188. if ((arg = strtok(NULL, "\n")) == NULL) break;
  189. (*argv)[*argc] = strdup(arg);
  190. }
  191. LOGI("Got arguments from boot message\n");
  192. } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
  193. LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
  194. }
  195. }
  196. // --- if that doesn't work, try the command file
  197. if (*argc <= 1) {
  198. FILE *fp = fopen_path(COMMAND_FILE, "r");
  199. if (fp != NULL) {
  200. char *token;
  201. char *argv0 = (*argv)[0];
  202. *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
  203. (*argv)[0] = argv0;  // use the same program name
  204. char buf[MAX_ARG_LENGTH];
  205. for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
  206. if (!fgets(buf, sizeof(buf), fp)) break;
  207. token = strtok(buf, "\r\n");
  208. if (token != NULL) {
  209. (*argv)[*argc] = strdup(token);  // Strip newline.
  210. } else {
  211. --*argc;
  212. }
  213. }
  214. check_and_fclose(fp, COMMAND_FILE);
  215. LOGI("Got arguments from %s\n", COMMAND_FILE);
  216. }
  217. }
  218. // --> write the arguments we have back into the bootloader control block
  219. // always boot into recovery after this (until finish_recovery() is called)
  220. strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
  221. strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
  222. int i;
  223. for (i = 1; i < *argc; ++i) {
  224. strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
  225. strlcat(boot.recovery, "\n", sizeof(boot.recovery));
  226. }
  227. set_bootloader_message(&boot);
  228. }
  229. static void
  230. set_sdcard_update_bootloader_message() {
  231. struct bootloader_message boot;
  232. memset(&boot, 0, sizeof(boot));
  233. strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
  234. strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
  235. set_bootloader_message(&boot);
  236. }
  237. // How much of the temp log we have copied to the copy in cache.
  238. static long tmplog_offset = 0;
  239. static void
  240. copy_log_file(const char* source, const char* destination, int append) {
  241. FILE *log = fopen_path(destination, append ? "a" : "w");
  242. if (log == NULL) {
  243. LOGE("Can't open %s\n", destination);
  244. } else {
  245. FILE *tmplog = fopen(source, "r");
  246. if (tmplog != NULL) {
  247. if (append) {
  248. fseek(tmplog, tmplog_offset, SEEK_SET);  // Since last write
  249. }
  250. char buf[4096];
  251. while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
  252. if (append) {
  253. tmplog_offset = ftell(tmplog);
  254. }
  255. check_and_fclose(tmplog, source);
  256. }
  257. check_and_fclose(log, destination);
  258. }
  259. }
  260. // Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max
  261. // Overwrites any existing last_log.$max.
  262. static void
  263. rotate_last_logs(int max) {
  264. char oldfn[256];
  265. char newfn[256];
  266. int i;
  267. for (i = max-1; i >= 0; --i) {
  268. snprintf(oldfn, sizeof(oldfn), (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i);
  269. snprintf(newfn, sizeof(newfn), LAST_LOG_FILE ".%d", i+1);
  270. // ignore errors
  271. rename(oldfn, newfn);
  272. }
  273. }
  274. static void
  275. copy_logs() {
  276. // Copy logs to cache so the system can find out what happened.
  277. copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);
  278. copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false);
  279. copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false);
  280. chmod(LOG_FILE, 0600);
  281. chown(LOG_FILE, 1000, 1000);   // system user
  282. chmod(LAST_LOG_FILE, 0640);
  283. chmod(LAST_INSTALL_FILE, 0644);
  284. sync();
  285. }
  286. // clear the recovery command and prepare to boot a (hopefully working) system,
  287. // copy our log file to cache as well (for the system to read), and
  288. // record any intent we were asked to communicate back to the system.
  289. // this function is idempotent: call it as many times as you like.
  290. static void
  291. finish_recovery(const char *send_intent) {
  292. // By this point, we're ready to return to the main system...
  293. if (send_intent != NULL) {
  294. FILE *fp = fopen_path(INTENT_FILE, "w");
  295. if (fp == NULL) {
  296. LOGE("Can't open %s\n", INTENT_FILE);
  297. } else {
  298. fputs(send_intent, fp);
  299. check_and_fclose(fp, INTENT_FILE);
  300. }
  301. }
  302. // Save the locale to cache, so if recovery is next started up
  303. // without a --locale argument (eg, directly from the bootloader)
  304. // it will use the last-known locale.
  305. if (locale != NULL) {
  306. LOGI("Saving locale \"%s\"\n", locale);
  307. FILE* fp = fopen_path(LOCALE_FILE, "w");
  308. fwrite(locale, 1, strlen(locale), fp);
  309. fflush(fp);
  310. fsync(fileno(fp));
  311. check_and_fclose(fp, LOCALE_FILE);
  312. }
  313. copy_logs();
  314. // Reset to normal system boot so recovery won't cycle indefinitely.
  315. struct bootloader_message boot;
  316. memset(&boot, 0, sizeof(boot));
  317. set_bootloader_message(&boot);
  318. // Remove the command file, so recovery won't repeat indefinitely.
  319. if (ensure_path_mounted(COMMAND_FILE) != 0 ||
  320. (unlink(COMMAND_FILE) && errno != ENOENT)) {
  321. LOGW("Can't unlink %s\n", COMMAND_FILE);
  322. }
  323. ensure_path_unmounted(CACHE_ROOT);
  324. sync();  // For good measure.
  325. }
  326. typedef struct _saved_log_file {
  327. char* name;
  328. struct stat st;
  329. unsigned char* data;
  330. struct _saved_log_file* next;
  331. } saved_log_file;
  332. static int
  333. erase_volume(const char *volume) {
  334. bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
  335. ui->SetBackground(RecoveryUI::ERASING);
  336. ui->SetProgressType(RecoveryUI::INDETERMINATE);
  337. saved_log_file* head = NULL;
  338. if (is_cache) {
  339. // If we're reformatting /cache, we load any
  340. // "/cache/recovery/last*" files into memory, so we can restore
  341. // them after the reformat.
  342. ensure_path_mounted(volume);
  343. DIR* d;
  344. struct dirent* de;
  345. d = opendir(CACHE_LOG_DIR);
  346. if (d) {
  347. char path[PATH_MAX];
  348. strcpy(path, CACHE_LOG_DIR);
  349. strcat(path, "/");
  350. int path_len = strlen(path);
  351. while ((de = readdir(d)) != NULL) {
  352. if (strncmp(de->d_name, "last", 4) == 0) {
  353. saved_log_file* p = (saved_log_file*) malloc(sizeof(saved_log_file));
  354. strcpy(path+path_len, de->d_name);
  355. p->name = strdup(path);
  356. if (stat(path, &(p->st)) == 0) {
  357. // truncate files to 512kb
  358. if (p->st.st_size > (1 << 19)) {
  359. p->st.st_size = 1 << 19;
  360. }
  361. p->data = (unsigned char*) malloc(p->st.st_size);
  362. FILE* f = fopen(path, "rb");
  363. fread(p->data, 1, p->st.st_size, f);
  364. fclose(f);
  365. p->next = head;
  366. head = p;
  367. } else {
  368. free(p);
  369. }
  370. }
  371. }
  372. closedir(d);
  373. } else {
  374. if (errno != ENOENT) {
  375. printf("opendir failed: %s\n", strerror(errno));
  376. }
  377. }
  378. }
  379. ui->Print("Formatting %s...\n", volume);
  380. ensure_path_unmounted(volume);
  381. int result = format_volume(volume);
  382. if (is_cache) {
  383. while (head) {
  384. FILE* f = fopen_path(head->name, "wb");
  385. if (f) {
  386. fwrite(head->data, 1, head->st.st_size, f);
  387. fclose(f);
  388. chmod(head->name, head->st.st_mode);
  389. chown(head->name, head->st.st_uid, head->st.st_gid);
  390. }
  391. free(head->name);
  392. free(head->data);
  393. saved_log_file* temp = head->next;
  394. free(head);
  395. head = temp;
  396. }
  397. // Any part of the log we'd copied to cache is now gone.
  398. // Reset the pointer so we copy from the beginning of the temp
  399. // log.
  400. tmplog_offset = 0;
  401. copy_logs();
  402. }
  403. return result;
  404. }
  405. static const char**
  406. prepend_title(const char* const* headers) {
  407. // count the number of lines in our title, plus the
  408. // caller-provided headers.
  409. int count = 3;   // our title has 3 lines
  410. const char* const* p;
  411. for (p = headers; *p; ++p, ++count);
  412. const char** new_headers = (const char**)malloc((count+1) * sizeof(char*));
  413. const char** h = new_headers;
  414. *(h++) = "Android system recovery <" EXPAND(RECOVERY_API_VERSION) "e>";
  415. *(h++) = recovery_version;
  416. *(h++) = "";
  417. for (p = headers; *p; ++p, ++h) *h = *p;
  418. *h = NULL;
  419. return new_headers;
  420. }
  421. static int
  422. get_menu_selection(const char* const * headers, const char* const * items,
  423. int menu_only, int initial_selection, Device* device) {
  424. // throw away keys pressed previously, so user doesn't
  425. // accidentally trigger menu items.
  426. ui->FlushKeys();
  427. ui->StartMenu(headers, items, initial_selection);
  428. int selected = initial_selection;
  429. int chosen_item = -1;
  430. while (chosen_item < 0) {
  431. int key = ui->WaitKey();
  432. int visible = ui->IsTextVisible();
  433. if (key == -1) {   // ui_wait_key() timed out
  434. if (ui->WasTextEverVisible()) {
  435. continue;
  436. } else {
  437. LOGI("timed out waiting for key input; rebooting.\n");
  438. ui->EndMenu();
  439. return 0; // XXX fixme
  440. }
  441. }
  442. int action = device->HandleMenuKey(key, visible);
  443. if (action < 0) {
  444. switch (action) {
  445. case Device::kHighlightUp:
  446. --selected;
  447. selected = ui->SelectMenu(selected);
  448. break;
  449. case Device::kHighlightDown:
  450. ++selected;
  451. selected = ui->SelectMenu(selected);
  452. break;
  453. case Device::kInvokeItem:
  454. chosen_item = selected;
  455. break;
  456. case Device::kNoAction:
  457. break;
  458. }
  459. } else if (!menu_only) {
  460. chosen_item = action;
  461. }
  462. }
  463. ui->EndMenu();
  464. return chosen_item;
  465. }
  466. static int compare_string(const void* a, const void* b) {
  467. return strcmp(*(const char**)a, *(const char**)b);
  468. }
  469. // Returns a malloc'd path, or NULL.
  470. static char*
  471. browse_directory(const char* path, Device* device) {
  472. ensure_path_mounted(path);
  473. const char* MENU_HEADERS[] = { "Choose a package to install:",
  474. path,
  475. "",
  476. NULL };
  477. DIR* d;
  478. struct dirent* de;
  479. d = opendir(path);
  480. if (d == NULL) {
  481. LOGE("error opening %s: %s\n", path, strerror(errno));
  482. return NULL;
  483. }
  484. const char** headers = prepend_title(MENU_HEADERS);
  485. int d_size = 0;
  486. int d_alloc = 10;
  487. char** dirs = (char**)malloc(d_alloc * sizeof(char*));
  488. int z_size = 1;
  489. int z_alloc = 10;
  490. char** zips = (char**)malloc(z_alloc * sizeof(char*));
  491. zips[0] = strdup("../");
  492. while ((de = readdir(d)) != NULL) {
  493. int name_len = strlen(de->d_name);
  494. if (de->d_type == DT_DIR) {
  495. // skip "." and ".." entries
  496. if (name_len == 1 && de->d_name[0] == '.') continue;
  497. if (name_len == 2 && de->d_name[0] == '.' &&
  498. de->d_name[1] == '.') continue;
  499. if (d_size >= d_alloc) {
  500. d_alloc *= 2;
  501. dirs = (char**)realloc(dirs, d_alloc * sizeof(char*));
  502. }
  503. dirs[d_size] = (char*)malloc(name_len + 2);
  504. strcpy(dirs[d_size], de->d_name);
  505. dirs[d_size][name_len] = '/';
  506. dirs[d_size][name_len+1] = '\0';
  507. ++d_size;
  508. } else if (de->d_type == DT_REG &&
  509. name_len >= 4 &&
  510. strncasecmp(de->d_name + (name_len-4), ".zip", 4) == 0) {
  511. if (z_size >= z_alloc) {
  512. z_alloc *= 2;
  513. zips = (char**)realloc(zips, z_alloc * sizeof(char*));
  514. }
  515. zips[z_size++] = strdup(de->d_name);
  516. }
  517. }
  518. closedir(d);
  519. qsort(dirs, d_size, sizeof(char*), compare_string);
  520. qsort(zips, z_size, sizeof(char*), compare_string);
  521. // append dirs to the zips list
  522. if (d_size + z_size + 1 > z_alloc) {
  523. z_alloc = d_size + z_size + 1;
  524. zips = (char**)realloc(zips, z_alloc * sizeof(char*));
  525. }
  526. memcpy(zips + z_size, dirs, d_size * sizeof(char*));
  527. free(dirs);
  528. z_size += d_size;
  529. zips[z_size] = NULL;
  530. char* result;
  531. int chosen_item = 0;
  532. while (true) {
  533. chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device);
  534. char* item = zips[chosen_item];
  535. int item_len = strlen(item);
  536. if (chosen_item == 0) {          // item 0 is always "../"
  537. // go up but continue browsing (if the caller is update_directory)
  538. result = NULL;
  539. break;
  540. }
  541. char new_path[PATH_MAX];
  542. strlcpy(new_path, path, PATH_MAX);
  543. strlcat(new_path, "/", PATH_MAX);
  544. strlcat(new_path, item, PATH_MAX);
  545. if (item[item_len-1] == '/') {
  546. // recurse down into a subdirectory
  547. new_path[strlen(new_path)-1] = '\0';  // truncate the trailing '/'
  548. result = browse_directory(new_path, device);
  549. if (result) break;
  550. } else {
  551. // selected a zip file: return the malloc'd path to the caller.
  552. result = strdup(new_path);
  553. break;
  554. }
  555. }
  556. int i;
  557. for (i = 0; i < z_size; ++i) free(zips[i]);
  558. free(zips);
  559. free(headers);
  560. return result;
  561. }
  562. static void
  563. wipe_data(int confirm, Device* device) {
  564. if (confirm) {
  565. static const char** title_headers = NULL;
  566. if (title_headers == NULL) {
  567. const char* headers[] = { "Confirm wipe of all user data?",
  568. "  THIS CAN NOT BE UNDONE.",
  569. "",
  570. NULL };
  571. title_headers = prepend_title((const char**)headers);
  572. }
  573. const char* items[] = { " No",
  574. " No",
  575. " No",
  576. " No",
  577. " No",
  578. " No",
  579. " No",
  580. " Yes -- delete all user data",   // [7]
  581. " No",
  582. " No",
  583. " No",
  584. NULL };
  585. int chosen_item = get_menu_selection(title_headers, items, 1, 0, device);
  586. if (chosen_item != 7) {
  587. return;
  588. }
  589. }
  590. ui->Print("\n-- Wiping data...\n");
  591. device->WipeData();
  592. erase_volume("/data");
  593. erase_volume("/cache");
  594. erase_persistent_partition();
  595. ui->Print("Data wipe complete.\n");
  596. }
  597. static void file_to_ui(const char* fn) {
  598. FILE *fp = fopen_path(fn, "re");
  599. if (fp == NULL) {
  600. ui->Print("  Unable to open %s: %s\n", fn, strerror(errno));
  601. return;
  602. }
  603. char line[1024];
  604. int ct = 0;
  605. redirect_stdio("/dev/null");
  606. while(fgets(line, sizeof(line), fp) != NULL) {
  607. ui->Print("%s", line);
  608. ct++;
  609. if (ct % 30 == 0) {
  610. // give the user time to glance at the entries
  611. ui->WaitKey();
  612. }
  613. }
  614. redirect_stdio(TEMPORARY_LOG_FILE);
  615. fclose(fp);
  616. }
  617. static void choose_recovery_file(Device* device) {
  618. int i;
  619. static const char** title_headers = NULL;
  620. char *filename;
  621. const char* headers[] = { "Select file to view",
  622. "",
  623. NULL };
  624. char* entries[KEEP_LOG_COUNT + 2];
  625. memset(entries, 0, sizeof(entries));
  626. for (i = 0; i < KEEP_LOG_COUNT; i++) {
  627. char *filename;
  628. if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i) == -1) {
  629. // memory allocation failure - return early. Should never happen.
  630. return;
  631. }
  632. if ((ensure_path_mounted(filename) != 0) || (access(filename, R_OK) == -1)) {
  633. free(filename);
  634. entries[i+1] = NULL;
  635. break;
  636. }
  637. entries[i+1] = filename;
  638. }
  639. entries[0] = strdup("Go back");
  640. title_headers = prepend_title((const char**)headers);
  641. while(1) {
  642. int chosen_item = get_menu_selection(title_headers, entries, 1, 0, device);
  643. if (chosen_item == 0) break;
  644. file_to_ui(entries[chosen_item]);
  645. }
  646. for (i = 0; i < KEEP_LOG_COUNT + 1; i++) {
  647. free(entries[i]);
  648. }
  649. }
  650. // Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER.  Returning NO_ACTION
  651. // means to take the default, which is to reboot or shutdown depending
  652. // on if the --shutdown_after flag was passed to recovery.
  653. static Device::BuiltinAction
  654. prompt_and_wait(Device* device, int status) {
  655. const char* const* headers = prepend_title(device->GetMenuHeaders());
  656. for (;;) {
  657. finish_recovery(NULL);
  658. switch (status) {
  659. case INSTALL_SUCCESS:
  660. case INSTALL_NONE:
  661. ui->SetBackground(RecoveryUI::NO_COMMAND);
  662. break;
  663. case INSTALL_ERROR:
  664. case INSTALL_CORRUPT:
  665. ui->SetBackground(RecoveryUI::ERROR);
  666. break;
  667. }
  668. ui->SetProgressType(RecoveryUI::EMPTY);
  669. int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device);
  670. // device-specific code may take some action here.  It may
  671. // return one of the core actions handled in the switch
  672. // statement below.
  673. Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
  674. int wipe_cache = 0;
  675. switch (chosen_action) {
  676. case Device::NO_ACTION:
  677. break;
  678. case Device::REBOOT:
  679. case Device::SHUTDOWN:
  680. case Device::REBOOT_BOOTLOADER:
  681. return chosen_action;
  682. case Device::WIPE_DATA:
  683. wipe_data(ui->IsTextVisible(), device);
  684. if (!ui->IsTextVisible()) return Device::NO_ACTION;
  685. break;
  686. case Device::WIPE_CACHE:
  687. ui->Print("\n-- Wiping cache...\n");
  688. erase_volume("/cache");
  689. ui->Print("Cache wipe complete.\n");
  690. if (!ui->IsTextVisible()) return Device::NO_ACTION;
  691. break;
  692. case Device::APPLY_EXT: {
  693. ensure_path_mounted(SDCARD_ROOT);
  694. char* path = browse_directory(SDCARD_ROOT, device);
  695. if (path == NULL) {
  696. ui->Print("\n-- No package file selected.\n", path);
  697. break;
  698. }
  699. ui->Print("\n-- Install %s ...\n", path);
  700. set_sdcard_update_bootloader_message();
  701. void* token = start_sdcard_fuse(path);
  702. int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, &wipe_cache,
  703. TEMPORARY_INSTALL_FILE, false);
  704. finish_sdcard_fuse(token);
  705. ensure_path_unmounted(SDCARD_ROOT);
  706. if (status == INSTALL_SUCCESS && wipe_cache) {
  707. ui->Print("\n-- Wiping cache (at package request)...\n");
  708. if (erase_volume("/cache")) {
  709. ui->Print("Cache wipe failed.\n");
  710. } else {
  711. ui->Print("Cache wipe complete.\n");
  712. }
  713. }
  714. if (status >= 0) {
  715. if (status != INSTALL_SUCCESS) {
  716. ui->SetBackground(RecoveryUI::ERROR);
  717. ui->Print("Installation aborted.\n");
  718. } else if (!ui->IsTextVisible()) {
  719. return Device::NO_ACTION;  // reboot if logs aren't visible
  720. } else {
  721. ui->Print("\nInstall from sdcard complete.\n");
  722. }
  723. }
  724. break;
  725. }
  726. case Device::APPLY_CACHE:
  727. ui->Print("\nAPPLY_CACHE is deprecated.\n");
  728. break;
  729. case Device::READ_RECOVERY_LASTLOG:
  730. choose_recovery_file(device);
  731. break;
  732. case Device::APPLY_ADB_SIDELOAD:
  733. status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
  734. if (status >= 0) {
  735. if (status != INSTALL_SUCCESS) {
  736. ui->SetBackground(RecoveryUI::ERROR);
  737. ui->Print("Installation aborted.\n");
  738. copy_logs();
  739. } else if (!ui->IsTextVisible()) {
  740. return Device::NO_ACTION;  // reboot if logs aren't visible
  741. } else {
  742. ui->Print("\nInstall from ADB complete.\n");
  743. }
  744. }
  745. break;
  746. }
  747. }
  748. }
  749. static void
  750. print_property(const char *key, const char *name, void *cookie) {
  751. printf("%s=%s\n", key, name);
  752. }
  753. static void
  754. load_locale_from_cache() {
  755. FILE* fp = fopen_path(LOCALE_FILE, "r");
  756. char buffer[80];
  757. if (fp != NULL) {
  758. fgets(buffer, sizeof(buffer), fp);
  759. int j = 0;
  760. unsigned int i;
  761. for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) {
  762. if (!isspace(buffer[i])) {
  763. buffer[j++] = buffer[i];
  764. }
  765. }
  766. buffer[j] = 0;
  767. locale = strdup(buffer);
  768. check_and_fclose(fp, LOCALE_FILE);
  769. }
  770. }
  771. static RecoveryUI* gCurrentUI = NULL;
  772. void
  773. ui_print(const char* format, ...) {
  774. char buffer[256];
  775. va_list ap;
  776. va_start(ap, format);
  777. vsnprintf(buffer, sizeof(buffer), format, ap);
  778. va_end(ap);
  779. if (gCurrentUI != NULL) {
  780. gCurrentUI->Print("%s", buffer);
  781. } else {
  782. fputs(buffer, stdout);
  783. }
  784. }
  785. int
  786. main(int argc, char **argv) {
  787. time_t start = time(NULL);
  788. //重定向标准输出和标准出错到/tmp/recovery.log 这个文件里
  789. //static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
  790. redirect_stdio(TEMPORARY_LOG_FILE);
  791. // If this binary is started with the single argument "--adbd",
  792. // instead of being the normal recovery binary, it turns into kind
  793. // of a stripped-down version of adbd that only supports the
  794. // 'sideload' command.  Note this must be a real argument, not
  795. // anything in the command file or bootloader control block; the
  796. // only way recovery should be run with this argument is when it
  797. // starts a copy of itself from the apply_from_adb() function.
  798. if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
  799. adb_main();
  800. return 0;
  801. }
  802. printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
  803. //装载recovery的分区表recovery.fstab
  804. load_volume_table();
  805. //在recovery中挂载/cache/recovery/last_log这个文件
  806. //#define LAST_LOG_FILE "/cache/recovery/last_log"
  807. ensure_path_mounted(LAST_LOG_FILE);
  808. rotate_last_logs(KEEP_LOG_COUNT);
  809. //获取参数
  810. //这个参数也可能是从/cache/recovery/command文件中得到相应的命令
  811. //也就是可以往command这个文件写入对应的格式的命令即可
  812. get_args(&argc, &argv);
  813. const char *send_intent = NULL;
  814. const char *update_package = NULL;
  815. int wipe_data = 0, wipe_cache = 0, show_text = 0;
  816. bool just_exit = false;
  817. bool shutdown_after = false;
  818. int arg;
  819. //参数有擦除分区,OTA更新等
  820. while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
  821. switch (arg) {
  822. case 's': send_intent = optarg; break;
  823. case 'u': update_package = optarg; break;
  824. case 'w': wipe_data = wipe_cache = 1; break;
  825. case 'c': wipe_cache = 1; break;
  826. case 't': show_text = 1; break;
  827. case 'x': just_exit = true; break;
  828. case 'l': locale = optarg; break;
  829. case 'g': {
  830. if (stage == NULL || *stage == '\0') {
  831. char buffer[20] = "1/";
  832. strncat(buffer, optarg, sizeof(buffer)-3);
  833. stage = strdup(buffer);
  834. }
  835. break;
  836. }
  837. case 'p': shutdown_after = true; break;
  838. case 'r': reason = optarg; break;
  839. case '?':
  840. LOGE("Invalid command argument\n");
  841. continue;
  842. }
  843. }
  844. //设置语言
  845. if (locale == NULL) {
  846. load_locale_from_cache();
  847. }
  848. printf("locale is [%s]\n", locale);
  849. printf("stage is [%s]\n", stage);
  850. printf("reason is [%s]\n", reason);
  851. //创建设备
  852. Device* device = make_device();
  853. //获取UI
  854. ui = device->GetUI();
  855. //设置当前的UI
  856. gCurrentUI = ui;
  857. //设置UI的语言信息
  858. ui->SetLocale(locale);
  859. //UI初始化
  860. ui->Init();
  861. int st_cur, st_max;
  862. if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) {
  863. ui->SetStage(st_cur, st_max);
  864. }
  865. //设置recovery的背景图
  866. ui->SetBackground(RecoveryUI::NONE);
  867. //设置界面上是否能够显示字符,使能ui->print函数开关
  868. if (show_text) ui->ShowText(true);
  869. //设置selinux权限,一般我会把selinux 给disabled
  870. struct selinux_opt seopts[] = {
  871. { SELABEL_OPT_PATH, "/file_contexts" }
  872. };
  873. sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
  874. if (!sehandle) {
  875. ui->Print("Warning: No file_contexts\n");
  876. }
  877. //虚函数,没有做什么流程
  878. device->StartRecovery();
  879. printf("Command:");
  880. for (arg = 0; arg < argc; arg++) {
  881. printf(" \"%s\"", argv[arg]);
  882. }
  883. printf("\n");
  884. //如果update_package(也就是要升级的OTA包)不为空的情况下
  885. //这里要对升级包的路径做一下路径转换,这里可以自由定制自己升级包的路径
  886. if (update_package) {
  887. // For backwards compatibility on the cache partition only, if
  888. // we're given an old 'root' path "CACHE:foo", change it to
  889. // "/cache/foo".
  890. //这里就是做转换的方法
  891. //先比较传进来的recovery参数的前6个byte是否是CACHE
  892. //如果是将其路径转化为/cache/CACHE: ......
  893. if (strncmp(update_package, "CACHE:", 6) == 0) {
  894. int len = strlen(update_package) + 10;
  895. char* modified_path = (char*)malloc(len);
  896. strlcpy(modified_path, "/cache/", len);
  897. strlcat(modified_path, update_package+6, len);
  898. printf("(replacing path \"%s\" with \"%s\")\n",
  899. update_package, modified_path);
  900. //这个update_package就是转换后的路径
  901. update_package = modified_path;
  902. }
  903. }
  904. printf("\n");
  905. property_list(print_property, NULL);
  906. //获取属性,这里应该是从一个文件中找到ro.build.display.id
  907. //获取recovery的版本信息
  908. property_get("ro.build.display.id", recovery_version, "");
  909. printf("\n");
  910. //定义一个安装成功的标志位INSTALL_SUCCESS  ----> 其实是个枚举,值为0
  911. int status = INSTALL_SUCCESS;
  912. //判断转换后的OTA升级包的路径是否不为空,如果不为空
  913. //执行install_package 函数进行升级
  914. if (update_package != NULL) {
  915. status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);
  916. //判断是否升级成功
  917. if (status == INSTALL_SUCCESS && wipe_cache) {
  918. //擦除这个路径,相当于删除了这个路径下的OTA升级包
  919. if (erase_volume("/cache")) {
  920. LOGE("Cache wipe (requested by package) failed.");
  921. }
  922. }
  923. //如果安装不成功
  924. if (status != INSTALL_SUCCESS) {
  925. ui->Print("Installation aborted.\n");
  926. // If this is an eng or userdebug build, then automatically
  927. // turn the text display on if the script fails so the error
  928. // message is visible.
  929. char buffer[PROPERTY_VALUE_MAX+1];
  930. property_get("ro.build.fingerprint", buffer, "");
  931. if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) {
  932. ui->ShowText(true);
  933. }
  934. }
  935. }
  936. //如果跑的是格式化数据区,那么就走这个流程
  937. else if (wipe_data) {
  938. if (device->WipeData()) status = INSTALL_ERROR;
  939. //格式化/data分区
  940. if (erase_volume("/data")) status = INSTALL_ERROR;
  941. if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
  942. if (erase_persistent_partition() == -1 ) status = INSTALL_ERROR;
  943. if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
  944. }
  945. //格式化cache分区
  946. else if (wipe_cache) {
  947. if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
  948. if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n");
  949. }
  950. else if (!just_exit) {
  951. status = INSTALL_NONE;  // No command specified
  952. ui->SetBackground(RecoveryUI::NO_COMMAND);
  953. }
  954. //如果安装失败或者。。。
  955. if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
  956. copy_logs();
  957. //显示错误的LOGO
  958. ui->SetBackground(RecoveryUI::ERROR);
  959. }
  960. Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
  961. if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
  962. Device::BuiltinAction temp = prompt_and_wait(device, status);
  963. if (temp != Device::NO_ACTION) after = temp;
  964. }
  965. // Save logs and clean up before rebooting or shutting down.
  966. //完成recovery升级
  967. finish_recovery(send_intent);
  968. switch (after) {
  969. case Device::SHUTDOWN:
  970. ui->Print("Shutting down...\n");
  971. property_set(ANDROID_RB_PROPERTY, "shutdown,");
  972. break;
  973. case Device::REBOOT_BOOTLOADER:
  974. ui->Print("Rebooting to bootloader...\n");
  975. property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
  976. break;
  977. default:
  978. ui->Print("Rebooting...\n");
  979. property_set(ANDROID_RB_PROPERTY, "reboot,");
  980. break;
  981. }
  982. sleep(5); // should reboot before this finishes
  983. return EXIT_SUCCESS;
  984. }

Android5.0 Recovery源代码分析与定制(一)【转】的更多相关文章

  1. 《LINUX3.0内核源代码分析》第二章:中断和异常 【转】

    转自:http://blog.chinaunix.net/uid-25845340-id-2982887.html 摘要:第二章主要讲述linux如何处理ARM cortex A9多核处理器的中断.异 ...

  2. 0. chromium源代码分析 - 序

    本打算在CSDN写完这系列文字,却因为在CSDN中误删了一篇blog,该篇blog被移到了回收站.然而CSDN居然没有从回收站撤销删除的操作方法.联想到之前CSDN泄密的问题,其可靠性值得怀疑.随转向 ...

  3. Android HandlerThread 源代码分析

    HandlerThread 简单介绍: 我们知道Thread线程是一次性消费品,当Thread线程运行完一个耗时的任务之后.线程就会被自己主动销毁了.假设此时我又有一 个耗时任务须要运行,我们不得不又 ...

  4. 获取android-5.0.2_r1代码6.7G

    获取 android-5.0.2_r1 源代码的坎坷路: 服务器相关 ====== * 国外服务器直接拉取,我一共有多个国外服务器,在获取android代码时下载速度都能到10MB/s的下载速度甚至更 ...

  5. Spark SQL Catalyst源代码分析之UDF

    /** Spark SQL源代码分析系列文章*/ 在SQL的世界里,除了官方提供的经常使用的处理函数之外.一般都会提供可扩展的对外自己定义函数接口,这已经成为一种事实的标准. 在前面Spark SQL ...

  6. Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

    Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了 ...

  7. Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四)

    Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四) 题记:本来计划的SolrCloud的Recovery策略的文章是3篇的,但是没想到Recovery的内容蛮多的,前面 ...

  8. Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)

    Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及P ...

  9. Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二)

    Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二) 题记:  前文<Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一)>中提 ...

随机推荐

  1. Linux+Apache+PHP+MySQL服务器环境配置(CentOS篇)

    1.配置php.ini vi /etc/php.ini 2.配置apache 先给需要配置的文件做个备份 cp /etc/httpd/conf/httpd.conf /etc/httpd/conf/h ...

  2. 图解TCP/IP笔记(1)——TCP/IP协议群

    转载请注明:https://www.cnblogs.com/igoslly/p/9167916.html TCP/IP制定  制定:IETF 记录:RFC - Request for comment ...

  3. [Windows Server 2008] 手工创建安全网站

    ★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频.★ 本节我们将带领大家:手工创建安全站 ...

  4. 【译】x86程序员手册10 - 第4章系统架构

    1.1.2 Part II -- Systems Programming 系统编程 This part presents those aspects of the architecture that ...

  5. javascript的严格模式:use strict

    ECMAscript 5添加的运行模式,禁止一些非标准.不安全的操作. <script> "use strict"; console.log("这是严格模式. ...

  6. linux中集群的免秘钥SSH直接登录

    这里以三台mysql的主从服务器为例:manage.master.slave1.slave2   给4个机器生成秘钥文件 以manage为例,执行命令,生成空字符串的秘钥(后面要使用公钥),命令是: ...

  7. GFS分布式文件系统脚本

    #!/bin/bashfor i in $(fdisk -l | grep -wo "/dev/sd[b-z]" | sort)dodd if=/dev/zero of=$i bs ...

  8. EF 配置

    DbContext public class ZSZDbContext : DbContext { //ILog ILogger 容易混淆 private static ILog log = LogM ...

  9. Linux下源码安装Peach-2.3.8教程

    在peach文件夹下运行 python peach.py ./samples/HelloWorld.xml 提示先安装4Suite-XML. 根据提示在dependences文件夹下安装,出现两次错误 ...

  10. 浏览器控制之 selenium,phantomJs谷无头浏览器

    目录 浏览器控制之 selenium,phantomJs谷无头浏览器 selenium phantomJs 需求是尽可能多的爬取豆瓣网中的电影信息 谷歌无头浏览器 浏览器控制之 selenium,ph ...