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 == '