AOSP Expert & Production Engineering
3 min read

Recovery OTA

Introduction to Recovery OTA

In the early days of Android, Over-The-Air (OTA) updates were delivered and applied through a specialized, minimal Linux environment known as Recovery Mode. This legacy method, while simple, required the device to reboot out of the main Android OS, resulting in user-visible downtime while the update was applied.

Although modern Android devices primarily use A/B (Seamless) updates or Virtual A/B, Recovery OTA remains essential for understanding the evolution of Android updates, and it is still used in specific embedded or low-cost devices.

The OTA Package Format

A Recovery OTA package is typically a zip archive containing the new system images, an updater binary, and a script.

Key Components of an OTA Zip:

  • META-INF/com/google/android/updater-script: The instructions for applying the update.
  • META-INF/com/google/android/update-binary: The executable that parses and runs the updater script.
  • payload.bin (or traditional patches/images): The actual update payload. Modern recovery OTAs often use a payload.bin similar to A/B updates, adapting it for recovery application.

Updater Script and the Edify Language

The updater-script uses a specialized, simple scripting language called Edify. The update-binary interprets these Edify commands to perform actions like formatting partitions, unpacking files, and verifying signatures.

Example: Edify Script Snippet

getprop("ro.product.device") == "marlin" || abort("E3004: This package is for marlin devices.");
ui_print("Patching system image...");
block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat.br", "system.patch.dat");
ui_print("Update complete!");

In the Android open-source project (AOSP), the Edify language parser and functions are defined in C++ under bootable/recovery/edify/.

Edify Function Registration (C++)

// bootable/recovery/updater/install.cpp
void RegisterInstallFunctions() {
    RegisterFunction("mount", MountFn);
    RegisterFunction("is_mounted", IsMountedFn);
    RegisterFunction("unmount", UnmountFn);
    RegisterFunction("format", FormatFn);
    RegisterFunction("show_progress", ShowProgressFn);
    RegisterFunction("set_progress", SetProgressFn);
    RegisterFunction("package_extract_file", PackageExtractFileFn);
    RegisterFunction("getprop", GetPropFn);
}

Sideloading OTA via ADB

During development or recovery, users can manually apply an OTA zip without downloading it directly on the device using adb sideload.

When you select "Apply update from ADB" in the recovery menu, the device starts an adbd daemon in sideload mode.

How to Sideload:

  1. Reboot to recovery:
    adb reboot recovery
    
  2. Select "Apply update from ADB" using device buttons.
  3. Execute the sideload command on the host machine:
    adb sideload ota_package.zip
    

Behind the scenes, the ADB client on the host acts as a server that streams the zip file chunks to the recovery daemon as requested by the update-binary.

Limitations of Recovery OTA

The transition to A/B updates was driven by several critical limitations of the Recovery OTA architecture:

  • Significant Downtime: The user cannot use the device for the entire duration of the update process (often 10 to 20 minutes).
  • Risk of Bricking: If the power fails during the block_image_update process or if an I/O error occurs, the partition is left corrupted.
  • Storage Requirements: The device needs enough /cache or /data space to store the downloaded OTA zip before rebooting into recovery.
  • Lack of Seamless Rollback: If an update fails to boot after installation, there is no automatic fallback mechanism to the previous working build.