Android OTA升级包制作脚本详解(五,升级脚本updater

写在前面:

首先当我们执行升级脚本updater-script的时候,就表示我们已经进入了升级安装状态。那么在我们就从实际的安装作为入口开始分析。也就是说我们从install.cpp中的install_package函数开始一步步来分析。

这里主要分析与脚本相关的部分,其他的请参考这位朋友的博文,我也很受启发。这里也借用一张图来帮助流程上的分析。

下面是调用的流程:install_package()–>really_install_package(),那么在really_install_package()函数中真正开始安装的逻辑如下:

/* Verify and install the contents of the package.*/ui->Print("Installing update…\n");err = try_update_binary(path, &zip, wipe_cache);if(err != INSTALL_SUCCESS)return err; try_update_binary是真正实现读取升级包中的脚本文件并执行相应的函数。在此函数中,通过调用fork函数创建出一个子进程,在子进程中开始读取并执行升级脚本文件。在此需要注意的是函数fork的用法,fork被调用一次,将做两次返回,在父进程中返回的是子进程的进程ID,为正数;而在子进程中,则返回0。子进程中所进行的操作,即execv(binary, args)。子进程创建成功后,开始执行升级代码,并通过管道与父进程交互(创建管道,并将pipefd[1]作为参数传递给子进程,子进程则将相关信息写入到此管道描述符中);而父进程则通过读取子进程传递过来的信息更新UI。

那么下面我们来具体来分析一下这个方法具体的逻辑。

static inttry_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {//定义一个常量用来封装查找相zip关信息的结果如“META-INF/com/google/android/update-binary”,便于后面进行解压,因为之前mzOpenZipArchive函数并没有对更新包进行解压操作,*zip是之前mzOpenZipArchive方法返回的一个ZipArchive对象。mzOpenZipArchive是打开升级包,,并将相关的信息拷贝到一个临时的ZipArchinve变量中。//ASSUMED_UPDATE_BINARY_NAME="META-INF/com/google/android/update-binary"const ZipEntry* binary_entry =mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);if (binary_entry == NULL) {mzCloseZipArchive(zip);return INSTALL_CORRUPT;}const char* binary = "/tmp/update_binary";unlink(binary);//创建“/tmp/update_binary”文件int fd = creat(binary, 0755);if (fd < 0) {mzCloseZipArchive(zip);LOGE("Can't make %s\n", binary);return INSTALL_ERROR;}//将META-INF/com/google/android/update-binary中的内容解压缩到"/tmp/update_binary"下面。其实这个方法中主要是对update_binary操作的,如解压和执行等。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.//const char** args = (const char**)malloc(sizeof(char*) * 5);args[0] = binary;args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mkchar* temp = (char*)malloc(10);sprintf(temp, "%d", pipefd[1]);args[2] = temp;args[3] = (char*)path;args[4] = NULL;//创建子进程。其中的子进程主要负责执行binary(execv(binary,args),即执行我们的安装命令脚本),父进程负责接受子进程发送的命令去更新ui显示(显示当前的进度)。子父进程间通信依靠管道。pid_t pid = fork();if (pid == 0) {close(pipefd[0]);//执行<span style="font-family: Arial, Helvetica, sans-serif;">update-binary</span><span style="font-family: Arial, Helvetica, sans-serif;">,这个程序的实质就是去解析update.zip包中的updater-script脚本中的命令并执行。由此,Recovery服务就进入了实际安装update.zip包的过程。</span><span style="font-family: Arial, Helvetica, sans-serif;">在执行execv函数时,execv会停止执行当前的进程,并且以progname应用进程替换被停止执行的进程,进程ID没有改变。这里的progname就是指</span><span style="font-family: Arial, Helvetica, sans-serif;">update-binary</span><span style="font-family: Arial, Helvetica, sans-serif;">,也就是被执行的应用程序。第二个参数argv,是指运行</span><span style="font-family: Arial, Helvetica, sans-serif;">update-binary</span><span style="font-family: Arial, Helvetica, sans-serif;">t时,传递给执行程序的参数列表, 注意,这个数组的第一个参数应该是应用程序名字本身,并且最后一个参数应该为NULL,不参将多个参数合并为一个参数放入数组。</span><span style="font-family: Arial, Helvetica, sans-serif;"></span>此外,如果应用程序正常执行完毕,那么execv是永远不会返回的;当execv在调用进程中返回时,那么这个应用程序应该出错了(可能是程序本身没找到,权限不够…),此时它的返回值应该是-1,具体的错误代码可以通过全局变量errno查看,还可以通过stderr得到具体的错误描述字符串。execv(binary, (char* const*)args);//其实我的理解就是<span style="font-family: Arial, Helvetica, sans-serif;">update-binary相当一个exe可执行程序,这里就是一个双击可执行程序的动作</span>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->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->Print("%s", str);} else {ui->Print("\n");}fflush(stdout);} else if (strcmp(command, "wipe_cache") == 0) {*wipe_cache = 1;#if 1 //wschen 2012-07-25} else if (strcmp(command, "special_factory_reset") == 0) {*wipe_cache = 2;#endif} else if (strcmp(command, "clear_display") == 0) {ui->SetBackground(RecoveryUI::NONE);} 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;}#ifdef SUPPORT_DATA_BACKUP_RESTORE //wschen 2011-03-09 //Skip userdata restore if updating from /data with no /data layout changeif(!usrdata_changed && update_from_data){ui->Print("/data offset remains the same no need to restore usrdata\n");}else{if (part_size_changed) {if (ensure_path_mounted("/sdcard") != 0) {LOGE("Can't mount %s\n", path);return INSTALL_NO_SDCARD;}if (userdata_restore(backup_path, 1)) {return INSTALL_FILE_SYSTEM_ERROR;}}}#endif //SUPPORT_DATA_BACKUP_RESTORE/* —————————– *//* SECURE BOOT UPDATE*//* —————————– */#ifdef SUPPORT_SBOOT_UPDATEsec_update(false);#endifreturn INSTALL_SUCCESS;}

如果有可能,我带你去远行。

Android OTA升级包制作脚本详解(五,升级脚本updater

相关文章:

你感兴趣的文章:

标签云: