From 70ec4de9965caf910eb30cac5a5d01231ed70497 Mon Sep 17 00:00:00 2001
From: Heidi von Markham You can enable block-based over-the-air (OTA) updates for new devices
+running Android 5.0. OTA is the mechanism by which OEMs remotely update the
+system partition of a device: Because block OTA ensures that each device uses the same partition, it
+enables the use of dm-verity to cryptographically sign the system partition.
+For details on dm-verity, see
+Secure Boot.
+ Note: You must have a working block OTA
+system before using dm-verity. For devices launching with Android 5.0 or later, use block OTA updates in
+the factory ROM. To generate a block-based OTA for subsequent updates, pass
+the For devices that launched with Android 4.4 or earlier, use file OTA
+updates. While is it possible to transition devices by sending a full block
+OTA of Android 5.0 or later, it requires sending out a full OTA that is
+significantly larger than an incremental OTA (and is therefore discouraged).
+ Because dm-verity requires bootloader support found only in new devices
+shipping with Android 5.0 or later, you cannot enable dm-verity for
+existing devices. Developers working on the Android OTA system (the recovery image and the
+scripts that generate OTAs) can keep up with changes by subscribing to the
+android-ota@googlegroups.com
+mailing list. During a file-based OTA, Android attempts to change the contents of the
+system partition at the filesystem layer (on a file-by-file basis). The update
+is not guaranteed to write files in a consistent order, have a consistent last
+modified time or superblock, or even place the blocks in the same location on
+the block device. For this reason, file-based OTAs fail on a dm-verity-enabled
+device; after the OTA attempt, the device does not boot. During a block-based OTA, Android serves the device the difference between
+the two block images (rather than two sets of files). The update checks a
+device build against the corresponding build server at the block level (below
+the filesystem) using one of the following methods: Note: For devices with unmodified system partitions running Android 5.0,
+the download and install process for a block OTA remains the same as for a
+file OTA. However, the OTA update itself might include one or more of the
+following differences:In this document
+
+
+
+
+Recommendations
+
+--block
option to ota_from_target_files
.File vs. Block OTAs
+
+
+
+
+adb fastboot
places the
+exact same bits on the device as a full OTA, so flashing is compatible with
+block OTA.Updating unmodified systems
+
+
+
Figure 1. Compare Nexus 6 OTA sizes +between Android 5.0 and Android 5.1 releases (varying target build changes)
+ +In general, incremental block OTA updates are larger than incremental file +OTA updates due to:
+For devices with modified system partitions running Android 5.0:
+adb remount
or as a result of malware. File
+OTA tolerates some changes to the partition, such as the addition of files
+that are not part of the source or target build. However, block OTA does not
+tolerate additions to the partition, so users will need to install a full OTA
+overwriting any system partition modifications) or flash a new system image to
+enable future OTAs.The recovery system includes several hooks for inserting device-specific +code so that OTA updates can also update parts of the device other than the +Android system (e.g., the baseband or radio processor).
+The following sections and examples customize the tardis device +produced by the yoyodyne vendor.
+ +As of Android 2.3, the platform supports eMMC flash devices and the ext4 +filesystem that runs on those devices. It also supports Memory Technology Device +(MTD) flash devices and the yaffs2 filesystem from older releases.
+The partition map file is specified by TARGET_RECOVERY_FSTAB; this file is +used by both the recovery binary and the package-building tools. You can +specify the name of the map file in TARGET_RECOVERY_FSTAB in BoardConfig.mk.
+A sample partition map file might look like this:
+ +device/yoyodyne/tardis/recovery.fstab
+# mount point fstype device [device2] [options (3.0+ only)] + +/sdcard vfat /dev/block/mmcblk0p1 /dev/block/mmcblk0 +/cache yaffs2 cache +/misc mtd misc +/boot mtd boot +/recovery emmc /dev/block/platform/s3c-sdhci.0/by-name/recovery +/system ext4 /dev/block/platform/s3c-sdhci.0/by-name/system length=-4096 +/data ext4 /dev/block/platform/s3c-sdhci.0/by-name/userdata ++ +
With the exception of /sdcard
, which is optional, all mount
+points in this example must be defined (devices may also add extra partitions).
+There are five supported filesystem types:
/proc/mtd
./proc/mtd
.All partitions must be mounted in the root directory (i.e. the mount point
+value must begin with a slash and have no other slashes). This restriction
+applies only to mounting filesystems in recovery; the main system is free to
+mount them anywhere. The directories /boot
, /recovery
,
+and /misc
should be raw types (mtd or emmc), while the
+directories /system
, /data
, /cache
, and
+/sdcard
(if available) should be filesystem types (yaffs2, ext4,
+or vfat).
Starting in Android 3.0, the recovery.fstab file gains an additional +optional field, options. Currently the only defined option is length +, which lets you explicitly specify the length of the partition. +This length is used when reformatting the partition (e.g., for the userdata +partition during a data wipe/factory reset operation, or for the system +partition during installation of a full OTA package). If the length value is +negative, then the size to format is taken by adding the length value to the +true partition size. For instance, setting "length=-16384" means the last 16k +of that partition will not be overwritten when that partition is +reformatted. This supports features such as encryption of the userdata +partition (where encryption metadata is stored at the end of the +partition that should not be overwritten).
+ +Note: The device2 and options +fields are optional, creating ambiguity in parsing. If the entry in the fourth +field on the line begins with a ‘/' character, it is considered a device2 + entry; if the entry does not begin with a ‘/' character, it is considered +an options field.
+ +To support devices with different available hardware (physical buttons, +LEDs, screens, etc.), you can customize the recovery interface to display +status and access the manually-operated hidden features for each device.
+Your goal is to build a small static library with a couple of C++ objects
+to provide the device-specific functionality. The file
+bootable/recovery/default_device.cpp
is used by default, and
+makes a good starting point to copy when writing a version of this file for
+your device.
device/yoyodyne/tardis/recovery/recovery_ui.cpp
+#include <linux/input.h> + +#include "common.h" +#include "device.h" +#include "screen_ui.h" ++ + +
The Device class requires functions for returning headers and items that +appear in the hidden recovery menu. Headers describe how to operate the menu +(i.e. controls to change/select the highlighted item).
+ ++static const char* HEADERS[] = { "Volume up/down to move highlight;", + "power button to select.", + "", + NULL }; + +static const char* ITEMS[] = {"reboot system now", + "apply update from ADB", + "wipe data/factory reset", + "wipe cache partition", + NULL }; ++ +
Note: Long lines are truncated (not wrapped), +so keep the width of your device screen in mind.
+ +Next, define your device's RecoveryUI implementation. This example assumes
+the tardis device has a screen, so you can inherit from the built-in
+ScreenRecoveryUIimplementation (see instructions for
+devices without a screen.) The only
+function to customize from ScreenRecoveryUI is CheckKey()
, which
+does the initial asynchronous key handling:
+class TardisUI : public ScreenRecoveryUI { + public: + virtual KeyAction CheckKey(int key) { + if (key == KEY_HOME) { + return TOGGLE; + } + return ENQUEUE; + } +}; ++ + +
The KEY_* constants are defined in linux/input.h
.
+CheckKey()
is called no matter what is going on in the rest of
+recovery: when the menu is toggled off, when it is on, during package
+installation, during userdata wiping, etc. It can return one of four constants:
+
CheckKey()
is called each time a key-down event is followed by
+a key-up event for the same key. (The sequence of events A-down B-down B-up
+A-up results only in CheckKey(B)
being called.) CheckKey()
+
can call IsKeyPressed()
, to find out if other keys are
+being held down. (In the above sequence of key events, if CheckKey(B)
+
called IsKeyPressed(A)
it would have returned true.)
CheckKey()
can maintain state in its class; this can be useful
+to detect sequences of keys. This example shows a slightly more complex
+setup: the display is toggled by holding down power and pressing volume-up,
+and the device can be rebooted immediately by pressing the power button five
+times in a row (with no other intervening keys):
+class TardisUI : public ScreenRecoveryUI { + private: + int consecutive_power_keys; + + public: + TardisUI() : consecutive_power_keys(0) {} + + virtual KeyAction CheckKey(int key) { + if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { + return TOGGLE; + } + if (key == KEY_POWER) { + ++consecutive_power_keys; + if (consecutive_power_keys >= 5) { + return REBOOT; + } + } else { + consecutive_power_keys = 0; + } + return ENQUEUE; + } +}; ++ + +
When using your own images (error icon, installation animation, progress +bars) with ScreenRecoveryUI, you might need to set some member variables to +specify attributes such as the number of frames, speed, and overlay offsets. +You can set the following variables:
+ +Variable Name | +Purpose | +Release | +
---|---|---|
animation_fps + | +speed (in frames per second) of animations + | +Android 5.x and earlier | +
installing_frames + | +number of frames in the installation animation + | +Android 4.x and earlier | +
install_overlay_offset_x, +install_overlay_offset_y + | +offset of the per-frame overlay (relative to the base image) for the +installation animation + | +Android 4.x and earlier | +
To set variables, override the ScreenRecoveryUI::Init()
+function in your subclass. Set the values, then call the parent Init()
+
function to complete initialization:
+class TardisUI : public ScreenRecoveryUI { + ... + void Init() { + // change the speed at which animations run + animation_fps = 30; + + ScreenRecoveryUI::Init(); + } ++ +
The default values correspond to the default recovery images; when using
+these images you don't need to provide an Init()
function. For
+details on images, see Recovery UI Images.
+
After you have a RecoveryUI implementation, define your device class
+(subclassed from the built-in Device class). It should create a single
+instance of your UI class and return that from the GetUI()
+function:
+class TardisDevice : public Device { + private: + TardisUI* ui; + + public: + TardisDevice() : + ui(new TardisUI) { + } + + RecoveryUI* GetUI() { return ui; } ++ +
The StartRecovery()
method is called at the start of recovery,
+after the UI has been initialized and after the arguments have been parsed,
+but before any action has been taken. The default implementation does nothing,
+so you do not need to provide this in your subclass if you have nothing to do:
+
+ void StartRecovery() { + // ... do something tardis-specific here, if needed .... + } ++ +
The system calls two methods to get the list of header lines and the list +of items. In this implementation, it returns the static arrays defined at the +top of the file:
+ ++const char* const* GetMenuHeaders() { return HEADERS; } +const char* const* GetMenuItems() { return ITEMS; } ++ +
Next, provide a HandleMenuKey()
function, which takes a
+keypress and the current menu visibility, and decides what action to take:
+ int HandleMenuKey(int key, int visible) { + if (visible) { + switch (key) { + case KEY_VOLUMEDOWN: return kHighlightDown; + case KEY_VOLUMEUP: return kHighlightUp; + case KEY_POWER: return kInvokeItem; + } + } + return kNoAction; + } ++ +
The method takes a key code (which has previously been processed and
+enqueued by the CheckKey()
method of the UI object), and the
+current state of the menu/text log visibility. The return value is an integer.
+If the value is 0 or higher, that is taken as the position of a menu item,
+which is invoked immediately (see the InvokeMenuItem()
method
+below). Otherwise it can be one of the following predefined constants:
As implied by the the visible argument, HandleMenuKey()
is
+called even if the menu is not visible. Unlike CheckKey()
, it is
+not called while recovery is doing something such as wiping data or
+installing a package—it's called only when recovery is idle and waiting for
+input.
If your device has a trackball-like input mechanism (generates input events
+with type EV_REL and code REL_Y), recovery synthesizes KEY_UP and KEY_DOWN
+keypresses whenever the trackball-like input device reports motion in the Y
+axis. All you need to do is map KEY_UP and KEY_DOWN events onto menu actions.
+This mapping does not happen for CheckKey()
, so you can't
+use trackball motions as triggers for rebooting or toggling the display.
To check for keys being held down as modifiers, call the IsKeyPressed()
+
method of your own UI object. For example, on some
+devices pressing Alt-W in recovery would start a data wipe whether the menu
+was visible or not. YOu could implement like this:
+ int HandleMenuKey(int key, int visible) { + if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { + return 2; // position of the "wipe data" item in the menu + } + ... + } ++ +
Note: If visible is false, it doesn't +make sense to return the special values that manipulate the menu (move +highlight, invoke highlighted item) since the user can't see the highlight. +However, you can return the values if desired.
+ +Next, provide an InvokeMenuItem()
method that maps integer
+positions in the array of items returned by GetMenuItems()
to
+actions. For the array of items in the tardis example, use:
+ BuiltinAction InvokeMenuItem(int menu_position) { + switch (menu_position) { + case 0: return REBOOT; + case 1: return APPLY_ADB_SIDELOAD; + case 2: return WIPE_DATA; + case 3: return WIPE_CACHE; + default: return NO_ACTION; + } + } ++ +
This method can return any member of the BuiltinAction enum to tell the +system to take that action (or the NO_ACTION member if you want the system to +do nothing). This is the place to provide additional recovery functionality +beyond what's in the system: Add an item for it in your menu, execute it here +when that menu item is invoked, and return NO_ACTION so the system does nothing +else.
+BuiltinAction contains the following values:
+The last method, WipeData()
, is optional and is called
+whenever a data wipe operation is initiated (either from recovery via the menu
+or when the user has chosen to do a factory data reset from the main system).
+This method is called before the user data and cache partitions are wiped. If
+your device stores user data anywhere other than those two partitions, you
+should erase it here. You should return 0 to indicate success and another
+value for failure, though currently the return value is ignored. The user data
+and cache partitions are wiped whether you return success or failure.
+ int WipeData() { + // ... do something tardis-specific here, if needed .... + return 0; + } ++ +
Finally, include some boilerplate at the end of the recovery_ui.cpp file
+for the make_device()
function that creates and returns an
+instance of your Device class:
+class TardisDevice : public Device { + // ... all the above methods ... +}; + +Device* make_device() { + return new TardisDevice(); +} ++ +
After completing the recovery_ui.cpp file, built it and link it to recovery +on your device. In Android.mk, create a static library that contains only this +C++ file:
+ +device/yoyodyne/tardis/recovery/Android.mk
+LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := eng +LOCAL_C_INCLUDES += bootable/recovery +LOCAL_SRC_FILES := recovery_ui.cpp + +# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk +LOCAL_MODULE := librecovery_ui_tardis + +include $(BUILD_STATIC_LIBRARY) ++ +
Then, in the board configuration for this device, specify your static +library as the value of TARGET_RECOVERY_UI_LIB.
+ ++device/yoyodyne/tardis/BoardConfig.mk + [...] + +# device-specific extensions to the recovery UI +TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis ++ + +
The recovery user interface consists images. Ideally, users never interact +with the UI: During a normal update, the phone boots into recovery, fills the +installation progress bar, and boots back into the new system without input +from the user. In the event of a system update problem, the only user action +that can be taken is to call customer care.
+An image-only interface obviates the need for localization. However, as of +Android 5.x the update can display a string of text (e.g. "Installing system +update...") along with the image. For details, see +Localized recovery text.
+ +The Android 5.x recovery UI uses two main images: the error image +and the installing animation.
+ +
+![]() Figure 1. icon_error.png + |
+
+![]() Figure 2. icon_installing.png + |
+
The installing animation is represented as a single PNG image with
+different frames of the animation interlaced by row. For example, for a
+7-frame animation that is 200x200, create a single 200x1400 image where first
+frame is rows 0, 7, 14, 21, ...; the second frame is rows 1, 8, 15, 22, ...;
+etc. The combined image includes a text chunk that indicates the number of
+animation frames. The tool bootable/recovery/interlace-frames.py
+takes a set of input frames and combines them into the necessary composite
+image used by recovery.
Default images are available in different densities and are located in
+bootable/recovery/res-$DENSITY/images
(e.g.,
+bootable/recovery/res-hdpi/images
). To use a static image during
+installation, you need only provide the icon_installing.png image and set the
+number of frames in the animation to 0 (the error icon is not animated; it is
+always a static image).
The Android 4.x and earlier recovery UI uses the error image (shown +above) and the installing animation plus several overlay images:
+ +
+![]() Figure 3. icon_installing.png + |
+
+![]() Figure 4. icon-installing_overlay01.png + + |
+
+![]() Figure 5. icon_installing_overlay07.png + + |
+
During installation, the on-screen display is constructed by drawing the +icon_installing.png image, then drawing one of the overlay frames on top of it +at the proper offset. Here, a red box is superimposed to highlight where the +overlay is placed on top of the base image:
+ +![]() Figure 6. Installing animation frame 1 +(icon_installing.png + icon_installing_overlay01.png) + |
+![]() Figure 7. Installing animation frame 7 +(icon_installing.png + icon_installing_overlay07.png) + |
+
Subsequent frames are displayed by drawing only the next overlay +image atop what's already there; the base image is not redrawn.
+ +The number of frames in the animation, desired speed, and x- and y-offsets
+of the overlay relative to the base are set by member variables of the
+ScreenRecoveryUI class. When using custom images instead of default images,
+override the Init()
method in your subclass to change these
+values for your custom images (for details, see
+ScreenRecoveryUI). The script bootable/recovery/make-overlay.py
+
can assist in converting a set of image frames to the "base image +
+overlay images" form needed by recovery, including computing of the necessary
+offsets.
Default images are located in bootable/recovery/res/images
. To
+use a static image during installation, you need only provide the
+icon_installing.png image and set the number of frames in the animation to 0
+(the error icon is not animated; it is always a static image).
Android 5.x displays a string of text (e.g., "Installing system update...") +along with the image. When the main system boots into recovery it passes the +user's current locale as a command-line option to recovery. For each message +to display, recovery includes a second composite image with pre-rendered text +strings for that message in each locale.
+ +Sample image of recovery text strings:
+ +Figure 8. Localized text for recovery +messages
+ +Recovery text can display the following messages:
+The Android app in development/tools/recovery_l10/
renders
+localizations of a message and creates the composite image. For details on
+using this app, refer to the comments in development/tools/recovery_l10n/
+src/com/android/recovery_l10n/Main.java
.
+
+
When a user boots into recovery manually, the locale might not be available +and no text is displayed. Do not make the text messages critical to the +recovery process.
+ +Note: The hidden interface that displays log +messages and allows the user to select actions from the menu is available only +in English.
+ + +Progress bars can appear below the main image (or animation). The progress +bar is made by combining two input images, which must be of the same size:
+ +Figure 9. progress_empty.png
+Figure 10. progress_fill.png
+ +The left end of the fill image is displayed next to the right end of +the empty image to make the progress bar. The position of the boundary +between the two images is changed to indicate the progress. For example, with +the above pairs of input images, display:
+ +Figure 11. Progress bar at 1%>
+Figure 12. Progress bar at 10%
+Figure 13. Progress bar at 50%
+ +You can provide device-specific versions of these images by placing them
+into (in this example) device/yoyodyne/tardis/recovery/res/images
+. Filenames must match the ones listed above; when a file is found in that
+directory, the build system uses it in preference to the corresponding default
+image. Only PNGs in RGB or RGBA format with 8-bit color depth are supported.
+
Note: In Android 5.x, if the locale is known +to recovery and is a right-to-left (RTL) language (Arabic, Hebrew, etc.), the +progress bar fills from right to left.
+ + +Not all Android devices have screens. If your device is a headless appliance +or has an audio-only interface, you may need to do more extensive customization +of recovery UI. Instead of creating a subclass of ScreenRecoveryUI, subclass its +parent class RecoveryUI directly.
+RecoveryUI has methods for handling a lower-level UI operations such as
+"toggle the display," "update the progress bar," "show the menu," "change the
+menu selection," etc. You can override these to provide an appropriate
+interface for your device. Maybe your device has LEDs where you can use
+different colors or patterns of flashing to indicate state, or maybe you can
+play audio. (Perhaps you don't want to support a menu or the "text display"
+mode at all; you can prevent accessing them with CheckKey()
and
+HandleMenuKey()
implementations that never toggle the display on
+or select a menu item. In this case, many of the RecoveryUI methods you need
+to provide can just be empty stubs.)
See bootable/recovery/ui.h
for the declaration of RecoveryUI
+to see what methods you must support. RecoveryUI is abstract—some methods are
+pure virtual and must be provided by subclasses—but it does contain the code
+to do processing of key inputs. You can override that too, if your device
+doesn't have keys or you want to process them differently.
You can use device-specific code in the installation of the update package +by providing your own extension functions that can be called from within your +updater script. Here's a sample function for the tardis device:
+ +device/yoyodyne/tardis/recovery/recovery_updater.c
+#include <stdlib.h> +#include <string.h> + +#include "edify/expr.h" ++ +
Every extension function has the same signature. The arguments are the name
+by which the function was called, a State*
cookie, the number of
+incoming arguments, and an array of Expr*
pointers representing
+the arguments. The return value is a newly-allocated Value*
.
+Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } ++ +
Your arguments have not been evaluated at the time your function is
+called—your function's logic determines which of them get evaluated and how
+many times. Thus, you can use extension functions to implement your own
+control structures. Call Evaluate()
to evaluate an Expr*
+
argument, returning a Value*
. If Evaluate()
+returns NULL, you should free any resources you're holding and immediately
+return NULL (this propagates aborts up the edify stack). Otherwise, you take
+ownership of the Value returned and are responsible for eventually calling
+FreeValue()
on it.
Suppose the function needs two arguments: a string-valued key and a +blob-valued image. You could read arguments like this:
+ ++ Value* key = EvaluateValue(state, argv[0]); + if (key == NULL) { + return NULL; + } + if (key->type != VAL_STRING) { + ErrorAbort(state, "first arg to %s() must be string", name); + FreeValue(key); + return NULL; + } + Value* image = EvaluateValue(state, argv[1]); + if (image == NULL) { + FreeValue(key); // must always free Value objects + return NULL; + } + if (image->type != VAL_BLOB) { + ErrorAbort(state, "second arg to %s() must be blob", name); + FreeValue(key); + FreeValue(image) + return NULL; + } ++ +
Checking for NULL and freeing previously evaluated arguments can get tedious
+for multiple arguments. The ReadValueArgs()
function can make this
+easier. Instead of the code above, you could have written this:
+ Value* key; + Value* image; + if (ReadValueArgs(state, argv, 2, &key, &image) != 0) { + return NULL; // ReadValueArgs() will have set the error message + } + if (key->type != VAL_STRING || image->type != VAL_BLOB) { + ErrorAbort(state, "arguments to %s() have wrong type", name); + FreeValue(key); + FreeValue(image) + return NULL; + } ++ +
ReadValueArgs()
doesn't do type-checking, so you must do that
+here; it's more convenient to do it with one if statement at
+the cost of producing a somewhat less specific error message when it fails.
+But ReadValueArgs()
does handle evaluating each argument and
+freeing all the previously-evaluated arguments (as well as setting a useful
+error message) if any of the evaluations fail. You can use a
+ReadValueVarArgs()
convenience function for evaluating a variable
+number of arguments (it returns an array of Value*
).
After evaluating the arguments, do the work of the function:
+ ++ // key->data is a NUL-terminated string + // image->data and image->size define a block of binary data + // + // ... some device-specific magic here to + // reprogram the tardis using those two values ... ++ +
The return value must be a Value*
object; ownership of this
+object will pass to the caller. The caller takes ownership of any data pointed
+to by this Value*
—specifically the datamember.
In this instance, you want to return a true or false value to indicate
+success. Remember the convention that the empty string is false and all
+other strings are true. You must malloc a Value object with a malloc'd
+copy of the constant string to return, since the caller will free()
+
both. Don't forget to call FreeValue()
on the objects you
+got by evaluating your arguments!
+ FreeValue(key); + FreeValue(image); + + Value* result = malloc(sizeof(Value)); + result->type = VAL_STRING; + result->data = strdup(successful ? "t" : ""); + result->size = strlen(result->data); + return result; +} ++ +
The convenience function StringValue()
wraps a string into a
+new Value object. Use to write the above code more succinctly:
+ FreeValue(key); + FreeValue(image); + + return StringValue(strdup(successful ? "t" : "")); +} ++ +
To hook functions into the edify interpreter, provide the function
+Register_foo
where foo is the name of the
+static library containing this code. Call RegisterFunction()
to
+register each extension function. By convention, name device-specific
+functions device.whatever
to avoid conflicts with
+future built-in functions added.
+void Register_librecovery_updater_tardis() { + RegisterFunction("tardis.reprogram", ReprogramTardisFn); +} ++ +
You can now configure the makefile to build a static library with your +code. (This is the same makefile used to customize the recovery UI in the +previous section; your device may have both static libraries defined here.)
+ +device/yoyodyne/tardis/recovery/Android.mk
+include $(CLEAR_VARS) +LOCAL_SRC_FILES := recovery_updater.c +LOCAL_C_INCLUDES += bootable/recovery ++ +
The name of the static library must match the name of the
+Register_libname
function contained within it.
+LOCAL_MODULE := librecovery_updater_tardis +include $(BUILD_STATIC_LIBRARY) ++ +
Finally, configure the build of recovery to pull in your library. Add your
+library to TARGET_RECOVERY_UPDATER_LIBS (which may contain multiple libraries;
+they all get registered). If your code depends on other static libraries that
+are not themselves edify extensions (i.e., they don't have a
+Register_libname
function), you can list those in
+TARGET_RECOVERY_UPDATER_EXTRA_LIBS to link them to updater without calling
+their (non-existent) registration function. For example, if your
+device-specific code wanted to use zlib to decompress data, you would include
+libz here.
device/yoyodyne/tardis/BoardConfig.mk
+ [...] + +# add device-specific extensions to the updater binary +TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis +TARGET_RECOVERY_UPDATER_EXTRA_LIBS += ++ +
The updater scripts in your OTA package can now call your function as any
+other. To reprogram your tardis device, the update script might contain:
+tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
+
. This uses the single-argument version of the built-in function
+package_extract_file()
, which returns the contents of a file extracted
+from the update package as a blob to produce the second argument to the new
+extension function.
The final component is getting the OTA package generation tools to know +about your device-specific data and emit updater scripts that include calls to +your extension functions.
+First, get the build system to know about a device-specific blob of data.
+Assuming your data file is in device/yoyodyne/tardis/tardis.dat
,
+declare the following in your device's AndroidBoard.mk:
device/yoyodyne/tardis/AndroidBoard.mk
+ [...] + +$(call add-radio-file,tardis.dat) ++ +
You could also put it in an Android.mk instead, but then it must to be +guarded by a device check, since all the Android.mk files in the tree are +loaded no matter what device is being built. (If your tree includes multiple +devices, you only want the tardis.dat file added when building the tardis +device.)
+ +device/yoyodyne/tardis/Android.mk
+ [...] + +# an alternative to specifying it in AndroidBoard.mk +ifeq (($TARGET_DEVICE),tardis) + $(call add-radio-file,tardis.dat) +endif ++ +
These are called radio files for historical reasons; they may have nothing
+to do with the device radio (if present). They are simply opaque blobs of data
+the build system copies into the target-files .zip used by the OTA generation
+tools. When you do a build, tardis.dat is stored in the target-files.zip as
+RADIO/tardis.dat
. You can call add-radio-file
+multiple times to add as many files as you want.
To extend the release tools, write a Python module (must be named +releasetools.py) the tools can call into if present. Example:
+ +device/yoyodyne/tardis/releasetools.py
+import common + +def FullOTA_InstallEnd(info): + # copy the data into the package. + tardis_dat = info.input_zip.read("RADIO/tardis.dat") + common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat) + + # emit the script code to install this data on the device + info.script.AppendExtra( + """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""") ++ +
A separate function handles the case of generating an incremental OTA +package. For this example, suppose you need to reprogram the tardis only when +the tardis.dat file has changed between two builds.
++def IncrementalOTA_InstallEnd(info): + # copy the data into the package. + source_tardis_dat = info.source_zip.read("RADIO/tardis.dat") + target_tardis_dat = info.target_zip.read("RADIO/tardis.dat") + + if source_tardis_dat == target_tardis_dat: + # tardis.dat is unchanged from previous build; no + # need to reprogram it + return + + # include the new tardis.dat in the OTA package + common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat) + + # emit the script code to install this data on the device + info.script.AppendExtra( + """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""") ++ +
You can provide the following functions in the module (implement only the +ones you need).
+FullOTA_Assertions()
FullOTA_InstallBegin()
FullOTA_InstallEnd()
IncrementalOTA_Assertions()
FullOTA_Assertions()
but called when generating an
+incremental update package.IncrementalOTA_VerifyBegin()
IncrementalOTA_VerifyEnd()
IncrementalOTA_InstallBegin()
IncrementalOTA_InstallEnd()
Note: If the device loses power, OTA +installation may restart from the beginning. Be prepared to cope with devices +on which these commands have already been run, fully or partially.
+ +Pass functions to a single info object that contains various useful items: +
+zipfile.ZipFile
+object for the input target-files .zip.zipfile.ZipFile
+
object for the source target-files .zip (the build already on the
+device when the incremental package is being installed).zipfile.ZipFile
+
object for the target target-files .zip (the build the incremental
+package puts on the device).zipfile.ZipFile
+
object opened for writing. Use common.ZipWriteStr(info.output_zip,
+filename, data) to add a file to the package.info.script.AppendExtra(script_text)
to output text into
+the script. Make sure output text ends with a semicolon so it does not run
+into commands emitted afterwards.For details on the info object, refer to the +Python Software Foundation +documentation for ZIP archives.
+ +Specify the location of your device's releasetools.py script in your +BoardConfig.mk file:
+ +device/yoyodyne/tardis/BoardConfig.mk
+ [...] + +TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis ++ +
If TARGET_RELEASETOOLS_EXTENSIONS is not set, it defaults to the
+$(TARGET_DEVICE_DIR)/../common
directory (device/yoyodyne/common
+
in this example). It's best to explicitly define the location of the
+releasetools.py script. When building the tardis device, the releasetools.py
+script is included in the target-files .zip file (META/releasetools.py
+
).
When you run the release tools (either img_from_target_files
+or ota_from_target_files
), the releasetools.py script in the
+target-files .zip, if present, is preferred over the one from the Android
+source tree. You can also explicitly specify the path to the device-specific
+extensions with the -s
(or --device_specific
)
+option, which takes the top priority. This enables you to correct errors and
+make changes in the releasetools extensions and apply those changes to old
+target-files.
Now, when you run ota_from_target_files
, it automatically
+picks up the device-specific module from the target_files .zip file and uses
+it when generating OTA packages:
+% ./build/tools/releasetools/ota_from_target_files \ + -i PREVIOUS-tardis-target_files.zip \ + dist_output/tardis-target_files.zip incremental_ota_update.zip +unzipping target target-files... +using device-specific extensions from target_files +unzipping source target-files... + [...] +done. ++ +
Alternatively, you can specify device-specific extensions when you run
+ota_from_target_files
.
+% ./build/tools/releasetools/ota_from_target_files \ + -s device/yoyodyne/tardis \ # specify the path to device-specific extensions + -i PREVIOUS-tardis-target_files.zip \ + dist_output/tardis-target_files.zip incremental_ota_update.zip +unzipping target target-files... +loaded device-specific extensions from device/yoyodyne/tardis +unzipping source target-files... + [...] +done. ++ +
Note: For a complete list of options, refer
+to the ota_from_target_files
comments in
+build/tools/releasetools/ota_from_target_files
.
Recovery has a sideloading mechanism for manually installing an +update package without downloading it over-the-air by the main system. +Sideloading is useful for debugging or making changes on devices where the +main system can't be booted.
+Historically, sideloading has been done through loading packages off the +device's SD card; in the case of a non-booting device, the package can be put +onto the SD card using some other computer and then the SD card inserted into +the device. To accommodate Android devices without removable external storage, +recovery supports two additional mechanisms for sideloading: loading packages +from the cache partition, and loading them over USB using adb.
+To invoke each sideload mechanism, your device's
+Device::InvokeMenuItem()
method can return the following values of
+BuiltinAction:
+/sdcard
directory). Your recovery.fstab must define the /sdcard
+
mount point. This is not usable on devices that emulate an SD card
+with a symlink to /data
(or some similar mechanism). /data
+
is typically not available to recovery because it may be encrypted.
+The recovery UI displays a menu of .zip files in /sdcard
and
+allows the user to select one./sdcard
+except that the /cache
directory (which is always
+available to recovery) is used instead. From the regular system, /cache
+
is only writable by privileged users, and if the device isn't bootable
+then the /cache
directory can't be written to at all (which makes
+this mechanism of limited utility).adb sideload filename
. The named file is sent
+from the host machine to the device, which then verifies and installs it just
+as if it had been on local storage.A few caveats:
+sideload
work (
+logcat
, reboot
, push
, pull
+, shell
, etc. all fail)./dev/null
(or anything else that's not a valid package) as the
+package, and then the device will fail to verify it and stop the installation
+procedure. The RecoveryUI implementation's CheckKey()
method
+will continue to be called for keypresses, so you can provide a key sequence
+that reboots the device and works in adb sideload mode.Android devices in the field can receive and install over-the-air (OTA) +updates to the system and application software. Devices have a special +recovery partition with the software needed to unpack a downloaded update +package and apply it to the rest of the system.
+This section describes the structure of these packages and the tools +provided to build them. It is intended for developers who want to +make the OTA update system work on new Android devices and those who are +building update packages for use with released devices. OTA updates are +designed to upgrade the underlying operating system and the read-only apps +installed on the system partition; these updates do not affect +applications installed by the user from Google Play. +
+This section describes the OTA system as of the Android 5.x release. For +help porting OTA-related code from older releases, see +Migrating from previous releases. +
+ +The flash space on an Android device typically contains the following +partitions.
+ +A typical OTA update contains the following steps:
+/system/etc/security/otacerts.zip
. User is prompted to install the
+update./cache/recovery/command
that point it to the downloaded package.
+/res/keys
(part of the RAM disk contained in the
+recovery partition)./system
). They are different, so the recovery partition is
+reflashed with the desired contents. (On subsequent boots, the recovery
+partition already contains the new contents, so no reflash is necessary.)The system update is complete!
+ +When migrating from Android 2.3/3.0/4.0 release, the major change is the +conversion of all the device-specific functionality from a set of C functions +with predefined names to C++ objects. The following table lists the old +functions and the new methods that serve a roughly equivalent purpose:
+ +C function | +C++ method | +
---|---|
device_recovery_start() | +Device::RecoveryStart() | +
device_toggle_display() +device_reboot_now() + |
+RecoveryUI::CheckKey() +(also RecoveryUI::IsKeyPressed()) + |
+
device_handle_key() | +Device::HandleMenuKey() | +
device_perform_action() | +Device::InvokeMenuItem() | +
device_wipe_data() | +Device::WipeData() | +
device_ui_init() | +ScreenRecoveryUI::Init() | +
Conversion of old functions to new methods should be reasonably
+straightforward. Don't forget to add the new make_device()
+function to create and return an instance of your new Device subclass.
The system builds the updater binary from bootable/recovery/updater
+
and uses it in an OTA package.
ota_update.zip
, incremental_ota_update.zip
) that
+contains the executable binary META-INF/com/google/android/update-binary
+
.
+
+Updater contains several builtin functions and an interpreter for an
+extensible scripting language (edify) that supports commands for typical
+update-related tasks. Updater looks in the package .zip file for a script in the
+file META-INF/com/google/android/updater-script
.
Note: Using the edify script and/or builtin +functions is not a common activity, but can be helpful if you need to debug the +update file.
+ +An edify script is a single expression in which all values are strings. +Empty strings are false in a Boolean context and all other strings are +true. Edify supports the following operators (with the usual meanings): +
+ ++(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 ++ +
Any string of the characters a-z, A-Z, 0-9, _, :, /, . that isn't a +reserved word is considered a string literal. (Reserved words are if else + then endif.) String literals may also appear in double-quotes; +this is how to create values with whitespace and other characters not in the +above set. \n, \t, \", and \\ serve as escapes within quoted strings, as does +\x##.
+The && and || operators are short-circuiting; the right side is not +evaluated if the logical result is determined by the left side. The +following are equivalent:
++e1 && e2 +if e1 then e2 endif+
The ; operator is a sequence point; it means to evaluate first the left +side and then the right side. Its value is the value of the right-side +expression. A semicolon can also appear after an expression, so the effect +simulates C-style statements:
+ ++prepare(); +do_other_thing("argument"); +finish_up(); ++ +
Most update functionality is contained in the functions available for
+execution by scripts. (Strictly speaking these are macros rather than
+functions in the Lisp sense, since they need not evaluate all of their
+arguments.) Unless otherwise noted, functions return true on success
+and false on error. If you want errors to abort execution of the
+script, use the abort()
and/or assert()
functions.
+The set of functions available in updater can also be extended to provide
+device-specific
+functionality.
+
+
abort([msg])
assert(expr[, expr, ...])
apply_patch(src_file, tgt_file, tgt_sha1,
+tgt_size, patch1_sha1, patch1_blob, [...])
The patching is done in a safe manner that guarantees the target file +either has the desired SHA1 hash and size, or it is untouched—it will not be +left in an unrecoverable intermediate state. If the process is interrupted +during patching, the target file may be in an intermediate state; a copy exists +in the cache partition so restarting the update can successfully update the +file.
+Special syntax is supported to treat the contents of Memory Technology +Device (MTD) partitions as files, allowing patching of raw partitions such as +boot. To read an MTD partition, you must know how much data you want to read +since the partition does not have an end-of-file notion. You can use the +string "MTD:partition:size_1:sha1_1:size_2: +sha1_2" as a filename to read the given partition. You must specify at +least one (size, sha-1) pair; you can specify more than one if there +are multiple possibilities for what you expect to read.
apply_patch_check(filename, sha1[, sha1, ...])
+
sha1_check(read_file(filename),
+sha1 [, ...])
in that it knows to check the cache partition copy,
+so apply_patch_check()
will succeed even if the file was corrupted
+by an interrupted apply_patch() update
.apply_patch_space(bytes)
concat(expr[, expr, ...])
delete([filename, ...])
delete_recursive([dirname, ...])
file_getprop(filename, key)
/system/build.prop
), and returns the value of the given key
+, or the empty string if key is not present.format(fs_type, partition_type, location,
+fs_size, mount_point)
getprop(key)
greater_than_int(a, b)
ifelse(cond, e1[, e2])
is_mounted(mount_point)
is_substring(needle, haystack)
less_than_int(a, b)
mount(fs_type, partition_type, name,
+mount_point)
/proc/mtd
on the device for a complete list).Recovery does not mount any filesystems by default (except the SD card if +the user is doing a manual install of a package from the SD card); your script +must mount any partitions it needs to modify.
package_extract_dir(package_dir, dest_dir)
package_extract_file(package_file[, dest_file])
+read_file(filename)
rename(src_filename, tgt_filename)
+rename("system/app/Hangouts/Hangouts.apk",
+"system/priv-app/Hangouts/Hangouts.apk")
.run_program(path[, arg, ...])
set_metadata(filename, key1, value1[, key2
+, value2, ...])
set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750,
+"selabel", "u:object_r:system_file:s0", "capabilities", 0x0)
.set_metadata_recursive(dirname, key1, value1[,
+key2, value2, ...])
set_metadata_recursive("/system", "uid",
+0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel",
+"u:object_r:system_file:s0", "capabilities", 0x0)
.set_progress(frac)
show_progress()
call. frac must be in the
+range [0.0, 1.0]. The progress meter never moves backwards; attempts to make
+it do so are ignored.sha1_check(blob[, sha1])
+read_file()
or the one-argument form of package_extract_file()
+
. With no sha1 arguments, this function returns the SHA1 hash of
+the blob (as a 40-digit hex string). With one or more sha1 arguments,
+this function returns the SHA1 hash if it equals one of the arguments, or the
+empty string if it does not equal any of them.show_progress(frac, secs)
+set_progress()
function defined above.sleep(secs)
stdout(expr[, expr, ...])
symlink(target[, source, ...])
tune2fs(device[, arg, …])
ui_print([text, ...])
unmount(mount_point)
wipe_block_device(block_dev, len)
wipe_cache()
write_raw_image(filename_or_blob, partition)
+write_raw_image(package_extract_file("zip_filename"), "partition_name");
+
+Note: Prior to Android 4.1, only filenames +were accepted, so to accomplish this the data first had to be unzipped into a +temporary local file.
\ No newline at end of file diff --git a/src/devices/tech/ota/sign_builds.jd b/src/devices/tech/ota/sign_builds.jd new file mode 100755 index 00000000..e9869205 --- /dev/null +++ b/src/devices/tech/ota/sign_builds.jd @@ -0,0 +1,269 @@ +page.title=Signing Builds for Release +@jd:body + + + +Android uses cryptographic signatures in two places:
+Each key comes in two files: the certificate, which has the +extension .x509.pem, and the private key, which has the extension .pk8. +The private key should be kept secret and is needed to sign a package. The key +may itself be protected by a password—a reasonable strategy is to store your +keys in source control along with the code—but keep them protected by a +password known only to the people who make final releases. The certificate, in +contrast, contains only the public half of the key, so it can be distributed +widely. It is used to verify a package has been signed by the corresponding +private key.
+The standard Android build uses four keys, all of which reside in
+build/target/product/security
:
Individual packages specify one of these keys by setting LOCAL_CERTIFICATE +in their Android.mk file. (testkey is used if this variable is not set.) You +can also specify an entirely different key by pathname, e.g.:
+ +device/yoyodyne/apps/SpecialApp/Android.mk
+ [...] + +LOCAL_CERTIFICATE := device/yoyodyne/security/special ++ +
Now the build uses the device/yoyodyne/security/special.{x509.pem,pk8}
+
key to sign SpecialApp.apk. The build can use only private keys that
+are not password protected.
Android uses 2048-bit RSA keys with public exponent 3. You can generate +certificate/private key pairs using the openssl tool from +openssl.org:
+ ++# generate RSA key +% openssl genrsa -3 -out temp.pem 2048 +Generating RSA private key, 2048 bit long modulus +....+++ +.....................+++ +e is 3 (0x3) + +# create a certificate with the public part of the key +% openssl req -new -x509 -key temp.pem -out releasekey.x509.pem \ + -days 10000 \ + -subj '/C=US/ST=California/L=San Narciso/O=Yoyodyne, Inc./OU=Yoyodyne Mobility/CN=Yoyodyne/emailAddress=yoyodyne@example.com' + +# create a PKCS#8-formatted version of the private key +% openssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt + +# securely delete the temp.pem file +% shred --remove temp.pem ++ +
The openssl pkcs8 command given above creates a .pk8 file with no
+password, suitable for use with the build system. To create a .pk8 secured
+with a password (which you should do for all actual release keys), replace the
+-nocrypt
argument with -passout stdin
; then openssl
+will encrypt the private key with a password read from standard input. No
+prompt is printed, so if stdin is the terminal the program will appear to hang
+when it's really just waiting for you to enter a password. Other values can be
+used for the-passout argument to read the password from other locations; for
+details, see the
+
+openssl documentation.
The temp.pem intermediate file contains the private key without any kind of +password protection, so dispose of it thoughtfully when generating release +keys. In particular, the GNUshred utility may not be effective on network or +journaled filesystems. You can use a working directory located in a RAM disk +(such as a tmpfs partition) when generating keys to ensure the intermediates +are not inadvertently exposed.
+ +The first step in preparing a build for release is to sign all the .apk
+files in it, replacing the test keys used by the build system. This is done
+with the sign_target_files_apks
script. It takes a target-files
+.zip as input and produces a new target-files .zip in which all the .apks have
+been signed with new keys.
When you run this script, you must specify on the command line a
+replacement key for each key used in the build. The -k src_key=
+dest_key
flag specifies key replacements one at a time. The flag
+-d dir
lets you specify a directory with four keys to
+replace all those in build/target/product/security
; it is
+equivalent to using -k
four times to specify the mappings:
+build/target/product/security/testkey = dir/releasekey +build/target/product/security/platform = dir/platform +build/target/product/security/shared = dir/shared +build/target/product/security/media = dir/media ++ +
For the hypothetical tardis product, you need five password-protected keys:
+four to replace the four in build/target/product/security
, and
+one to replace the additional keydevice/yoyodyne/security/special
+required by SpecialApp in the example above. If the keys were in the following
+files:
+vendor/yoyodyne/security/tardis/releasekey.x509.pem +vendor/yoyodyne/security/tardis/releasekey.pk8 +vendor/yoyodyne/security/tardis/platform.x509.pem +vendor/yoyodyne/security/tardis/platform.pk8 +vendor/yoyodyne/security/tardis/shared.x509.pem +vendor/yoyodyne/security/tardis/shared.pk8 +vendor/yoyodyne/security/tardis/media.x509.pem +vendor/yoyodyne/security/tardis/media.pk8 +vendor/yoyodyne/security/special.x509.pem +vendor/yoyodyne/security/special.pk8 # NOT password protected +vendor/yoyodyne/security/special-release.x509.pem +vendor/yoyodyne/security/special-release.pk8 # password protected ++ +
Then you would sign all the apps like this:
+ ++% ./build/tools/releasetools/sign_target_files_apks \ + -d vendor/yoyodyne/security/tardis \ + -k vendor/yoyodyne/special=vendor/yoyodyne/special-release \ + -o \ # explained in the next section + tardis-target_files.zip signed-tardis-target_files.zip +Enter password for vendor/yoyodyne/security/special-release key> +Enter password for vendor/yoyodyne/security/tardis/media key> +Enter password for vendor/yoyodyne/security/tardis/platform key> +Enter password for vendor/yoyodyne/security/tardis/releasekey key> +Enter password for vendor/yoyodyne/security/tardis/shared key> + signing: Phone.apk (vendor/yoyodyne/security/tardis/platform) + signing: Camera.apk (vendor/yoyodyne/security/tardis/media) + signing: Special.apk (vendor/yoyodyne/security/special-release) + signing: Email.apk (vendor/yoyodyne/security/tardis/releasekey) + [...] + signing: ContactsProvider.apk (vendor/yoyodyne/security/tardis/shared) + signing: Launcher.apk (vendor/yoyodyne/security/tardis/shared) +rewriting SYSTEM/build.prop: + replace: ro.build.description=tardis-user Eclair ERC91 15449 test-keys + with: ro.build.description=tardis-user Eclair ERC91 15449 release-keys + replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys + with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys + signing: framework-res.apk (vendor/yoyodyne/security/tardis/platform) +rewriting RECOVERY/RAMDISK/default.prop: + replace: ro.build.description=tardis-user Eclair ERC91 15449 test-keys + with: ro.build.description=tardis-user Eclair ERC91 15449 release-keys + replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys + with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys +using: + vendor/yoyodyne/security/tardis/releasekey.x509.pem +for OTA package verification +done. ++ +
After prompting the user for passwords for all password-protected keys, the +script re-signs all the .apk files in the input target .zip with the release +keys. Before running the command, you can also set the ANDROID_PW_FILE +environment variable to a temporary filename; the script then invokes your +editor to allow you to enter passwords for all keys (this may be a more +convenient way to enter passwords).
+
sign_target_files_apks
also rewrites the build description and
+fingerprint in the build properties files to reflect the fact that this is a
+signed build. The -t
flag can control what edits are made to the
+fingerprint. Run the script with -h
to see documentation on all
+flags.
You need the following components to sign OTA packages:
+To achieve these components:
+-o
flag to
+sign_target_files_apks
replaces this key with the release key from your
+build.-k
option when
+generating it to specify the key. You should give ota_from_target_files
+
the signed version of the target-files .zip as well:
++% ./build/tools/releasetools/ota_from_target_files \ + -k vendor/yoyodyne/security/tardis/releasekey \ + signed-tardis-target_files.zip \ + signed-ota_update.zip +unzipping target target-files... +(using device-specific extensions from target_files) +Enter password for vendor/yoyodyne/security/tardis/releasekey key> +done.
Sideloading does not bypass recovery's normal package signature +verification mechanism—before installing a package, recovery will verify that +it is signed with one of the private keys matching the public keys stored in +the recovery partition, just as it would for a package delivered over-the-air. +
+Update packages received from the main system are typically verified twice:
+once by the main system, using the RecoverySystem.
+verifyPackage()
method in the android API, and then again by
+recovery. The RecoverySystem API checks the signature against public keys
+stored in the main system, in the file /system/etc/security/otacerts.zip
+
(by default). Recovery checks the signature against public keys stored
+in the recovery partition RAM disk, in the file /res/keys
.
Normally these two locations store the same set of keys. By adding a key to +just the recovery set of keys, it's possible to sign packages that can +be installed only via sideloading (assuming the main system's update download +mechanism is correctly doing verification against otacerts.zip). You can +specify extra keys to be included only in recovery by setting the +PRODUCT_EXTRA_RECOVERY_KEYS variable in your product definition:
+ +vendor/yoyodyne/tardis/products/tardis.mk
+ [...] + +PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload ++ +
This includes the public key vendor/yoyodyne/security/tardis/sideload.
+x509.pem
in the recovery keys file so it can install packages signed
+with it. The extra key is not included in otacerts.zip though, so
+systems that correctly verify downloaded packages do not invoke recovery for
+packages signed with this key.
The ota_from_target_files tool provided in
+build/tools/releasetools
can build two types of package: full
+ and incremental. The tool takes the target-files .zip file
+produced by the Android build system as input.
A full update is one where the entire final state of the device +(system, boot, and recovery partitions) is contained in the package. As long +as the device is capable of receiving the package and booting the recovery +system, the package can install the desired build regardless of the current +state of the device.
+Example: Using the release tools to build a full update for the +hypothetical tardis device:
+ ++# first, build the target-files .zip +% . build/envsetup.sh && lunch tardis-eng +% mkdir dist_output +% make dist DIST_DIR=dist_output + [...] +% ls -l dist_output/*target_files* +-rw-r----- 1 user eng 69965275 Sep 29 15:51 tardis-target_files.zip ++ +
The target-files .zip contains everything needed to construct OTA packages. +
+ ++% ./build/tools/releasetools/ota_from_target_files \ + dist_output/tardis-target_files.zip ota_update.zip +unzipping target target-files... +done. +% ls -l ota_update.zip +-rw-r----- 1 user eng 62236561 Sep 29 15:58 ota_update.zip ++ +
The ota_update.zip is now ready to be sent to test devices (everything is +signed with the test key). For user devices, generate and use your own private +keys as detailed in Signing builds for release. + +
An incremental update contains a set of binary patches to be applied +to the data already on the device. This can result in considerably smaller +update packages:
+You can install the incremental update package only on a device that has +the old or source build used when constructing the package. To build an +incremental update, you need the target_files .zip from the previous build +(the one you want to update from) as well as the target_files .zip from +the new build.
+ ++% ./build/tools/releasetools/ota_from_target_files \ + -i PREVIOUS-tardis-target_files.zip \ # make incremental from this older version + dist_output/tardis-target_files.zip incremental_ota_update.zip +unzipping target target-files... +unzipping source target-files... + [...] +done. +% ls -l incremental_ota_update.zip +-rw-r----- 1 user eng 1175314 Sep 29 16:10 incremental_ota_update.zip ++ +
This build is very similar to the previous build, and the incremental +update package is much smaller than the corresponding full update (about 1 MB +instead of 60 MB).
+Note: To generate a
+block-based OTA
+for subsequent updates, pass the --block
option to
+ota_from_target_files
.
Distribute an incremental package only to devices running exactly the same +previous build used as the incremental package's starting point. Attempting to +install the incremental package on a device with some other build results in +the recovery error icon. Rebooting the device at this point returns the user +to the old system; the package verifies the previous state of all the files it +updates before touching them, so the device should not be left in a half +upgraded state if this occurs.
+ +An update package (ota_update.zip
,
+incremental_ota_update.zip
) is a .zip file that contains the
+executable binary META-INF/com/google/android/update-binary
. After
+verifying the signature on the package, recovery extracts this binary to
+/tmp
and runs it, passing the following arguments:
A recovery package can use any statically-linked binary as the update
+binary. The OTA package construction tools use the updater program (source in
+bootable/recovery/updater
), which provides a simple scripting
+language that can do many installation tasks. You can substitute any other
+binary running on the device.
For details on the updater binary, edify syntax, and builtin functions, see +Inside OTA Packages +. \ No newline at end of file diff --git a/src/source/community/index.jd b/src/source/community/index.jd index 31361ca9..8845469a 100644 --- a/src/source/community/index.jd +++ b/src/source/community/index.jd @@ -10,125 +10,213 @@ page.title=Android Community
Welcome to the Android community!
-The key to any community is, obviously, communication. Like most projects, -Android communicates via mailing lists. Because Android is an extremely large +
The key to any community is communication. Like most projects, Android +communicates via mailing lists. Because Android is an extremely large project with many components, we have many discussion forums, each focusing on a different topic.
-Please check out the groups below, and join any that seem interesting to +
Check out the groups below and join any that seem interesting to you. Note that if you're a user looking for help with your Android device, this page probably isn't for you; you should contact your carrier or retailer for help with your phone.
-Please note that if you're looking for information about building -applications for Android, you can find a separate set of groups for those at -our sister site, developer.android.com: [https://developer.android.com/resources/community-groups.html]
+If you're looking for information about building applications for Android, +you can find a separate set of groups at our sister site + +developer.android.com.
android-platform: - This list is for general discussion about the Android open-source project or the platform technologies.
+android-platform: +This list is for general discussion about the Android open-source project or +the platform technologies.
android-building: - Subscribe to this list for discussion and help on building the Android source code, and on the build system. If you've just checked out the source code and have questions about how to turn it into binaries, start here.
+Subscribe to this list for discussion and help on building the Android source +code, and on the build system. If you've just checked out the source code and +have questions about how to turn it into binaries, start here.android-porting: - This list is for developers who want to port Android to a new device. If you're wondering how to combine the Android source code with your hardware, this is the right group for you. Discuss here the specifics of porting Android to individual devices, from obtaining toolchains and merging kernel drivers all the way to configuring or modifying applications for your specific +This list is for developers who want to port Android to a new device. If +you're wondering how to combine the Android source code with your hardware, +this is the right group for you. Discuss here the specifics of porting Android +to individual devices, from obtaining toolchains and merging kernel drivers +all the way to configuring or modifying applications for your specific configuration.
android-contrib: - This list is for developers who want to contribute code to Android. This is a working list, and is not appropriate for general discussion. We ask that general discussion go to android-platform. Note: contributors to the Android kernel should go to the android-kernel list, below.
+This list is for developers who want to contribute code to Android. This is a +working list, and is not appropriate for general discussion. We ask that +general discussion go to android-platform (and contributors to the Android +kernel should go to android-kernel).android-kernel: - This list is for deveopers who want to contribute to the Linux kernel that Android devices use. If you've downloaded the kernel code, if you know how to compile it, if you want to write kernel code to specifically support Android, -this is your place. This group isn't for user-space topics (see android-platform for that), and people will shake their fingers at you and call you naughty if you ask user-space questions here.
+This list is for developers who want to contribute to the Linux kernel used by +Android devices. If you've downloaded the kernel code, know how to compile it, +and want to write kernel code to support Android, this is your place. This +group is not for user-space topics (see android-platform); people +will shake their fingers at you and call you naughty if you ask user-space +questions here. +android-ota: +This list is for developers working on the Android OTA system (the recovery +image and the scripts that generate OTAs).
-These discussion groups are intended for developers working with the Android platform. Everyone is welcome to join in, provided you follow our community's policies described below. Our users help each other, and many experts post to these groups, including members of the Open Handset Alliance. -
--No topic is off-limits, provided it relates to Android in some way. However, since these are very busy lists, search the archives before posting your question; you may find your question has already been answered. -
+These discussion groups are intended for developers working with the Android +platform. Everyone is welcome to join in, provided you follow the community +policies described below. Our users help each other, and many experts post to +these groups, including members of the Open Handset Alliance.
+No topic is off-limits, provided it relates to Android in some way. However, +since these are very busy lists, search the archives before posting your +question; you may find your question has already been answered.
Please consider the following before you post to our lists.
Read the Charter for our forums. This explains the (few) rules and guidelines for our community.
+Read the Charter for our forums. This +explains the (few) rules and guidelines for our community.
Search the group archives to see whether your questions have already been discussed. This avoids time-wasting redundant discussions.
+Search the group archives to see whether your questions have already +been discussed. This avoids time-wasting redundant discussions.
Use a clear, relevant message subject. This helps everyone, both those trying to answer your question as well as those who may be looking for information in the future.
+Use a clear, relevant message subject. This helps everyone, both +those trying to answer your question as well as those who may be looking for +information in the future.
Give plenty of details in your post. Code or log snippets, pointers to screenshots, and similar details will get better results and make for better discussions. For a great guide to phrasing your questions, read How to Ask Questions the Smart Way.
+Give plenty of details in your post. Code or log snippets, +pointers to screenshots, and similar details will get better results and make +for better discussions. For a great guide to phrasing your questions, read +How to Ask +Questions the Smart Way.
We love simplicity and hate restrictions, so we keep our policies minimal. The rules -below describe what's expected of subscribers to the Android mailing lists. +
We love simplicity and hate restrictions, so we keep our policies minimal. +The rules below describe what's expected of subscribers to the Android mailing +lists.
-The most important rule is friendliness. Remember: disrespect and rudeness are not welcome in our community under any circumstances. We don't have a formal policy on dealing with troublemakers, and we hope we never need one. That said, we do pledge to do our best to be fair, and we will always try to warn someone before banning him or her. -
--If you see anyone being rude, call them out on it. This is your group too, and you don't have to accept someone else being disrespectful just because it wasn't directed at you. Just remember to be polite and courteous yourself! Don't add fuel to the fire. -
--But if you see an outrageous violation, want to report spam, feel very strongly about something, or even if you just want to chat, then contact the mailing list's owners. It's what we're here for! -
- +The most important rule is friendliness. Remember: disrespect and rudeness +are not welcome in our community under any circumstances. We don't have a +formal policy on dealing with troublemakers, and we hope we never need one. +That said, we do pledge to do our best to be fair, and we will always try to +warn someone before banning him or her.
+If you see anyone being rude, call them out on it. This is your group too, +and you don't have to accept someone else being disrespectful just because it +wasn't directed at you. Just remember to be polite and courteous yourself! +Don't add fuel to the fire.
+But if you see an outrageous violation, want to report spam, feel strongly +about something, or just want to chat, then contact the mailing list owners. +It's what we're here for!
Instead of using the Google groups site, you can use your email client of choice to participate in the mailing lists.
-To subscribe to a group without using the Google Groups site, use the link under "subscribe via email" in the lists above.
+Instead of using the Google groups +site, you can use your email client of choice to participate in the mailing +lists. To subscribe to a group without using the Google Groups site, use the link +under "subscribe via email" in the lists above.
To set up how you receive mailing list postings by email:
Sign into the group via the Google Groups site. For example, for the android-platform group you would use [https://groups.google.com/forum/?fromgroups#!forum/android-platform].
+Sign into the group via the Google Groups site. For example, for the +android-platform group you would use + +https://groups.google.com/forum/?fromgroups#!forum/android-platform.
Click "My membership" on the right side.
@@ -138,33 +226,36 @@ But if you see an outrageous violation, want to report spam, feel very stronglyWe also have a presence on IRC via freenode. -We maintain two official IRC channels on irc.freenode.net (access via the web -at freenode webchat)
+Android has a presence on IRC via +freenode. We maintain two official IRC +channels on irc.freenode.net (access via +the web at freenode webchat)
#android - dedicated to general Android discussion and porting concerns
+#android - dedicated to +general Android discussion and porting concerns
#android-dev - dedicated to discussion about writing Android applications
The channels above are official. There are a few other channels the -community is using, but are not official. These aren't official or officially -moderated/managed, so you use the channels below at your own risk. The Open -Handset Alliance doesn't endorse these channels, there's no warranty express -or implied, and so on. There may be more channels than just these listed.
+The community also uses several unofficial channels that are not not officially moderated or managed. The Open Handset Alliance does not endorse unofficial channels and there's no warranty express or implied, so use them at your own risk. Here's a list of a few unofficial channels (many more may exist):
+#android-firehose - displays in real-time the commits to the Android Open Source Project
+#android-firehose - +displays in real-time the commits to the Android Open Source Project
#android-fr - pour discuter d'Android en français
+#android-fr - pour discuter +d'Android en français
#android-offtopic - for, well, off-topic discussions
+#android-offtopic - +for, well, off-topic discussions
#android-root - for discussion related to off-label uses of hardware
+#android-root - for +discussion related to off-label uses of hardware