您的位置:首页 > 移动开发 > Android开发

Android OTA 升级(四):进入根文件系统 .

2013-07-27 10:23 344 查看
一、简介

        从bootloader 进入Recovery 模式后,首先也是运行Linux内核,该内核跟普通模式没有区别(减轻了BSP开发者的任务)。区别从执行文件系统开始。 Recovery 模式的细节就隐藏在其根文件系统中。

下面,我们就看看进入Recovery 根文件系统都干些啥。

二、init.rc
       和正常启动一样,内核进入文件系统会执行/init, init 的配置文件就是 /init.rc, 前面文章讲过,这个文件来自:bootable/recovery/etc/init.rc,下面,我们看看它的内容。

[on early-init  

    start ueventd  
  
on init  
    export PATH /sbin  
    export ANDROID_ROOT /system  
    export ANDROID_DATA /data  
    export EXTERNAL_STORAGE /sdcard  
  
    symlink /system/etc /etc  
  
    mkdir /sdcard  
    mkdir /system  
    mkdir /data  
    mkdir /cache  
    mount /tmp /tmp tmpfs  
  
on boot  
    ifup lo  
    hostname localhost  
    domainname localdomain  
  
    class_start default  
  
service ueventd /sbin/ueventd  
    critical  
  
service recovery /sbin/recovery  
  
service adbd /sbin/adbd recovery  
    disabled  
  
# Always start adbd on userdebug and eng builds
  
on property:ro.debuggable=1  
    write /sys/class/android_usb/android0/enable 0  
    write /sys/class/android_usb/android0/idVendor 18D1  
    write /sys/class/android_usb/android0/idProduct D001  
    write /sys/class/android_usb/android0/functions adb  
    write /sys/class/android_usb/android0/enable 1  
    write /sys/class/android_usb/android0/iManufacturer $ro.product.manufacturer  
    write /sys/class/android_usb/android0/iProduct $ro.product.model  
    write /sys/class/android_usb/android0/iSerial $ro.serialno  
    start adbd  
  
# Restart adbd so it can run as root
  
on property:service.adb.root=1  
    write /sys/class/android_usb/android0/enable 0  
    restart adbd  
    write /sys/class/android_usb/android0/enable 1  

可以看到,它很非常简单:

1)   设置几个环境变量。备用。

2)   建立 etc 链接。

3)   造几个目录。备用。

4)   Mount /tmp 目录为内存文件系统 tmpfs,后面会用到。

5)   Trival 设置,不必关心。

6)   启动 recovery主程序。

7)   如果是eng模式(此时persist.service.adb.enable),启动adb

当然,init主程序还会装载属性配置文件 /default.prop, 它包含了很多系统属性设置,比如,ro.build.*, 等等。很明显,这里最重要的就是recovery主程序,下面,我们分析它。

 三、Recovery.c 分析

bootable/recovery/recovery.c

Recovery.c: main->

     install_package->

            really_install_package->

                    try_update_binary->

                        execv(META-INF/com/google/android/update-binary, args);

/*

* The recovery tool communicates with the main system through /cache files.

*   /cache/recovery/command - INPUT - command line for tool, one arg per line

*   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)

*   /cache/recovery/intent - OUTPUT - intent that was passed in

*

* The arguments which may be supplied in the recovery.command file:

*   --send_intent=anystring - write the text out to recovery.intent

*   --update_package=path - verify install an OTA package file

*   --wipe_data - erase user data (and cache), then reboot

*   --wipe_cache - wipe cache (but not user data), then reboot

*   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs

*

* After completing, we remove /cache/recovery/command and reboot.

* Arguments may also be supplied in the bootloader control block (BCB).

* These important scenarios must be safely restartable at any point:

*

* FACTORY RESET

* 1. user selects "factory reset"

* 2. main system writes "--wipe_data" to /cache/recovery/command

* 3. main system reboots into recovery

* 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"

*    -- after this, rebooting will restart the erase --

* 5. erase_volume() reformats /data

* 6. erase_volume() reformats /cache

* 7. finish_recovery() erases BCB

*    -- after this, rebooting will restart the main system --

* 8. main() calls reboot() to boot main system

*

* 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

*/

 代码及注释如下:

int

