SkillAgentSearch skills...

Custota

Android A/B OTA updater app for custom OTA servers

Install / Use

/learn @chenxiaolong/Custota
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Custota

<img src="app/images/icon.svg" alt="app icon" width="72" />

latest release badge license badge

Custota is an app for installing Android A/B OTA updates from a custom OTA server. When paired with avbroot, it can be used to seamlessly install OTAs signed by a custom key.

Custota is installed via a Magisk/KernelSU module so that it can run as a system app.

<img src="app/images/light.png" alt="light mode screenshot" width="200" /> <img src="app/images/dark.png" alt="dark mode screenshot" width="200" />

Features

  • Supports Android 13 and newer
  • Supports pausing, resuming, and cancelling updates
  • Supports skipping optional post-install scripts to speed up updates
  • Never communicates with any server besides the configured OTA server
  • OTA updates safely continue running even if the app crashes or is uninstalled during the operation
  • Supports Direct Boot, allowing updates to install before the device is initially unlocked

Limitations

  • The device must support A/B updates.
    • This notably excludes all Samsung devices.
  • Pre-downloading an update to install later is not supported.
    • Custota runs update_engine in streaming mode, which downloads and installs OTAs at the same time.
  • The stock OS' Settings app on Pixel devices always launches the builtin OTA updater.
    • These shortcuts in the Settings app are loaded from GmsCore (part of Google Play Services) via a mechanism called "settings slices" and cannot be overridden. Apps that launch the OTA updater via the standard android.settings.SYSTEM_UPDATE_SETTINGS intent will show a prompt to pick between Custota or the builtin OTA updater.

Usage

  1. Follow the instructions in the OTA server section to set up a webserver and generate the metadata files for the OTA zips.

    Alternatively, Custota supports installing OTAs from a local directory instead of downloading them from an OTA server. When using a local directory, the expected directory structure is exactly the same as how it would be with a server.

  2. If you're installing OTA updates signed with a custom key, follow the instructions in the Custom Verification Key section.

  3. Download the latest version from the releases page. To verify the digital signature, see the verifying digital signatures section.

  4. Install the Custota module in Magisk/KernelSU.

  5. Reboot and open Custota.

  6. Set the OTA server URL to point to your OTA server.

  7. That's it!

Once the OTA server URL is configured, Custota will automatically check for updates periodically. The checks can be turned off completely or extended to automatically install the updates as well. To reduce battery usage, the timing of the periodic update checks is controlled by Android. They run at most once every 6 hours.

If OTAs are installed from an OTA server (instead of a local directory), then automatic checks and automatic installs will work even before the device is initially unlocked following a reboot.

OTA server

Custota only requires a basic webserver capable of serving static files and supporting the HTTP Range header. Any standard webserver, like Apache, Nginx, or Caddy, will do the trick. For testing, Caddy is very useful because it can serve files from a directory without setting up any config files:

caddy file-server --access-log --listen :8080 --root /path/to/ota/directory

The following static files need to be hosted:

  • OTA zip
    • The filename can be anything.
  • Csig ("Custota signature") file
    • This is a file that contains a digital signature of the metadata sections of the OTA. This allows Custota to securely read the OTA's metadata without downloading the entire zip.
    • The filename can be anything, but is commonly <ota filename>.csig.
  • Update info JSON
    • This contains the path or URL to the .zip and .csig files.
    • The filename must be <device codename>.json.

To generate the csig and update info files:

  1. Download custota-tool from the release page. Binaries are provided for Linux, Windows, and Mac. To compile from source instead, follow the instructions here.

  2. Generate the csig file from the OTA zip. The keypair that was used to sign the OTA can also be used to create the csig.

    ./custota-tool \
        gen-csig \
        --input path/to/ota.zip \
        --key path/to/ota.key \
        --cert path/to/ota.crt
    

    If the OTA was signed with a different keypair, use --cert-verify to specify the certificate for verifying the OTA. This is not needed if the OTA and csig are signed with the same keypair.

    The csig will be saved to <input>.csig. -o/--output can be used to specify a different output path.

    If the private key is encrypted, an interactive prompt for the passphrase will be shown. For automation, see --help for information on providing the passphrase via an environment variable or a file.

  3. Create the update info JSON file.

    ./custota-tool \
        gen-update-info \
        --file <device codename>.json \
        --location <ota filename>.zip
    

    The location can be set to:

    • A relative path (computed starting from the directory containing the update info file). This is the easy option if the update info, OTA, and csig files are all stored in the same directory tree. Subdirectories are supported.
    • A full URL. This is useful if the OTA and csig files are stored on different servers (eg. cloud object storage).

    By default, the csig location is set to <location>.csig. This can be changed with the -c/--csig-location option.

    If needed, this file can be easily edited by hand.

