【android系统】android系统升级流程分析(一)---recovery模式中进行update包升级流程分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/twk121109281/article/details/90748513

今天我们直接来看下android中具体的升级过程是如何的。


升级流程概述

升级的流程图:

升级流程分析

第一步:升级包获取

升级获取可以通过远程下载,也可直接拷贝到指定目录即可。

第二步:准备升级

然后调用RecoverySystem.installPackage将目录通过传参方式传递下去。这是应用层代码如下:

public void updateSystem(String updateSavePath) {
    File packageFile = new File(updateSavePath);
    try {
        //调用升级接口
        RecoverySystem.installPackage(this, packageFile);
    } catch (IOException e) {
        LOG.e(TAG, "RecoverySystem ERROR!!!");
        e.printStackTrace();
        String errDesc =String.format("execute system update fail. reason-> %s", e.toString());
        reportUpdateError(errDesc);
    }

    LOG.d(TAG, "RecoverySystem OK!!!");
    copyUpdateFileIsSuccess = false;
}

在应用层下载升级包后,会调用RecoverySystem.installPackage(Context context, File packageFile)函数来发起安装过程,这个过程主要的原理,实际上只是往 /cache/recovery/command 写入升级包存放路径,然后重启到recovery模式,仅此而已。

public static void installPackage(Context context, File packageFile)
    throws IOException {
    String filename = packageFile.getCanonicalPath();
    Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");

    //升级包路径
    final String filenameArg = "--update_package=" + filename;
    final String localeArg = "--locale=" + Locale.getDefault().toString();
    bootCommand(context, filenameArg, localeArg);
}

private static void bootCommand(Context context, String... args) throws IOException {
    RECOVERY_DIR.mkdirs();  // In case we need it
    COMMAND_FILE.delete();  // In case it's not writable
    LOG_FILE.delete();
    FileWriter command = new FileWriter(COMMAND_FILE);
    try {
        for (String arg : args) {
            if (!TextUtils.isEmpty(arg)) {
                command.write(arg);
                command.write("\n");
            }
        }
    } finally {
        command.close();
    }
    // Having written the command file, go ahead and reboot
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    //重启到recovery模式
    pm.reboot(PowerManager.REBOOT_RECOVERY);
    throw new IOException("Reboot failed (no permissions?)");
}

PowerManager.REBOOT_RECOVERY的值为字符串"recovery",pm.reboot()方法最后会将recovery写入到misc分区的BCB中去。BCB即Bootloader Control Block,启动控制信息块,位于misc分区,从代码上看,就是一个结构体。如下:

struct bootloader_message {  
    char command[32];  
    char status[32];  
    char recovery[1024];  
}; 

bootloader_message 结构体包含三个字段,具体含义如下:

command 字段中存储的是命令,它有以下几个可能值: 
* boot-recovery:系统将启动进入Recovery模式 
* update-radia 或者 update-hboot:系统将启动进入更新firmware的模式,这个更新过程由bootloader完成 
* NULL:空值,系统将启动进入Main System主系统,正常启动。

status 字段存储的是更新的结果。更新结束后,由Recovery或者Bootloader将更新结果写入到这个字段中。

recovery 字段存放的是recovry模块的启动参数,一般包括升级包路径。其存储结构如下:第一行存放字符串“recovery”,第二行存放路径信息“–update_package=/mnt/sdcard/update.zip”等。 因此,参数之间是以“\n”分割的。

第三步:系统重启进入Recovery模式,进行升级操作

 Android系统进行升级的时候,有两种途径:

一种是通过接口传递升级包路径自动升级(Android系统SD卡升级),升级完之后系统自动重启。

另一种是手动进入recovery模式下,选择升级包进行升级,升级完成之后停留在recovery界面,需要手动选择重启。

前者多用于手机厂商的客户端在线升级,后者多用于开发和测试人员。但不管哪种,原理都是一样的,都要在recovery模式下进行升级。

下面介绍的是升级包保存在cache目录下,且升级包路径保存在/cache/recovery/command中的方式(升级包的存放路径,从BCB或者/cache/recovery/command里面解析得到的)。

