android源码分析1--updater(l上)

本文深入解析了Android系统的更新流程,包括recovery模式下调用updater的实现细节,以及如何通过管道进行状态同步。同时,文章详细介绍了脚本解析与执行过程。
install.cpp中调用updater:
const char* binary = "/tmp/update_binary";

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;

pid_t pid = fork();
if (pid == 0) {
umask(022);
close(pipefd[0]);
execv(binary, (char* const*)args);
fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); //updater执行正确永远不会被调用
_exit(-1);
}
close(pipefd[1]);

    #include < unistd.h >
        int execv(const char *progname, char *const argv[]); 
   1.2 用法介绍
        execv会停止执行当前的进程, 并且以progname应用进程替换被停止执行的进程,进程ID没有改变
        progname: 被执行的应用程序。
         argv: 传递给应用程序的参数列表, 注意这个数组的第一个参数应该是应用程序名字本身(即argv[0] = progname), 并且最后一个参数应该为NULL ,不能将多个参数合并为一个参数放入数组。
    1.3 返回值
       如果应用程序正常执行完毕, 那么execv是永远不会返回的;当execv在调用进程中返回时,那么这个应用程序应该出错了 (可能是程序本身没找到,权限不够等), 此时它的返回值应该是-1,具体的错误代码可以通过全局变量errno查看,还可以通过stderr得到具体的错误描述字符串。

调用updater的3个参数:
1 recovery API: the version number for this interface
2 一个管道的fd,updater向这个管道写,用于更新进度条 an fd to which the program can write in order to update the progress bar
int pipefd[2];
pipe(pipefd);

char* temp = (char*)malloc(10);
sprintf(temp, "%d", pipefd[1]); //把pipefd[1]代表的管道的fd,作为字符串参数传给updater
args[2] = temp;
args[3] = (char*)path;
args[4] = NULL;

pid_t pid = fork();
if (pid == 0) {
umask(022);
close(pipefd[0]); //updater中关闭pipefd[0]
execv(binary, (char* const*)args);
fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
_exit(-1);
}
close(pipefd[1]); //recovery中关闭pipefd[1]

FILE* from_child = fdopen(pipefd[0], "r"); //recovery从管道接收来自updater的信息
while (fgets(buffer, sizeof(buffer), from_child) != NULL) {

3 升级包路径 the name of the package zip file

二 recovery/updater.c中:
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, NULL);
// 创建子进程时,父进程的缓冲区也被复制到子进程了。所以子进程在printf时,就一起printf出来了,因为recovery中已经将 stdout stderr重定向到了文件中,所以这里把输出缓冲区设置为无缓冲,直接从流输出数据
//setbuf函数的第二个参数取值可以为null,此时标准输出不需要进行缓冲。这种情况下,程序仍然能够工作,只不过速度较慢而已
     setbuf (stderr, NULL);
 
     if   (argc != 4) {
         printf ( "unexpected number of arguments (%d)\n" , 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.
         printf ( "wrong updater binary API; expected 1, 2, or 3; "
                         "got %s\n" ,
                 argv[1]);
         return   2;
     }
 
     // Set up the pipe for sending commands back to the parent process.
  // 将recovery传递的字符串格式的pipe[1]的fd,转换为fd,再打开这个管道
     int   fd =  atoi (argv[2]);
     FILE * cmd_pipe = fdopen(fd,  "wb" );
     setlinebuf(cmd_pipe);
 
     // Extract the script from the package.
 
     const   char * package_filename = argv[3]; //argv[3]就是升级包完成路径
     MemMapping map;
     if   (sysMapFile(package_filename, &map) != 0) { //将升级包映射到内存中
         printf ( "failed to map package %s\n" , argv[3]);
         return   3;
     }
     ZipArchive za;
     int   err;
     err = mzOpenZipArchive(map.addr, map.length, &za); //根据内存中的起始地址和长度,打开这个文件
     if   (err != 0) {
         printf ( "failed to open package %s: %s\n" ,
                argv[3],  strerror (err));
         return   3;
     }
 
     const   ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); //在文件中查找升级脚本这个entry
     if   (script_entry == NULL) {
         printf ( "failed to find %s in %s\n" , SCRIPT_NAME, package_filename);
         return   4;
     }
 
     char * script =  malloc (script_entry->uncompLen+1) ;
// 根据升级脚本的实际大小分配一段内存,将升级脚本所有内容读到script中
     if   (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) {
         printf ( "failed to read script from package\n" );
         return   5;
     }
     script[script_entry->uncompLen] =  '\0' ;
 
     // Configure edify's functions.
 
     RegisterBuiltins();
     RegisterInstallFunctions();
     RegisterBlockImageFunctions();
     RegisterDeviceExtensions();
     FinishRegistration();
 
     // Parse the script.
 
     Expr* root;
