This page describes how to handle issues with integrity verdicts.

After an integrity token is requested, you have the option to display a Google
Play dialog to the user. You may display the dialog when there is one or more
issues with the integrity verdict or if an exception occurred during an
Integrity API request. Once the dialog is closed, you can verify that the issue
is fixed with another integrity token request. If you make [standard requests](https://developer.android.com/google/play/integrity/standard), you need to warm up the token provider again
to obtain a fresh verdict.
| **Note:** All integrity dialog types can be shown to users, regardless of the contents of the integrity token. You should check the contents of the integrity token on your server to determine which dialog should be shown.

## Request an integrity dialog to fix a verdict issue

When the client requests an integrity token, you can use the method offered in
the [`StandardIntegrityToken`](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/StandardIntegrityManager.StandardIntegrityToken#showDialog(android.app.Activity,%20int)) (Standard API) and
[`IntegrityTokenResponse`](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/IntegrityTokenResponse#showDialog(android.app.Activity,%20int)) (Classic API):
`showDialog(Activity activity, int integrityDialogTypeCode)`.
| **Note:** To use integrity dialogs, you must be using Integrity API Android library version 1.3.0 or higher.

The following steps outline how you can use the Play Integrity API to show a
remediation dialog using the [`GET_LICENSED`](https://developer.android.com/google/play/integrity/remediation#get-licensed-dialog) dialog code.
Other dialog codes that your app can request are listed after this section.

1. Request an integrity token from your app, and send the token to your server.
   You can use the Standard or Classic request.

   ### Kotlin

   ```kotlin
   // Request an integrity token
   val tokenResponse: StandardIntegrityToken = requestIntegrityToken()
   // Send token to app server and get response on what to do next
   val yourServerResponse: YourServerResponse = sendToServer(tokenResponse.token())  
   ```

   ### Java

   ```java
   // Request an integrity token
   StandardIntegrityToken tokenResponse = requestIntegrityToken();
   // Send token to app server and get response on what to do next
   YourServerResponse yourServerResponse = sendToServer(tokenResponse.token());  
   ```

   ### Unity

   ```c#
   // Request an integrity token
   StandardIntegrityToken tokenResponse = RequestIntegrityToken();
   // Send token to app server and get response on what to do next
   YourServerResponse yourServerResponse = sendToServer(tokenResponse.Token); 
   ```

   ### Unreal Engine

   ```c++
   // Request an integrity token
   StandardIntegrityToken* Response = RequestIntegrityToken();
   // Send token to app server and get response on what to do next
   YourServerResponse YourServerResponse = SendToServer(Response->Token); 
   ```

   ### Native

   ```c++
   /// Request an integrity token
   StandardIntegrityToken* response = requestIntegrityToken();
   /// Send token to app server and get response on what to do next
   YourServerResponse yourServerResponse = sendToServer(StandardIntegrityToken_getToken(response));
   ```
2. On your server, decrypt the integrity token and check the
   `appLicensingVerdict` field. It could look something like this:

   ```json
   // Licensing issue
   {
     ...
     "accountDetails": {
         "appLicensingVerdict": "UNLICENSED"
     }
   }
   ```
3. If the token contains `appLicensingVerdict: "UNLICENSED"`, reply to your app
   client, requesting it show the licensing dialog:

   ### Kotlin

   ```kotlin
   private fun getDialogTypeCode(integrityToken: String): Int{
     // Get licensing verdict from decrypted and verified integritytoken
     val licensingVerdict: String = getLicensingVerdictFromDecryptedToken(integrityToken)

     return if (licensingVerdict == "UNLICENSED") {
             1 // GET_LICENSED
         } else 0
   }
   ```

   ### Java

   ```java
   private int getDialogTypeCode(String integrityToken) {
     // Get licensing verdict from decrypted and verified integrityToken
     String licensingVerdict = getLicensingVerdictFromDecryptedToken(integrityToken);

     if (licensingVerdict.equals("UNLICENSED")) {
       return 1; // GET_LICENSED
     }
     return 0;
   }
   ```

   ### Unity

   ```c#
   private int GetDialogTypeCode(string IntegrityToken) {
     // Get licensing verdict from decrypted and verified integrityToken
     string licensingVerdict = GetLicensingVerdictFromDecryptedToken(IntegrityToken);

     if (licensingVerdict == "UNLICENSED") {
       return 1; // GET_LICENSED
     }
     return 0;
   } 
   ```

   ### Unreal Engine

   ```c++
   private int GetDialogTypeCode(FString IntegrityToken) {
     // Get licensing verdict from decrypted and verified integrityToken
     FString LicensingVerdict = GetLicensingVerdictFromDecryptedToken(IntegrityToken);

     if (LicensingVerdict == "UNLICENSED") {
       return 1; // GET_LICENSED
     }
     return 0;
   } 
   ```

   ### Native

   ```c++
   private int getDialogTypeCode(string integrity_token) {
     /// Get licensing verdict from decrypted and verified integrityToken
     string licensing_verdict = getLicensingVerdictFromDecryptedToken(integrity_token);

     if (licensing_verdict == "UNLICENSED") {
       return 1; // GET_LICENSED
     }
     return 0;
   }
   ```
4. On your app, call `showDialog` with the requested code retrieved from your
   server:

   ### Kotlin

   ```kotlin
   // Show dialog as indicated by the server
   val showDialogType: Int? = yourServerResponse.integrityDialogTypeCode()
   if (showDialogType != null) {
     // Call showDialog with type code, the dialog will be shown on top of the
     // provided activity and complete when the dialog is closed.
     val integrityDialogResponseCode: Task<Int> =
     tokenResponse.showDialog(activity, showDialogType)
     // Handle response code, call the Integrity API again to confirm that
     // verdicts have been resolved.
   } 
   ```

   ### Java

   ```java
   // Show dialog as indicated by the server
   @Nullable Integer showDialogType = yourServerResponse.integrityDialogTypeCode();
   if (showDialogType != null) {
     // Call showDialog with type code, the dialog will be shown on top of the
     // provided activity and complete when the dialog is closed.
     Task<Integer> integrityDialogResponseCode =
         tokenResponse.showDialog(activity, showDialogType);
     // Handle response code, call the Integrity API again to confirm that
     // verdicts have been resolved.
   }
   ```

   ### Unity

   ```c#
   IEnumerator ShowDialogCoroutine() {
     int showDialogType = yourServerResponse.IntegrityDialogTypeCode();

     // Call showDialog with type code, the dialog will be shown on top of the
     // provided activity and complete when the dialog is closed.
     var showDialogTask = tokenResponse.ShowDialog(showDialogType);

     // Wait for PlayAsyncOperation to complete.
     yield return showDialogTask;

     // Handle response code, call the Integrity API again to confirm that
     // verdicts have been resolved.
   } 
   ```

   ### Unreal Engine

   ```c++
   // .h
   void MyClass::OnShowDialogCompleted(
     EStandardIntegrityErrorCode Error,
     EIntegrityDialogResponseCode Response)
   {
     // Handle response code, call the Integrity API again to confirm that
     // verdicts have been resolved.
   }

   // .cpp
   void MyClass::RequestIntegrityToken()
   {
     UStandardIntegrityToken* Response = ...
     int TypeCode = YourServerResponse.integrityDialogTypeCode();

     // Create a delegate to bind the callback function.
     FShowDialogStandardOperationCompletedDelegate Delegate;

     // Bind the completion handler (OnShowDialogCompleted) to the delegate.
     Delegate.BindDynamic(this, &MyClass::OnShowDialogCompleted);

     // Call ShowDialog with TypeCode which completes when the dialog is closed.
     Response->ShowDialog(TypeCode, Delegate);
   }
   ```

   ### Native

   ```c++
   // Show dialog as indicated by the server
   int show_dialog_type = yourServerResponse.integrityDialogTypeCode();
   if (show_dialog_type != 0) {
     /// Call showDialog with type code, the dialog will be shown on top of the
     /// provided activity and complete when the dialog is closed.
     StandardIntegrityErrorCode error_code =
         IntegrityTokenResponse_showDialog(response, activity, show_dialog_type);

     /// Proceed to polling iff error_code == STANDARD_INTEGRITY_NO_ERROR
     if (error_code != STANDARD_INTEGRITY_NO_ERROR)
     {
         /// Remember to call the *_destroy() functions.
         return;
     }

     /// Use polling to wait for the async operation to complete.
     /// Note, the polling shouldn't block the thread where the IntegrityManager
     /// is running.

     IntegrityDialogResponseCode* response_code;
     error_code = StandardIntegrityToken_getDialogResponseCode(response, response_code);

     if (error_code != STANDARD_INTEGRITY_NO_ERROR)
     {
         /// Remember to call the *_destroy() functions.
         return;
     }

     /// Handle response code, call the Integrity API again to confirm that
     /// verdicts have been resolved.
   }
   ```
5. The dialog is displayed on top of the provided activity. When the user has
   closed the dialog, the Task completes with a [response code](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityDialogResponseCode).

6. (Optional) Request another token to display any further dialogs. If you make
   [standard requests](https://developer.android.com/google/play/integrity/standard), you need to warm up
   the token provider again to obtain a fresh verdict.

## Request an integrity dialog to fix a client side exception

If an Integrity API request fails with a [`StandardIntegrityException`](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/StandardIntegrityException)
(Standard API) or [`IntegrityServiceException`](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/IntegrityServiceException) (Classic API) and the
exception is remediable, you can use either the [`GET_INTEGRITY`](https://developer.android.com/google/play/integrity/remediation#get-integrity-dialog) or
[`GET_STRONG_INTEGRITY`](https://developer.android.com/google/play/integrity/remediation#get-strong-integrity-dialog) dialogs to fix the error.
| **Note:** Using the new `GET_INTEGRITY` or `GET_STRONG_INTEGRITY` dialogs requires the Integrity API Android library version 1.5.0 or higher. This update provides access to the new `showDialog` method in [`IntegrityManager`](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/IntegrityManager) (Classic API) or [`StandardIntegrityManager`](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/StandardIntegrityManager) (Standard API) for resolving client-side exceptions.

The following steps outline how you can use the [`GET_INTEGRITY`](https://developer.android.com/google/play/integrity/remediation#get-integrity-dialog)
dialog to fix a remediable client side error reported by Integrity API.

1. Check that the exception returned from an Integrity API request is remediable.

   ### Kotlin

   ```kotlin
   private fun isExceptionRemediable(exception: ExecutionException): Boolean {
     val cause = exception.cause
     if (cause is StandardIntegrityException && cause.isRemediable) {
         return true
     }
     return false
   }
    
   ```

   ### Java

   ```java
   private boolean isExceptionRemediable(ExecutionException exception) {
     Throwable cause = exception.getCause();
     if (cause instanceof StandardIntegrityException integrityException
   && integrityException.isRemediable()) {
         return true;
     }
     return false;
   }
    
   ```
2. If the exception is remediable, request the `GET_INTEGRITY` dialog using the
   returned exception. The dialog will be displayed over the provided activity and
   the returned Task completes with a [response code](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityDialogResponseCode) after the user closes the
   dialog.

   ### Kotlin

   ```kotlin
   private fun showDialog(exception: StandardIntegrityException) {
     // Create a dialog request
     val standardIntegrityDialogRequest =
         StandardIntegrityDialogRequest.builder()
             .setActivity(activity)
             .setType(IntegrityDialogTypeCode.GET_INTEGRITY)
             .setStandardIntegrityResponse(ExceptionDetails(exception))
             .build()

     // Request dialog
     val responseCode: Task<Int> =
           standardIntegrityManager.showDialog(standardIntegrityDialogRequest)
   }
    
   ```

   ### Java

   ```java
   private void showDialog(StandardIntegrityException exception) {
     // Create a dialog request
     StandardIntegrityDialogRequest standardIntegrityDialogRequest =
         StandardIntegrityDialogRequest.builder()
             .setActivity(this.activity)
             .setType(IntegrityDialogTypeCode.GET_INTEGRITY)
             .setStandardIntegrityResponse(new ExceptionDetails(exception))
             .build();

     // Request dialog
     Task<Integer> responseCode =
           standardIntegrityManager.showDialog(standardIntegrityDialogRequest);
   }  
   ```
3. If the returned response code indicates a success, the next request for an
   integrity token should succeed without any exceptions. If you make standard
   requests, you need to warm up the token provider again to obtain a fresh
   verdict.

## Integrity dialog codes

### [GET_LICENSED](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityDialogTypeCode#GET_LICENSED) (Type Code 1)

#### Verdict issue

This dialog is appropriate for two issues:

- **Unauthorized access** : `appLicensingVerdict: "UNLICENSED"`. This means the user account does not have an entitlement for your app, which can happen if the user sideloaded it or acquired it from a different app store than Google Play.
- **Tampered app** : `appRecognitionVerdict: "UNRECOGNIZED_VERSION"`. This means that your app's binary has been modified or is not a version recognized by Google Play.

#### Remediation

You can show the `GET_LICENSED` dialog to prompt the user to acquire the
genuine app from Google Play. This single dialog addresses both scenarios:

- For an *unlicensed* user, it grants them a Play license. This enables the user to receive app updates from Google Play.
- For a user with a *tampered* app version, it guides them to installing the unmodified app from Google Play.

When the user completes the dialog, subsequent integrity checks return
`appLicensingVerdict: "LICENSED"` and `appRecognitionVerdict: "PLAY_RECOGNIZED"`.

#### Example UX

![](https://developer.android.com/static/images/google/play/integrity/get_licensing_ux.png) **Figure 1.** GET_LICENSED Play dialog.

### [CLOSE_UNKNOWN_ACCESS_RISK](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityDialogTypeCode#CLOSE_UNKNOWN_ACCESS_RISK) (Type Code 2)

#### Verdict issue

When `environmentDetails.appAccessRiskVerdict.appsDetected` contains
`"UNKNOWN_CAPTURING"` or `"UNKNOWN_CONTROLLING"`, it means there are other apps
(not installed by Google Play or preloaded on the system partition by the device
manufacturer) running on the device that could be capturing the screen or
controlling the device.

#### Remediation

You can show the `CLOSE_UNKNOWN_ACCESS_RISK` dialog to prompt the user to close
all unknown apps which could be capturing the screen or controlling the device.
If the user taps the `Close all` button, all such apps are closed.

#### Example UX

![](https://developer.android.com/static/images/google/play/integrity/close_unknown_access_risk_ux.png) **Figure 2.** Dialog for close unknown access risk.

### [CLOSE_ALL_ACCESS_RISK](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityDialogTypeCode#CLOSE_ALL_ACCESS_RISK) (Type Code 3)

#### Verdict issue

When `environmentDetails.appAccessRiskVerdict.appsDetected` contains any of
`"KNOWN_CAPTURING"`, `"KNOWN_CONTROLLING"`,`"UNKNOWN_CAPTURING"` or
`"UNKNOWN_CONTROLLING"`, it means there are apps running on the device that
could be capturing the screen or controlling the device.

#### Remediation

You can show the `CLOSE_ALL_ACCESS_RISK` dialog to prompt the user to close all
the apps which could be capturing the screen or controlling the device. If the
user taps the `Close all` button, all such apps are closed on the device.

#### Example UX

![](https://developer.android.com/static/images/google/play/integrity/close_unknown_access_risk_ux.png) **Figure 3.** Dialog for close all access risk.

### [GET_INTEGRITY](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityDialogTypeCode#GET_INTEGRITY) (Type Code 4)

#### Verdict issue

This dialog is appropriate for any of the following issues:

- **Weak device integrity** : When the `deviceRecognitionVerdict` does
  not contain `MEETS_DEVICE_INTEGRITY`, the device might not be a genuine and
  certified Android device. This can happen, for example, if the
  device's bootloader is unlocked or its loaded Android OS is not a certified
  manufacturer image.

- **Unauthorized access** : `appLicensingVerdict: "UNLICENSED"`. This means the
  user account does not have an entitlement for your app, which can happen if the
  user sideloaded it or acquired it from a different app store than Google Play.

- **Tampered app** : `appRecognitionVerdict: "UNRECOGNIZED_VERSION"`. This means
  that your app's binary has been modified or is not a version recognized by
  Google Play.

- **Client side exceptions** : When a remediable exception occurs during an
  Integrity API request. Remediable exceptions are Integrity API exceptions with
  error codes like
  `PLAY_SERVICES_VERSION_OUTDATED`, `NETWORK_ERROR`, `PLAY_SERVICES_NOT_FOUND`,
  etc. You can use the [`exception.isRemediable()`](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/StandardIntegrityException#isRemediable()) method to check if an
  exception is fixable by the dialog.

#### Remediation

The `GET_INTEGRITY` dialog is designed to streamline the user's experience by
handling multiple remediation steps within a single, continuous flow. This
prevents the user from having to interact with multiple separate dialogs to fix
different issues.

When you request the dialog, it automatically detects which of the targeted
verdict issues are present and provides the appropriate remediation steps.
This means a single dialog request can address multiple problems at once,
including:

- **Device integrity** : If a device integrity issue is detected, the dialog will guide the user to improve the device's security status to meet the requirements for a `MEETS_DEVICE_INTEGRITY` verdict.
- **App integrity**: If issues like unauthorized access or app tampering are detected, the dialog will direct users to acquire the app from the Play Store to fix them.

| **Note:** `GET_INTEGRITY` dialog automatically detects and tries to resolve multiple verdict issues when requested. To remediate only issues with account licensing or app integrity, we recommend using the [`GET_LICENSED`](https://developer.android.com/google/play/integrity/remediation#get-licensed-dialog) dialog.

- **Client-side exceptions**: The dialog checks for and attempts to resolve any underlying issues that caused an Integrity API exception. For example, it might prompt the user to update an outdated version of Google Play services.

#### Example UX

![](https://developer.android.com/static/images/google/play/integrity/get_integrity_network_error_ux.gif) **Figure 4.** GET_INTEGRITY dialog network error remediation flow

### [GET_STRONG_INTEGRITY](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityDialogTypeCode#GET_STRONG_INTEGRITY) (Type Code 5)

#### Verdict issue

This dialog is designed to fix all of the same issues that
[GET_INTEGRITY](https://developer.android.com/google/play/integrity/remediation#get-integrity-dialog)
addresses, with the added capability of fixing problems that prevent a device
from receiving a `MEETS_STRONG_INTEGRITY` verdict and fixing Play Protect
verdict issues.
| **Note:** [MEETS_STRONG_INTEGRITY](https://developer.android.com/google/play/integrity/verdicts#optional-device-labels) and [playProtectVerdict](https://developer.android.com/google/play/integrity/verdicts#environment-details-field) are optional verdicts and, you should only check for them if you've [opted in to receive additional labels](https://developer.android.com/google/play/integrity/setup#configure-api).

#### Remediation

`GET_STRONG_INTEGRITY` is designed to streamline the user's experience by
handling multiple remediation steps within a single, continuous flow. The dialog
automatically checks for an address applicable integrity problems, including:

- **Device integrity** : If a device integrity issue is detected, the dialog will guide the user to improve the device's security status to meet the requirements for a `MEETS_STRONG_INTEGRITY` verdict.
- **Play protect status** : If the `playProtectVerdict` indicates an issue, the
  dialog will guide the user to fix it:

  - If Play Protect is disabled (`playProtectVerdict == POSSIBLE_RISK`), the dialog will prompt the user to enable it and perform a scan of all apps on the device.
  - If harmful apps are detected (`playProtectVerdict == MEDIUM_RISK` or `HIGH_RISK`), the dialog will direct the user to uninstall them using Google Play Protect.
- **App integrity**: If issues like unauthorized access or app tampering are
  detected, the dialog will prompt the user to acquire the app from the Play Store
  to fix the problem.

- **Client-side exceptions** : The dialog also attempts to resolve any underlying
  issues that caused an Integrity API exception. For example, it may prompt the
  user to enable Google Play services if it's found to be disabled. Remediable
  exceptions are Integrity API exceptions with error codes like
  `PLAY_SERVICES_VERSION_OUTDATED`,
  `NETWORK_ERROR`, or
  `PLAY_SERVICES_NOT_FOUND`.
  You can use the [`exception.isRemediable()`](https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/StandardIntegrityException#isRemediable()) method to check if an error is
  fixable by the dialog.

#### Example UX

![](https://developer.android.com/static/images/google/play/integrity/get_strong_integrity_update_play_services_ux.gif) **Figure 5.** GET_STRONG_INTEGRITY dialog updating Play services.