android recovery 主系统代码分析【转】
本文转载自:http://blog.csdn.net/andyhuabing/article/details/9248713
阅读完上一篇文章: http://blog.csdn.net/andyhuabing/article/details/9226569
我们已经清楚了如何进入正常模式和Recovery模式已有深刻理解了,假设进入了Recovery模式,那么其核心代码是怎么做的呢?
代码路径在 Android 源码的根路径: bootable\recovery 其入口文件就是 recovery.c 中 main函数
下面就开始逐步了解其Recovery的设计思想:
static const char *COMMAND_FILE = "/cache/recovery/command";
static const char *INTENT_FILE = "/cache/recovery/intent";
static const char *LOG_FILE = "/cache/recovery/log";
注解里面描述的相当清楚:
* The recovery tool communicates with the main system through /cache files.
* /cache/recovery/command - INPUT - command line for tool, one arg per line
* /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
* /cache/recovery/intent - OUTPUT - intent that was passed in
static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
static const char *CACHE_ROOT = "/cache";
static const char *SDCARD_ROOT = "/sdcard";
下面的描述针对写入的 command 有大致的介绍:
* The arguments which may be supplied in the recovery.command file:
* --send_intent=anystring - write the text out to recovery.intent
* --update_package=path - verify install an OTA package file
* --wipe_data - erase user data (and cache), then reboot
* --wipe_cache - wipe cache (but not user data), then reboot
* --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
两种升级模式步骤说明:
* After completing, we remove /cache/recovery/command and reboot.
* Arguments may also be supplied in the bootloader control block (BCB).
* These important scenarios must be safely restartable at any point:
*
* FACTORY RESET
* 1. user selects "factory reset"
* 2. main system writes "--wipe_data" to /cache/recovery/command
* 3. main system reboots into recovery
* 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
* -- after this, rebooting will restart the erase --
* 5. erase_volume() reformats /data
* 6. erase_volume() reformats /cache
* 7. finish_recovery() erases BCB
* -- after this, rebooting will restart the main system --
* 8. main() calls reboot() to boot main system
*
* OTA INSTALL
* 1. main system downloads OTA package to /cache/some-filename.zip
* 2. main system writes "--update_package=/cache/some-filename.zip"
* 3. main system reboots into recovery
* 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
* -- after this, rebooting will attempt to reinstall the update --
* 5. install_package() attempts to install the update
* NOTE: the package install must itself be restartable from any point
* 6. finish_recovery() erases BCB
* -- after this, rebooting will (try to) restart the main system --
* 7. ** if install failed **
* 7a. prompt_and_wait() shows an error icon and waits for the user
* 7b; the user reboots (pulling the battery, etc) into the main system
* 8. main() calls maybe_install_firmware_update()
* ** if the update contained radio/hboot firmware **:
* 8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
* -- after this, rebooting will reformat cache & restart main system --
* 8b. m_i_f_u() writes firmware image into raw cache partition
* 8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
* -- after this, rebooting will attempt to reinstall firmware --
* 8d. bootloader tries to flash firmware
* 8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
* -- after this, rebooting will reformat cache & restart main system --
* 8f. erase_volume() reformats /cache
* 8g. finish_recovery() erases BCB
* -- after this, rebooting will (try to) restart the main system --
* 9. main() calls reboot() to boot main system
从上面的几段注解中,基本上就明白的 Recovery 是如何工作的啦。下面就从具体代码开始一步步分析。
1、recovery main 函数
- int
- main(int argc, char **argv) {
- time_t start = time(NULL);
- // If these fail, there's not really anywhere to complain...
- freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
- freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
- printf("Starting recovery on %s", ctime(&start));
将标准输出和标准错误输出重定位到"/tmp/recovery.log",如果是eng模式,就可以通过adb pull /tmp/recovery.log, 看到当前的log信息,这为我们提供了有效的调试手段。
ui_init();
一个简单的基于framebuffer的ui系统,叫miniui 主要建立了图像部分(gglInit、gr_init_font、framebuffer)及进度条和事件处理(input_callback)
load_volume_table();
根据 /etc/recovery.fstab 建立分区表
// command line args come from, in decreasing precedence:
// - the actual command line
// - the bootloader control block (one per line, after "recovery")
// - the contents of COMMAND_FILE (one per line)
get_args(&argc, &argv);
从misc 分区以及 CACHE:recovery/command 文件中读入参数,写入到argc, argv (get_bootloader_message) 并有可能写回 misc 分区(set_bootloader_message)
做完以上事情后就开始解析具体参数:
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
switch (arg) {
case 'p': previous_runs = atoi(optarg); break;
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;
case 't': ui_show_text(1); break;
case '?':
LOGE("Invalid command argument\n");
continue;
}
}
printf("Command:");
for (arg = 0; arg < argc; arg++) {
printf(" \"%s\"", argv[arg]);
}
printf("\n");
以上仅仅是打印表明进入到哪一步,方便调试情况的掌握
下面的代码就是具体干的事情了:
if (update_package != NULL) {
status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);
if (status == INSTALL_SUCCESS && wipe_cache) {
if (erase_volume("/cache")) {
LOGE("Cache wipe (requested by package) failed.");
}
}
if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
} else if (wipe_data) {
if (device_wipe_data()) status = INSTALL_ERROR;
if (erase_volume("/data")) status = INSTALL_ERROR;
if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
clear_sdcard_update_bootloader_message();
} else if (wipe_cache) {
if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");
clear_sdcard_update_bootloader_message();
} else {
status = update_by_key(); // No command specified
}
根据用户提供参数,调用各项功能,比如,安装一个升级包,擦除cache分区, 擦除user data分区等等,后在会将继续详细分解。
if (status != INSTALL_SUCCESS) prompt_and_wait();
如果前面做的操作成功则进入重启流程,否则由用户操作,可选操作为: reboot, 安装update.zip,除cache分区, 擦除user data分区
// Otherwise, get ready to boot the main system...
finish_recovery(send_intent);
先看函数注解:
// clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read), and
// record any intent we were asked to communicate back to the system.
// this function is idempotent: call it as many times as you like.
其实主要的就是如下函数操作:
// Remove the command file, so recovery won't repeat indefinitely.
if (ensure_path_mounted(COMMAND_FILE) != 0 ||
(unlink(COMMAND_FILE) && errno != ENOENT)) {
LOGW("Can't unlink %s\n", COMMAND_FILE);
}
将指定分区mounted 成功并 unlink 删除一个文件的目录项并减少它的链接数
ensure_path_unmounted(CACHE_ROOT);
将指定分区 unmounted
sync(); // For good measure.
对于上面的代码总结:
它的功能如下:
1、将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command
2、将 /tmp/recovery.log 复制到 "CACHE:recovery/log";
3、清空 misc 分区,这样重启就不会进入recovery模式
4、删除command 文件:CACHE:recovery/command;
最后重启机器
ui_print("Rebooting...\n");
android_reboot(ANDROID_RB_RESTART, 0, 0);
2、factory reset 核心代码实现
按照前面所列的8条步骤,其中1-6及7-8都与 main 通用流程一样,不再复述。
* 5. erase_volume() reformats /data
* 6. erase_volume() reformats /cache
这两个操作是如何做到的呢?
if (erase_volume("/data")) status = INSTALL_ERROR;
if (erase_volume("/cache")) status = INSTALL_ERROR;
最后就是
clear_sdcard_update_bootloader_message();
看看 erase_volume() 函数先:
- static int
- erase_volume(const char *volume) {
- ui_set_background(BACKGROUND_ICON_INSTALLING);
- ui_show_indeterminate_progress();
- ui_print("Formatting %s...\n", volume);
- ensure_path_unmounted(volume);
- if (strcmp(volume, "/cache") == 0) {
- // Any part of the log we'd copied to cache is now gone.
- // Reset the pointer so we copy from the beginning of the temp
- // log.
- tmplog_offset = 0;
- }
- return format_volume(volume);
- }
上面红字标明的是重要函数调用
int ensure_path_unmounted(const char* path) {
Volume* v = volume_for_path(path);
result = scan_mounted_volumes();
return unmount_mounted_volume(mv);
}
就是将指定的path中径mount point进行卸载掉,而 format_volume的主要功能就是:
MtdWriteContext *write = mtd_write_partition(partition);
mtd_erase_blocks(write, -1);
mtd_write_close(write);
不要细说了吧,就是将整个分区数据全清掉。
最后一个函数:
void
clear_sdcard_update_bootloader_message() {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
set_bootloader_message(&boot);
}
就是将misc分区数据重置清0
这样子就完成的恢复出厂设置的情况了。将 data/cache分区erase擦掉就好了。
3、OTA 安装 核心代码实现
主要函数就是如何安装 Package :
* 5. install_package() attempts to install the update
* NOTE: the package install must itself be restartable from any point
int
install_package(const char* path, int* wipe_cache, const char* install_file)
-->
static int
really_install_package(const char *path, int* wipe_cache){
clear_sdcard_update_bootloader_message();
ui_set_background(BACKGROUND_ICON_INSTALLING);
ui_print("Finding update package...\n");
ui_show_indeterminate_progress();
LOGI("Update location: %s\n", path);
更新 ui 显示
for(;((i < 5)&&(ensure_path_mounted(path) != 0));i++){
LOGE("Can't mount %s\n",path);
sleep(1);
}
if((i >= 5)&&(ensure_path_mounted(path) != 0)){
return INSTALL_CORRUPT;
}
确保升级包所在分区已经mount,通常为 cache 分区或者 SD 分区
RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
// Look for an RSA signature embedded in the .ZIP file comment given
// the path to the zip. Verify it matches one of the given public
// keys.
//
// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
// or no key matches the signature).
err = verify_file(path, loadedKeys, numKeys);
从/res/keys中装载公钥,并进行确认文件的合法性
/* Try to open the package.
*/
ZipArchive zip;
err = mzOpenZipArchive(path, &zip);
打开升级包,将相关信息存到ZipArchive数据机构中,便于后面处理。
/* Verify and install the contents of the package.
*/
ui_print("Installing update...\n");
return try_update_binary(path, &zip, wipe_cache);
进行最后的安装包文件
}
// If the package contains an update binary, extract it and run it.
static int
try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {
const ZipEntry* binary_entry =
mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
char* binary = "/tmp/update_binary";
unlink(binary);
int fd = creat(binary, 0755);
bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
close(fd);
mzCloseZipArchive(zip);
将升级包内文件META-INF/com/google/android/update-binary 复制为/tmp/update_binary
// When executing the update binary contained in the package, the
// arguments passed are:
//
// - the version number for this interface
//
// - an fd to which the program can write in order to update the
// progress bar. The program can write single-line commands:
int pipefd[2];
pipe(pipefd);
char** args = malloc(sizeof(char*) * 5);
args[0] = binary;
args[1] = EXPAND(RECOVERY_API_VERSION); // defined in android.mk
args[2] = malloc(10);
sprintf(args[2], "%d", pipefd[1]);
args[3] = (char*)path;
args[4] = buf_uuid;
args[5] = NULL;
组装新的进程参数
pid_t pid = fork();
if (pid == 0) { // child process
close(pipefd[0]);
execv(binary, args);
}
// parent process
close(pipefd[1]);
ui_show_progress
ui_set_progress
ui_print
总结一下代码主要行为功能:
1、将会创建新的进程,执行:/tmp/update_binary
2、同时,会给该进程传入一些参数,其中最重要的就是一个管道fd,供新进程与原进程通信。
3、新进程诞生后,原进程就变成了一个服务进程,它提供若干UI更新服务:
a) progress
b) set_progress
c) ui_print
这样,新进程就可以通过老进程的UI系统完成显示任务。而其他功能就靠它自己了。
android recovery 主系统代码分析【转】的更多相关文章
- android recovery 主系统代码分析
阅读完上一篇文章: http://blog.csdn.net/andyhuabing/article/details/9226569 我们已经清楚了如何进入正常模式和Recovery模式已有深刻理解了 ...
- Android 4.2启动代码分析(一)
Android系统启动过程分析 Android系统的框架架构图如下(来自网上): Linux内核启动之后----->就到Android的Init进程 ----->进而启动Android ...
- android recovery 系统代码分析 -- 选择进入【转】
本文转载自:http://blog.csdn.net/andyhuabing/article/details/9226569 最近做Recovery的规范及操作指导文档,花了一些时间将流程搞清. An ...
- android recover 系统代码分析 -- 选择进入
最近做Recovery的规范及操作指导文档,花了一些时间将流程搞清. Android利用Recovery模式,进行恢复出厂设置,OTA升级,patch升级及firmware升级.而在进入Recover ...
- android adb 流程原理代码分析(一)
由于要用到adb的知识,但是对adb啥也不了解,看了下android的代码,adb的源码在system/core/adb下面,然后网上搜下了资料,发现很多大神的源码分析,瞬间信心爆棚,把大神写的博客都 ...
- 例子Architecting Android…The clean way?----代码分析
Presention层: 整个应用启动的时候,就执行依赖的初始化.编译项目之后,Dagger依赖框架使用ApplicationComponent生成一个DaggerApplicationCOmpo ...
- Android 7.0系统代码调用安装apk时报错FileUriExposedException完美解决
项目更新遇到问题 Android项目开发中经常遇到下载更新的需求,以前调用系统安装器执行安装操作代码如下: Intent intent = new Intent(); intent.setActi ...
- 吴裕雄--天生自然Android开发学习:android 背景相关与系统架构分析
1.Android背景与当前的状况 Android系统是由Andy Rubin创建的,后来被Google收购了:最早的版本是:Android 1.1版本 而现在最新的版本是今年5.28,Google ...
- 考勤系统代码分析——主页布局easyui框架
考勤系统主页的布局用的是easyui的Layout控件 Layout:布局容器有5个区域:北.南.东.西和中间.中间区域面板是必须的,边缘的面板都是可选的.每个边缘区域面板都可以通过拖拽其边框改变大小 ...
随机推荐
- html5——动画案例(时钟)
1.秒钟转360度需要60s分60步 2.分针转360度需要3600s分60步 3.秒钟转360度需要43200s分60步 <!DOCTYPE html> <html lang=&q ...
- JS——null
变量被赋值为null,目的往往是为了销毁这个对象: var n1 = 1; n1 = null;
- Oracle、Db2、SqlServer、MySQL 数据库插入当前系统时间
做易买网项目,由于对数据库插入系统时间不了解,常常遇到的问题: 1.java.sql.SQLException: ORA-01861: 文字与格式字符串不匹配.原因:由于获取系统时间类型不对,应为sy ...
- HDU_1023_Train Problem II_卡特兰数
Train Problem II Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- Objective-C在ARC下结合GCD的单例模式和宏模版
单例模式在iOS开发过程中经常用到,苹果提供过objective c单例的比较官方的写法: static MyGizmoClass *sharedGizmoManager = nil; + (MyGi ...
- CAD调用移动命令
主要用到函数说明: _DMxDrawX::SendStringToExecuteFun 把命令当着函数执行,可以传参数,详细说明如下: 参数 说明 IDispatch* pParam 命令参数,IMx ...
- copy contents of file with variable number in Matlab
input : transient.case output: transient_1.case, transient_2.case, transient_3.case ... ************ ...
- PAT 1119 Pre- and Post-order Traversals
Suppose that all the keys in a binary tree are distinct positive integers. A unique binary tree can ...
- hdu 4862KM&最小费用最大流
/*最小K路径覆盖的模型,用费用流或者KM算法解决, 构造二部图,X部有N*M个节点,源点向X部每个节点连一条边, 流量1,费用0,Y部有N*M个节点,每个节点向汇点连一条边,流量1, 费用0,如果X ...
- 同开三本DJANGO,需要提升一下本职工作的能力啦
慢慢将一些概念固化到基因内,才有可能和SPRING MVC,MEAN之类的好好作比较吧. 全都是基于1.8版本的教材,爽!!!