Device Updates

The Device Updates API allow an application to control and monitor applying over the air (OTA) software updates to the device.

Updating a device involves performing the following operations:

  • Checking for an available software update
  • Downloading and applying an update
  • Rebooting the device into the new software version

The default initial state for the device update service is STATE_IDLE. The update service checks in with the update server periodically (every few hours) to determine if a new OTA update was posted through the Android Things Console. When a new update is detected, the UpdateManager reports STATE_UPDATE_AVAILABLE.

Once an update is available, the automatic behavior of the update service depends on the update policy configured for the device. When the policy indicates that an update should be applied, the update service begins downloading and verifying the update contents. To complete the update process, the device must reboot. Unless handled automatically by the update policy, the UpdateManager reports STATE_UPDATED_NEEDS_REBOOT to indicate that your app should reboot the device using the DeviceManager.

Adding the required permissions

Add the required permissions for this API to your app's manifest file:

<uses-permission android:name="com.google.android.things.permission.MANAGE_UPDATE_POLICY" />
<uses-permission android:name="com.google.android.things.permission.PERFORM_UPDATE_NOW" />

Configuring the update policy

The UpdateManager class defines a set of policies that are used to determine which of these steps are taken automatically and which need to be initiated by the device's application software.

  • POLICY_CHECKS_ONLY - Only check for an available update.
  • POLICY_APPLY_ONLY - Download and apply an available update.
  • POLICY_APPLY_AND_REBOOT - Download and apply an available update. Reboot the device automatically. This is the default policy.

When setting the policy, you must also provide a deadline for applying the update. The timeout starts when the device first sees that an update is available. After this timeout, the UpdateManager automatically downloads the update (if needed), applies it, and reboots the device. This provides a safety net so that developers can be certain that updates will eventually be applied, even if the device is using a policy that restricts automatically applying an update, rebooting the device, or both.

The following example could be used by devices that can automatically download and apply an available update, but may not be able to reboot immediately:

UpdateManager manager = UpdateManager.getInstance();
UpdatePolicy policy = new UpdatePolicy.Builder()
        .setPolicy(UpdatePolicy.POLICY_APPLY_ONLY)
        .setApplyDeadline(7L, TimeUnit.DAYS)
        .build();
manager.setPolicy(policy);

Configuring the update channel

Your devices can subscribe to OTA updates on different channels, enabling the Android Things Console to deliver different build versions for development, beta testing, and production.

By default, devices subscribe to the stable-channel. You can use the setChannel() method to subscribe your device to a different update channel, passing in the new channel name. This channel setting is persistent, it does not need to be updated again after a device reboot.

The following example would subscribe a device to the dev-channel updates:

UpdateManager manager = UpdateManager.getInstance();
manager.setChannel("dev-channel");

See how to select an update channel in the Android Things Console documentation for more information on the available update channels and creating your own custom channels.

Monitoring update status

You can add a StatusListener to monitor status changes to UpdateManager, such as when a new update is available or when it has been downloaded to the device. To get the current state, use the getStatus() method.

When using POLICY_APPLY_ONLY, your app is in control of when to reboot the device to apply the update. For example, you may want to delay until a time when no one will be interacting with the device.

public class UpdateService extends Service {

    UpdateManager mUpdateManager;

    @Override
    public void onCreate() {
        super.onCreate();

        mUpdateManager = UpdateManager.getInstance();
        UpdatePolicy policy = new UpdatePolicy.Builder()
                .setPolicy(UpdatePolicy.POLICY_APPLY_ONLY)
                .setApplyDeadline(7L, TimeUnit.DAYS)
                .build();
        mUpdateManager.setPolicy(policy);
        mUpdateManager.addStatusListener(mUpdateStatusListener);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        mUpdateManager.removeStatusListener(mUpdateStatusListener);
    }

    private StatusListener mUpdateStatusListener = new StatusListener() {
        @Override
        public void onStatusUpdate(UpdateManagerStatus status) {
            switch (status.currentState) {
                case UpdateManagerStatus.STATE_DOWNLOADING_UPDATE:
                    /* Download in progress */
                    break;
                case UpdateManagerStatus.STATE_FINALIZING_UPDATE:
                    /* Download complete */
                    break;
                case UpdateManagerStatus.STATE_UPDATED_NEEDS_REBOOT:
                    /* Reboot device to apply update */
                    break;
            }
        }
    };

    ...
}

Checking for updates

To perform a manual update check, use the performUpdateNow() method. The policy passed to this method temporarily overrides the policy set by setPolicy(). For example, you can use this during initial device setup to check for an update and then prompt the user to download it.

public class SetupActivity extends Activity {

    private UpdateManager mUpdateManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mUpdateManager = UpdateManager.getInstance();
        mUpdateManager.addStatusListener(new StatusListener() {
            @Override
            public void onStatusUpdate(UpdateManagerStatus status) {
                switch (status.currentState) {
                    case UpdateManagerStatus.STATE_UPDATE_AVAILABLE:
                        /* Notify user of the update */
                        break;
                    case UpdateManagerStatus.STATE_DOWNLOADING_UPDATE:
                        /* Update UI to show progress */
                        break;
                }
            }
        });

        // Trigger an update check immediately
        mUpdateManager.performUpdateNow(UpdatePolicy.POLICY_CHECKS_ONLY);
    }

    ...
}