/* 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
*/
// 如果OTA升级包内包含update-binary,就会将它解压出来作为安装执行器。即fork的子进程会运行这个bin,这个bin真正执行将OTA升级包写入系统。
static int
try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache,
std::vector<std::string>& log_buffer, int retry_count)
{
read_source_target_build(zip, log_buffer);
// 这里是父进程准备与子进程通信的pipe
int pipefd[2];
pipe(pipefd);
std::vector<std::string> args;
// 根据系统是否是AB升级,确定升级命令,实际就是从OTA包中取出update-binary并组建升级命令
// 升级命令既是调用子进程运行update-binary的指令
int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args);
mzCloseZipArchive(zip);
if (ret) {
close(pipefd[0]);
close(pipefd[1]);
return ret;
}
// 接下来通过注释阐述了父进程,即当前进程能够支持的函数
// 这些函数将由子进程(即将创建的update-binary的进程)通过pipe向父进程(本进程)以字符串的形式发出函数名称和参数,进而根据函数名称调用函数
// 基本常用的包括设置进度(显示到ui)、ui打印和清除数据
// 子进程将被创建以执行升级,而本进程(父进程)会循环等待pipe的数据,并解释后执行对应的函数,实现进程分离独立运行(父进程实现UI和其他工作,子进程负责安装)
// 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.
//
// wipe_cache
// a wipe of cache will be performed following a successful
// installation.
//
// clear_display
// turn off the text display.
//
// enable_reboot
// packages can explicitly request that they want the user
// to be able to reboot during installation (useful for
// debugging packages that don't exit).
//
// - the name of the package zip file.
//
// - an optional argument "retry" if this update is a retry of a failed
// update attempt.
//
// 准备创建子进程,准备子进程参数,准备管道
// Convert the vector to a NULL-terminated char* array suitable for execv.
const char* chr_args[args.size() + 1];
chr_args[args.size()] = NULL;
for (size_t i = 0; i < args.size(); i++) {
chr_args[i] = args[i].c_str();
}
pid_t pid = fork();
if (pid == -1) {
close(pipefd[0]);
close(pipefd[1]);
LOGE("Failed to fork update binary: %s\n", strerror(errno));
return INSTALL_ERROR;
}
// 子进程,执行update-binary,跳转到updater.cpp
if (pid == 0) {
umask(022);
close(pipefd[0]);
execv(chr_args[0], const_cast<char**>(chr_args));
fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
_exit(-1);
}
close(pipefd[1]);
*wipe_cache = false;
bool retry_update = false;
// 父进程,在管道的一端进行读循环,直到子进程退出时关闭管道后,跳出循环
// 子进程(update-binary)会向管道写入函数名和参数,这些字符串形式的信息在接下来的while()中被解析,然后由父进程(本进程)执行对应的函数。
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->ShowProgress(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->SetProgress(fraction);
} else if (strcmp(command, "ui_print") == 0) {
char* str = strtok(NULL, "\n");
if (str) {
ui->PrintOnScreenOnly("%s", str);
} else {
ui->PrintOnScreenOnly("\n");
}
fflush(stdout);
} else if (strcmp(command, "wipe_cache") == 0) {
*wipe_cache = true;
} else if (strcmp(command, "clear_display") == 0) {
ui->SetBackground(RecoveryUI::NONE);
} else if (strcmp(command, "enable_reboot") == 0) {
// packages can explicitly request that they want the user
// to be able to reboot during installation (useful for
// debugging packages that don't exit).
ui->SetEnableReboot(true);
} else if (strcmp(command, "retry_update") == 0) {
retry_update = true;
} else if (strcmp(command, "log") == 0) {
// Save the logging request from updater and write to
// last_install later.
log_buffer.push_back(std::string(strtok(NULL, "\n")));
} else if (strcmp(command, "wipe_all") == 0){
printf("set bWipeAfterUpdate to true.\n");
bWipeAfterUpdate = true;
}else {
LOGE("unknown command [%s]\n", command);
}
}
fclose(from_child);
// 子进程结束,这里获取子进程的退出状态码
int status;
waitpid(pid, &status, 0);
if (retry_update) {
return INSTALL_RETRY;
}
// 分别获取子进程是否正常退出(不是因为崩溃或信号退出的),并获取退出状态码。若子进程正常退出,则这里已经完成了安装,接下来回到install.cpp完成收尾后重启。
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
return INSTALL_ERROR;
}
return INSTALL_SUCCESS;
}