本章关键点总结 & 说明:
导图是不断迭代的,这里主要关注➕ recovery模式启动部分即可,主要从 更新包简介,mian函数解析开始解读,分析了main函数中关键的方法 获取参数getargs和执行菜单命令prompt_and_wait。
对于recovery模式,一般均采用第三方的方案,比如:在使用MTK平台时,我们这边一般会直接采用广升FOTA的服务商直接进行升级相关的工作。对于第三方的recovery代码,实际上也是参考android原生代码进行改动,核心原理不变,因此这里对recovery模式的代码研究还是采用 google 的原生recovery代码。
android 启动时会通过组合键 判定是否进入recovery模式,也可以通过android的RecoverySystem来进入。recovery模式下还是会启动bootloader、kernel,最后会通过bootargs来判定,如果进入recovery模式则加载recovery专属的rc文件,进而进入到recovery模式。
recovery模式主要是使用升级包进行升级,升级包解压后一般是这样
包含的文件简要说明:
- boot.img:更新boot分区所需要的文件。这个boot.img主要包括kernel+ramdisk。
- system/ :内容在升级后会放在系统的system分区。主要用来更新系统的一些应用或则应用会用到的一些库
- recovery/ :中的recovery-from-boot.p是boot.img和recovery.img的补丁(patch),主要用来更新recovery分区,其中etc/目录下的install-recovery.sh是更新脚本。
- META-INF放的是更新包的签名文件和更新脚本,只有更新包的签名和设备签名匹配才能进行系统升级,包含三个关键文件。
最后META-INF的几个文件解读如下所示:
- CERT.RSA:与签名文件相关联的签名程序块文件,它存储了用于签名JAR文件的公共签名。
- CERT.SF:这是JAR文件的签名文件,其中前缀CERT代表签名者。
- MANIFEST.MF:manifest文件定义了与包的组成结构相关的数据。
- update-binary:二进制文件,相当于脚本解释器,能够识别updater-script中描述的操作。
- updater-script:脚本文件,具体描述了更新过程。我们可以根据具体情况编写该脚本来适应我们的具体需求。
- metadata:描述设备信息及环境变量的元数据。主要包括一些编译选项,签名公钥,时间戳以及设备型号等。
接下来我们从main函数启动开始分析,到如何传递参数,以及如何执行菜单命令 角度来逐步分析。
1 main函数启动分析
int main(int argc, char **argv) {
time_t start = time(NULL);
redirect_stdio(TEMPORARY_LOG_FILE);
if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
adb_main();//如果参数中有adb,作为adbd的daemon启动
return 0;
}
//读取/etc/recovery.fstab文件,保存了recovery模式下分区情况(名称+参数)
load_volume_table();
ensure_path_mounted(LAST_LOG_FILE);
rotate_last_logs(KEEP_LOG_COUNT);
//获得启动参数(按照优先级,分别是recovery->misc->/cache/recovery/command中命令)
get_args(&argc, &argv);
const char *send_intent = NULL;
const char *update_package = NULL;
int wipe_data = 0, wipe_cache = 0, show_text = 0;
bool just_exit = false;
bool shutdown_after = false;
int arg;
//解析启动参数
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
switch (arg) {
case 's': send_intent = optarg; break;
case 'u': update_package = optarg; break; //升级系统
case 'w': wipe_data = wipe_cache = 1; break; //擦出数据
case 'c': wipe_cache = 1; break; //擦除cache
case 't': show_text = 1; break;//指示升级时是否显示UI
case 'x': just_exit = true; break; //退出
case 'l': locale = optarg; break; //指定locale
//...
case '?':
LOGE("Invalid command argument\n");
continue;