程序员人生 网站导航

Android OTA升级包制作脚本详解(五,升级脚本updater-script的执行<1>)

栏目:综合技术时间:2015-03-17 08:39:51

写在前面:

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

    这里主要分析与脚本相干的部份,其他的请参考这位朋友的博文http://blog.chinaunix.net/uid⑵2028566-id⑶533856.html,我也很受启发。这里也借用1张图来帮助流程上的分析。


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

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

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

static int try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { //定义1个常量用来封装查找相zip关信息的结果如“META-INF/com/google/android/update-binary”,便于后面进行解压,由于之前mzOpenZipArchive函数并没有对更新包进行解压操作,*zip是之前mzOpenZipArchive方法返回的1个ZipArchive对象。mzOpenZipArchive是打开升级包,并将相干的信息拷贝到1个临时的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 ", 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 ", 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.mk char* 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;">,也就是被履行的利用程序。第2个参数argv,是指运行</span><span style="font-family: Arial, Helvetica, sans-serif;">update-binary</span><span style="font-family: Arial, Helvetica, sans-serif;">t时,传递给履行程序的参数列表, 注意,这个数组的第1个参数应当是利用程序名字本身,并且最后1个参数应当为NULL,不参将多个参数合并为1个参数放入数组。</span><span style="font-family: Arial, Helvetica, sans-serif;"> </span>另外,如果利用程序正常履行终了,那末execv是永久不会返回的;当execv在调用进程中返回时,那末这个利用程序应当出错了(多是程序本身没找到,权限不够...),此时它的返回值应当是⑴,具体的毛病代码可以通过全局变量errno查看,还可以通过stderr得到具体的毛病描写字符串。 execv(binary, (char* const*)args);//其实我的理解就是<span style="font-family: Arial, Helvetica, sans-serif;">update-binary相当1个exe可履行程序,这里就是1个双击可履行程序的动作</span> fprintf(stdout, "E:Can't run %s (%s) ", binary, strerror(errno)); _exit(⑴); } 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, " "); if (command == NULL) { continue; } else if (strcmp(command, "progress") == 0) { char* fraction_s = strtok(NULL, " "); char* seconds_s = strtok(NULL, " "); 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, " "); float fraction = strtof(fraction_s, NULL); ui->SetProgress(fraction); } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, " "); if (str) { ui->Print("%s", str); } else { ui->Print(" "); } fflush(stdout); } else if (strcmp(command, "wipe_cache") == 0) { *wipe_cache = 1; #if 1 //wschen 2012-07⑵5 } 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] ", command); } } fclose(from_child); int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOGE("Error in %s (Status %d) ", 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 change if(!usrdata_changed && update_from_data){ ui->Print("/data offset remains the same no need to restore usrdata "); }else{ if (part_size_changed) { if (ensure_path_mounted("/sdcard") != 0) { LOGE("Can't mount %s ", 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_UPDATE sec_update(false); #endif return INSTALL_SUCCESS; }
------分隔线----------------------------
------分隔线----------------------------

最新技术推荐