这篇文章主要通过分析高通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;
cas