main(int argc, char **argv) {

    time_t start = time(NULL);
    // 将标准输出和标准错误输出重定位到"/tmp/recovery.log",如果是eng模式,

    // 就可以通过adb pull /tmp/recovery.log, 看到当前的log信息,这为我们

    // 提供了有效的调试手段。后面还会看到,recovery模式运行完毕后,

    // 会将其拷贝到cache分区,以便后续分析。

    // If these fail, there's not really anywhere to complain...

    freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);

    freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
    freopen("/dev/ttyS1", "a", stdout); setbuf(stdout, NULL);

    freopen("/dev/ttyS1", "a", stderr); setbuf(stderr, NULL);
    printf("Starting recovery on %s", ctime(&start));

   

    // Recovery 使用了一个简单的基于framebuffer的ui系统,叫miniui,这里,

    // 进行了简单的初始化(主要是图形部分以及事件部分),并启动了一个

    // event 线程用于响应用户按键。  

    device_ui_init(&ui_parameters);

    ui_init();

    ui_set_background(BACKGROUND_ICON_INSTALLING);

    load_volume_table();

    SetSdcardRootPath();

    SureCacheMount();

    ui_print("Recovery system v4.00 \n\n");

 

    // 从misc 分区以及 CACHE:recovery/command 文件中读入参数,写入到argc, argv ,

    // 并且,如果有必要,回写入misc分区。这样,如果recovery没有操作成功

    // (比如,升级还没有结束,就拔电池),系统会一直进入recovery模式。

    // 提醒用户继续升级,直到成功。

    get_args(&argc, &argv);
  // 解析参数

    int previous_runs = 0;

    const char *send_intent = NULL;

    const char *update_package = NULL;

    const char *update_rkimage = NULL;

    int wipe_data = 0, wipe_cache = 0, wipe_all = 0;

    int factory_test_en = 0;

    char prop[64] = {0};

    int arg;

    while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {

        switch (arg) {

        case 'p': previous_runs = atoi(optarg); break;

        case 's': send_intent = optarg; break;

        case 'u': update_package = optarg; break;

  case 'r':  update_rkimage = optarg; break;

        case 'w': wipe_data = wipe_cache = 1; break;

        case 'c': wipe_cache = 1; break;

        case 'f'+'t':{ factory_test_en = 1;ui_show_text(1);} break;

        case 't': ui_show_text(1); break;

        case 'w'+'a':{ wipe_all = wipe_data = wipe_cache = 1;ui_show_text(1);

} break;

        case '?':

            LOGE("Invalid command argument\n");

            continue;

        }

    }

    // 这个函数没干什么。看名字,它給设备制造商提供了一个调用机会,可写入设备相关初始化代码。

    device_recovery_start();

   

    // 打印出命令,比如,正常启动进入recovery模式,会打印:Command: "/sbin/recovery"

    printf("Command:");

    for (arg = 0; arg < argc; arg++) {

        printf(" \"%s\"", argv[arg]);

    }

    printf("\n");
    if (update_package) {

        // For backwards compatibility on the cache partition only, if

        // we're given an old 'root' path "CACHE:foo", change it to

        // "/cache/foo".

        if (strncmp(update_package, "CACHE:", 6) == 0) {

            int len = strlen(update_package) + 10;

            char* modified_path = malloc(len);

            strlcpy(modified_path, "/cache/", len);

            strlcat(modified_path, update_package+6, len);

            printf("(replacing path \"%s\" with \"%s\")\n",

                   update_package, modified_path);

            update_package = modified_path;

        }

    strcpy(updatepath,update_package);

    }

    printf("\n");

   

    if (update_rkimage) {

        // For backwards compatibility on the cache partition only, if

        // we're given an old 'root' path "CACHE:foo", change it to

        // "/cache/foo".

        if (strncmp(update_rkimage, "CACHE:", 6) == 0) {

            int len = strlen(update_rkimage) + 10;

            char* modified_path = malloc(len);

            strlcpy(modified_path, "/cache/", len);

            strlcat(modified_path, update_rkimage+6, len);

            printf("(replacing path \"%s\" with \"%s\")\n",

                   update_rkimage, modified_path);

            update_rkimage = modified_path;

        }

        strcpy(updatepath,update_rkimage);

    }

    printf("\n");

 

    // 打印出所有的系统属性(from default.prop)到log文件。

    property_list(print_property, NULL);

    printf("\n");
    int status = INSTALL_SUCCESS;

    if(factory_test_en)

    {

        ui_print("\nwait factory test tool connect........\n");

        char factory_test_finish = 0;   

        while(1)

        {

            property_get("FACTORY_TEST_FINISH", prop, "0");

            factory_test_finish = (unsigned long)strtoumax(prop, 0, 16);

            if(factory_test_finish != 0)break;

            sleep(1);

        }

        ui_print("\n factory test finish........\n");

       

    }
    // 根据用户提供参数,调用各项功能,比如,安装一个升级包,擦除cache分区,

    // 擦除user data分区,install_package比较复杂,后面专门分析,其它都很简单。忽略。

    if (update_package != NULL) {

  printf("update_package = %s", update_package);

        status = install_package(update_package, &wipe_cache,

TEMPORARY_INSTALL_FILE);

        if (status == INSTALL_SUCCESS && wipe_cache) {

            if (erase_volume("/cache")) {

                LOGE("Cache wipe (requested by package) failed.");

            }

        }

        if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");

        else

      bAutoUpdateComplete=true;

    } else if (update_rkimage != NULL) {

        status = install_rkimage(update_rkimage);

        if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");

        else

      bAutoUpdateComplete=true;

    } else if (wipe_data) {

        if (device_wipe_data()) status = INSTALL_ERROR;

        if (erase_volume("/data")) status = INSTALL_ERROR;

        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;

        if (wipe_all && erase_volume(IN_SDCARD_ROOT)) status = INSTALL_ERROR;

        if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");

    } else if (wipe_cache) {

        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;

        if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");

    } else {

        status = INSTALL_ERROR;  // No command specified

    }
    //如果前面已经做了某项操作并且成功,则进入重启流程。否则,等待用户选择具体操作。

    // 而用户可选操作为: reboot, 安装update.zip,除cache分区, 擦除user data分区,

    // 如前所述,只有安装package 比较复杂,其它简单。

    if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);

    if (status != INSTALL_SUCCESS) {

     bClearbootmessage = false;

        prompt_and_wait();

    }

   // 1)将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command

   // 2)将 /tmp/recovery.log 复制到 "CACHE:recovery/log";

   // 3)清空 misc 分区,这样重启就不会进入recovery模式

   // 4)删除command 文件:CACHE:recovery/command;

    // Otherwise, get ready to boot the main system...

    finish_recovery(send_intent);

    ui_print("Rebooting...\n");

    android_reboot(ANDROID_RB_RESTART, 0, 0);

    return EXIT_SUCCESS;

}

 四、install_package分析

