1page.title=Device-Specific Code 2@jd:body 3 4<!-- 5 Copyright 2015 The Android Open Source Project 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18--> 19 20<div id="qv-wrapper"> 21 <div id="qv"> 22 <h2>In this document</h2> 23 <ol id="auto-toc"> 24 </ol> 25 </div> 26</div> 27 28<p>The recovery system includes several hooks for inserting device-specific 29code so that OTA updates can also update parts of the device other than the 30Android system (e.g., the baseband or radio processor).</p> 31<p>The following sections and examples customize the <b>tardis</b> device 32produced by the <b>yoyodyne</b> vendor.</p> 33 34<h2>Partition map</h2> 35<p>As of Android 2.3, the platform supports eMMC flash devices and the ext4 36filesystem that runs on those devices. It also supports Memory Technology Device 37(MTD) flash devices and the yaffs2 filesystem from older releases.</p> 38<p>The partition map file is specified by TARGET_RECOVERY_FSTAB; this file is 39used by both the recovery binary and the package-building tools. You can 40specify the name of the map file in TARGET_RECOVERY_FSTAB in BoardConfig.mk.</p> 41<p>A sample partition map file might look like this:</p> 42 43<p><code>device/yoyodyne/tardis/recovery.fstab</code></p> 44 45<pre> 46# mount point fstype device [device2] [options (3.0+ only)] 47 48/sdcard vfat /dev/block/mmcblk0p1 /dev/block/mmcblk0 49/cache yaffs2 cache 50/misc mtd misc 51/boot mtd boot 52/recovery emmc /dev/block/platform/s3c-sdhci.0/by-name/recovery 53/system ext4 /dev/block/platform/s3c-sdhci.0/by-name/system length=-4096 54/data ext4 /dev/block/platform/s3c-sdhci.0/by-name/userdata 55</pre> 56 57<p>With the exception of <code>/sdcard</code>, which is optional, all mount 58points in this example must be defined (devices may also add extra partitions). 59There are five supported filesystem types:</p> 60<dl> 61<dt>yaffs2</dt> 62<dd>A yaffs2 filesystem atop an MTD flash device. "device" must be the name of 63the MTD partition and must appear in <code>/proc/mtd</code>.</dd> 64<dt>mtd</dt> 65<dd>A raw MTD partition, used for bootable partitions such as boot and 66recovery. MTD is not actually mounted, but the mount point is used as a key to 67locate the partition. "device" must be the name of the MTD partition in 68<code>/proc/mtd</code>.</dd> 69<dt>ext4</dt> 70<dd>An ext4 filesystem atop an eMMC flash device. "device" must be the path of 71the block device.</dd> 72<dt>emmc</dt> 73<dd>A raw eMMC block device, used for bootable partitions such as boot and 74recovery. Similar to the mtd type, eMMc is never actually mounted, but the 75mount point string is used to locate the device in the table.</dd> 76<dt>vfat</dt> 77<dd>A FAT filesystem atop a block device, typically for external storage such 78as an SD card. The device is the block device; device2 is a second block 79device the system attempts to mount if mounting the primary device fails (for 80compatibility with SD cards which may or may not be formatted with a partition 81table). 82<p>All partitions must be mounted in the root directory (i.e. the mount point 83value must begin with a slash and have no other slashes). This restriction 84applies only to mounting filesystems in recovery; the main system is free to 85mount them anywhere. The directories <code>/boot</code>, <code>/recovery</code>, 86and <code>/misc</code> should be raw types (mtd or emmc), while the 87directories <code>/system</code>, <code>/data</code>, <code>/cache</code>, and 88<code>/sdcard</code> (if available) should be filesystem types (yaffs2, ext4, 89or vfat).</p></dd></dl> 90 91<p>Starting in Android 3.0, the recovery.fstab file gains an additional 92optional field, <i>options</i>. Currently the only defined option is <i>length 93</i>, which lets you explicitly specify the length of the partition. 94This length is used when reformatting the partition (e.g., for the userdata 95partition during a data wipe/factory reset operation, or for the system 96partition during installation of a full OTA package). If the length value is 97negative, then the size to format is taken by adding the length value to the 98true partition size. For instance, setting "length=-16384" means the last 16k 99of that partition will <i>not</i> be overwritten when that partition is 100reformatted. This supports features such as encryption of the userdata 101partition (where encryption metadata is stored at the end of the 102partition that should not be overwritten).</p> 103 104<p class="note"><strong>Note:</strong> The <b>device2</b> and <b>options</b> 105fields are optional, creating ambiguity in parsing. If the entry in the fourth 106field on the line begins with a ‘/' character, it is considered a <b>device2 107</b> entry; if the entry does not begin with a ‘/' character, it is considered 108an <b>options</b> field.</p> 109 110<h2 id="recovery-ui">Recovery UI</h2> 111<p>To support devices with different available hardware (physical buttons, 112LEDs, screens, etc.), you can customize the recovery interface to display 113status and access the manually-operated hidden features for each device.</p> 114<p>Your goal is to build a small static library with a couple of C++ objects 115to provide the device-specific functionality. The file <code> 116<b>bootable/recovery/default_device.cpp</b></code> is used by default, and 117makes a good starting point to copy when writing a version of this file for 118your device.</p> 119 120<p><code>device/yoyodyne/tardis/recovery/recovery_ui.cpp</code></p> 121 122<pre> 123#include <linux/input.h> 124 125#include "common.h" 126#include "device.h" 127#include "screen_ui.h" 128</pre> 129 130 131<h3 id="header-item-functions">Header and item functions</h3> 132<p>The Device class requires functions for returning headers and items that 133appear in the hidden recovery menu. Headers describe how to operate the menu 134(i.e. controls to change/select the highlighted item).</p> 135 136<pre> 137static const char* HEADERS[] = { "Volume up/down to move highlight;", 138 "power button to select.", 139 "", 140 NULL }; 141 142static const char* ITEMS[] = {"reboot system now", 143 "apply update from ADB", 144 "wipe data/factory reset", 145 "wipe cache partition", 146 NULL }; 147</pre> 148 149<p class="note"><strong>Note:</strong> Long lines are truncated (not wrapped), 150so keep the width of your device screen in mind.</p> 151 152<h3 id="customize-checkkey">Customizing CheckKey</h3> 153<p>Next, define your device's RecoveryUI implementation. This example assumes 154the <b>tardis</b> device has a screen, so you can inherit from the built-in 155ScreenRecoveryUIimplementation (see instructions for 156<a href="#devices-without-screens">devices without a screen</a>.) The only 157function to customize from ScreenRecoveryUI is <code>CheckKey()</code>, which 158does the initial asynchronous key handling:</p> 159 160<pre> 161class TardisUI : public ScreenRecoveryUI { 162 public: 163 virtual KeyAction CheckKey(int key) { 164 if (key == KEY_HOME) { 165 return TOGGLE; 166 } 167 return ENQUEUE; 168 } 169}; 170</pre> 171 172 173<h4 id="key-constants">KEY constants</h4> 174<p>The KEY_* constants are defined in <code>linux/input.h</code>.<code> 175CheckKey()</code> is called no matter what is going on in the rest of 176recovery: when the menu is toggled off, when it is on, during package 177installation, during userdata wiping, etc. It can return one of four constants: 178</p> 179 180<ul> 181<li><b>TOGGLE</b>. Toggle the display of the menu and/or text log on or off 182</li> 183<li><b>REBOOT</b>. Immediately reboot the device</li> 184<li><b>IGNORE</b>. Ignore this keypress</li> 185<li><b>ENQUEUE</b>. Enqueue this keypress to be consumed synchronously (i.e., 186by the recovery menu system if the display is enabled)</li> 187</ul> 188 189<p><code>CheckKey()</code> is called each time a key-down event is followed by 190a key-up event for the same key. (The sequence of events A-down B-down B-up 191A-up results only in <code>CheckKey(B)</code> being called.) <code>CheckKey() 192</code> can call <code>IsKeyPressed()</code>, to find out if other keys are 193being held down. (In the above sequence of key events, if <code>CheckKey(B) 194</code> called <code>IsKeyPressed(A)</code> it would have returned true.)</p> 195<p><code>CheckKey()</code> can maintain state in its class; this can be useful 196to detect sequences of keys. This example shows a slightly more complex 197setup: the display is toggled by holding down power and pressing volume-up, 198and the device can be rebooted immediately by pressing the power button five 199times in a row (with no other intervening keys):</p> 200 201<pre> 202class TardisUI : public ScreenRecoveryUI { 203 private: 204 int consecutive_power_keys; 205 206 public: 207 TardisUI() : consecutive_power_keys(0) {} 208 209 virtual KeyAction CheckKey(int key) { 210 if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { 211 return TOGGLE; 212 } 213 if (key == KEY_POWER) { 214 ++consecutive_power_keys; 215 if (consecutive_power_keys >= 5) { 216 return REBOOT; 217 } 218 } else { 219 consecutive_power_keys = 0; 220 } 221 return ENQUEUE; 222 } 223}; 224</pre> 225 226 227<h3 id="screenrecoveryui">ScreenRecoveryUI</h3> 228<p>When using your own images (error icon, installation animation, progress 229bars) with ScreenRecoveryUI, you can set the variable <code>animation_fps</code> 230to control the speed in frames per second (FPS) of animations.</p> 231 232<p class="note"><strong>Note:</strong> The current 233<code>interlace-frames.py</code> script enables you to store the 234<code>animation_fps</code> information in the image itself. In earlier versions 235of Android it was necessary to set <code>animation_fps</code> yourself.</p> 236 237<p>To set the variable <code>animation_fps</code>, override the 238<code>ScreenRecoveryUI::Init()</code> function in your subclass. Set the value, 239then call the <code>parent Init() </code> function to complete initialization. 240The default value (20 FPS) corresponds to the default recovery images; when 241using these images you don't need to provide an <code>Init()</code> function. 242For details on images, see <a href="#recovery-ui-images">Recovery UI 243Images</a>.</p> 244 245 246<h3 id="device-class">Device Class</h3> 247<p>After you have a RecoveryUI implementation, define your device class 248(subclassed from the built-in Device class). It should create a single 249instance of your UI class and return that from the <code>GetUI()</code> 250function:</p> 251 252<pre> 253class TardisDevice : public Device { 254 private: 255 TardisUI* ui; 256 257 public: 258 TardisDevice() : 259 ui(new TardisUI) { 260 } 261 262 RecoveryUI* GetUI() { return ui; } 263</pre> 264 265<h3 id="startrecovery">StartRecovery</h3> 266<p>The <code>StartRecovery()</code> method is called at the start of recovery, 267after the UI has been initialized and after the arguments have been parsed, 268but before any action has been taken. The default implementation does nothing, 269so you do not need to provide this in your subclass if you have nothing to do: 270</p> 271 272<pre> 273 void StartRecovery() { 274 // ... do something tardis-specific here, if needed .... 275 } 276</pre> 277 278<h3 id="supply-manage-recovery-menu">Supplying and managing recovery menu</h3> 279<p>The system calls two methods to get the list of header lines and the list 280of items. In this implementation, it returns the static arrays defined at the 281top of the file:</p> 282 283<pre> 284const char* const* GetMenuHeaders() { return HEADERS; } 285const char* const* GetMenuItems() { return ITEMS; } 286</pre> 287 288<h4 id="handlemenukey">HandleMenuKey</h4> 289<p>Next, provide a <code>HandleMenuKey()</code> function, which takes a 290keypress and the current menu visibility, and decides what action to take:</p> 291 292<pre> 293 int HandleMenuKey(int key, int visible) { 294 if (visible) { 295 switch (key) { 296 case KEY_VOLUMEDOWN: return kHighlightDown; 297 case KEY_VOLUMEUP: return kHighlightUp; 298 case KEY_POWER: return kInvokeItem; 299 } 300 } 301 return kNoAction; 302 } 303</pre> 304 305<p>The method takes a key code (which has previously been processed and 306enqueued by the <code>CheckKey()</code> method of the UI object), and the 307current state of the menu/text log visibility. The return value is an integer. 308If the value is 0 or higher, that is taken as the position of a menu item, 309which is invoked immediately (see the <code>InvokeMenuItem()</code> method 310below). Otherwise it can be one of the following predefined constants:</p> 311 312<ul> 313<li><b>kHighlightUp</b>. Move the menu highlight to the previous item</li> 314<li><b>kHighlightDown</b>. Move the menu highlight to the next item</li> 315<li><b>kInvokeItem</b>. Invoke the currently highlighted item</li> 316<li><b>kNoAction</b>. Do nothing with this keypress</li> 317</ul> 318 319<p>As implied by the the visible argument, <code>HandleMenuKey()</code> is 320called even if the menu is not visible. Unlike <code>CheckKey()</code>, it is 321<i>not</i> called while recovery is doing something such as wiping data or 322installing a package—it's called only when recovery is idle and waiting for 323input.</p> 324 325<h4 id="trackball-mechanism">Trackball Mechanisms</h4> 326<p>If your device has a trackball-like input mechanism (generates input events 327with type EV_REL and code REL_Y), recovery synthesizes KEY_UP and KEY_DOWN 328keypresses whenever the trackball-like input device reports motion in the Y 329axis. All you need to do is map KEY_UP and KEY_DOWN events onto menu actions. 330This mapping does <i>not</i> happen for <code>CheckKey()</code>, so you can't 331use trackball motions as triggers for rebooting or toggling the display.</p> 332 333<h4 id="modifier-keys">Modifier Keys</h4> 334<p>To check for keys being held down as modifiers, call the <code>IsKeyPressed() 335</code> method of your own UI object. For example, on some 336devices pressing Alt-W in recovery would start a data wipe whether the menu 337was visible or not. YOu could implement like this:</p> 338 339<pre> 340 int HandleMenuKey(int key, int visible) { 341 if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { 342 return 2; // position of the "wipe data" item in the menu 343 } 344 ... 345 } 346</pre> 347 348<p class="note"><strong>Note:</strong> If <b>visible</b> is false, it doesn't 349make sense to return the special values that manipulate the menu (move 350highlight, invoke highlighted item) since the user can't see the highlight. 351However, you can return the values if desired.</p> 352 353<h4 id="invokemenuitem">InvokeMenuItem</h4> 354<p>Next, provide an <code>InvokeMenuItem()</code> method that maps integer 355positions in the array of items returned by <code>GetMenuItems()</code> to 356actions. For the array of items in the tardis example, use:</p> 357 358<pre> 359 BuiltinAction InvokeMenuItem(int menu_position) { 360 switch (menu_position) { 361 case 0: return REBOOT; 362 case 1: return APPLY_ADB_SIDELOAD; 363 case 2: return WIPE_DATA; 364 case 3: return WIPE_CACHE; 365 default: return NO_ACTION; 366 } 367 } 368</pre> 369 370<p>This method can return any member of the BuiltinAction enum to tell the 371system to take that action (or the NO_ACTION member if you want the system to 372do nothing). This is the place to provide additional recovery functionality 373beyond what's in the system: Add an item for it in your menu, execute it here 374when that menu item is invoked, and return NO_ACTION so the system does nothing 375else.</p> 376<p>BuiltinAction contains the following values:</p> 377<ul> 378<li><b>NO_ACTION</b>. Do nothing.</li> 379<li><b>REBOOT</b>. Exit recovery and reboot the device normally.</li> 380<li><b>APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD</b>. Install an update 381package from various places. For details, see 382<a href="#sideloading">Sideloading</a>.</li> 383<li><b>WIPE_CACHE</b>. Reformat the cache partition only. No confirmation 384required as this is relatively harmless.</li> 385<li><b>WIPE_DATA</b>. Reformat the userdata and cache partitions, also known 386as a factory data reset. The user is asked to confirm this action before 387proceeding.</li> 388</ul> 389<p>The last method, <code>WipeData()</code>, is optional and is called 390whenever a data wipe operation is initiated (either from recovery via the menu 391or when the user has chosen to do a factory data reset from the main system). 392This method is called before the user data and cache partitions are wiped. If 393your device stores user data anywhere other than those two partitions, you 394should erase it here. You should return 0 to indicate success and another 395value for failure, though currently the return value is ignored. The user data 396and cache partitions are wiped whether you return success or failure.</p> 397 398<pre> 399 int WipeData() { 400 // ... do something tardis-specific here, if needed .... 401 return 0; 402 } 403</pre> 404 405<h4 id="make-device">Make Device</h4> 406<p>Finally, include some boilerplate at the end of the recovery_ui.cpp file 407for the <code>make_device()</code> function that creates and returns an 408instance of your Device class:</p> 409 410<pre> 411class TardisDevice : public Device { 412 // ... all the above methods ... 413}; 414 415Device* make_device() { 416 return new TardisDevice(); 417} 418</pre> 419 420<h3 id="build-link-device-recovery">Build and link to device recovery</h3> 421<p>After completing the recovery_ui.cpp file, built it and link it to recovery 422on your device. In Android.mk, create a static library that contains only this 423C++ file:</p> 424 425<p><code>device/yoyodyne/tardis/recovery/Android.mk</code></p> 426 427<pre> 428LOCAL_PATH := $(call my-dir) 429include $(CLEAR_VARS) 430 431LOCAL_MODULE_TAGS := eng 432LOCAL_C_INCLUDES += bootable/recovery 433LOCAL_SRC_FILES := recovery_ui.cpp 434 435# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk 436LOCAL_MODULE := librecovery_ui_tardis 437 438include $(BUILD_STATIC_LIBRARY) 439</pre> 440 441<p>Then, in the board configuration for this device, specify your static 442library as the value of TARGET_RECOVERY_UI_LIB.</p> 443 444<pre> 445device/yoyodyne/tardis/BoardConfig.mk 446 [...] 447 448# device-specific extensions to the recovery UI 449TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis 450</pre> 451 452 453<h2 id="recovery-ui-images">Recovery UI images</h2> 454<p>The recovery user interface consists images. Ideally, users never interact 455with the UI: During a normal update, the phone boots into recovery, fills the 456installation progress bar, and boots back into the new system without input 457from the user. In the event of a system update problem, the only user action 458that can be taken is to call customer care.</p> 459<p>An image-only interface obviates the need for localization. However, as of 460Android 5.x the update can display a string of text (e.g. "Installing system 461update...") along with the image. For details, see <a href="#recovery-text"> 462Localized recovery text</a>.</p> 463 464<h3 id="recovery-5.x">Android 5.x</h3> 465<p>The Android 5.x recovery UI uses two main images: the <b>error</b> image 466and the <b>installing</b> animation.</p> 467 468<table> 469<tbody> 470<tr> 471<td> 472<img src="../images/icon_error.png" alt="image shown during ota error"> 473<p class="img-caption"><strong>Figure 1.</strong> icon_error.png</p> 474</td> 475<td> 476<img src="../images/icon_installing_5x.png" alt="image shown during ota install" 477height="275"> 478<p class="img-caption"><strong>Figure 2.</strong> icon_installing.png</p> 479</td> 480</tr> 481</tbody> 482</table> 483 484<p>The installing animation is represented as a single PNG image with 485different frames of the animation interlaced by row (which is why Figure 2 486appears squished). For example, for a 200x200 seven-frame animation, create 487a single 200x1400 image where first frame is rows 0, 7, 14, 21, ...; the second 488frame is rows 1, 8, 15, 22, ...; etc. The combined image includes a text chunk 489that indicates the number of animation frames and the number of frames per 490second (FPS). The tool <code>bootable/recovery/interlace-frames.py</code> 491takes a set of input frames and combines them into the necessary composite 492image used by recovery.</p> 493 494<p>Default images are available in different densities and are located in 495<code>bootable/recovery/res-$DENSITY/images</code> (e.g., 496<code>bootable/recovery/res-hdpi/images</code>). To use a static image during 497installation, you need only provide the icon_installing.png image and set the 498number of frames in the animation to 0 (the error icon is not animated; it is 499always a static image).</p> 500 501 502<h3 id="recovery-4.x">Android 4.x and earlier</h3> 503<p>The Android 4.x and earlier recovery UI uses the <b>error</b> image (shown 504above) and the <b>installing</b> animation plus several overlay images:</p> 505 506<table> 507<tbody> 508<tr> 509<td rowspan="2"> 510<img src="../images/icon_installing.png" alt="image shown during ota install"> 511<p class="img-caption"><strong>Figure 3.</strong> icon_installing.png</p> 512</td> 513<td> 514<img src="../images/icon_installing_overlay01.png" alt="image shown as first 515overlay"> 516<p class="img-caption"><strong>Figure 4.</strong> icon-installing_overlay01.png 517</p> 518</td> 519</tr> 520<tr> 521<td> 522<img src="../images/icon_installing_overlay07.png" alt="image shown as seventh 523overlay"> 524<p class="img-caption"><strong>Figure 5.</strong> icon_installing_overlay07.png 525</p> 526</td> 527</tr> 528</tbody> 529</table> 530 531 532<p>During installation, the on-screen display is constructed by drawing the 533icon_installing.png image, then drawing one of the overlay frames on top of it 534at the proper offset. Here, a red box is superimposed to highlight where the 535overlay is placed on top of the base image:</p> 536 537<table style="border-collapse:collapse;"> 538<tbody> 539<tr> 540<td><img align="center" src="../images/composite01.png" alt="composite image of 541install plus first overlay"> 542<p class="img-caption"><strong>Figure 6.</strong> Installing animation frame 1 543(icon_installing.png + icon_installing_overlay01.png) 544</td> 545<td><img align="center" src="../images/composite07.png" alt="composite image of 546install plus seventh overlay"> 547<p class="img-caption"><strong>Figure 7.</strong> Installing animation frame 7 548(icon_installing.png + icon_installing_overlay07.png) 549</td> 550</tr> 551</tbody> 552</table> 553 554<p>Subsequent frames are displayed by drawing <i>only</i> the next overlay 555image atop what's already there; the base image is not redrawn.</p> 556 557<p>The number of frames in the animation, desired speed, and x- and y-offsets 558of the overlay relative to the base are set by member variables of the 559ScreenRecoveryUI class. When using custom images instead of default images, 560override the <code>Init()</code> method in your subclass to change these 561values for your custom images (for details, see <a href="#screenrecoveryui"> 562ScreenRecoveryUI</a>). The script <code>bootable/recovery/make-overlay.py 563</code> can assist in converting a set of image frames to the "base image + 564overlay images" form needed by recovery, including computing of the necessary 565offsets.</p> 566 567<p>Default images are located in <code>bootable/recovery/res/images</code>. To 568use a static image during installation, you need only provide the 569icon_installing.png image and set the number of frames in the animation to 0 570(the error icon is not animated; it is always a static image).</p> 571 572 573<h3 id="recovery-text">Localized recovery text</h3> 574<p>Android 5.x displays a string of text (e.g., "Installing system update...") 575along with the image. When the main system boots into recovery it passes the 576user's current locale as a command-line option to recovery. For each message 577to display, recovery includes a second composite image with pre-rendered text 578strings for that message in each locale.</p> 579 580<p>Sample image of recovery text strings:</p> 581 582<img src="../images/installing_text.png" alt="image of recovery text"> 583<p class="img-caption"><strong>Figure 8.</strong> Localized text for recovery 584messages</p> 585 586<p>Recovery text can display the following messages:</p> 587<ul> 588<li>Installing system update...</li> 589<li>Error!</li> 590<li>Erasing... (when doing a data wipe/factory reset)</li> 591<li>No command (when a user boots into recovery manually)</li> 592</ul> 593 594<p>The Android app in <code>development/tools/recovery_l10/</code> renders 595localizations of a message and creates the composite image. For details on 596using this app, refer to the comments in <code>development/tools/recovery_l10n/ 597src/com/android/recovery_l10n/Main.java</code>. 598 599<p>When a user boots into recovery manually, the locale might not be available 600and no text is displayed. Do not make the text messages critical to the 601recovery process.</p> 602 603<p class="note"><strong>Note:</strong> The hidden interface that displays log 604messages and allows the user to select actions from the menu is available only 605in English.</p> 606 607 608<h2 id="progress-bars">Progress bars</h2> 609<p>Progress bars can appear below the main image (or animation). The progress 610bar is made by combining two input images, which must be of the same size:</p> 611 612<img src="../images/progress_empty.png" alt="empty progress bar"> 613<p class="img-caption"><strong>Figure 9.</strong> progress_empty.png</p> 614<img src="../images/progress_fill.png" alt="full progress bar"> 615<p class="img-caption"><strong>Figure 10.</strong> progress_fill.png</p> 616 617<p>The left end of the <i>fill</i> image is displayed next to the right end of 618the <i>empty</i> image to make the progress bar. The position of the boundary 619between the two images is changed to indicate the progress. For example, with 620the above pairs of input images, display:</p> 621 622<img src="../images/progress_1.png" alt="progress bar at 1%"> 623<p class="img-caption"><strong>Figure 11.</strong> Progress bar at 1%></p> 624<img src="../images/progress_10.png" alt="progress bar at 10%"> 625<p class="img-caption"><strong>Figure 12.</strong> Progress bar at 10%</p> 626<img src="../images/progress_50.png" alt="progress bar at 50%"> 627<p class="img-caption"><strong>Figure 13.</strong> Progress bar at 50%</p> 628 629<p>You can provide device-specific versions of these images by placing them 630into (in this example) <code>device/yoyodyne/tardis/recovery/res/images</code> 631. Filenames must match the ones listed above; when a file is found in that 632directory, the build system uses it in preference to the corresponding default 633image. Only PNGs in RGB or RGBA format with 8-bit color depth are supported. 634</p> 635 636<p class="note"><strong>Note:</strong> In Android 5.x, if the locale is known 637to recovery and is a right-to-left (RTL) language (Arabic, Hebrew, etc.), the 638progress bar fills from right to left.</p> 639 640 641<h2 id="devices-without-screens">Devices without screens</h2> 642<p>Not all Android devices have screens. If your device is a headless appliance 643or has an audio-only interface, you may need to do more extensive customization 644of recovery UI. Instead of creating a subclass of ScreenRecoveryUI, subclass its 645parent class RecoveryUI directly.</p> 646<p>RecoveryUI has methods for handling a lower-level UI operations such as 647"toggle the display," "update the progress bar," "show the menu," "change the 648menu selection," etc. You can override these to provide an appropriate 649interface for your device. Maybe your device has LEDs where you can use 650different colors or patterns of flashing to indicate state, or maybe you can 651play audio. (Perhaps you don't want to support a menu or the "text display" 652mode at all; you can prevent accessing them with <code>CheckKey()</code> and 653<code>HandleMenuKey()</code> implementations that never toggle the display on 654or select a menu item. In this case, many of the RecoveryUI methods you need 655to provide can just be empty stubs.)</p> 656<p>See <code>bootable/recovery/ui.h</code> for the declaration of RecoveryUI 657to see what methods you must support. RecoveryUI is abstract—some methods are 658pure virtual and must be provided by subclasses—but it does contain the code 659to do processing of key inputs. You can override that too, if your device 660doesn't have keys or you want to process them differently.</p> 661 662<h2 id="updater">Updater</h2> 663<p>You can use device-specific code in the installation of the update package 664by providing your own extension functions that can be called from within your 665updater script. Here's a sample function for the tardis device:</p> 666 667<p><code>device/yoyodyne/tardis/recovery/recovery_updater.c</code></p> 668<pre> 669#include <stdlib.h> 670#include <string.h> 671 672#include "edify/expr.h" 673</pre> 674 675<p>Every extension function has the same signature. The arguments are the name 676by which the function was called, a <code>State*</code> cookie, the number of 677incoming arguments, and an array of <code>Expr*</code> pointers representing 678the arguments. The return value is a newly-allocated <code>Value*</code>.</p> 679 680<pre> 681Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) { 682 if (argc != 2) { 683 return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); 684 } 685</pre> 686 687<p>Your arguments have not been evaluated at the time your function is 688called—your function's logic determines which of them get evaluated and how 689many times. Thus, you can use extension functions to implement your own 690control structures. <code>Call Evaluate()</code> to evaluate an <code>Expr* 691</code> argument, returning a <code>Value*</code>. If <code>Evaluate()</code> 692returns NULL, you should free any resources you're holding and immediately 693return NULL (this propagates aborts up the edify stack). Otherwise, you take 694ownership of the Value returned and are responsible for eventually calling 695<code>FreeValue()</code> on it.</p> 696 697<p>Suppose the function needs two arguments: a string-valued <b>key</b> and a 698blob-valued <b>image</b>. You could read arguments like this:</p> 699 700<pre> 701 Value* key = EvaluateValue(state, argv[0]); 702 if (key == NULL) { 703 return NULL; 704 } 705 if (key->type != VAL_STRING) { 706 ErrorAbort(state, "first arg to %s() must be string", name); 707 FreeValue(key); 708 return NULL; 709 } 710 Value* image = EvaluateValue(state, argv[1]); 711 if (image == NULL) { 712 FreeValue(key); // must always free Value objects 713 return NULL; 714 } 715 if (image->type != VAL_BLOB) { 716 ErrorAbort(state, "second arg to %s() must be blob", name); 717 FreeValue(key); 718 FreeValue(image) 719 return NULL; 720 } 721</pre> 722 723<p>Checking for NULL and freeing previously evaluated arguments can get tedious 724for multiple arguments. The <code>ReadValueArgs()</code> function can make this 725easier. Instead of the code above, you could have written this:</p> 726 727<pre> 728 Value* key; 729 Value* image; 730 if (ReadValueArgs(state, argv, 2, &key, &image) != 0) { 731 return NULL; // ReadValueArgs() will have set the error message 732 } 733 if (key->type != VAL_STRING || image->type != VAL_BLOB) { 734 ErrorAbort(state, "arguments to %s() have wrong type", name); 735 FreeValue(key); 736 FreeValue(image) 737 return NULL; 738 } 739</pre> 740 741<p><code>ReadValueArgs()</code> doesn't do type-checking, so you must do that 742here; it's more convenient to do it with one <b>if</b> statement at 743the cost of producing a somewhat less specific error message when it fails. 744But <code>ReadValueArgs()</code> does handle evaluating each argument and 745freeing all the previously-evaluated arguments (as well as setting a useful 746error message) if any of the evaluations fail. You can use a <code> 747ReadValueVarArgs()</code> convenience function for evaluating a variable 748number of arguments (it returns an array of <code>Value*</code>).</p> 749 750<p>After evaluating the arguments, do the work of the function:</p> 751 752<pre> 753 // key->data is a NUL-terminated string 754 // image->data and image->size define a block of binary data 755 // 756 // ... some device-specific magic here to 757 // reprogram the tardis using those two values ... 758</pre> 759 760<p>The return value must be a <code>Value*</code> object; ownership of this 761object will pass to the caller. The caller takes ownership of any data pointed 762to by this <code>Value*</code>—specifically the datamember.</p> 763<p>In this instance, you want to return a true or false value to indicate 764success. Remember the convention that the empty string is <i>false</i> and all 765other strings are <i>true</i>. You must malloc a Value object with a malloc'd 766copy of the constant string to return, since the caller will <code>free() 767</code> both. Don't forget to call <code>FreeValue()</code> on the objects you 768got by evaluating your arguments!</p> 769 770<pre> 771 FreeValue(key); 772 FreeValue(image); 773 774 Value* result = malloc(sizeof(Value)); 775 result->type = VAL_STRING; 776 result->data = strdup(successful ? "t" : ""); 777 result->size = strlen(result->data); 778 return result; 779} 780</pre> 781 782<p>The convenience function <code>StringValue()</code> wraps a string into a 783new Value object. Use to write the above code more succinctly:</p> 784 785<pre> 786 FreeValue(key); 787 FreeValue(image); 788 789 return StringValue(strdup(successful ? "t" : "")); 790} 791</pre> 792 793<p>To hook functions into the edify interpreter, provide the function 794<code>Register_<i>foo</i></code> where <i>foo</i> is the name of the 795static library containing this code. Call <code>RegisterFunction()</code> to 796register each extension function. By convention, name device-specific 797functions <code><i>device</i>.<i>whatever</i></code> to avoid conflicts with 798future built-in functions added.</p> 799 800<pre> 801void Register_librecovery_updater_tardis() { 802 RegisterFunction("tardis.reprogram", ReprogramTardisFn); 803} 804</pre> 805 806<p>You can now configure the makefile to build a static library with your 807code. (This is the same makefile used to customize the recovery UI in the 808previous section; your device may have both static libraries defined here.)</p> 809 810<p><code>device/yoyodyne/tardis/recovery/Android.mk</code></p> 811 812<pre> 813include $(CLEAR_VARS) 814LOCAL_SRC_FILES := recovery_updater.c 815LOCAL_C_INCLUDES += bootable/recovery 816</pre> 817 818<p>The name of the static library must match the name of the 819<code>Register_<i>libname</i></code> function contained within it.</p> 820 821<pre> 822LOCAL_MODULE := librecovery_updater_tardis 823include $(BUILD_STATIC_LIBRARY) 824</pre> 825 826<p>Finally, configure the build of recovery to pull in your library. Add your 827library to TARGET_RECOVERY_UPDATER_LIBS (which may contain multiple libraries; 828they all get registered). If your code depends on other static libraries that 829are not themselves edify extensions (i.e., they don't have a 830<code>Register_<i>libname</i></code> function), you can list those in 831TARGET_RECOVERY_UPDATER_EXTRA_LIBS to link them to updater without calling 832their (non-existent) registration function. For example, if your 833device-specific code wanted to use zlib to decompress data, you would include 834libz here.</p> 835 836<p><code>device/yoyodyne/tardis/BoardConfig.mk</code></p> 837 838<pre> 839 [...] 840 841# add device-specific extensions to the updater binary 842TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis 843TARGET_RECOVERY_UPDATER_EXTRA_LIBS += 844</pre> 845 846<p>The updater scripts in your OTA package can now call your function as any 847other. To reprogram your tardis device, the update script might contain: 848<code>tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) 849</code>. This uses the single-argument version of the built-in function <code> 850package_extract_file()</code>, which returns the contents of a file extracted 851from the update package as a blob to produce the second argument to the new 852extension function.</p> 853 854<h2>OTA package generation</h2> 855<p>The final component is getting the OTA package generation tools to know 856about your device-specific data and emit updater scripts that include calls to 857your extension functions.</p> 858<p>First, get the build system to know about a device-specific blob of data. 859Assuming your data file is in <code>device/yoyodyne/tardis/tardis.dat</code>, 860declare the following in your device's AndroidBoard.mk:</p> 861 862<p><code>device/yoyodyne/tardis/AndroidBoard.mk</code></p> 863<pre> 864 [...] 865 866$(call add-radio-file,tardis.dat) 867</pre> 868 869<p>You could also put it in an Android.mk instead, but then it must to be 870guarded by a device check, since all the Android.mk files in the tree are 871loaded no matter what device is being built. (If your tree includes multiple 872devices, you only want the tardis.dat file added when building the tardis 873device.)</p> 874 875<p><code>device/yoyodyne/tardis/Android.mk</code></p> 876<pre> 877 [...] 878 879# an alternative to specifying it in AndroidBoard.mk 880ifeq (($TARGET_DEVICE),tardis) 881 $(call add-radio-file,tardis.dat) 882endif 883</pre> 884 885<p>These are called radio files for historical reasons; they may have nothing 886to do with the device radio (if present). They are simply opaque blobs of data 887the build system copies into the target-files .zip used by the OTA generation 888tools. When you do a build, tardis.dat is stored in the target-files.zip as 889<code>RADIO/tardis.dat</code>. You can call <code>add-radio-file</code> 890multiple times to add as many files as you want.</p> 891 892<h3 id="python-module">Python module</h3> 893<p>To extend the release tools, write a Python module (must be named 894releasetools.py) the tools can call into if present. Example:</p> 895 896<p><code>device/yoyodyne/tardis/releasetools.py</code></p> 897<pre> 898import common 899 900def FullOTA_InstallEnd(info): 901 # copy the data into the package. 902 tardis_dat = info.input_zip.read("RADIO/tardis.dat") 903 common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat) 904 905 # emit the script code to install this data on the device 906 info.script.AppendExtra( 907 """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""") 908</pre> 909 910<p>A separate function handles the case of generating an incremental OTA 911package. For this example, suppose you need to reprogram the tardis only when 912the tardis.dat file has changed between two builds.</p> 913<pre> 914def IncrementalOTA_InstallEnd(info): 915 # copy the data into the package. 916 source_tardis_dat = info.source_zip.read("RADIO/tardis.dat") 917 target_tardis_dat = info.target_zip.read("RADIO/tardis.dat") 918 919 if source_tardis_dat == target_tardis_dat: 920 # tardis.dat is unchanged from previous build; no 921 # need to reprogram it 922 return 923 924 # include the new tardis.dat in the OTA package 925 common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat) 926 927 # emit the script code to install this data on the device 928 info.script.AppendExtra( 929 """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""") 930</pre> 931 932<h4 id="module-functions">Module functions</h4> 933<p>You can provide the following functions in the module (implement only the 934ones you need).</p> 935<dl> 936<dt><code>FullOTA_Assertions()</code></dt> 937<dd>Called near the start of generating a full OTA. This is a good place to 938emit assertions about the current state of the device. Do not emit script 939commands that make changes to the device.</dd> 940<dt><code>FullOTA_InstallBegin()</code></dt> 941<dd>Called after all the assertions about the device state have passed but 942before any changes have been made. You can emit commands for device-specific 943updates that must run before anything else on the device has been changed.</dd> 944<dt><code>FullOTA_InstallEnd()</code></dt> 945<dd>Called at the end of the script generation, after the script commands to 946update the boot and system partitions have been emitted. You can also emit 947additional commands for device-specific updates.</dd> 948<dt><code>IncrementalOTA_Assertions()</code></dt> 949<dd>Similar to <code>FullOTA_Assertions()</code> but called when generating an 950incremental update package.</dd> 951<dt><code>IncrementalOTA_VerifyBegin()</code></dt> 952<dd>Called after all assertions about the device state have passed but before 953any changes have been made. You can emit commands for device-specific updates 954that must run before anything else on the device has been changed.</dd> 955<dt><code>IncrementalOTA_VerifyEnd()</code></dt> 956<dd>Called at the end of the verification phase, when the script has finished 957confirming the files it is going to touch have the expected starting contents. 958At this point nothing on the device has been changed. You can also emit code for 959additional device-specific verifications.</dd> 960<dt><code>IncrementalOTA_InstallBegin()</code></dt> 961<dd>Called after files to be patched have been verified as having the expected 962<i>before</i> state but before any changes have been made. You can emit 963commands for device-specific updates that must run before anything else on the 964device has been changed.</dd> 965<dt><code>IncrementalOTA_InstallEnd()</code></dt> 966<dd>Similar to its full OTA package counterpart, this is called at the end of 967the script generation, after the script commands to update the boot and system 968partitions have been emitted. You can also emit additional commands for 969device-specific updates.</dd> 970</dl> 971 972<p class="note"><strong>Note:</strong> If the device loses power, OTA 973installation may restart from the beginning. Be prepared to cope with devices 974on which these commands have already been run, fully or partially.</p> 975 976<h4 id="pass-functions-to-info">Pass functions to info objects</h4> 977<p>Pass functions to a single info object that contains various useful items: 978</p> 979<ul> 980<li><b>info.input_zip</b>. (Full OTAs only) The <code>zipfile.ZipFile</code> 981object for the input target-files .zip.</li> 982<li><b>info.source_zip</b>. (Incremental OTAs only) The <code>zipfile.ZipFile 983</code> object for the source target-files .zip (the build already on the 984device when the incremental package is being installed).</li> 985<li><b>info.target_zip</b>. (Incremental OTAs only) The <code>zipfile.ZipFile 986</code> object for the target target-files .zip (the build the incremental 987package puts on the device).</li> 988<li><b>info.output_zip</b>. Package being created; a <code>zipfile.ZipFile 989</code> object opened for writing. Use common.ZipWriteStr(info.output_zip, 990<i>filename</i>, <i>data</i>) to add a file to the package.</li> 991<li><b>info.script</b>. Script object to which you can append commands. Call 992<code>info.script.AppendExtra(<i>script_text</i>)</code> to output text into 993the script. Make sure output text ends with a semicolon so it does not run 994into commands emitted afterwards.</li> 995</ul> 996 997<p>For details on the info object, refer to the 998<a href="http://docs.python.org/library/zipfile.html">Python Software Foundation 999documentation for ZIP archives</a>.</p> 1000 1001<h4 id="specify-module-location">Specify module location</h4> 1002<p>Specify the location of your device's releasetools.py script in your 1003BoardConfig.mk file:</p> 1004 1005<p><code>device/yoyodyne/tardis/BoardConfig.mk</code></p> 1006 1007<pre> 1008 [...] 1009 1010TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis 1011</pre> 1012 1013<p>If TARGET_RELEASETOOLS_EXTENSIONS is not set, it defaults to the <code> 1014$(TARGET_DEVICE_DIR)/../common</code> directory (<code>device/yoyodyne/common 1015</code> in this example). It's best to explicitly define the location of the 1016releasetools.py script. When building the tardis device, the releasetools.py 1017script is included in the target-files .zip file (<code>META/releasetools.py 1018</code>).</p> 1019<p>When you run the release tools (either <code>img_from_target_files</code> 1020or <code>ota_from_target_files</code>), the releasetools.py script in the 1021target-files .zip, if present, is preferred over the one from the Android 1022source tree. You can also explicitly specify the path to the device-specific 1023extensions with the <code>-s</code> (or <code>--device_specific</code>) 1024option, which takes the top priority. This enables you to correct errors and 1025make changes in the releasetools extensions and apply those changes to old 1026target-files.</p> 1027<p>Now, when you run <code>ota_from_target_files</code>, it automatically 1028picks up the device-specific module from the target_files .zip file and uses 1029it when generating OTA packages:</p> 1030 1031<pre> 1032% <b>./build/tools/releasetools/ota_from_target_files \ 1033 -i PREVIOUS-tardis-target_files.zip \ 1034 dist_output/tardis-target_files.zip incremental_ota_update.zip</b> 1035unzipping target target-files... 1036<b>using device-specific extensions from target_files</b> 1037unzipping source target-files... 1038 [...] 1039done. 1040</pre> 1041 1042<p>Alternatively, you can specify device-specific extensions when you run 1043<code>ota_from_target_files</code>.</p> 1044 1045<pre> 1046% <b>./build/tools/releasetools/ota_from_target_files \ 1047 -s device/yoyodyne/tardis \ # specify the path to device-specific extensions 1048 -i PREVIOUS-tardis-target_files.zip \ 1049 dist_output/tardis-target_files.zip incremental_ota_update.zip</b> 1050unzipping target target-files... 1051<b>loaded device-specific extensions from device/yoyodyne/tardis</b> 1052unzipping source target-files... 1053 [...] 1054done. 1055</pre> 1056 1057<p class="note"><strong>Note:</strong> For a complete list of options, refer 1058to the <code>ota_from_target_files</code> comments in <code> 1059build/tools/releasetools/ota_from_target_files</code>.</p> 1060 1061 1062<h2 id="sideloading">Sideloading</h2> 1063<p>Recovery has a <b>sideloading</b> mechanism for manually installing an 1064update package without downloading it over-the-air by the main system. 1065Sideloading is useful for debugging or making changes on devices where the 1066main system can't be booted.</p> 1067<p>Historically, sideloading has been done through loading packages off the 1068device's SD card; in the case of a non-booting device, the package can be put 1069onto the SD card using some other computer and then the SD card inserted into 1070the device. To accommodate Android devices without removable external storage, 1071recovery supports two additional mechanisms for sideloading: loading packages 1072from the cache partition, and loading them over USB using adb.</p> 1073<p>To invoke each sideload mechanism, your device's <code> 1074Device::InvokeMenuItem()</code> method can return the following values of 1075BuiltinAction:</p> 1076 1077<ul> 1078<li><b>APPLY_EXT</b>. Sideload an update package from external storage (<code> 1079/sdcard</code> directory). Your recovery.fstab must define the <code>/sdcard 1080</code> mount point. This is not usable on devices that emulate an SD card 1081with a symlink to <code>/data</code> (or some similar mechanism). <code>/data 1082</code> is typically not available to recovery because it may be encrypted. 1083The recovery UI displays a menu of .zip files in <code>/sdcard</code> and 1084allows the user to select one.</li> 1085<li><b>APPLY_CACHE</b>. Similar to loading a package from <code>/sdcard</code> 1086except that the <code>/cache</code> directory (which <i>is</i> always 1087available to recovery) is used instead. From the regular system, <code>/cache 1088</code> is only writable by privileged users, and if the device isn't bootable 1089then the <code>/cache</code> directory can't be written to at all (which makes 1090this mechanism of limited utility).</li> 1091<li><b>APPLY_ADB_SIDELOAD</b>. Allows user to send a package to the device via 1092a USB cable and the adb development tool. When this mechanism is invoked, 1093recovery starts up its own mini version of the adbd daemon to let adb on a 1094connected host computer talk to it. This mini version supports only a single 1095command: <code>adb sideload <i>filename</i></code>. The named file is sent 1096from the host machine to the device, which then verifies and installs it just 1097as if it had been on local storage.</li> 1098</ul> 1099 1100<p>A few caveats:</p> 1101<ul> 1102<li>Only USB transport is supported.</li> 1103<li>If your recovery runs adbd normally (usually true for userdebug and eng 1104builds), that will be shut down while the device is in adb sideload mode and 1105will be restarted when adb sideload has finished receiving a package. While in 1106adb sideload mode, no adb commands other than <code>sideload</code> work ( 1107<code>logcat</code>, <code>reboot</code>, <code>push</code>, <code>pull</code> 1108, <code>shell</code>, etc. all fail).</li> 1109<li>You cannot exit adb sideload mode on the device. To abort, you can send 1110<code>/dev/null</code> (or anything else that's not a valid package) as the 1111package, and then the device will fail to verify it and stop the installation 1112procedure. The RecoveryUI implementation's <code>CheckKey()</code> method 1113will continue to be called for keypresses, so you can provide a key sequence 1114that reboots the device and works in adb sideload mode.</li> 1115</ul>