重启进入升级主要流程:

  1. 系统重启进入Recovery模式。读取BCB的command,读取到”boot-recovery”后,加载recovery.img,启动recovery。
  2. 在install.cpp进行升级操作
  3. try_update_binary执行升级脚本
  4. 调用finish_recovery方法,清除BCB信息,重启

1、系统重启进入Recovery模式

    系统重启时会判断/cache/recovery目录下是否有command文件,如果存在就进入recovery模式,否则就正常启动。

    进入到Recovery模式下,将执行recovery.cpp的main函数,下面贴出关键代码片段:

... ...
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {  
    switch (arg) {  
    case 's': send_intent = optarg; break;  
    case 'u': update_package = optarg; break;  
    ... ... 
} 
... ...

2、在install.cpp进行升级操作

在main函数中最终会调用到install_package方法。

... ...
if (update_package != NULL) {  
    status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);  
    ... ...
} 
    ... ...

具体的升级过程都是在install.cpp中执行的,再来看看install_package方法。

int install_package(const char* path, int* wipe_cache, const char* install_file,  
                bool needs_mount)  
{  
    ... ...
    if (setup_install_mounts() != 0) {  
        ... ...
    } else {  
        result = really_install_package(path, wipe_cache, needs_mount);  
    }
    ... ...
}  

install_package方法中创建了log文件(记录升级报错的信息)。然后调用really_install_package方法。

static int really_install_package(const char *path, int* wipe_cache, bool needs_mount)  
{  
    ... ...
    // 装入签名文件
    Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
    ... ...
    // 验证签名
    err = verify_file(map.addr, map.length, loadedKeys, numKeys);
    ... ...
    // 打开升级包  
    err = mzOpenZipArchive(map.addr, map.length, &zip);
    ... ... 
    // 执行升级脚本文件,开始升级  
    int result = try_update_binary(path, &zip, wipe_cache);
    ... ...
}  

really_install_package方法中,首先验证签名,再读取升级包,然后进行调用try_update_binary方法升级

3、try_update_binary执行升级脚本

static int try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {

    const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
    ... ...
    const char* binary = "/tmp/update_binary";
    unlink(binary);
    int fd = creat(binary, 0755); 
    ... ...
    //将升级包里面的update_binary解压到/tmp/update_binary
    bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
    ... ...

    const char** args = (const char**)malloc(sizeof(char*) * 5);
    args[0] = binary; //update_binary存放路径
    args[1] = EXPAND(RECOVERY_API_VERSION);  // Recovery版本号
    char* temp = (char*)malloc(10);
    sprintf(temp, "%d", pipefd[1]);
    args[2] = temp;
    args[3] = (char*)path; //升级包存放路径
    args[4] = NULL;

    pid_t pid = fork();//fork一个子进程
    if (pid == 0) {
        close(pipefd[0]);
        //子进程调用update-binary执行升级操作
        execv(binary, (char* const*)args);
        fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
        _exit(-1);
    }
    //父进程负责接受子进程发送的命令去更新ui显示 
    ... ...
    int status;
    waitpid(pid, &status, 0);
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        //安装失败,返回INSTALL_ERROR
        return INSTALL_ERROR;
    }
     //安装成功,返回INSTALL_SUCCESS
    return INSTALL_SUCCESS;
}

4、调用finish_recovery方法,清除BCB信息,重启

OTA升级成功,清空misc分区(BCB置零),并将保存到内存系统的升级日志/tmp/recovery.log保存到/cache/recovery/last_log。重启设备进入Main System,升级完成。

结束语

以上就是对升级流程的分析,希望对大家有所帮助。后续我们还会带来系统升级流程相关的介绍,感兴趣的同学可以关注我们的微信公众号。

å¨è¿éæå¥å¾çæè¿°

上一篇:目录

下一篇:【android系统】android系统升级流程分析(二)---update包分析

猜你喜欢

转载自blog.csdn.net/twk121109281/article/details/90748513
今日推荐