Google is committed to advancing racial equity for Black communities. See how.

Building OTA Packages

You can use the ota_from_target_files tool provided in build/make/tools/releasetools to build full and incremental OTA packages for devices using A/B system updates or non-A/B system updates. The tool takes the target-files.zip file produced by the Android build system as input.

For devices running Android 11 or higher, you can build one OTA package for multiple devices with different SKUs. Doing so requires configuring the target devices to use dynamic fingerprints and updating the OTA metadata to include the device name and fingerprint in the pre and postcondition entries.

Android 8.0 deprecated file-based OTA packages for non-A/B devices, which must instead use block-based OTA packages. To generate block-based OTA packages or devices running Android 7.x or lower, pass the --block option to the ota_from_target_files parameter.

Building full updates

A full update is an OTA package that contains the entire final state of the device (system, boot, and recovery partitions). As long as the device is capable of receiving and applying the package, the package can install the build regardless of the current state of the device. For example, the following commands use release tools to build the target-files.zip archive for the tardis device.

. build/envsetup.sh && lunch tardis-eng
mkdir dist_output
make dist DIST_DIR=dist_output

The resultant .zip file contains everything needed to construct OTA packages for the tardis device.

./build/make/tools/releasetools/ota_from_target_files dist_output/tardis-target_files.zip ota_update.zip

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.

Building incremental updates

An incremental update is an OTA package that contains binary patches to data already on the device. Packages with incremental updates are typically smaller as they don't need to include unchanged files. In addition, as changed files are often very similar to their previous versions, the package only needs to include an encoding of the differences between the two files.

You can install an incremental update package only on devices that have the source build used in constructing the package. To build an incremental update, you need the target_files.zip file from the previous build (the one you want to update from) as well as the target_files.zip file from the new build. For example, the following commands use release tools to build an incremental update for the tardis device.

./build/make/tools/releasetools/ota_from_target_files -i PREVIOUS-tardis-target_files.zip dist_output/tardis-target_files.zip incremental_ota_update.zip

This build is very similar to the previous build, and the incremental update package (incremental_ota_update.zip) is much smaller than the corresponding full update (about 1 MB instead of 60 MB).

Distribute an incremental package only to devices running exactly the same previous build used as the incremental package's starting point. You must flash the images in PREVIOUS-tardis-target_files.zip or PREVIOUS-tardis-img.zip (both built with make dist, to be flashed with fastboot update), instead of the ones under the PRODUCT_OUT directory (built with make, which will be flashed with fastboot flashall). Attempting to install the incremental package on a device with some other build results in an installation error. When the install fails, the device remains in the same working state (running the old system); the package verifies the previous state of all the files it updates before touching them, so the device isn't stranded in a half upgraded state.

For the best user experience, offer a full update for every 3–4 incremental updates. This helps users catch up to the latest release and avoid a long install sequence of incremental updates.

Building OTA packages for multiple SKUs

Android 11 or higher supports using a single OTA package for multiple devices with different SKUs. Doing so requires configuring the target devices to use dynamic fingerprints and updating the OTA metadata (using OTA tools) to include the device name and fingerprint in the pre and post condition entries.

About SKUs

The format of a SKU is a variation of combined build parameter values and is typically an undeclared subset of the current build_fingerprint parameters. OEMs can use any combination of CDD-approved build parameters for a SKU while also using a single image for those SKUs. For example, the following SKU has multiple variations:

SKU = <product><device><modifierA><modifierB><modifierC>
  • modifierA is the device level (such as Pro, Premium, or Plus)
  • modifierB is the hardware variation (such as radio)
  • modifierC is the region, which can be general (such as NA, EMEA, or CHN ) or country- or language-specific (such as JPN, ENG, or CHN)

Many OEMs use a single image for multiple SKUs, then derive the final product name and device fingerprint at runtime after the device boots up. This process simplifies the platform development process, enabling devices with minor customizations but different product names to share common images (such as tardis and tardispro).

Using dynamic fingerprints

A fingerprint is a defined concatenation of build parameters such as ro.product.brand, ro.product.name, and ro.product.device. The fingerprint of a device is derived from the system partition fingerprint and is used as an unique identifier of the images (and bytes) running on the device. To create a dynamic fingerprint, use dynamic logic in the device's build.prop file to get the value of bootloader variables at device boot time, then use that data to create a dynamic fingerprint for that device.

For example, to use dynamic fingerprints for tardis and tardispro devices, update the following files as shown below.

  • Update the odm/etc/build_std.prop file to contain the following line.

    ro.odm.product.device=tardis
    
  • Update the odm/etc/build_pro.prop file to contain the following line.

    ro.odm.product.device=tardispro
    
  • Update the odm/etc/build.prop file to contain the following lines.

    ro.odm.product.device=tardis
    import /odm/etc/build_${ro.boot.product.hardware.sku}.prop
    

These lines dynamically set the device name, fingerprint, and ro.build.fingerprint values based on the value of the ro.boot.product.hardware.sku bootloader property (which is read-only).

Updating OTA package metadata

An OTA package contains a metadata file (META-INF/com/android/metadata) that describes the package, including the precondition and postcondition of the OTA package. For example, the following code is the metadata file for an OTA package targeting the tardis device.

post-build=google/tardis/tardis:11/RP1A.200521.001/6516341:userdebug/dev-keys
post-build-incremental=6516341
post-sdk-level=30
post-security-patch-level=2020-07-05
post-timestamp=1590026334
pre-build=google/tardis/tardis:11/RP1A.200519.002.A1/6515794:userdebug/dev-keys
pre-build-incremental=6515794
pre-device=tardis

The pre-device, pre-build-incremental, and pre-build values define the state a device must have before the OTA package can install. The post-build-incremental and post-build values define the state a device is expected to have after the OTA package installs. The values of pre- and post- fields are derived from the following corresponding build properties.

  • The pre-device value is derived from the ro.product.device build property.
  • The pre-build-incremental and post-build-incremental values are derived from the ro.build.version.incremental build property.
  • The pre-build and post-build values are derived from the ro.build.fingerprint build property.

On devices running Android 11 or higher, you can use the --boot_variable_file flag in OTA tools to specify a path to a file that contains the values of the runtime variables used in creating the device's dynamic fingerprint. The data is then used to update the OTA metadata to include the device name and fingerprint in the pre- and post- conditions (using the pipe character | as the delimiter). The --boot_variable_file flag has the following syntax and description.

  • Syntax: --boot_variable_file <path>
  • Description: Specifies a path to a file that contains the possible values of ro.boot.* properties. Used to calculate the possible runtime fingerprints when some ro.product.* properties are overridden by the import statement. The file expects one property per line where each line has the following format: prop_name=value1,value2.

For example, when the property is ro.boot.product.hardware.sku=std,pro, the OTA metadata for tardis and tardispro devices is as shown below.

post-build=google/tardis/tardis:11/<suffix>|google/tardis/tardispro:11/<suffix>
pre-build=google/tardis/tardis:11/<suffix>|google/tardis/tardispro:11/<suffix>
pre-device=tardis|tardispro

To support this functionality on devices running Android 10, see the reference implementation. This changelist conditionally parses the import statements in the build.prop file, which enables property overrides to be recognized and reflected in the final OTA metadata.