Recovery启动流程–recovery.cpp分析
- 2019 年 10 月 7 日
- 笔记
这篇文章主要通过分析高通recovery目录下的recovery.cpp源码,对recovery启动流程有一个宏观的了解。
当开机以后,在lk阶段,如果是recovery,会设置boot_into_recovery=1,然后读取recovery.img镜像,把recovery.img的地址和ramdisk等信息作为参数启动kernel,从而进入recovery模式,下面进行简单的分析。
为什么要分析recovery.cpp这个文件?
下面的代码位于bootable/recovery/etc/init.rc,由此可知,进入recovery模式后会执行sbin /recovery,此文件是bootable/recovery/recovery.cpp生成(可查看对应目录的Android.mk查看),所以recovery.cpp是recovery模式的入口。
service recovery /sbin/recovery seclabel u:r:recovery:s0
1. 前期准备:
首先列出recovery流程的几个重要点,接着会详细分析
- 加载recovery.fstab分区表
- 解析传入的参数
- recovery界面相关的设置
- 执行命令
- 如果没有命令,等待用户输入
- 结束recovery
bootable/recovery/recovery.cpp int main(int argc, char **argv) { // Take last pmsg contents and rewrite it to the current pmsg session. static const char filter[] = "recovery/"; // Do we need to rotate? bool doRotate = false; __android_log_pmsg_file_read( LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logbasename, &doRotate); //这里的意思暂时不理解 // Take action to refresh pmsg contents __android_log_pmsg_file_read( LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &doRotate); // If this binary is started with the single argument "--adbd", // instead of being the normal recovery binary, it turns into kind // of a stripped-down version of adbd that only supports the // 'sideload' command. Note this must be a real argument, not // anything in the command file or bootloader control block; the // only way recovery should be run with this argument is when it // starts a copy of itself from the apply_from_adb() function. //如果二进制文件使用单个参数"--adbd"启动 //而不是正常的recovery启动(不带参数即为正常启动) //它变成精简版命令时只支持sideload命令。它必须是一个正确可用的参数 //不在/cache/recovery/command中,也不受B2B控制 //是apply_from_adb()的副本 if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { adb_server_main(0, DEFAULT_ADB_PORT, -1); return 0; } time_t start = time(NULL); // redirect_stdio should be called only in non-sideload mode. Otherwise // we may have two logger instances with different timestamps. redirect_stdio(TEMPORARY_LOG_FILE); printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table(); //从上面建立的分区表信息中读取是否有cache分区,因为log等重要信息都存在cache分区里 has_cache = volume_for_path(CACHE_ROOT) != nullptr; //从传入的参数或/cache/recovery/command文件中得到相应的命令 get_args(&argc, &argv); const char *send_intent = NULL; const char *update_package = NULL; bool should_wipe_data = false; bool should_wipe_cache = false; bool should_wipe_ab = false; size_t wipe_package_size = 0; bool show_text = false; bool sideload = false; bool sideload_auto_reboot = false; bool just_exit = false; bool shutdown_after = false; int retry_count = 0; bool security_update = false; int status = INSTALL_SUCCESS; bool mount_required = true; int arg; int option_index; //while循环解析command或者传入的参数,并把对应的功能设置为true或给相应的变量赋值 while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { switch (arg) { case 'i': send_intent = optarg; break; case 'n': android::base::ParseInt(optarg, &retry_count, 0); break; case 'u': update_package = optarg; break; case 'w': should_wipe_data = true; break; case 'c': should_wipe_cache = true; break; case 't': show_text = true; break; case 's': sideload = true; break; case 'a': sideload = true; sideload_auto_reboot = true; break; case 'x': just_exit = true; break; case 'l': locale = optarg; break; case 'g': { if (stage == NULL || *stage == '