五、try_update_binary详细流程
本篇将介绍try_update_binary 执行升级包中的updater_binary可执行文件解析update-scrypt进行升级流程
bootable/recovery/install.cpp static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount, std::vector<std::string>* log_buffer, int retry_count, int* max_temperature) { ...... ...... //根据之前install.cpp的流程分析,这里传入升级包的路径,和从内存地址中读出的升级包数据 int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature); ...... ...... }
try_update_binary方法
bootable/recovery/install.cpp // If the package contains an update binary, extract it and run it. static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache, std::vector<std::string>* log_buffer, int retry_count, int* max_temperature) { //将META/com/android/metadata中一些值写入到last_install中 read_source_target_build(zip, log_buffer); //调用pipe在父子进程间使用管道 //pipefd[0]用于读取数据 ,pipefd[1]用于写入数据 int pipefd[2]; pipe(pipefd); std::vector<std::string> args; #ifdef AB_OTA_UPDATER int ret = update_binary_command(package, zip, "/sbin/update_engine_sideload", retry_count, pipefd[1], &args); #else //找到update-binary,放入到tmp路径,和其他一些参数放入args容器中 int ret = update_binary_command(package, zip, "/tmp/update-binary", retry_count, pipefd[1], &args); #endif if (ret) { close(pipefd[0]); close(pipefd[1]); log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); return ret; } // 将args中的参数转换为数组形式 const char* chr_args[args.size() + 1]; chr_args[args.size()] = nullptr; for (size_t i = 0; i < args.size(); i++) { chr_args[i] = args[i].c_str(); } //fork一个子进程 pid_t pid = fork(); if (pid == -1) { close(pipefd[0]); close(pipefd[1]); PLOG(ERROR) << "Failed to fork update binary"; log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure)); return INSTALL_ERROR; } if (pid == 0) { umask(022); close(pipefd[0]); //exevc调用可执行程序 执行updater-binary //int execl(const char *path, const char *arg, ...); //path:可执行文件的路径 //arg:传递的参数(一定要传递NULL) execv(chr_args[0], const_cast<char**>(chr_args)); // Bug: 34769056 // We shouldn't use LOG/PLOG in the forked process, since they may cause // the child process to hang. This deadlock results from an improperly // copied mutex in the ui functions. fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno)); _exit(EXIT_FAILURE); } close(pipefd[1]); std::atomic<bool> logger_finished(false); std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished)); *wipe_cache = false; bool retry_update = false; char buffer[1024]; //函数说明:fdopen()会将参数fildes 的文件描述词, 转换为对应的文件指针后返回. FILE* from_child = fdopen(pipefd[0], "r"); //C 库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。 //当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。 //下面的while循环主要为了更新父进程UI,并通过管道与父进程交互 while (fgets(buffer, sizeof(buffer), from_child) != nullptr) { std::string line(buffer); size_t space = line.find_first_of(" \n"); std::string command(line.substr(0, space)); if (command.empty()) continue; ...... ...... return INSTALL_SUCCESS; }
update_binary_command方法
int update_binary_command(const std::string& package, ZipArchiveHandle zip, const std::string& binary_path, int retry_count, int status_fd, std::vector<std::string>* cmd) { CHECK(cmd != nullptr); // On traditional updates we extract the update binary from the package. //constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。 //最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果, //而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。 //constexpr所修饰的变量一定是编译期可求值的,所修饰的函数在其所有参数都是constexpr时,一定会返回constexpr。 static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; //在升级包中找到脚本解释器updater-binary ZipString binary_name(UPDATE_BINARY_NAME); ZipEntry binary_entry; if (FindEntry(zip, binary_name, &binary_entry) != 0) { LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME; return INSTALL_CORRUPT; } unlink(binary_path.c_str()); //创建/tmp/update-binary路径,并给到权限 int fd = open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755); if (fd == -1) { PLOG(ERROR) << "Failed to create " << binary_path; return INSTALL_ERROR; } //将updater-binary放入到文件夹中 int32_t error = ExtractEntryToFile(zip, &binary_entry, fd); close(fd); if (error != 0) { LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error); return INSTALL_ERROR; } //定义cmd容器中所包含的元素 *cmd = { binary_path,//updater-binary路径 std::to_string(kRecoveryApiVersion),//recoveryapi版本 std::to_string(status_fd),//pipefd[1]写入数据 package,//升级包路径 }; if (retry_count > 0) { cmd->push_back("retry"); } return 0; }
execv 执行update-binary,update-binary是由bootable/recovery/updater/ 编译生成的可执行文件,所以执行后,将进入updater.cpp的main方法
int main(int argc, char** argv) { // Various things log information to stdout or stderr more or less // at random (though we've tried to standardize on stdout). The // log file makes more sense if buffering is turned off so things // appear in the right order. setbuf(stdout, nullptr); setbuf(stderr, nullptr); // We don't have logcat yet under recovery. Update logs will always be written to stdout // (which is redirected to recovery.log). android::base::InitLogging(argv, &UpdaterLogger); //我们执行update-binary,首先还是看下时什么参数传进了main方法,以上次的经验,还是放在initlog之后 fprintf(stderr, "\n============updater.cpp main============\n"); int i; for (i = 0; i < argc; i++){ fprintf(stderr, "argc && argv we need know how much the argc argv\n"); fprintf(stderr, "argv %d is %s\n", i, argv[i]); } printf("\n========================================\n"); if (argc != 4 && argc != 5) { LOG(ERROR) << "unexpected number of arguments: " << argc; return 1; } char* version = argv[1]; if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') { // We support version 1, 2, or 3. LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1]; return 2; } // Set up the pipe for sending commands back to the parent process. int fd = atoi(argv[2]); FILE* cmd_pipe = fdopen(fd, "wb"); setlinebuf(cmd_pipe); // Extract the script from the package. // 从升级包中提取出updater-scrypt const char* package_filename = argv[3]; //加载内存空间 MemMapping map; if (!map.MapFile(package_filename)) { LOG(ERROR) << "failed to map package " << argv[3]; return 3; } //获取压缩包 ZipArchiveHandle za; int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za); if (open_err != 0) { LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err); CloseArchive(za); return 3; } //获取updater-scrypt脚本,读取到内存中 ZipString script_name(SCRIPT_NAME); ZipEntry script_entry; int find_err = FindEntry(za, script_name, &script_entry); if (find_err != 0) { LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": " << ErrorCodeString(find_err); CloseArchive(za); return 4; } std::string script; script.resize(script_entry.uncompressed_length); int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast<uint8_t*>(&script[0]), script_entry.uncompressed_length); if (extract_err != 0) { LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err); CloseArchive(za); return 5; } #if 1 //打印出脚本的内容 fprintf(stderr, "====== Updater-Script:\n"); fprintf(stderr, "%s\n\n", script.c_str()); #endif // Configure edify's functions. //注册脚本中语句处理函数,识别脚本中的命令 RegisterBuiltins(); RegisterInstallFunctions(); RegisterBlockImageFunctions(); RegisterDeviceExtensions(); // Parse the script. // 智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象, unique_ptr 独占所指向的对象 // 解析update-scrypt脚本 std::unique_ptr<Expr> root; int error_count = 0; int error = parse_string(script.c_str(), &root, &error_count); if (error != 0 || error_count > 0) { LOG(ERROR) << error_count << " parse errors"; CloseArchive(za); return 6; } sehandle = selinux_android_file_context_handle(); selinux_android_set_sehandle(sehandle); if (!sehandle) { fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n"); } // Evaluate the parsed script. 执行解析后的脚本 UpdaterInfo updater_info; updater_info.cmd_pipe = cmd_pipe; updater_info.package_zip = za; updater_info.version = atoi(version); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; State state(script, &updater_info); if (argc == 5) { if (strcmp(argv[4], "retry") == 0) { state.is_retry = true; } else { printf("unexpected argument: %s", argv[4]); } } ota_io_init(za, state.is_retry); std::string result; bool status = Evaluate(&state, root, &result); if (have_eio_error) { fprintf(cmd_pipe, "retry_update\n"); } if (!status) { if (state.errmsg.empty()) { LOG(ERROR) << "script aborted (no error message)"; fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); } else { LOG(ERROR) << "script aborted: " << state.errmsg; const std::vector<std::string> lines = android::base::Split(state.errmsg, "\n"); for (const std::string& line : lines) { // Parse the error code in abort message. // Example: "E30: This package is for bullhead devices." if (!line.empty() && line[0] == 'E') { if (sscanf(line.c_str(), "E%d: ", &state.error_code) != 1) { LOG(ERROR) << "Failed to parse error code: [" << line << "]"; } } fprintf(cmd_pipe, "ui_print %s\n", line.c_str()); } } // Installation has been aborted. Set the error code to kScriptExecutionFailure unless // a more specific code has been set in errmsg. if (state.error_code == kNoError) { state.error_code = kScriptExecutionFailure; } fprintf(cmd_pipe, "log error: %d\n", state.error_code); // Cause code should provide additional information about the abort. if (state.cause_code != kNoCause) { fprintf(cmd_pipe, "log cause: %d\n", state.cause_code); if (state.cause_code == kPatchApplicationFailure) { LOG(INFO) << "Patch application failed, retry update."; fprintf(cmd_pipe, "retry_update\n"); } } if (updater_info.package_zip) { CloseArchive(updater_info.package_zip); } return 7; } else { fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str()); } if (updater_info.package_zip) { CloseArchive(updater_info.package_zip); } return 0; }