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

一、简介

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

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

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

  1. on early-init  
  2.     start ueventd  
  3.   
  4. on init  
  5.     export PATH /sbin  
  6.     export ANDROID_ROOT /system  
  7.     export ANDROID_DATA /data  
  8.     export EXTERNAL_STORAGE /sdcard  
  9.   
  10.     symlink /system/etc /etc  
  11.   
  12.     mkdir /sdcard  
  13.     mkdir /system  
  14.     mkdir /data  
  15.     mkdir /cache  
  16.     mount /tmp /tmp tmpfs  
  17.   
  18. on boot  
  19.     ifup lo  
  20.     hostname localhost  
  21.     domainname localdomain  
  22.   
  23.     class_start default  
  24.   
  25. service ueventd /sbin/ueventd  
  26.     critical  
  27.   
  28. service recovery /sbin/recovery  
  29.   
  30. service adbd /sbin/adbd recovery  
  31.     disabled  
  32.   
  33. # Always start adbd on userdebug and eng builds   
  34. on property:ro.debuggable=1  
  35.     write /sys/class/android_usb/android0/enable 0  
  36.     write /sys/class/android_usb/android0/idVendor 18D1  
  37.     write /sys/class/android_usb/android0/idProduct D001  
  38.     write /sys/class/android_usb/android0/functions adb  
  39.     write /sys/class/android_usb/android0/enable 1  
  40.     write /sys/class/android_usb/android0/iManufacturer $ro.product.manufacturer  
  41.     write /sys/class/android_usb/android0/iProduct $ro.product.model  
  42.     write /sys/class/android_usb/android0/iSerial $ro.serialno  
  43.     start adbd  
  44.   
  45. # Restart adbd so it can run as root   
  46. on property:service.adb.root=1  
  47.     write /sys/class/android_usb/android0/enable 0  
  48.     restart adbd  
  49.     write /sys/class/android_usb/android0/enable 1  
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
*/


 代码及注释如下:

  1. int  
  2. main(int argc, char **argv) {  
  3.     time_t start = time(NULL);  
  4.   
  5.     // 将标准输出和标准错误输出重定位到"/tmp/recovery.log",如果是eng模式,   
  6.     // 就可以通过adb pull /tmp/recovery.log, 看到当前的log信息,这为我们   
  7.     // 提供了有效的调试手段。后面还会看到,recovery模式运行完毕后,   
  8.     // 会将其拷贝到cache分区,以便后续分析。   
  9.     // If these fail, there's not really anywhere to complain...   
  10.     freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);  
  11.     freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);  
  12.   
  13.     freopen("/dev/ttyS1""a", stdout); setbuf(stdout, NULL);  
  14.     freopen("/dev/ttyS1""a", stderr); setbuf(stderr, NULL);  
  15.   
  16.     printf("Starting recovery on %s", ctime(&start));  
  17.       
  18.     // Recovery 使用了一个简单的基于framebuffer的ui系统,叫miniui,这里,   
  19.     // 进行了简单的初始化(主要是图形部分以及事件部分),并启动了一个    
  20.     // event 线程用于响应用户按键。      
  21.     device_ui_init(&ui_parameters);  
  22.     ui_init();  
  23.     ui_set_background(BACKGROUND_ICON_INSTALLING);  
  24.     load_volume_table();  
  25.     SetSdcardRootPath();  
  26.     SureCacheMount();  
  27.     ui_print("Recovery system v4.00 \n\n");  
  28.    
  29.     // 从misc 分区以及 CACHE:recovery/command 文件中读入参数,写入到argc, argv ,   
  30.     // 并且,如果有必要,回写入misc分区。这样,如果recovery没有操作成功   
  31.     // (比如,升级还没有结束,就拔电池),系统会一直进入recovery模式。   
  32.     // 提醒用户继续升级,直到成功。   
  33.     get_args(&argc, &argv);  
  34.   
  35.         // 解析参数   
  36.     int previous_runs = 0;  
  37.     const char *send_intent = NULL;  
  38.     const char *update_package = NULL;  
  39.     const char *update_rkimage = NULL;  
  40.     int wipe_data = 0, wipe_cache = 0, wipe_all = 0;  
  41.     int factory_test_en = 0;  
  42.     char prop[64] = {0};  
  43.     int arg;  
  44.     while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {  
  45.         switch (arg) {  
  46.         case 'p': previous_runs = atoi(optarg); break;  
  47.         case 's': send_intent = optarg; break;  
  48.         case 'u': update_package = optarg; break;  
  49.      case 'r':  update_rkimage = optarg; break;  
  50.         case 'w': wipe_data = wipe_cache = 1; break;  
  51.         case 'c': wipe_cache = 1; break;  
  52.         case 'f'+'t':{ factory_test_en = 1;ui_show_text(1);} break;  
  53.         case 't': ui_show_text(1); break;  
  54.         case 'w'+'a':{ wipe_all = wipe_data = wipe_cache = 1;ui_show_text(1);  
  55. break;  
  56.         case '?':  
  57.             LOGE("Invalid command argument\n");  
  58.             continue;  
  59.         }  
  60.     }  
  61.     // 这个函数没干什么。看名字,它給设备制造商提供了一个调用机会,可写入设备相关初始化代码。   
  62.     device_recovery_start();  
  63.       
  64.     // 打印出命令,比如,正常启动进入recovery模式,会打印:Command: "/sbin/recovery"   
  65.     printf("Command:");  
  66.     for (arg = 0; arg < argc; arg++) {  
  67.         printf(" \"%s\"", argv[arg]);  
  68.     }  
  69.     printf("\n");  
  70.   
  71.     if (update_package) {  
  72.         // For backwards compatibility on the cache partition only, if   
  73.         // we're given an old 'root' path "CACHE:foo", change it to   
  74.         // "/cache/foo".   
  75.         if (strncmp(update_package, "CACHE:", 6) == 0) {  
  76.             int len = strlen(update_package) + 10;  
  77.             char* modified_path = malloc(len);  
  78.             strlcpy(modified_path, "/cache/", len);  
  79.             strlcat(modified_path, update_package+6, len);  
  80.             printf("(replacing path \"%s\" with \"%s\")\n",  
  81.                    update_package, modified_path);  
  82.             update_package = modified_path;  
  83.         }  
  84.                 strcpy(updatepath,update_package);  
  85.     }  
  86.     printf("\n");  
  87.       
  88.     if (update_rkimage) {  
  89.         // For backwards compatibility on the cache partition only, if   
  90.         // we're given an old 'root' path "CACHE:foo", change it to   
  91.         // "/cache/foo".   
  92.         if (strncmp(update_rkimage, "CACHE:", 6) == 0) {  
  93.             int len = strlen(update_rkimage) + 10;  
  94.             char* modified_path = malloc(len);  
  95.             strlcpy(modified_path, "/cache/", len);  
  96.             strlcat(modified_path, update_rkimage+6, len);  
  97.             printf("(replacing path \"%s\" with \"%s\")\n",  
  98.                    update_rkimage, modified_path);  
  99.             update_rkimage = modified_path;  
  100.         }  
  101.         strcpy(updatepath,update_rkimage);  
  102.     }  
  103.     printf("\n");  
  104.     
  105.     // 打印出所有的系统属性(from default.prop)到log文件。   
  106.     property_list(print_property, NULL);  
  107.     printf("\n");  
  108.   
  109.     int status = INSTALL_SUCCESS;  
  110.     if(factory_test_en)  
  111.     {  
  112.         ui_print("\nwait factory test tool connect........\n");  
  113.         char factory_test_finish = 0;      
  114.         while(1)  
  115.         {  
  116.             property_get("FACTORY_TEST_FINISH", prop, "0");  
  117.             factory_test_finish = (unsigned long)strtoumax(prop, 0, 16);  
  118.             if(factory_test_finish != 0)break;  
  119.             sleep(1);  
  120.         }  
  121.         ui_print("\n factory test finish........\n");  
  122.           
  123.     }  
  124.   
  125.     // 根据用户提供参数,调用各项功能,比如,安装一个升级包,擦除cache分区,    
  126.     // 擦除user data分区,install_package比较复杂,后面专门分析,其它都很简单。忽略。   
  127.     if (update_package != NULL) {  
  128.         printf("update_package = %s", update_package);  
  129.         status = install_package(update_package, &wipe_cache,   
  130. TEMPORARY_INSTALL_FILE);  
  131.         if (status == INSTALL_SUCCESS && wipe_cache) {  
  132.             if (erase_volume("/cache")) {  
  133.                 LOGE("Cache wipe (requested by package) failed.");  
  134.             }  
  135.         }  
  136.         if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");  
  137.         else  
  138.                     bAutoUpdateComplete=true;  
  139.     } else if (update_rkimage != NULL) {  
  140.         status = install_rkimage(update_rkimage);  
  141.         if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");  
  142.         else  
  143.                     bAutoUpdateComplete=true;  
  144.     } else if (wipe_data) {  
  145.         if (device_wipe_data()) status = INSTALL_ERROR;  
  146.         if (erase_volume("/data")) status = INSTALL_ERROR;  
  147.         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;  
  148.         if (wipe_all && erase_volume(IN_SDCARD_ROOT)) status = INSTALL_ERROR;  
  149.         if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");  
  150.     } else if (wipe_cache) {  
  151.         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;  
  152.         if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");  
  153.     } else {  
  154.         status = INSTALL_ERROR;  // No command specified   
  155.     }  
  156.   
  157.     //如果前面已经做了某项操作并且成功,则进入重启流程。否则,等待用户选择具体操作。   
  158.     // 而用户可选操作为: reboot, 安装update.zip,除cache分区, 擦除user data分区,   
  159.     // 如前所述,只有安装package 比较复杂,其它简单。   
  160.     if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);  
  161.     if (status != INSTALL_SUCCESS) {  
  162.         bClearbootmessage = false;  
  163.         prompt_and_wait();  
  164.     }  
  165.   
  166.   
  167.    // 1)将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command   
  168.    // 2)将 /tmp/recovery.log 复制到 "CACHE:recovery/log";   
  169.    // 3)清空 misc 分区,这样重启就不会进入recovery模式   
  170.    // 4)删除command 文件:CACHE:recovery/command;   
  171.     // Otherwise, get ready to boot the main system...   
  172.     finish_recovery(send_intent);  
  173.     ui_print("Rebooting...\n");  
  174.     android_reboot(ANDROID_RB_RESTART, 0, 0);  
  175.     return EXIT_SUCCESS;  
  176. }  
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

 

  1. int  
  2. install_package(const char* path, int* wipe_cache, const char* install_file)  
  3. {  
  4.     FILE* install_log = fopen_path(install_file, "w");  
  5.     if (install_log) {  
  6.         fputs(path, install_log);  
  7.         fputc('\n', install_log);  
  8.     } else {  
  9.         LOGE("failed to open last_install: %s\n", strerror(errno));  
  10.     }  
  11.     int result = really_install_package(path, wipe_cache);  
  12.     if (install_log) {  
  13.         fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);  
  14.         fputc('\n', install_log);  
  15.         fclose(install_log);  
  16.     }  
  17.     return result;  
  18. }  
  19.   
  20. static int  
  21. really_install_package(const char *path, int* wipe_cache)  
  22. {  
  23.     // 更新 UI 显示   
  24.     ui_set_background(BACKGROUND_ICON_INSTALLING);  
  25.     ui_print("Finding update package...\n");  
  26.     ui_show_indeterminate_progress();  
  27.     LOGI("Update location: %s\n", path);  
  28.       
  29.     //确保升级包所在分区已经mount,通常为 cache 分区或者 SD 分区   
  30.     if (ensure_path_mounted(path) != 0) {  
  31.         LOGE("Can't mount %s\n", path);  
  32.         return INSTALL_CORRUPT;  
  33.     }  
  34.   
  35.     ui_print("Opening update package...\n");  
  36.       
  37.     //从/res/keys中装载公钥。   
  38.     int numKeys;  
  39.     RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);  
  40.     if (loadedKeys == NULL) {  
  41.         LOGE("Failed to load keys\n");  
  42.         return INSTALL_CORRUPT;  
  43.     }  
  44.     LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);  
  45.   
  46.     // Give verification half the progress bar...   
  47.     ui_print("Verifying update package...\n");  
  48.     ui_show_progress(  
  49.             VERIFICATION_PROGRESS_FRACTION,  
  50.             VERIFICATION_PROGRESS_TIME);  
  51.   
  52.         // 根据公钥验证升级包verify_file   
  53.     int err;  
  54.     err = verify_file(path, loadedKeys, numKeys);  
  55.     free(loadedKeys);  
  56.     LOGI("verify_file returned %d\n", err);  
  57.     if (err != VERIFY_SUCCESS) {  
  58.         LOGE("signature verification failed\n");  
  59.         return INSTALL_CORRUPT;  
  60.     }  
  61.   
  62.         // 打开升级包,将相关信息存到ZipArchive数据结构中,便于后面处理。   
  63.     /* Try to open the package. 
  64.      */  
  65.     ZipArchive zip;  
  66.     err = mzOpenZipArchive(path, &zip);  
  67.     if (err != 0) {  
  68.         LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");  
  69.         return INSTALL_CORRUPT;  
  70.     }  
  71.   
  72.     /* Verify and install the contents of the package. 
  73.      */  
  74.     ui_print("Installing update...\n");  
  75.     return try_update_binary(path, &zip, wipe_cache);  
  76. }  
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

 

  1. // If the package contains an update binary, extract it and run it.   
  2. static int  
  3. try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {  
  4.     const ZipEntry* binary_entry =  
  5.             mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);  
  6.     if (binary_entry == NULL) {  
  7.         mzCloseZipArchive(zip);  
  8.         return INSTALL_CORRUPT;  
  9.     }  
  10.   
  11.     // 将升级包内文件META-INF/com/google/android/update-binary    
  12.     // 复制为/tmp/update_binary   
  13.     char* binary = "/tmp/update_binary";  
  14.     unlink(binary);  
  15.     int fd = creat(binary, 0755);  
  16.     if (fd < 0) {  
  17.         mzCloseZipArchive(zip);  
  18.         LOGE("Can't make %s\n", binary);  
  19.         return INSTALL_ERROR;  
  20.     }  
  21.     bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);  
  22.     close(fd);  
  23.     mzCloseZipArchive(zip);  
  24.   
  25.     if (!ok) {  
  26.         LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);  
  27.         return INSTALL_ERROR;  
  28.     }  
  29.   
  30.     int pipefd[2];  
  31.     pipe(pipefd);  
  32.   
  33.     // When executing the update binary contained in the package, the   
  34.     // arguments passed are:   
  35.     //   
  36.     //   - the version number for this interface   
  37.     //   
  38.     //   - an fd to which the program can write in order to update the   
  39.     //     progress bar.  The program can write single-line commands:   
  40.     //   
  41.     //        progress <frac> <secs>   
  42.     //            fill up the next <frac> part of of the progress bar   
  43.     //            over <secs> seconds.  If <secs> is zero, use   
  44.     //            set_progress commands to manually control the   
  45.     //            progress of this segment of the bar   
  46.     //   
  47.     //        set_progress <frac>   
  48.     //            <frac> should be between 0.0 and 1.0; sets the   
  49.     //            progress bar within the segment defined by the most   
  50.     //            recent progress command.   
  51.     //   
  52.     //        firmware <"hboot"|"radio"> <filename>   
  53.     //            arrange to install the contents of <filename> in the   
  54.     //            given partition on reboot.   
  55.     //   
  56.     //            (API v2: <filename> may start with "PACKAGE:" to   
  57.     //            indicate taking a file from the OTA package.)   
  58.     //   
  59.     //            (API v3: this command no longer exists.)   
  60.     //   
  61.     //        ui_print <string>   
  62.     //            display <string> on the screen.   
  63.     //   
  64.     //   - the name of the package zip file.   
  65.     //   
  66.   
  67.     char** args = malloc(sizeof(char*) * 5);  
  68.     args[0] = binary;  
  69.     args[1] = EXPAND(RECOVERY_API_VERSION);   // defined in Android.mk   
  70.     args[2] = malloc(10);  
  71.     sprintf(args[2], "%d", pipefd[1]);  
  72.     args[3] = (char*)path;  
  73.     args[4] = NULL;  
  74.   
  75.         // 创建新的进程,执行:/tmp/update_binary   
  76.         // 同时,会给该进程传入一些参数,其中最重要的就是一个管道fd,供新进程与原进程通信。   
  77.         // 新进程诞生后,原进程就变成了一个服务进程,它提供若干UI更新服务   
  78.         // 新进程就可以通过老进程的UI系统完成显示任务。而其他功能就靠它自己了。   
  79.     pid_t pid = fork();  
  80.     if (pid == 0) {  
  81.         close(pipefd[0]);  
  82.         execv(binary, args);  
  83.         fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));  
  84.         _exit(-1);  
  85.     }  
  86.     close(pipefd[1]);  
  87.   
  88.     *wipe_cache = 0;  
  89.       
  90.     // 更新UI   
  91.     char buffer[1024];  
  92.     FILE* from_child = fdopen(pipefd[0], "r");  
  93.     while (fgets(buffer, sizeof(buffer), from_child) != NULL) {  
  94.         char* command = strtok(buffer, " \n");  
  95.         if (command == NULL) {  
  96.             continue;  
  97.         } else if (strcmp(command, "progress") == 0) {  
  98.             char* fraction_s = strtok(NULL, " \n");  
  99.             char* seconds_s = strtok(NULL, " \n");  
  100.   
  101.             float fraction = strtof(fraction_s, NULL);  
  102.             int seconds = strtol(seconds_s, NULL, 10);  
  103.   
  104.             ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION),  
  105.                              seconds);  
  106.         } else if (strcmp(command, "set_progress") == 0) {  
  107.             char* fraction_s = strtok(NULL, " \n");  
  108.             float fraction = strtof(fraction_s, NULL);  
  109.             ui_set_progress(fraction);  
  110.         } else if (strcmp(command, "ui_print") == 0) {  
  111.             char* str = strtok(NULL, "\n");  
  112.             if (str) {  
  113.                 ui_print("%s", str);  
  114.             } else {  
  115.                 ui_print("\n");  
  116.             }  
  117.         } else if (strcmp(command, "wipe_cache") == 0) {  
  118.             *wipe_cache = 1;  
  119.         } else {  
  120.             LOGE("unknown command [%s]\n", command);  
  121.         }  
  122.     }  
  123.     fclose(from_child);  
  124.   
  125.     int status;  
  126.     waitpid(pid, &status, 0);  
  127.     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {  
  128.         LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));  
  129.         return INSTALL_ERROR;  
  130.     }  
  131.   
  132.     return INSTALL_SUCCESS;  
  133. }  
// 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,下回继续研究。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值