Creating over the air updates

Google provided plenty of developer tools to generate the different types of OTA. If you want to generate a Full Update OTA, the following two steps are required:

  1. Generate a ZIP file containing the full update files
  2. Generate the OTA package with all the necessary toolsets for the update

To generate the zip file containing the chosen target files, navigate to the root folder of the AOSP sources and run the following commands:

. build/envsetup.sh && lunch aosp-shamu
mkdir dist_output
make dist DIST_DIR=dist_output

If the process has been successful, we should have the zip file containing the target files in the directory dist_output. As an example, let's try listing the folder content with the following command:

ls -l dist_output/*target_files*

Now we should see a .zip file that will also have in its name the name of the target we are compiling for.

At this point, you only need to generate the OTA package containing all the necessary files for the update. Among the available tools, there's a utility that will help us do so, through the following command:

./build/tools/releasetools/ota_from_target_files 
    dist_output/aosp_shamu-target_files-eng.esteban.zip ota_update.zip

As shown here, you'll find the screen with the generated OTA package and the command output:

Creating over the air updates

Now we have our OTA package ready to be installed on development devices, because the default OTA is signed with test keys. If you want to provide your users with an installable OTA package, you need to sign the OTA with your own private keys, using the specific option provided by the OTA-generation tool.

In order to generate an incremental OTA, the procedure is nearly the same, except that you also need to indicate the ZIP file containing the previous OTA version. The command will be something like the following:

./build/tools/releasetools/ota_from_target_files 
    -i PREVIOUS-aosp-shamu-target_files.zip  
    dist_output/aosp-shamu-target_files.zip incremental_ota_update.zip

As for our previous example, you'll get a ZIP file containing the incremental backup.

Finally, there are no predefined tools for the composition of the Update OTA package, as it's up to us to decide what to install/update through the update script, which we will examine in detail later.

OTA internals

As anticipated in the previous section, an OTA package contains a binary file in its folder tree:

META-INF/com/google/android/update-binary

This binary file is generated by Android's build system, in the bootable/recovery/updater folder, and it is used to properly perform the update.

The binary contains internal routines and an interpreter for the scripting language called Edify. This language supports a set of ad hoc commands in order to allow the correct execution of a system update without affecting the integrity of the system itself. You can find an example of an Edify script in one of the OTA ZIP files you have just generated, at:

META-INF/com/google/android/updater-script

Shown here is an example screenshot for an Edify script:

OTA internals

Usually, we don't need to manually write any Edify code, because in a standard scenario there are automated tools that generate the correct OTA packages containing all the necessary files, but it could be useful to manually modify them when debugging, or in case we are building our custom ROM from binaries and we need to customize the installation on the flash memory of the relative files.

Let's have a look at the Edify syntax in the next section.

Edify syntax

The first thing to know is that Edify evaluates every expression as all string type values. An empty string is considered as false in a Boolean context, while any other value is considered as true. To recapitulate, Edify supports all the following expression types:

(expr )
 expr + expr  # string concatenation, not integer addition
 expr == expr
 expr != expr
 expr && expr
 expr || expr
 ! expr
 if expr then expr endif
 if expr then expr else expr endif
 function_name(expr, expr,...)
 expr; expr

Every string that contains the following type of character, which of course are not reserved words, are considered as string literal:

a-z, A-Z, 0-9, _, :, /, .

With reserved words, we refer to words such as if else, and endif.

Constant strings can also be written using double-quotes, in order to create strings with spaces or other characters not listed in the previous example, such as the following:

, 	,

It can also be respectively written as follows for the new line and tab:

", \ 

As an escape character, we use " and in a string written with double-quotes.

The operators are simply short-circuiting, that is, the right side isn't even considered if the logic result is determined by the left side of the expression. The syntax can be very concise, as shown in the following snippet; the two lines are equivalent:

a1 && a2
if a1 then a2 endif

The ; character is a sequence point, meaning that what's at its left is considered before, and what's at its right is considered after.

Let's see a richer example:

show_progress(0.750000, 0);
ui_print("Android Shamu");
mount("ext4", "EMMC", "/dev/block/…/system", "system");
unmount("/system");

The interpreter contains all the functions that are necessary to complete a correct update. Unless differently specified, the functions usually render true in case of success and false in case of error.

The language provides utility methods to control the flow and manage edge situations. If, for example, we want to trigger an error to block the installation, we can use the following functions:

abort();
assert();

As you can expect, in case you want to add a new feature, you can do that by modifying the sources, but before that, let's have a look at some of the most useful functions already available:

  • abort([msg]): This method gives you the opportunity to abort the currently running script. It also takes a string argument, msg, that can be shown to the user as further information about the abort.
  • assert(expr[, expr, ...]): This method takes a list of expressions as argument and evaluates them one by one. If any of these expressions fail, or returns false, the whole script execution stops. The system also shows an "Assert failed" message and the assert text that just failed.
  • apply_patch(src_file, tgt_file, tgt_sha1, tgt_size, patch1_sha1, patch1_blob, [...]): This method takes a patch1_blob file and applies it as a binary patch to the source file src_file to produce the target tgt_file.
  • delete_recursive([dirname, ...]): This function takes a list of folder names as argument and deletes them, also deleting every single file they contain.
  • file_getprop(filename, key): This method can be considered as a properties file inspector. It takes a couple of arguments, a filename and a key, and scans the file as if it were a property file, looking for the provided key. If the key is found, its value is returned.
  • format(fs_type, partition_type, location, fs_size, mount_point): This method provides a powerful way to format partitions.
  • ifelse(cond, e1[, e2]): This method represents the common it-then-else computer science statement.
  • is_mounted(mount_point): This method helps to detect mounted partitions.
  • mount(fs_type, partition_type, name, mount_point): This method mounts a filesystem of fs_type at mount_point.
  • rename(src_filename, tgt_filename): This method takes two arguments, to perform a renaming from src_filename to tgt_filename.
  • run_program(path[, arg, ...]): This method executes the binary at path, passing args, and it returns the program's exit status.
  • sleep(secs): This method takes an integer, secs, as an argument and pauses the execution for secs seconds.
  • symlink(target[, source, ...]): This method takes a target file and a list of sources and creates all sources as symlinks to target.
  • unmount(mount_point): This is the counterpart of mount. This method unmounts the filesystem mounted at mount_point.
  • This is just a subset of all the available commands. If you are curious about the whole list, you can check the official Google documentation at http://source.android.com/devices/tech/ota/inside_packages.html.

We are now able to modify—or create from scratch—an Edify script for an Update installation. This knowledge will turn out to be very useful with the custom ROM, especially when the sources are not available, in case you want to modify the system through a custom recovery, installing specific files in the read-only system partitions.

OTA for custom ROM

As already anticipated, out of the OTA concept we get a convenient system for the custom ROM installation. The reason for this is that most custom ROMs are distributed as Update ZIP packages, to be fed to the custom Recovery, which will then take care of the package installation in the system. Analyzing the OTA structure—as we did in the previous section—we can intuitively understand how to organize a specific package to install a modified version of Android. In fact, through an ad hoc Edify script, it is possible to format and reinstall all the files that are contained in any system partition, in order to distribute your own modified Android version.

This task is left as an exercise to the reader as it can be achieved with the knowledge acquired so far.

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

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