struct Expr {
Function fn;
char* name;
int argc;
Expr** argv;
int start, end;
};
     int   error_count = 0;
     int   error = parse_string(script, &root, &error_count); //解析脚本
     if   (error != 0 || error_count > 0) {
         printf ( "%d parse errors\n" , error_count);
         return   6;
     }
 
     struct   selinux_opt seopts[] = {
       { SELABEL_OPT_PATH,  "/file_contexts"   }
     };
 
     sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
 
     if   (!sehandle) {
         fprintf (cmd_pipe,  "ui_print Warning: No file_contexts\n" );
     }
 
     // Evaluate the parsed script.
 
     UpdaterInfo updater_info;
typedef struct {
FILE* cmd_pipe;
ZipArchive* package_zip;
int version;

uint8_t* package_zip_addr;
size_t package_zip_len;
} UpdaterInfo;
     updater_info.cmd_pipe = cmd_pipe; //updater_info.cmd_pipe取得了updater打开的管道
     updater_info.package_zip = &za; //updater_info.package_zip 内存中的zip升级包
     updater_info.version =  atoi (version); //updater_info.version recovery api版本
     updater_info.package_zip_addr = map.addr; //updater_info.package_zip_addr zip升级包在内存中的起始地址
     updater_info.package_zip_len = map.length; // updater_info.package_zip_len zip升级包在内存中的长度
 
     State state;
typedef struct {
// Optional pointer to app-specific data; the core of edify never
// uses this value.
void* cookie;
// The source of the original script. Must be NULL-terminated,
// and in writable memory (Evaluate may make temporary changes to
// it but will restore it when done).
char* script;
// The error message (if any) returned if the evaluation aborts.
// Should be NULL initially, will be either NULL or a malloc'd
// pointer after Evaluate() returns.
char* errmsg;
} State;
     state.cookie = &updater_info;
     state.script = script; //现在state.script指向的就是脚本内容
     state.errmsg = NULL;
 
     char * result = Evaluate(&state, root); //执行脚本
     if   (result == NULL) {
         if   (state.errmsg == NULL) {
             printf ( "script aborted (no error message)\n" );
             fprintf (cmd_pipe,  "ui_print script aborted (no error message)\n" );
         else   {
             printf ( "script aborted: %s\n" , state.errmsg);
             char * line =  strtok (state.errmsg,  "\n" );
             while   (line) {
                 fprintf (cmd_pipe,  "ui_print %s\n" , line);
                 line =  strtok (NULL,  "\n" );
             }
             fprintf (cmd_pipe,  "ui_print\n" );
         }
         free (state.errmsg);
         return   7;
     else   {
         fprintf (cmd_pipe,  "ui_print script succeeded: result was [%s]\n" , result);
         free (result);
     }
 
     if   (updater_info.package_zip) {
         mzCloseZipArchive(updater_info.package_zip);
     }
     sysReleaseMap(&map);
     free (script);
 
     return   0;
}

三 recovery/edify/expr.c
typedef struct {
int type;
ssize_t size;
char* data;
} Value;
struct Expr {
Function fn;
char* name;
int argc;
Expr** argv;
int start, end;
};
typedef struct {
void* cookie;
char* script;
char* errmsg;
} State;
char * Evaluate(State* state, Expr* expr) {
     Value* v = expr->fn(expr->name, state, expr->argc, expr->argv);
typedef Value* (*Function)(const char* name, State* state, int argc, Expr* argv[]);
     if   (v == NULL)  return   NULL;
     if   (v->type != VAL_STRING) {
         ErrorAbort(state,  "expecting string, got value type %d" , v->type);
         FreeValue(v);
         return   NULL;
     }
     char * result = v->data;
     free (v);
     return   result;
}

Evaluate()函数主要是调用了expr的fn()函数,参数expr的类型是Expr,定义如下:
[java]   view plain   copy
  1. struct Expr {  
  2.     Function fn;  
  3.     char* name;  
  4.     int argc;  
  5.     Expr** argv;  
  6.     int start, end;  
  7. };  
从Expr的定义中可以看到它有一个字段argv,这个字段是Expr指针的指针类型,它实际上会指向一个Expr指针的数组对象,表示Expr对象的所有下一级对象。通过这个字段,脚本解析后得到的所有命令都串接在一起,而且命令的执行函数还会调用Ecaluate()来继续执行argv中的Expr对象,因此,虽然Evaluate()中只调用了root对象的fn()函数,但是实际上会执行脚本中的所有命令。














// args:
// - block device (or file) to modify in-place
// - transfer list (blob)
// - new data stream (filename within package.zip)
// - patch stream (filename within package.zip, must be uncompressed)

Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
Value* blockdev_filename;
Value* transfer_list_value;
char* transfer_list = NULL;
Value* new_data_fn;
Value* patch_data_fn;
bool success = false;

if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
&new_data_fn, &patch_data_fn) < 0) {
return NULL;
}

脚本中调用block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat");
就会执行BlockImageUpdateFn函数
1 ReadValueArgs取得脚本中的/dev/block/bootdevice/by-name/system,package_extract_file("system.transfer.list"),system.new.dat", "system.patch.dat"这四个参数,赋值给blockdev_filename ,transfer_list_value, new_data_fn,patch_data_fn
typedef struct {
int type;
ssize_t size;
char* data;
} Value;
2




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值