bootable/recovery/install.c

int

main(int argc, char **argv) {

    time_t start = time(NULL);

    // 将标准输出和标准错误输出重定位到"/tmp/recovery.log",如果是eng模式,

    // 就可以通过adb pull /tmp/recovery.log, 看到当前的log信息,这为我们

    // 提供了有效的调试手段。后面还会看到,recovery模式运行完毕后,

    // 会将其拷贝到cache分区,以便后续分析。

    // If these fail, there's not really anywhere to complain...

    freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);

    freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);

    freopen("/dev/ttyS1", "a", stdout); setbuf(stdout, NULL);

    freopen("/dev/ttyS1", "a", stderr); setbuf(stderr, NULL);

    printf("Starting recovery on %s", ctime(&start));

   

    // Recovery 使用了一个简单的基于framebuffer的ui系统,叫miniui,这里,

    // 进行了简单的初始化(主要是图形部分以及事件部分),并启动了一个

    // event 线程用于响应用户按键。  

    device_ui_init(&ui_parameters);

    ui_init();

    ui_set_background(BACKGROUND_ICON_INSTALLING);

    load_volume_table();

    SetSdcardRootPath();

    SureCacheMount();

    ui_print("Recovery system v4.00 \n\n");

 

    // 从misc 分区以及 CACHE:recovery/command 文件中读入参数,写入到argc, argv ,

    // 并且,如果有必要,回写入misc分区。这样,如果recovery没有操作成功

    // (比如,升级还没有结束,就拔电池),系统会一直进入recovery模式。

    // 提醒用户继续升级,直到成功。

    get_args(&argc, &argv);

  // 解析参数

    int previous_runs = 0;

    const char *send_intent = NULL;

    const char *update_package = NULL;

    const char *update_rkimage = NULL;

    int wipe_data = 0, wipe_cache = 0, wipe_all = 0;

    int factory_test_en = 0;

    char prop[64] = {0};

    int arg;

    while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {

        switch (arg) {

        case 'p': previous_runs = atoi(optarg); break;

        case 's': send_intent = optarg; break;

        case 'u': update_package = optarg; break;

  case 'r':  update_rkimage = optarg; break;

        case 'w': wipe_data = wipe_cache = 1; break;

        case 'c': wipe_cache = 1; break;

        case 'f'+'t':{ factory_test_en = 1;ui_show_text(1);} break;

        case 't': ui_show_text(1); break;

        case 'w'+'a':{ wipe_all = wipe_data = wipe_cache = 1;ui_show_text(1);

} break;

        case '?':

            LOGE("Invalid command argument\n");

            continue;

        }

    }

    // 这个函数没干什么。看名字,它給设备制造商提供了一个调用机会,可写入设备相关初始化代码。

    device_recovery_start();

   

    // 打印出命令,比如,正常启动进入recovery模式,会打印:Command: "/sbin/recovery"

    printf("Command:");

    for (arg = 0; arg < argc; arg++) {

        printf(" \"%s\"", argv[arg]);

    }

    printf("\n");

    if (update_package) {

        // For backwards compatibility on the cache partition only, if

        // we're given an old 'root' path "CACHE:foo", change it to

        // "/cache/foo".

        if (strncmp(update_package, "CACHE:", 6) == 0) {

            int len = strlen(update_package) + 10;

            char* modified_path = malloc(len);

            strlcpy(modified_path, "/cache/", len);

            strlcat(modified_path, update_package+6, len);

            printf("(replacing path \"%s\" with \"%s\")\n",

                   update_package, modified_path);

            update_package = modified_path;

        }

    strcpy(updatepath,update_package);

    }

    printf("\n");

   

    if (update_rkimage) {

        // For backwards compatibility on the cache partition only, if

        // we're given an old 'root' path "CACHE:foo", change it to

        // "/cache/foo".

        if (strncmp(update_rkimage, "CACHE:", 6) == 0) {

            int len = strlen(update_rkimage) + 10;

            char* modified_path = malloc(len);

            strlcpy(modified_path, "/cache/", len);

            strlcat(modified_path, update_rkimage+6, len);

            printf("(replacing path \"%s\" with \"%s\")\n",

                   update_rkimage, modified_path);

            update_rkimage = modified_path;

        }

        strcpy(updatepath,update_rkimage);

    }

    printf("\n");

 

    // 打印出所有的系统属性(from default.prop)到log文件。

    property_list(print_property, NULL);

    printf("\n");

    int status = INSTALL_SUCCESS;

    if(factory_test_en)

    {

        ui_print("\nwait factory test tool connect........\n");

        char factory_test_finish = 0;   

        while(1)

        {

            property_get("FACTORY_TEST_FINISH", prop, "0");

            factory_test_finish = (unsigned long)strtoumax(prop, 0, 16);

            if(factory_test_finish != 0)break;

            sleep(1);

        }

        ui_print("\n factory test finish........\n");

       

    }

    // 根据用户提供参数,调用各项功能,比如,安装一个升级包,擦除cache分区,

    // 擦除user data分区,install_package比较复杂,后面专门分析,其它都很简单。忽略。

    if (update_package != NULL) {

  printf("update_package = %s", update_package);

        status = install_package(update_package, &wipe_cache,

TEMPORARY_INSTALL_FILE);

        if (status == INSTALL_SUCCESS && wipe_cache) {

            if (erase_volume("/cache")) {

                LOGE("Cache wipe (requested by package) failed.");

            }

        }

        if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");

        else

      bAutoUpdateComplete=true;

    } else if (update_rkimage != NULL) {

        status = install_rkimage(update_rkimage);

        if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");

        else

      bAutoUpdateComplete=true;

    } else if (wipe_data) {

        if (device_wipe_data()) status = INSTALL_ERROR;

        if (erase_volume("/data")) status = INSTALL_ERROR;

        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;

        if (wipe_all && erase_volume(IN_SDCARD_ROOT)) status = INSTALL_ERROR;

        if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");

    } else if (wipe_cache) {

        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;

        if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");

    } else {

        status = INSTALL_ERROR;  // No command specified

    }

    //如果前面已经做了某项操作并且成功,则进入重启流程。否则,等待用户选择具体操作。

    // 而用户可选操作为: reboot, 安装update.zip,除cache分区, 擦除user data分区,

    // 如前所述,只有安装package 比较复杂,其它简单。

    if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);

    if (status != INSTALL_SUCCESS) {

     bClearbootmessage = false;

        prompt_and_wait();

    }

   // 1)将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command

   // 2)将 /tmp/recovery.log 复制到 "CACHE:recovery/log";

   // 3)清空 misc 分区,这样重启就不会进入recovery模式

   // 4)删除command 文件:CACHE:recovery/command;

    // Otherwise, get ready to boot the main system...

    finish_recovery(send_intent);

    ui_print("Rebooting...\n");

    android_reboot(ANDROID_RB_RESTART, 0, 0);

    return EXIT_SUCCESS;

}

 四、install_package分析

