Android 12 Developer Preview is here! Try it out, and give us your feedback!

Support in-app updates (Native)

This guide describes how to support in-app updates in your app using native code (C or C++). There are separate guides for cases where your implementation uses the Kotlin programming language or the Java programming language, and cases where your implementation uses Unity.

Native SDK overview

The Play Core Native SDK is part of the Play Core SDK family. The Native SDK includes a C header file, app_update.h, that wraps AppUpdateManager from the Java Play Core SDK. This header file allows your app to call the API for in-app updates directly from your native code.

Set up your development environment

To set up your development environment, follow the setup instructions in the Native section of the Play Core library guide.

After you integrate the Play Core Native SDK into your project, include the following line in files that contain API calls:

#include "play/app_update.h"

Initialize the in-app update API

Whenever you use the in-app update API, initialize it first by calling the AppUpdateManager_init() function, as shown in the following example built with android_native_app_glue.h:

void android_main(android_app* app) {
  app->onInputEvent = HandleInputEvent;

  AppUpdateErrorCode error_code =
    AppUpdateManager_init(app->activity->vm, app->activity->clazz);
  if (error_code == APP_UPDATE_NO_ERROR) {
    // You can use the API.
  }
}

Check for update availability

Before you request an update, check if there is an update available for your app. AppUpdateManager_requestInfo() starts an asynchronous request that gathers the required information to launch the in-app update flow later. The function returns APP_UPDATE_NO_ERROR if the request starts successfully.

AppUpdateErrorCode error_code = AppUpdateManager_requestInfo()

if (error_code == APP_UPDATE_NO_ERROR) {
    // The request has successfully started, check the result using
    // AppUpdateManager_getInfo.
}

You can track the ongoing process and result of the request using AppUpdateManager_getInfo(). In addition to the error code, this function returns an AppUpdateInfo opaque struct, which you can use to retrieve information about the update request. For example, you might want to call this function in every game loop until it returns a non-null result for info:

AppUpdateInfo* info;
GameUpdate() {

   // Keep calling this in every game loop until info != nullptr
   AppUpdateErrorCode error_code = AppUpdateManager_getInfo(&info);


   if (error_code == APP_UPDATE_NO_ERROR && info != nullptr) {
       // Successfully started, check the result in the following functions
   }
...
}

Check update staleness

In addition to checking whether an update is available, you might also want to check how much time has passed since the user was last notified of an update through the Play Store. This can help you decide whether you should initiate a flexible update or an immediate update. For example, you might wait a few days before notifying the user with a flexible update, and a few days after that before requiring an immediate update.

Use AppUpdateInfo_getClientVersionStalenessDays() to check the number of days since the update became available through the Play Store:

int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);

Check update priority

The Google Play Developer API allows you to set the priority of each update. This allows your app to decide how strongly to recommend an update to the user. For example, consider the following strategy for setting update priority:

  • Minor UI improvements: Low-priority update; request neither a flexible update nor an immediate update. Update only when the user isn't interacting with your app.
  • Performance improvements: Medium-priority update; request a flexible update.
  • Critical security update: High-priority update; request an immediate update.

To determine priority, Google Play uses an integer value between 0 and 5, with 0 being the default and 5 being the highest priority. To set the priority for an update, use the inAppUpdatePriority field under Edits.tracks.releases in the Google Play Developer API. All newly-added versions in the release are considered to be the same priority as the release. Priority can only be set when rolling out a new release and cannot be changed later.

Set the priority using the Google Play Developer API, as described in the Play Developer API documentation. Specify in-app update priority in the Edit.tracks resource passed in the Edit.tracks: update method. The following example demonstrates releasing an APK with version code 88 and inAppUpdatePriority 5:

{
  "releases": [{
      "versionCodes": ["88"],
      "inAppUpdatePriority": 5,
      "status": "completed"
  }]
}

In your app's code, you can check the priority level for a given update using AppUpdateInfo_getPriority():

int32_t priority = AppUpdateInfo_getPriority(info);

Start an update

After you confirm that an update is available, you can request an update using AppUpdateManager_requestStartUpdate(). Before you request an update, get an up-to-date AppUpdateInfo object and create an AppUpdateOptions object to configure the update flow. An AppUpdateOptions object defines options for an in-app update flow, including whether the update should be flexible or immediate.

The following example creates an AppUpdateOptions object for a flexible update flow:

// Creates an AppUpdateOptions configuring a flexible in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_FLEXIBLE, &options);

The following example creates an AppUpdateOptions object for an immediate update flow:

// Creates an AppUpdateOptions configuring an immediate in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_IMMEDIATE, &options);

The AppUpdateOptions object also contains an AllowAssetPackDeletion field that defines whether the update is allowed to clear asset packs in case of limited device storage. This field is set to false by default, but you can use the AppUpdateOptions_setAssetPackDeletionAllowed() method to set it to true instead:

bool allow = true;
AppUpdateErrorCode error_code = AppUpdateOptions_setAssetPackDeletionAllowed(options, allow);

After you have an up-to-date AppUpdateInfo object and a properly-configured AppUpdateOptions object, call AppUpdateManager_requestStartUpdate() to asynchronously request an update flow, passing in an Android Activity jobject for the final parameter.

AppUpdateErrorCode request_error_code =
AppUpdateManager_requestStartUpdate(info, options, app->activity->clazz);

To free up resources, release instances of AppUpdateInfo and AppUpdateOptions that you no longer need by calling AppUpdateInfo_destroy() and AppUpdateOptions_destroy(), respectively.

AppUpdateInfo_destroy(info);
AppUpdateOptions_destroy(options);

For an immediate update flow, Google Play displays a user confirmation page. When the user accepts the request, Google Play automatically downloads and installs the update in the foreground, then restarts the app to the updated version if installation is successful.

For a flexible update flow, you can keep requesting up-to-date AppUpdateInfo objects to keep track of the current update status while the user continues to interact with the app. After the download finishes successfully, you must trigger the completion of the update by calling AppUpdateManager_requestCompleteUpdate(), as shown in the following example:

AppUpdateStatus status = AppUpdateInfo_getStatus(info);
if (status == APP_UPDATE_DOWNLOADED) {
    AppUpdateErrorCode error_code = AppUpdateManager_requestCompleteUpdate();
    if (error_code != APP_UPDATE_NO_ERROR)
    {
      // There was an error while completing the update flow.
    }
}

Free up resources by calling the AppUpdateManager_destroy() function after your app has finished using the API.

Error handling

This section describes solutions for common errors indicated by specific AppUpdateErrorCode values:

  • An error code of -110, APP_UPDATE_INITIALIZATION_NEEDED indicates that the API has not been initialized successfully. Call AppUpdateManager_init() to initialize the API.
  • An error code of -4, APP_UPDATE_INVALID_REQUEST indicates that some parameters of the update flow request are malformed. Check to make sure that the AppUpdateInfo and AppUpdateOptions objects are not null and are correctly formatted.
  • An error code of -5, APP_UPDATE_UNAVAILABLE indicates that there is no applicable update available. Make sure that the target version has the same package name, application ID, and signing key. If there is an update available, clear the app's cache and call AppUpdateManager_requestAppUpdateInfo() again to refresh AppUpdateInfo.
  • An error code of -6, APP_UPDATE_NOT_ALLOWED indicates that the update type indicated by the AppUpdateOption object is not allowed. Check whether the AppUpdateInfo object indicates that the update type is allowed before starting the update flow.

Next steps

Test your app's in-app updates to verify that your integration is working correctly.