五、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;
}
详解Android系统升级流程
本文深入探讨了Android系统的升级过程,特别关注了try_update_binary函数的详细执行流程,包括从升级包中解析并运行updater_binary,以及通过管道在父进程与子进程间的数据交互。此外,还介绍了如何通过调用execv执行updater-binary,进而进入updater.cpp的main方法,执行update-scrypt脚本,完成系统的升级。
8719





