Updater

updater is an individual executable for the target device in the AOSP source tree. It can be found in the $AOSP/bootable/recovery/updater folder. Let's look at the main function in the updater.cpp file. Since the main function is a little long, let's look at it in several paragraphs:

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "edify/expr.h"
#include "updater.h"
#include "install.h"
#include "blockimg.h"
#include "minzip/Zip.h"
#include "minzip/SysUtil.h"

#include "register.inc"

#define SCRIPT_NAME "META-INF/com/google/android/updater-script"

extern bool have_eio_error;

struct selabel_handle *sehandle;

int main(int argc, char** argv) {
setbuf(stdout, NULL);
setbuf(stderr, NULL);

if (argc != 4 && argc != 5) {
printf("unexpected number of arguments (%d) ", argc);
return 1;
}

char* version = argv[1];
if ((version[0] != '1' && version[0] != '2' && version[0] != '3')
||
version[1] != '') {
// We support version 1, 2, or 3.
printf("wrong updater binary API; expected 1, 2, or 3; "
"got %s ",
argv[1]);
return 2;
}

The updater has four arguments. The first thing it will do is check whether there are four arguments passed to it. As we can see from the code, these four arguments are:

  • The first argument is the executable name, which is update-binary here
  • The second argument is the updater version
  • The third argument is the pipe that can be used to communicate to the recovery
  • The fourth argument is the OTA package path

It will check the updater version before it continues:

// Set up the pipe for sending commands back to the parent process. 

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];
MemMapping map;
if (sysMapFile(package_filename, &map) != 0) {
printf("failed to map package %s ", 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 ",
argv[3], strerror(err));
return 3;
}
ota_io_init(&za);

const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME);
if (script_entry == NULL) {
printf("failed to find %s in %s ", SCRIPT_NAME, package_filename);
return 4;
}

char* script = reinterpret_cast<char*>(malloc(script_entry->uncompLen+1));
if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) {
printf("failed to read script from package ");
return 5;
}
script[script_entry->uncompLen] = '';

The next thing to do is to open the pipe to establish the communication channel with recovery. Then it extracts updater-script from the OTA package to prepare for the execution of the script:

// Configure edify's functions. 

RegisterBuiltins();
RegisterInstallFunctions();
RegisterBlockImageFunctions();
RegisterDeviceExtensions();
FinishRegistration();

// Parse the script.

Expr* root;
int error_count = 0;
int error = parse_string(script, &root, &error_count);
if (error != 0 || error_count > 0) {
printf("%d parse errors ", 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 ");
}

// Evaluate the parsed script.

UpdaterInfo updater_info;
updater_info.cmd_pipe = cmd_pipe;
updater_info.package_zip = &za;
updater_info.version = atoi(version);
updater_info.package_zip_addr = map.addr;
updater_info.package_zip_len = map.length;

State state;
state.cookie = &updater_info;
state.script = script;
state.errmsg = NULL;

if (argc == 5) {
if (strcmp(argv[4], "retry") == 0) {
state.is_retry = true;
} else {
printf("unexpected argument: %s", argv[4]);
}
}

char* result = Evaluate(&state, root);

if (have_eio_error) {
fprintf(cmd_pipe, "retry_update ");
}

if (result == NULL) {
if (state.errmsg == NULL) {
printf("script aborted (no error message) ");
fprintf(cmd_pipe, "ui_print script aborted (no error
message) ");
} else {
printf("script aborted: %s ", state.errmsg);
char* line = strtok(state.errmsg, " ");
while (line) {
if (*line == 'E') {
if (sscanf(line, "E%u: ", &state.error_code) != 1) {
printf("Failed to parse error code: [%s] ", line);
}
}
fprintf(cmd_pipe, "ui_print %s ", line);
line = strtok(NULL, " ");
}
fprintf(cmd_pipe, "ui_print ");
}

if (state.error_code != kNoError) {
fprintf(cmd_pipe, "log error: %d ", state.error_code);
if (state.cause_code != kNoCause) {
fprintf(cmd_pipe, "log cause: %d ", state.cause_code);
}
}

free(state.errmsg);
return 7;
} else {
fprintf(cmd_pipe, "ui_print script succeeded: result was [%s] ",
result);
free(result);
}

if (updater_info.package_zip) {
mzCloseZipArchive(updater_info.package_zip);
}
sysReleaseMap(&map);
free(script);

return 0;
}

Before it can start to execute the update script, it needs to register functions to interpret edify language inside the update script. As we can see from the preceding code, these functions include the following four categories:

  • Built-in functions to support the edify language syntax. These functions are implemented in bootable/recovery/edify/expr.cpp.
  • Package installation related functions. These functions are implemented in bootable/recovery/updater/install.cpp.
  • Functions to handle block-based OTA packages. In Android 4.4 and earlier versions, the file-based OTA updates are used. In Android 5.0 and later versions, the block-based OTA updates are used. Refer to the following URL about file versus block OTAs:
    https://source.android.com/devices/tech/ota/block.html
    The block-based functions are implemented in bootable/recovery/updater/blockimg.cpp.
  • The developers can extend recovery and updater to provide device-specific OTA extensions.

After it registers all functions, it calls the parse_string function to parse the script. Finally, it calls the Evaluate function to execute the script.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.68.3