安卓recovery流程分析【第二篇】
Android Recovery 源码解析和界面定制
Recovery主要功能
源码路径和主要原文件
recoverycpp
命令行参数
main 函数
界面定制
实现Recovery UI
实现头部显示和列表项
实现ScreenRecoveryUI
实现设备类
添加编译实现
Android Recovery 源码解析和界面定制
Recovery主要功能
深入了解recovery源码前,先浏览下recovery能够给我们提供哪些功能;
首先是我们熟悉的恢复工厂设置 (清除数据,清楚缓存)–> wipe_data wipe_cache
刷升级包,可以通过sdcard升级,通常说的卡刷,有些还提供ADB sideload升级;
可以进行系统的系统的OTA升级,本质上同手动刷包一样;
源码路径和主要原文件
在Android源码环境中,recovery的源码主要在bootable/recovery文件下,另外再device目录下,会根据各个设备定制自己的接口以及UI界面,也就是文章后半部分分析的界面定制的内容;
在bootable/recovery目录下,主要的源文件有:
LOCAL_SRC_FILES :=
adb_install.cpp
asn1_decoder.cpp
bootloader.cpp
device.cpp
fuse_sdcard_provider.c
install.cpp
recovery.cpp
roots.cpp
screen_ui.cpp
ui.cpp
verifier.cpp \
该部分代码在编译后,会统一输出到 out/recovery/root/目录;
recovery.cpp
命令行参数
recovery最后是编译成一个可执行的命令,放在recovery文件系统中的/sbin/recovery;所以我们可以在终端中直接运行该命令,具体的参数如下:
–send_intent=anystring - 传递给recovery的信息
–adbd -adb sideload升级
–update_package=path - 指定OTA升级包
–wipe_data - 清楚用户数据并重启
–wipe_cache - 清楚缓存并重启
–set_encrypted_filesystem=on|off - 使能或者关闭文件系统加密
–just_exit - 退出并重启
recovery.cpp的main 函数
从main入口函数分析recovery的主要源码:
输出重定向
redirect_stdio(TEMPORARY_LOG_FILE);
//redirect log to serial output
#ifdef LogToSerial
freopen("/dev/ttyFIQ0", "a", stdout); setbuf(stdout, NULL);
freopen("/dev/ttyFIQ0", "a", stderr); setbuf(stderr, NULL);
#endif
这部分代码很容易理解,主要作用是输出log到/tem/recovery.log文件中
执行adb sideload分支
if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {adb_main(0, DEFAULT_ADB_PORT);return 0;
}
判断命令行参数是否为–adbd,并执行adb_main函数,这部分代码在后续adb_install.cpp中分析;
填充fstab结构体
在main函数中调用 load_volume_table(),读取/etc/recovery.emmc.fstab文件内容,并填充fstab结构体,但是并没有执行挂载操作:
load_volume_table函数在roots.cpp文件中,也是很容易理解:
void load_volume_table()
{
...
int emmcState = getEmmcState();//判断是否为emmc设备
if(emmcState) {fstab = fs_mgr_read_fstab("/etc/recovery.emmc.fstab");
}else {fstab = fs_mgr_read_fstab("/etc/recovery.fstab");
}
...
//读取文件中每个条目内容,填充fstab结构体
ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk");
...
//日志打印fstable信息
printf("recovery filesystem table\n");
printf("=========================\n");
for (i = 0; i < fstab->num_entries; ++i) {Volume* v = &fstab->recs[i];printf(" %d %s %s %s %lld\n", i, v->mount_point, v->fs_type,v->blk_device, v->length);
}
printf("\n");
}
读取控制参数
recovery 和 bootloader 必须通过内存的一个特定分区,才能进行相互的通信,这个分区一般是/misc;
对应的信息数据结构体为bootloader_message;
参照源码中bootloader_message 的注释
struct bootloader_message {
char command[32];//bootloader 启动时读取改数据,决定是否进入recovery模式
char status[32];//由bootloader进行更新,标识升级的结果;
char recovery[768];//由Android系统进行写入,recovery从中读取信息;
char stage[32];
char reserved[224];
};
recovery 根据命令行参数,再从/misc分区中解析出对应的参数,进行后续的操作,具体的调用函数为get_args(&argc, &argv);
static void
get_args(int *argc, char ***argv) {
struct bootloader_message boot;//参数结构体
memset(&boot, 0, sizeof(boot));
get_bootloader_message(&boot); // 具体的读取信息的函数,可能为空的情况
stage = strndup(boot.stage, sizeof(boot.stage));
...// 如果上述情况为空,则从/cache/recovery/command获取参数,其中 COMMAND_FILE=/cache/recovery/command
if (*argc <= 1) {FILE *fp = fopen_path(COMMAND_FILE, "r");if (fp != NULL) {char *token;char *argv0 = (*argv)[0];*argv = (char **) malloc(sizeof(char *) * MAX_ARGS);(*argv)[0] = argv0; // use the same program namechar buf[MAX_ARG_LENGTH];for (*argc = 1; *argc < MAX_ARGS; ++*argc) {if (!fgets(buf, sizeof(buf), fp)) break;token = strtok(buf, "\r\n");if (token != NULL) {(*argv)[*argc] = strdup(token); // Strip newline.} else {--*argc;}}check_and_fclose(fp, COMMAND_FILE);LOGI("Got arguments from %s\n", COMMAND_FILE);}
}//把从/cache/recovery/command获取参数重新写回到/misc分区
// --> write the arguments we have back into the bootloader control block
// always boot into recovery after this (until finish_recovery() is called)
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
int i;
for (i = 1; i < *argc; ++i) {strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
set_bootloader_message(&boot);
}
解析命令行参数
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {switch (arg) {case 'f': factory_mode = optarg; bFactoryMode = true; break;case 'i': send_intent = optarg; break;case 'u': update_package = optarg; break;case 'w': should_wipe_data = true; break;case 'k': update_rkimage = optarg;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 == '\0') {char buffer[20] = "1/";strncat(buffer, optarg, sizeof(buffer)-3);stage = strdup(buffer);}break;}case 'f'+'w': //fw_updateif((optarg)&&(!sdboot_update_package)){sdboot_update_package = strdup(optarg);}break;case 'd': //demo_copyif((optarg)&&(! demo_copy_path)){demo_copy_path = strdup(optarg);}break;case 'p': shutdown_after = true; break;case 'r': reason = optarg; break;case 'w'+'a': { should_wipe_all = should_wipe_data = should_wipe_cache = true;show_text = true;} break;case '?':LOGE("Invalid command argument\n");continue;}
}
这部分代码很简单,就是通过getopt_long进行命令行参数的解析并赋值;
显示界面和功能选项
接下来就是创建device,显示对应UI界面和功能选项;
Device* device = make_device();//可以自己实现一个设备
ui = device->GetUI();
gCurrentUI = ui;//赋值ui界面ui->SetLocale(locale);//获取归属地信息
ui->Init();//初始化,可以重载,在init中实现相应功能
ui->SetStage(st_cur, st_max);
ui->SetBackground(RecoveryUI::NONE);
进行分区挂载操作
ensure_path_mountedint ensure_path_mounted(const char* path) {
...
Volume* v = volume_for_path(path);//根据路径名获取分区信息
...
int result;
result = scan_mounted_volumes();const MountedVolume* mv =find_mounted_volume_by_mount_point(v->mount_point);//根据挂载点,获取已挂载分区的信息,如果不为空,说明已经成功挂载
if (mv) {// volume is already mountedreturn 0;
}result = mkdir(v->mount_point, 0755); // 创建对应目录,确保目录存在,也有可能目录已经存在
if (result!=0)
{printf("failed to create %s dir,err=%s!\n",v->mount_point,strerror(errno));
}// 根据文件系统类型,执行mount操作
if (strcmp(v->fs_type, "yaffs2") == 0) {// mount an MTD partition as a YAFFS2 filesystem.mtd_scan_partitions();const MtdPartition* partition;partition = mtd_find_partition_by_name(v->blk_device);if (partition == NULL) {LOGE("failed to find \"%s\" partition to mount at \"%s\"\n",v->blk_device, v->mount_point);return -1;}return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0);
} else if (strcmp(v->fs_type, "ext4") == 0 ||strcmp(v->fs_type, "ext3") == 0) {result = mount(v->blk_device, v->mount_point, v->fs_type,MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");if (result == 0) return 0;LOGE("failed to mount %s %s (%s)\n", v->mount_point, v->blk_device, strerror(errno));return -1;
} else if (strcmp(v->fs_type, "vfat") == 0) {result = mount(v->blk_device, v->mount_point, v->fs_type,MS_NOATIME | MS_NODEV | MS_NODIRATIME, "shortname=mixed,utf8");if (result == 0) return 0;LOGW("trying mount %s to ntfs\n", v->blk_device);result = mount(v->blk_device, v->mount_point, "ntfs",MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");if (result == 0) return 0;char *sec_dev = v->fs_options;if(sec_dev != NULL) {char *temp = strchr(sec_dev, ',');if(temp) {temp[0] = '\0';}result = mount(sec_dev, v->mount_point, v->fs_type,MS_NOATIME | MS_NODEV | MS_NODIRATIME, "shortname=mixed,utf8");if (result == 0) return 0;LOGW("trying mount %s to ntfs\n", sec_dev);result = mount(sec_dev, v->mount_point, "ntfs",MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");if (result == 0) return 0;}LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));return -1;
}else if (strcmp(v->fs_type, "ntfs") == 0) {LOGW("trying mount %s to ntfs\n", v->blk_device);result = mount(v->blk_device, v->mount_point, "ntfs",MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");if (result == 0) return 0;LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));return -1;
}LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point);
return -1;
}
界面定制
实现Recovery UI
在自己的设备目录下:device/vendor/recovery/recovery_ui.cpp
#include <linux/input.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>#include "common.h"
#include "device.h"
#include "screen_ui.h"
实现头部显示和列表项
const char* HEADERS[] = { "Volume up/down to move highlight;","power button to select.","",NULL };const char* ITEMS[] ={ "reboot system now",//"apply update from ADB","apply update from external storage","update rkimage from external storage","apply update from cache","wipe data/factory reset","wipe cache partition","recovery system from backup",NULL };
实现ScreenRecoveryUI
class DeviceUI : public ScreenRecoveryUI {public:DeviceUI () :consecutive_power_keys(0) {
}//实现自己的识别key类型的功能,可以为不同的输入设备适配recovery功能
virtual KeyAction CheckKey(int key) {if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {return TOGGLE;}if (key == KEY_POWER) {++consecutive_power_keys;if (consecutive_power_keys >= 7) {return REBOOT;}} else {consecutive_power_keys = 0;}return ENQUEUE;
}private:
int consecutive_power_keys;
};
实现设备类
class MyDevice : public Device {public:
RkDevice() :ui(new DeviceUI ) {
}RecoveryUI* GetUI() { return ui; }int HandleMenuKey(int key_code, int visible) {if (visible) {switch (key_code) {case KEY_DOWN:case KEY_VOLUMEDOWN:return kHighlightDown;case KEY_UP:case KEY_VOLUMEUP:return kHighlightUp;case KEY_ENTER:case KEY_POWER:return kInvokeItem;}}return kNoAction;
}BuiltinAction InvokeMenuItem(int menu_position) {switch (menu_position) {case 0: return REBOOT;//case 1: return APPLY_ADB_SIDELOAD;case 1: return APPLY_EXT;case 2: return APPLY_INT_RKIMG;case 3: return APPLY_CACHE;case 4: return WIPE_DATA;case 5: return WIPE_CACHE;case 6: return RECOVER_SYSTEM;default: return NO_ACTION;}
}const char* const* GetMenuHeaders() { return HEADERS; }
const char* const* GetMenuItems() { return ITEMS; }private:
RecoveryUI* ui;
};//创建自己实现的设备
Device* make_device() {
return new MyDevice ;
}
安卓recovery流程分析【第二篇】相关推荐
- Android 8.0 学习(23)---recovery 流程分析
Android 8.0 recovery 流程分析 这里主要分析non A/B模式下的recovery流程 A/B模式下的recovery在boot中 后续会不断补充,如果有疏漏或者错误的地方,请 ...
- SAP PP COR2下达工单系统报错说-系统状态APNG是激活的- 分析第二篇
SAP PP COR2下达工单系统报错说-系统状态APNG是激活的- 分析第二篇 笔者所在的项目上启用了ECM(Engineer Change Management)功能,重要数据的修改都要事先创建一 ...
- internetreadfile读取数据长度为0_Go发起HTTP2.0请求流程分析(后篇)——标头压缩
阅读建议 这是HTTP2.0系列的最后一篇,笔者推荐阅读顺序如下: Go中的HTTP请求之--HTTP1.1请求流程分析 Go发起HTTP2.0请求流程分析(前篇) Go发起HTTP2.0请求流程分析 ...
- Android 8.0 recovery 流程分析
这里主要分析non A/B模式下的recovery流程 A/B模式下的recovery在boot中 后续会不断补充,如果有疏漏或者错误的地方,请指出,共同学习,谢谢! 一.流程分析 首先列出recov ...
- android dex加载过程,8.1版本dex加载流程笔记--第二篇:DexFile::Open流程与简单脱壳原理...
在看雪发了,52再发一下,共同学习 菜鸟刚刚学完了dex_file.cc这个源码,大致搞明白了大佬们hook脱整体加固的原理了,原理在帖子最后 学习了大佬angelToms的帖子https://bbs ...
- Android recovery 流程分析
Recovery简介 Android利用Recovery模式,进行恢复出厂设置,OTA升级,patch升级及firmware升级. 升级一般通过运行升级包中的META-INF/com/google/a ...
- Realtek 8125驱动分析第二篇——触发硬件中断
书接上文,本文讲述瑞昱2.5G网卡rtl8125的触发硬件中断相关内容. 目录 1 中断处理函数(ISR)注册 2 触发硬件中断 可以通过下面的命令来看interrupts. howard@B150: ...
- nutch代码分析第二篇——crawl.crawl
2021SC@SDUSC 顾名思义,org.apache.nutch.crawl.Crawl实现的是一个完整的抓取过程,包括各种方法的初始化,url集的建立 /* Perform complete c ...
- 高通Android智能平台环境搭建_编译流程分析
高通Android智能平台环境搭建_编译流程分析 高通平台环境搭建,编译,系统引导流程分析 TOC \o \h \z \u 1. 高通平台android开发总结. 7 1.1 搭建高通平台环境开发环境 ...
- 高通平台环境搭建,编译,系统引导流程分析 .
1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通开发板上烧录文件系统 建立高通平台开发环境 高通平台,android和 modem 编译流程分析 高通平台 7620 启动流 ...
最新文章
- kotlin Bean加载失败lateinit property has not been initialized
- 烧水壶起水沟了怎么办?
- C语言删掉无关变量无输出,C语言变量类型与输出控制用法实例教程
- SpringMVC日期类型转换问题处理方法归纳
- java面试手写单链表_(转)面试大总结之一:Java搞定面试中的链表题目
- 工业机器人控制问题---来自睿慕课
- 读取excel内容在网页上显示出来
- 红包不是你想送就能送 摩拜物联网技术成行业壁垒
- nginx报错The program 'nginx' can be found in the following packages
- 第05课 Linux命令初探(一)
- Stm32 基于蓝牙的串口通信 详细篇
- Visio连接线设置箭头形状失效
- 耶鲁大学 博弈论(Game Theory) 笔记1
- 【论文阅读笔记】High Quality Monocular Depth Estimation via Transfer Learning
- 【转】26张PPT让你告别拖延症
- linux系统重启网卡命令
- ShareSDK 新浪微博平台注册指南
- 【附源码】计算机毕业设计SSM天润律师事务所管理系统
- HyperMate Pro硬件钱包全体验
- 三星台式计算机参数,评测三星平板电脑Galaxy Tab S7 2020款怎么样?三星Galaxy Tab S7 2020款参数配置如何?...
热门文章
- Oracle ADF开发实战指南pdf
- Linux下svn的部署
- WMI 脚本入门:第二部分 (MSDN)
- lambda表达式和切片
- 干货 | 找工作的经验总结(一)
- keras_contrib安装
- 【每日算法Day 82】面试经典题:求第K大数,我写了11种实现,不来看看吗?
- poj3276(Face The Right Way)反转(开关问题)
- 数据预处理—7.数据插补之拉格朗日插值法、牛顿差值法及python实现
- pandas—pandas.read_parquet