bootable/recovery/install.c

int

install_package(const char* path, int* wipe_cache, const char* install_file)

{

    FILE* install_log = fopen_path(install_file, "w");

    if (install_log) {

        fputs(path, install_log);

        fputc('\n', install_log);

    } else {

        LOGE("failed to open last_install: %s\n", strerror(errno));

    }

    int result = really_install_package(path, wipe_cache);

    if (install_log) {

        fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);

        fputc('\n', install_log);

        fclose(install_log);

    }

    return result;

}

static int

really_install_package(const char *path, int* wipe_cache)

{

    // 更新 UI 显示

    ui_set_background(BACKGROUND_ICON_INSTALLING);

    ui_print("Finding update package...\n");

    ui_show_indeterminate_progress();

    LOGI("Update location: %s\n", path);

   

    //确保升级包所在分区已经mount,通常为 cache 分区或者 SD 分区

    if (ensure_path_mounted(path) != 0) {

        LOGE("Can't mount %s\n", path);

        return INSTALL_CORRUPT;

    }

    ui_print("Opening update package...\n");

   

    //从/res/keys中装载公钥。

    int numKeys;

    RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);

    if (loadedKeys == NULL) {

        LOGE("Failed to load keys\n");

        return INSTALL_CORRUPT;

    }

    LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);

    // Give verification half the progress bar...

    ui_print("Verifying update package...\n");

    ui_show_progress(

            VERIFICATION_PROGRESS_FRACTION,

            VERIFICATION_PROGRESS_TIME);

  // 根据公钥验证升级包verify_file

    int err;

    err = verify_file(path, loadedKeys, numKeys);

    free(loadedKeys);

    LOGI("verify_file returned %d\n", err);

    if (err != VERIFY_SUCCESS) {

        LOGE("signature verification failed\n");

        return INSTALL_CORRUPT;

    }

  // 打开升级包,将相关信息存到ZipArchive数据结构中,便于后面处理。

    /* Try to open the package.

     */

    ZipArchive zip;

    err = mzOpenZipArchive(path, &zip);

    if (err != 0) {

        LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");

        return INSTALL_CORRUPT;

    }

    /* Verify and install the contents of the package.

     */

    ui_print("Installing update...\n");

    return try_update_binary(path, &zip, wipe_cache);

}

