recovery代码分析之二
来源:互联网 发布:千里眼淘宝版 编辑:程序博客网 时间:2024/05/17 23:05
http://blog.csdn.net/sjz_iron/article/details/8707044
1.函数get_args()
首先考虑如下情形:在main system下,我们通过OTA客户端(自己编写)选择升级包update.zip(假设存放于/mnt/sdcard中)后进入recovery模式下进行升级。在这一过程中,需要解决两个问题,第一:如果使机器重启并自动进入recovery模式;第二:如何将升级包的路径/mnt/sdcard/update.zip传入到recovery模式,使其能查找到升级包。这涉及到android系统的启动流程,如下图所示:
图1 系统启动流程
机器启动时,首先检测是否有组合键按下,如检测到(音量下+power)组合键,则进入recovery;否则检测系统的/misc分区,根据此分区存储的命令选择不同的模式。
/misc分区下存储着结构体bootloader_message,称之为BCB块,其定义如下:
struct bootloader_message{
char command[32]; //存放不同的启动命令
char status[32]; //存放执行结果
char recovery[1024]; //存放/cache/recovery/command中的命令
};
结构体成员command[32]中存放着不同的启动命令:
- boot-recovery:系统会进入Recovery模式
- update-radia或update-hboot:
- command为空:正常启动,进入main system
而recovery[1024]中则存放着升级包路径,其存储结构如下:第一行存放字符串“recovery”;第二行存放路径信息“--update=/mnt/sdcard/update.zip”等。
除了BCB块外,还可以将路径信息--update=/mnt/sdcard/update.zip写入文件/cache/recovery/command传递给recovery模式。
进入recovery模式,系统通过get_args函数(如下代码所示)获取升级包信息。此函数首先获取BCB块信息(代码07-34),如果未检测到相关信息,则继续检测/cache/
recovery/command文件(代码36-53);最后,将启动命令boot-recovery及升级包路径--update=/mnt/sdcard/update.zip重新写入到BCB块中(代码55-64),以便系统下次启动时再次进入到recovery模式,直到升级成功后执行finish_recovery函数清空BCB及/cache/recovery/command文件。
- // 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)
- static void
- get_args(int *argc, char ***argv) {
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
- if (boot.command[0] != 0 && boot.command[0] != 255) {
- LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
- }
- if (boot.status[0] != 0 && boot.status[0] != 255) {
- LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
- }
- // --- if arguments weren't supplied, look in the bootloader control block
- if (*argc <= 1) {
- boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
- const char *arg = strtok(boot.recovery, "\n");
- if (arg != NULL && !strcmp(arg, "recovery")) {
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = strdup(arg);
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if ((arg = strtok(NULL, "\n")) == NULL) break;
- (*argv)[*argc] = strdup(arg);
- }
- LOGI("Got arguments from boot message\n");
- } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
- LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
- }
- }
- // --- if that doesn't work, try the command file
- if (*argc <= 1) {
- FILE *fp = fopen_path(COMMAND_FILE, "r");
- if (fp != NULL) {
- char *argv0 = (*argv)[0];
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = argv0; // use the same program name
- char buf[MAX_ARG_LENGTH];
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if (!fgets(buf, sizeof(buf), fp)) break;
- (*argv)[*argc] = strdup(strtok(buf, "\r\n")); // Strip newline.
- }
- check_and_fclose(fp, COMMAND_FILE);
- LOGI("Got arguments from %s\n", COMMAND_FILE);
- }
- }
- // --> 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);
- }
代码段1 get_args()函数
2.函数install_package()
此函数定义如下
- int
- install_package(const char* path, int* wipe_cache, const char* install_file)
- {
- FILE* install_log = fopen_path(install_file, "w");
- if (install_log) {
- fputs(path, install_log);
- fputc('\n', install_log);
- } else {
- LOGE("failed to open last_install: %s\n", strerror(errno));
- }
- int result = really_install_package(path, wipe_cache);
- if (install_log) {
- fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);
- fputc('\n', install_log);
- fclose(install_log);
- }
- return result;
- }
代码段2 install_package()函数定义
此函数所实现的功能为:调用函数really_install_package进行升级,并将升级结果写入到文件install_file中。recovery.c中对于存放升级结果的文件定义如下:
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
当升级完成后进入main system,可以通过此文件读取升级结果,并给出用户相应的提示:升级成功或失败。
而函数really_install_package会对升级包进行一系列的校验,通过校验后,调用try_update_binary函数完成升级。因此,try_update_binary()才是真正升级的地方。如下:
- // 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);
- if (binary_entry == NULL) {
- mzCloseZipArchive(zip);
- return INSTALL_CORRUPT;
- }
- char* binary = "/tmp/update_binary";
- unlink(binary);
- int fd = creat(binary, 0755);
- if (fd < 0) {
- mzCloseZipArchive(zip);
- LOGE("Can't make %s\n", binary);
- return INSTALL_ERROR;
- }
- bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
- close(fd);
- mzCloseZipArchive(zip);
- if (!ok) {
- LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
- return INSTALL_ERROR;
- }
- int pipefd[2];
- pipe(pipefd);
- // 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:
- //
- // progress <frac> <secs>
- // fill up the next <frac> part of of the progress bar
- // over <secs> seconds. If <secs> is zero, use
- // set_progress commands to manually control the
- // progress of this segment of the bar
- //
- // set_progress <frac>
- // <frac> should be between 0.0 and 1.0; sets the
- // progress bar within the segment defined by the most
- // recent progress command.
- //
- // firmware <"hboot"|"radio"> <filename>
- // arrange to install the contents of <filename> in the
- // given partition on reboot.
- //
- // (API v2: <filename> may start with "PACKAGE:" to
- // indicate taking a file from the OTA package.)
- //
- // (API v3: this command no longer exists.)
- //
- // ui_print <string>
- // display <string> on the screen.
- //
- // - the name of the package zip file.
- //
- 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] = NULL;
- pid_t pid = fork();
- if (pid == 0) {
- close(pipefd[0]);
- execv(binary, args);
- fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
- _exit(-1);
- }
- close(pipefd[1]);
- *wipe_cache = 0;
- char buffer[1024];
- FILE* from_child = fdopen(pipefd[0], "r");
- while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
- char* command = strtok(buffer, " \n");
- if (command == NULL) {
- continue;
- } else if (strcmp(command, "progress") == 0) {
- char* fraction_s = strtok(NULL, " \n");
- char* seconds_s = strtok(NULL, " \n");
- float fraction = strtof(fraction_s, NULL);
- int seconds = strtol(seconds_s, NULL, 10);
- ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION),
- seconds);
- } else if (strcmp(command, "set_progress") == 0) {
- char* fraction_s = strtok(NULL, " \n");
- float fraction = strtof(fraction_s, NULL);
- ui_set_progress(fraction);
- } else if (strcmp(command, "ui_print") == 0) {
- char* str = strtok(NULL, "\n");
- if (str) {
- ui_print("%s", str);
- } else {
- ui_print("\n");
- }
- } else if (strcmp(command, "wipe_cache") == 0) {
- *wipe_cache = 1;
- } else {
- LOGE("unknown command [%s]\n", command);
- }
- }
- fclose(from_child);
- int status;
- waitpid(pid, &status, 0);
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
- return INSTALL_ERROR;
- }
- return INSTALL_SUCCESS;
- }
代码段3 try_update_binary
代码04-21:提取升级包中的"META-INF/com/google/android/update-binary"文件,此文件定义了系统升级所需要进行的操作,系统将根据其中的命令执行相应的操作;
代码73-80:创建一个子进程,执行脚本文件META-INF/com/google/android/update-binary,这是系统升级的核心所在。如下是某升级包中update-script(update-script
是update-binary文件的文本形式,我们可以通过此文件来了解升级时的具体操作)中的部分指令:
- mount("ext4", "EMMC", "/dev/block/mmcblk0p12", "/system");
- assert(file_getprop("/system/build.prop", "ro.build.fingerprint") == "ETON/sp8825eabase/sp8825ea:4.0.3/IML74K/D525A-V1_A04_20130322-094434:userdebug/test-keys" ||
- file_getprop("/system/build.prop", "ro.build.fingerprint") == "ETON/sp8825eabase/sp8825ea:4.0.3/IML74K/D525A-V1_A05_20130322-094434:userdebug/test-keys");
- assert(getprop("ro.product.device") == "sp8825ea" ||
- getprop("ro.build.product") == "sp8825ea");
- ui_print("Verifying current system...");
- ui_print("pre-build:ETON/sp8825eabase/sp8825ea:4.0.3/IML74K/D525A-V1_A04_20130322-094434:userdebug/test-keys");
- ui_print("post-build:ETON/sp8825eabase/sp8825ea:4.0.3/IML74K/D525A-V1_A05_20130322-094434:userdebug/test-keys");
- show_progress(0.100000, 0);
- assert(apply_disk_space(8928364));
- assert(apply_patch_check("/system/build.prop", "098db2c72e97c3e84a1b14b782befe9446e44f49", "64a46c872b8766b20feeaf2a4802ecae53a75e97"));
- set_progress(0.000285);
- assert(apply_patch_check("/system/lib/modules/blcr.ko", "78dbaacbe60f9106d160dbe5cbd3778441c3b999", "7eb55dbf8b929c8296a51f62d2c0cfb56a8a7f32"));
- set_progress(0.014533);
- assert(apply_patch_check("/system/lib/modules/blcr_imports.ko", "9a42096b4b1da8345e9f87da68a492740de50a61", "288ff8c8aafbd6c00b467ed89c4f2580937f77a8"));
- set_progress(0.016106);
- assert(apply_patch_check("EMMC:/dev/block/mmcblk0p2:5509716:78bbdc657109f192eb606371f12a30b881fbeda9:5512524:b1d56633f28bbed2a7530daeeee85b6710173ee0"));
- set_progress(0.623307);
- assert(apply_patch_check("EMMC:/dev/block/mmcblk0p3:3014656:406b98e17bae26628b46003189122b74b64c5992:3014656:c0e9e938c6a42fe42531d92cfb8dc0301be99885"));
- set_progress(0.955538);
- assert(apply_patch_check("EMMC:/dev/block/mmcblk0p1:344064:3e46eecb33050e461a80d38787e9e773ab09ff04:339968:c2e4ed3c6d9554fd1415a473f7097ec1a5d0845f"));
- set_progress(0.993456);
- assert(apply_patch_space(129286));
- # ---- start making changes here ----
- ui_print("Removing unneeded files...");
- delete("/system/app/CalllogManager.apk", "/system/lib/libext2_blkid.so",
- "/system/lib/libext2_com_err.so", "/system/lib/libext2_e2p.so",
- "/system/lib/libext2_profile.so", "/system/lib/libext2_uuid.so",
- "/system/lib/libext2fs.so",
- "/system/recovery.img");
- show_progress(0.800000, 0);
- ui_print("Patching system files...");
- apply_patch("/system/lib/modules/blcr.ko", "-",
- 78dbaacbe60f9106d160dbe5cbd3778441c3b999, 129286,
- 7eb55dbf8b929c8296a51f62d2c0cfb56a8a7f32, package_extract_file("patch/system/lib/modules/blcr.ko.p"));
- set_progress(0.014247);
- apply_patch("/system/lib/modules/blcr_imports.ko", "-",
- 9a42096b4b1da8345e9f87da68a492740de50a61, 14273,
- 288ff8c8aafbd6c00b467ed89c4f2580937f77a8, package_extract_file("patch/system/lib/modules/blcr_imports.ko.p"));
- set_progress(0.015820);
- delete("/system/recovery-from-boot.p",
- "/system/etc/install-recovery.sh");
- ui_print("Patching modem ...");
- apply_patch("EMMC:/dev/block/mmcblk0p2:5509716:78bbdc657109f192eb606371f12a30b881fbeda9:5512524:b1d56633f28bbed2a7530daeeee85b6710173ee0",
- "/sdcard/modem.bin", b1d56633f28bbed2a7530daeeee85b6710173ee0,
- 5512524,
- 78bbdc657109f192eb606371f12a30b881fbeda9, package_extract_file("patch/modem.bin.p"));
- set_progress(0.623294);
代码段4 update-script内容
尽管并不确定这些代码的含义,但一眼望去我们仍能够分辨出其中包含的一些命令,诸如:mount,assert,ui_print,package_extract_file,apply_patch等。OK,继续往下读try_update_binary函数,读到一段while循环(86-116),心中一喜,这应该就是对应于各种命令的执行代码吧。遗憾的是,仔细读来,发现并非如此。while循环中只针对于progress、set_progress、wipe_cache、ui_print四种命令进行了处理,而package_extract_file、apply_patch等关键命令却了无踪影。显然,升级代码并非我想象的这么简单,在这个try_update_binary之后,一定还隐藏着其他东东。于是在./bootable目录下尝试搜索了apply_patch关键词,终于在./bootable/recovery/updater/install.c中找到了答案。
先看./bootable/recovery/updater/install.c中的一段代码:
- void RegisterInstallFunctions() {
- RegisterFunction("mount", MountFn);
- RegisterFunction("is_mounted", IsMountedFn);
- RegisterFunction("unmount", UnmountFn);
- RegisterFunction("format", FormatFn);
- RegisterFunction("show_progress", ShowProgressFn);
- RegisterFunction("set_progress", SetProgressFn);
- RegisterFunction("delete", DeleteFn);
- RegisterFunction("delete_recursive", DeleteFn);
- RegisterFunction("package_extract_dir", PackageExtractDirFn);
- RegisterFunction("package_extract_file", PackageExtractFileFn);
- RegisterFunction("retouch_binaries", RetouchBinariesFn);
- RegisterFunction("undo_retouch_binaries", UndoRetouchBinariesFn);
- RegisterFunction("symlink", SymlinkFn);
- RegisterFunction("set_perm", SetPermFn);
- RegisterFunction("set_perm_recursive", SetPermFn);
- RegisterFunction("getprop", GetPropFn);
- RegisterFunction("file_getprop", FileGetPropFn);
- RegisterFunction("write_raw_image", WriteRawImageFn);
- RegisterFunction("apply_patch", ApplyPatchFn);
- RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
- RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
- RegisterFunction("read_file", ReadFileFn);
- RegisterFunction("sha1_check", Sha1CheckFn);
- RegisterFunction("wipe_cache", WipeCacheFn);
- RegisterFunction("ui_print", UIPrintFn);
- RegisterFunction("run_program", RunProgramFn);
- }
代码段5 updater-script与执行函数的映射
显而易见,对应于updater-script中的命令操作全都定义在./bootable/recovery/updater/install.c。系统升级时,由try_update_binary中创建的子进程执行这些操作,而try_
update_binary中的while循环只是负责更新升级进度条而已。
结合脚本与实现代码,可以对操作命令有较为清楚的认识。以脚本appy_patch为例,可以将其以函数的形式写为apply_patch(src_file, target_file, target_sha1, target_size, sha1_1, patch_1),它实现的功能为:将源文件src_file利用差分文件patch_1进行升级,并将结果文件写入到target_file中。target_size表示目录文件的大小,target_sha和sha1_1分别为相应的校验数据。
其他命令及对应的代码不再详述。
3.函数finish_recovery()
- static void
- finish_recovery(const char *send_intent) {
- // By this point, we're ready to return to the main system...
- if (send_intent != NULL) {
- FILE *fp = fopen_path(INTENT_FILE, "w");
- if (fp == NULL) {
- LOGE("Can't open %s\n", INTENT_FILE);
- } else {
- fputs(send_intent, fp);
- check_and_fclose(fp, INTENT_FILE);
- }
- }
- // Copy logs to cache so the system can find out what happened.
- copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);
- copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false);
- copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false);
- chmod(LOG_FILE, 0600);
- chown(LOG_FILE, 1000, 1000); // system user
- chmod(LAST_LOG_FILE, 0640);
- chmod(LAST_INSTALL_FILE, 0644);
- // Reset to mormal system boot so recovery won't cycle indefinitely.
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- set_bootloader_message(&boot);
- // 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);
- }
- ensure_path_unmounted(CACHE_ROOT);
- sync(); // For good measure.
- }
代码段6 finish_recovery函数
此函数完成的功能包括:
代码04-12:将send_intent写入文件并传递给main system;
代码14-21:将日志信息从TEMPORARY_LOG_FILE复制到LOG_FILE、LAST_LOG_FILE等文件中(这些文件存放在/cache目录);
代码23-26:清空BCB;
代码28-32:删除/cache/recovery/command文件。
显然,当系统升级成功后,执行finish_recovery函数重启,系统将不再进入recovery模式。
- recovery代码分析之二
- recovery代码分析之二
- recovery代码分析之三:try_update_binary
- recovery代码分析之三:try_update_binary
- Android Recovery 代码分析
- Android Recovery 代码分析
- Android Recovery 代码分析
- Recovery代码分析之一
- Recovery代码分析之一
- Android Recoveryの代码分析
- Android Recoveryの代码分析
- Android Recovery 的代码分析
- Android Recovery 的代码分析
- Android Recovery 的代码分析
- Android Recovery 的代码分析
- recovery ui界面代码分析
- Android系统Recovery工作原理之使用update.zip升级过程分析(二)
- Android系统Recovery工作原理之使用update.zip升级过程分析(二)
- Android项目打包jar 之一
- Block的学习
- Java网络消息交互,对响应的byte消息的典型解析:
- android开发中经常看到@Override
- 一些不错的资源收集
- recovery代码分析之二
- 小米4安卓开发真机调试在Eclipse中Unknown的解决方法
- WebView全屏播放h5视频
- MYSQL的用户变量(@)和系统变量(@@)
- 日经社説 20150303 ロシアに漂う閉塞感を憂う
- Docker 在Centos 6.5 X64下安装
- gdb调试
- dropzone.js点击删除图片按钮后只执行action不跳转到新的jsp页面,也不刷新本页面
- 面向服务的架构SOA