Android OTA Upgrade Principle and Process Analysis (7) - - The Core Installat_package Function of Recovery Service

Keywords: Android Apache Google

Principle and Process Analysis of Android OTA Upgrade (7) - The Core Installat_package Function of Recovery Service

1. Recovery service core install_package (update.zip-specific)

          Unlike wipe_data and wipe_cache in Recovery Services, install_package() is a unique and core part of upgrading update.zip. This is where we really started processing our update. zip package. Let's start with this part. Look at the illustrations first.

The source files for this section are located at: / gingerbread0919/bootable/recovery/install.c. Is this a source file without main function, or do you paste the source code first as follows:

    /* 
     * Copyright (C) 2007 The Android Open Source Project 
     * 
     * Licensed under the Apache License, Version 2.0 (the "License"); 
     * you may not use this file except in compliance with the License. 
     * You may obtain a copy of the License at 
     * 
     *      http://www.apache.org/licenses/LICENSE-2.0 
     * 
     * Unless required by applicable law or agreed to in writing, software 
     * distributed under the License is distributed on an "AS IS" BASIS, 
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
     * See the License for the specific language governing permissions and 
     * limitations under the License. 
     */  

    #include <ctype.h>  
    #include <errno.h>  
    #include <fcntl.h>  
    #include <limits.h>  
    #include <sys/stat.h>  
    #include <sys/wait.h>  
    #include <unistd.h>  

    #include "common.h"  
    #include "install.h"  
    #include "mincrypt/rsa.h"  
    #include "minui/minui.h"  
    #include "minzip/SysUtil.h"  
    #include "minzip/Zip.h"  
    #include "mtdutils/mounts.h"  
    #include "mtdutils/mtdutils.h"  
    #include "roots.h"  
    #include "verifier.h"  

    #define ASSUMED_UPDATE_BINARY_NAME  "META-INF/com/google/android/update-binary"  
    #define PUBLIC_KEYS_FILE "/res/keys"  

    // If the package contains an update binary, extract it and run it.  
    static int  
    try_update_binary(const char *path, ZipArchive *zip) {  
        const ZipEntry* binary_entry =  
                mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);  
        if (binary_entry == NULL) {  
            mzCloseZipArchive(zip);  
            return INSTALL_CORRUPT;  
        }  

        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 1;  
        }  
        bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);  
        close(fd);  
        mzCloseZipArchive(zip);  

        if (!ok) {  
            LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);  
            return 1;  
        }  

        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;  

        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]);  

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

    // Reads a file containing one or more public keys as produced by  
    // DumpPublicKey:  this is an RSAPublicKey struct as it would appear  
    // as a C source literal, eg:  
    //  
    //  "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"  
    //  
    // (Note that the braces and commas in this example are actual  
    // characters the parser expects to find in the file; the ellipses  
    // indicate more numbers omitted from this example.)  
    //  
    // The file may contain multiple keys in this format, separated by  
    // commas.  The last key must not be followed by a comma.  
    //  
    // Returns NULL if the file failed to parse, or if it contain zero keys.  
    static RSAPublicKey*  
    load_keys(const char* filename, int* numKeys) {  
        RSAPublicKey* out = NULL;  
        *numKeys = 0;  

        FILE* f = fopen(filename, "r");  
        if (f == NULL) {  
            LOGE("opening %s: %s\n", filename, strerror(errno));  
            goto exit;  
        }  

        int i;  
        bool done = false;  
        while (!done) {  
            ++*numKeys;  
            out = realloc(out, *numKeys * sizeof(RSAPublicKey));  
            RSAPublicKey* key = out + (*numKeys - 1);  
            if (fscanf(f, " { %i , 0x%x , { %u",  
                       &(key->len), &(key->n0inv), &(key->n[0])) != 3) {  
                goto exit;  
            }  
            if (key->len != RSANUMWORDS) {  
                LOGE("key length (%d) does not match expected size\n", key->len);  
                goto exit;  
            }  
            for (i = 1; i < key->len; ++i) {  
                if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;  
            }  
            if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;  
            for (i = 1; i < key->len; ++i) {  
                if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;  
            }  
            fscanf(f, " } } ");  

            // if the line ends in a comma, this file has more keys.  
            switch (fgetc(f)) {  
                case ',':  
                    // more keys to come.  
                    break;  

                case EOF:  
                    done = true;  
                    break;  

                default:  
                    LOGE("unexpected character between keys\n");  
                    goto exit;  
            }  
        }  

        fclose(f);  
        return out;  

    exit:  
        if (f) fclose(f);  
        free(out);  
        *numKeys = 0;  
        return NULL;  
    }  

    int  
    install_package(const char *path)  
    {  
        ui_set_background(BACKGROUND_ICON_INSTALLING);  
        ui_print("Finding update package...\n");  
        ui_show_indeterminate_progress();  
        LOGI("Update location: %s\n", path);  

        if (ensure_path_mounted(path) != 0) {  
            LOGE("Can't mount %s\n", path);  
            return INSTALL_CORRUPT;  
        }  

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

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

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

        /* 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);  
    } 



Following the above flow chart and source code to analyze this process:

        Guarantee_path_mount(): First, determine whether the partition where the updated.zip package path is passed is mounted. If not, mount first.

        (2) load_keys(): Load the public key source file with the path at / res/keys. This file is in the root file system of the Recovery mirror.

        (3) verify_file(): verify the signature of update.zip package.

        mzOpenZipArchive(): Open the upgrade package and copy the relevant information into a temporary ZipArchinve variable. This step did not unzip our update.zip package.

        try_update_binary(): This function is where we update.zip. This function starts by copying the update_binary file to / tmp/update_binary of the memory file system based on the zip package information we obtained in the next step and the absolute path of the upgrade package. For later use.

        pipe(): Create a pipe for communication between the following child and parent processes.

        fork(): Create subprocesses. The child process is mainly responsible for executing binary (execv(binary,args), that is, executing our installation command script), and the parent process is responsible for receiving the command sent by the child process to update the ui display (showing the current progress). Son-parent interprocess communication relies on pipelines.

        (5) Among them, the parent process has two functions after creating the child process. One is to update UI display by pipeline receiving commands sent by subprocesses. The second is to wait for the child process to exit and return to INSTALL SUCCESS. The following commands are sent by the child process while parsing and executing the installation script:

                   Progress < frac > < secs >: Set the progress bar according to the second parameter secs (secs).

                   Set_progress < frac >: Set the progress bar directly, and the frac is between 0.0 and 0.1.


                   Firmware <"hboot"|"radio"> <filename>: Used when updating firmware, no longer used in API V3.

                   Ui_print < string >: Displays a string on the screen, which is the printing update process.

             The function of execv(binary,args) is to execute the binary program, which is essentially to parse and execute the commands in the updater-script script in the update.zip package. Thus, the Recovery service enters the process of actually installing the update.zip package.

             The next article continues to analyze the process of parsing and executing updater-script using update-binary.

Document reference:
1. Android OTA Upgrade Principle and Process Analysis (7) - The Core Installat_package Function of Recovery Service
http://blog.csdn.net/LOVE000520/article/details/51818695

Posted by agentsmith on Fri, 04 Jan 2019 17:45:09 -0800