五、try_update_binary分析

bootable/recovery/install.c

// If the package contains an update binary, extract it and run it.

static int

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

    const ZipEntry* binary_entry =

            mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);

    if (binary_entry == NULL) {

        mzCloseZipArchive(zip);

        return INSTALL_CORRUPT;

    }

    // 将升级包内文件META-INF/com/google/android/update-binary

    // 复制为/tmp/update_binary

    char* binary = "/tmp/update_binary";

    unlink(binary);

    int fd = creat(binary, 0755);

    if (fd < 0) {

        mzCloseZipArchive(zip);

        LOGE("Can't make %s\n", binary);

        return INSTALL_ERROR;

    }

    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.

    //

    char** args = malloc(sizeof(char*) * 5);

    args[0] = binary;

    args[1] = EXPAND(RECOVERY_API_VERSION);   // defined in Android.mk

    args[2] = malloc(10);

    sprintf(args[2], "%d", pipefd[1]);

    args[3] = (char*)path;

    args[4] = NULL;

  // 创建新的进程,执行:/tmp/update_binary

  // 同时,会给该进程传入一些参数,其中最重要的就是一个管道fd,供新进程与原进程通信。

  // 新进程诞生后,原进程就变成了一个服务进程,它提供若干UI更新服务

  // 新进程就可以通过老进程的UI系统完成显示任务。而其他功能就靠它自己了。

    pid_t pid = fork();

    if (pid == 0) {

        close(pipefd[0]);

        execv(binary, args);

        fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));

        _exit(-1);

    }

    close(pipefd[1]);

    *wipe_cache = 0;

   

    // 更新UI

    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_show_progress(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_set_progress(fraction);

        } else if (strcmp(command, "ui_print") == 0) {

            char* str = strtok(NULL, "\n");

            if (str) {

                ui_print("%s", str);

            } else {

                ui_print("\n");

            }

        } else if (strcmp(command, "wipe_cache") == 0) {

            *wipe_cache = 1;

        } 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;

    }

    return INSTALL_SUCCESS;

}

这样,我们又回到了升级包中的文件:META-INF/com/google/android/update-binary,下回继续研究。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android OTA