|Version 1 (modified by 6 years ago) ( diff ),
Android Updates and Recovery
Android has a built-in recovery and update system comprising of the following:
- recovery boot mode (Android can instruct the board to reboot into recovery mode)
- recovery partition consisting of its own kernel, device-tree, and ramdisk consisting of filesystem tools, and the recovery console application
- extensible recovery console (RC) which is the application that is run when booting into recovery mode
- android.os.RecoverySystem class providing OTA verification and power management calls that you can use to implement your own OTA application
- android build system 'make dist' target to build an OTA image with extensible features
In general, Android can boot into 'normal' mode, or 'recovery' mode. Normal mode is used to boot Android for normal usage. Recovery mode is used to boot Android into either an automated update mode, or a minimal menu driven UI for basic recovery commands.
An 'Over The Air' (OTA) update is commonly used to distribute updates and typically they are in the form of a zip file containing a system image and an update script. Once the OTA image is downloaded to the cache partition, the system is rebooted in recovery mode which automatically detects the zip, applies it, then reboots to standard mode.
The OTA updates have an embedded digital signature (verified by android.os.RecoverySystem.verifyPackage()).
Note that the Android recovery mechanism is not without its flaws:
- does not allow disk re-partitioning which causes wasted space (must leave slack space in /system, boot, and recovery for future android updates, /cache needs to be large enough to hold a compressed sw update which typically is two-thirds of /system size to be safe)
- device is unusable during update
The Gateworks Android BSP provides the following OS OTA support:
- single or two-step OTA generation and usage
- single OTA supporting both NAND/ubifs and blockdev/ext4 support
The 'reboot to recovery' mode is a bit device-specific with regards to how the device is rebooted and how it determines if it should boot into recovery mode or normal mode (specifically, the communication between the OS and the bootloader). The policy used needs to be supported in both the kernel (or Android bionic libc) as well the bootloader (or bootscript). Freescale's Android BSP uses a register in the IMX6 SoC which retains its value on a chip-level reset and checks for this in their bootloader. For the Gateworks Ventana Android BSP we changed this to use an EEPROM register in the GSC so that a full board power-cycle can still detect if the board needs to boot to recovery. Technically the bootloader could use the same mechanisms that the OS and recovery application use but that typically puts too many requirements on the capaiblities of the bootloader.
Android recovery is similar to the BOOT image in that it has a kernel, device-tree blobs, and ramdisk with Android init (using init.recovery.*.rc) and an fstab (recovery_fstab). Recovery mode boots data from a separate 'recovery' partition instead of the typical 'boot' partition.
The recovery filesystem is built in $(OUTDIR)/recovery/root which is built into a ramdisk ($(OUTDIR)/ramdisk-recovery.img) and wrapped with a U-Boot header ($(OUTDIR)/uramdisk-recovery.img). In addition, an 'Android image' is built in $(OUTDIR)/recovery.img however we don't use Android images for Ventana because they do not allow for multiple kernel device-tree's.
The Android project that builds the recovery 'application', which typically consists of a simple menu-driven interface and 'edify' script processor is in the bootable/recovery directory.
The RecoverySystem class provides the functionality of kicking off a recovery. The job of obtaining a file is outside of the scope of the Android Open Source project (AOSP), however Freescale has a simple app that can be used or modified to your needs. The RecoverySystem class will erase the log file (/cache/recovery/log), and write to the command file. For example when installPackage(Context context, File packageFile) is called, a command of '--update_package=<filename>' will be stored in /cache/recovery/command then perform a reboot via PowerManager?.reboot().
Creating an OTA image
The Android build system make dist target will create OTA images. These images will be in the form of .zip files in the $(PRODUCT_OUT) dir and are also copied to the $(DIST_DIR) directory which can be specified in the env (make dist DIST_DIR=dist_output). OTA package names will be names by the target device (ie ventana) and the BUILD_ID (from device/gateworks/ventana/build_id.mk)
The 'make dist' target will first create a 'target_files' .zip archive in the DIST_DIR directory (<target-device>-target_files-<BUILD_ID>.zip) which contains all files needed for a complete Android install (all partitions: system, boot, and recovery). You will want to keep this file for any releases you push as it may be useful later to create very small 'incremental' OTA releases from. The make dist target creates the following archives in DIR_DIR:
- <name>-apps-<BUILD_ID>.zip - A zip of Android apps
- <name>-img-<BUILD_ID>.zip - A zip of the various .img files (These are Android Images, which we don't use)
- <name>-ota-<BUILD_ID>.zip - An OTA update package
- <name>-symbols-<BUILD_ID>.zip - symbols directory
- <name>-target_files-<BUILD_ID>.zip - a zip of the directories that map to the target filesystem which can be used to create an OTA package or filesystem image as a post-build step
The ota_from_target_files tool uses this archive to create either 'full' or 'incremental' OTA update packages. The resulting OTA package is named <target-device>-ota-<BUILD_ID>.zip and will be in the PRODUCT_OUT directory (out/target/product/ventana/) as well as the DIST_DIR (which defaults to out/dist).
The OTA images are signed with a key defined by PRODUCT_DEFAULT_DEV_CERTIFICATE (which defaults to device/fsl/common/security/testkey). You can read more about Freescale's 'test key' from device/fsl/common/security/README. Once development is complete and you need to distribute a 'user' build OTA image, you need to generate a user image using your own 'private keys' as detailed in Signing builds for release. Note that the key used in OTA's must match the key used for the original system image.
An 'incremental' update contains a set of binary patches to be applied to the data already on the device which can result in considerably smaller update packages. These images can only be installed on a device that has a build where you still have the 'target_files' .zip archive to reference. If you have a previous 'target_files' .zip archive. Gateworks does not currently support the creation and installation of incremental updates.
A 'two-step' OTA will update the recovery partition first, then update the system partition. This takes place over multiple boot cycles and is necessary when the recovery requires a kernel or tool update in order to perform its duty. By default the make dist target will create an single-step OTA that only updates the system partition. In order to create a 'two-step' you must run it manually and provide the --two-step argument.
There are additional options that can be passed to the ota_from_target_files tool:
- -w,--wipe_user_data - wipe the user data partition when installed
- -n,--no_prereq - omit the timestamp prereq check normally included at the top of the build scripts allowing an older OTA to install over a newer build ID
- -e,--extra_script <file> - insert contents of file at the end of the update script
- -2,--two_step - Generate a 'two-step' OTA package where recovery is updated first, so that any changes made to the system partition are done using the new recovery (updated kernel, etc)
An OTA image can be customized in the following ways:
- add additional data files to the OTA image by adding them to the target-files .zip (add to BUILT_TARGET_FILES_PACKAGE dependency in build/core/Makefile)
- adding executables to recovery for use in scripts (add them to PRODUCT_PACKAGES)
- copy and install files to the target device during the OTA (modify the releasetools.py script)
- modify existing script in build/tools/releasetools/ota_from_target_files (WriteFullOTAPackage function)
- modify the recovery user interface (UI) (provide a custom implementation via TARGET_RECOVERY_UI_LIB)
See https://source.android.com/devices/tech/ota/device_code.html for more details on customization of OTA's.
During OTA creation a target can provide a python script that extends the OTA release in a couple of ways:
- define TARGET_RELEASETOOLS_EXTENSIONS directory that contains releasetools.py
- provide a releasetools.py in the $(TARGET_DEVICE_DIR)/../common directory
Typically a custom releasetools.py is used to do very target specific things such as:
- update the bootloader or SPL
- update device firmware
If present, the following functions can be defined that will be called allowing you to provide simple 'additions' to the existing OTA script:
- FullOTA_Assertions - called after emiiting the block of assertions at the top of a full OTA package
- FullOTA_InstallBegin - called at the start of full OTA installation
- FullOTA_InstallEnd - called at the end of full OTA installation (typically used to install the image for the device's baseband processor)
- IncrementalOTA_Assertions - called after emiiting the block of assertions at the top of an incremental OTA package
- IncrementalOTA_InstallBegin - called at the start of an incremental OTA installation
- IncrementalOTA_InstallEnd - called at the end of an incremental OTA installation (typically used to install the image for the device's baseband processor)
The Gateworks Android BSP uses this to extend the the AOSP releasetools to add updating of the bootloader and SPL.
Sometimes the releasetools.py extensions do not offer enough customization. Gateworks has modified the ota_from_target_files script for the following:
- we do not use recovery.img and boot.img Android Images - instead we use filesystems so that we can have a more flexible choice in device-tree's when booting the kernel
For an example see device/gateworks/common/releasetools.py
Custom Recovery UI
A customized version of the recovery user interface can be provided by setting TARGET_RECOVERY_UI_LIB. The default implementation is to use bootable/recovery/default_device.cpp
Applying an OTA
Once an OTA image has been created you can deploy it to your devices. The Life of an OTA update is well described in on the Android OTA Updates page.
Typically a custom Android application will poll an update server for OTA updates and when one is found will download its description and present the user with a dialog asking if they wish to download and install the update. Once the update is downloaded and verified the recovery process can begin. This is done by the application copying the file to the /cache/recovery directory, creating a /cache/recovery/command file with recovery commands, and rebooting the board into recovery mode.
The 'reboot to recovery' mode is a bit device-specific with regards to how the device is rebooted and how it determines if it should boot into recovery mode or normal mode. The policy used needs to be supported in both the kernel (or Android bionic libc) as well the bootloader (or bootscript). Freescale's Android BSP uses a register in the IMX6 SoC which retains its value on a chip-level reset and checks for this in their bootloader. For the Gateworks Ventana Android BSP we changed this to use an EEPROM register in the GSC so that a full board power-cycle can still detect if the board needs to boot to recovery.
You can test an OTA manually without the need for an OTA manager by copying the OTA to the cache partition, creating a command file, and rebooting to recovery mode. For example the following via a shell prompt:
busybox wget -P /cache http://server/ota.zip echo "--update_package=/cache/recovery/ota.zip" > /cache/command reboot recovery
Getting out of Recovery mode
If you are stuck continually booting into recovery mode you can clear the bootmode flag with the following command in the bootloader:
i2c dev 0 && i2c mw 51 80 0xff 1
OTA manager - FSLOta
There is no Android Open Source Project (AOSP) project that provides an OTA manager, but Freescale has a simple one called FSLOta that can be used, or used for reference: FSLOta source
Android has some built-in classes that aid in the recovery process:
- android.os.RecoverySystem - provides a verifyPackage function, an installPackage function, and a handleAftermath function which is called by the BootReciever following a recovery and deletes the contents of /cache/recovery
- android.os.PowerManager - provides a reboot function that can reboot into recovery mode (Note that RecoverySystem.installPackage() calls this) </ul