Incremental OTAs

Generating incremental OTAs is out of scope for this project, but if they are generated some other way, Custota can use them. The process is a bit more tedious and requires the following two components:

  • The csig file for the full OTA for the source OS version
  • The incremental OTA that upgrades the source OS version to the target OS version

With those components available, follow these steps to generate a csig file for the incremental OTA and update the update info JSON file accordingly:

  1. Generate a csig file for the incremental OTA. This is the same command as for generating csig files for full OTAs.

    custota-tool \
        gen-csig \
        --input <incremental OTA>.zip \
        --key path/to/ota.key \
        --cert path/to/ota.crt
    
  2. Get the vbmeta digest of the source full OTA from its csig file:

    custota-tool show-csig -i <source full OTA>.zip.csig
    

    To do this programmatically, use -r and parse the JSON output. For example:

    custota-tool show-csig -i <source full OTA>.zip.csig -r | jq -r .vbmeta_digest
    
  3. Take the existing update info JSON file (which should already have the full OTA location) and update it with the incremental OTA information. Replace <source vbmeta digest> with the digest from the previous step.

    ./custota-tool \
        gen-update-info \
        --file <device codename>.json \
        --location <incremental OTA>.zip \
        --inc-vbmeta-digest <source vbmeta digest>
    

When checking for updates, Custota will look for an incremental OTA matching the vbmeta digest of the currently running OS. If an incremental OTA does not exist, it will use the full OTA instead.

HTTPS

To use a self-signed certificate or a custom CA certificate, it needs to be installed into the system CA trust store. To generate a module that does this, run the following command and then flash the generated module zip.

custota-tool gen-cert-module -o system-ca-certs.zip /path/to/cert.pem

Custom verification key

Android's update_engine verifies OTA signatures against certificates contained within /system/etc/security/otacerts.zip. If your OTAs were signed by a custom key via avbroot, make sure it was done with avbroot 3.0.0 or newer, which added support for patching the system partition's copy of otacerts.zip (instead of just the recovery partition's copy).

Permissions

  • ACCESS_CACHE_FILESYSTEM (automatically granted by system app permissions)
    • Needed to store temporary OTA files.
  • ACCESS_NETWORK_STATE (automatically granted at install time)
    • Needed on Android 14+ for unmetered network background run condition.
  • FOREGROUND_SERVICE, FOREGROUND_SERVICE_SPECIAL_USE (automatically granted at install time)
    • Needed to run the OTA update service in the background.
  • INTERNET (automatically granted at install time)
    • Needed to communicate with the OTA server. Custota does not and will never communicate with any server outside of the configured OTA server. There are no ads, analytics, or any sort of tracking.
  • MANAGE_CARRIER_OEM_UNLOCK_STATE, MANAGE_USER_OEM_UNLOCK_STATE, READ_OEM_UNLOCK_STATE (automatically granted by system app permissions)
    • Needed to show the bootloader unlock status.
  • POST_NOTIFICATIONS (must be granted by the user)
    • Android requires a notification to be shown in order for the updater service to reliably run in the background.
  • REBOOT (automatically granted by system app permissions)
    • Needed to reboot the device when the user explicitly presses the reboot button in Custota's notification after an update is installed.
  • RECEIVE_BOOT_COMPLETED (automatically granted at install time)
    • Needed to schedule periodic update checks.
    • Needed to show the OTA cleanup notification on the reboot following an OTA update.
  • WAKE_LOCK (**automatically gra

Related Skills

View on GitHub
GitHub Stars205
CategoryDevelopment
Updated5d ago
Forks36

Languages

Kotlin

Security Score

95/100

Audited on Mar 23, 2026

No findings