Diálogos de corrección

En esta página, se describe cómo abordar problemas relacionados con los veredictos de integridad.

Cuando se solicita un token de integridad, tienes la opción de mostrarle al usuario un diálogo de Google Play. Puedes mostrar el diálogo cuando haya uno o más problemas con el veredicto de integridad. El diálogo se muestra en la parte superior de la app y les pide a los usuarios que resuelvan la causa del problema. Una vez que se cierra el cuadro de diálogo, puedes verificar que el problema se haya solucionado con otra solicitud a la API de Integrity.

Cómo solicitar un diálogo de integridad

Cuando el cliente solicite un token de integridad, puedes usar el método que se ofrece en StandardIntegrityToken (API estándar) y en IntegrityTokenResponse (API clásica): showDialog(Activity activity, int integrityDialogTypeCode).

En los siguientes pasos, se describe cómo puedes usar la API de Play Integrity para mostrar un diálogo de corrección con el código del diálogo GET_LICENSED. Después de esta sección, se enumeran otros códigos de diálogo que tu app puede solicitar.

  1. Solicita un token de integridad a tu app y envíalo a tu servidor. Puedes usar la solicitud estándar o clásica.

    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

    // 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

    // 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

    // 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); 

    Nativo

    /// 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. En tu servidor, desencripta el token de integridad y verifica el campo appLicensingVerdict. Tal vez se vería así:

    // Licensing issue
    {
      ...
      accountDetails: {
          appLicensingVerdict: "UNLICENSED"
      }
    }
  3. Si el token contiene appLicensingVerdict: "UNLICENSED", responde al cliente de tu app y solicítale que muestre el diálogo de licencia:

    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

    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

    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

    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;
    } 

    Nativo

    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. En tu app, llama a showDialog con el código solicitado recuperado de tu servidor:

    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

    // 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

    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

    // .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);
    }

    Nativo

    // 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. El diálogo se muestra en la parte superior de la actividad proporcionada. Cuando el usuario cierra el diálogo, la tarea se completa con un código de respuesta.

  6. (Opcional) Solicita otro token para mostrar otros diálogos. Si realizas solicitudes estándar, debes volver a preparar el proveedor de tokens para obtener un veredicto nuevo.

Códigos de diálogo de integridad

GET_LICENSED (código de tipo 1)

Problema con el veredicto

Este diálogo es adecuado para dos problemas:

  • Acceso no autorizado: appLicensingVerdict: "UNLICENSED". Esto significa que la cuenta del usuario no tiene derecho a tu app, lo que puede suceder si el usuario la transfirió desde una fuente desconocida o la adquirió en una tienda de aplicaciones diferente a Google Play.
  • App alterada: appRecognitionVerdict: "UNRECOGNIZED_VERSION". Esto significa que el objeto binario de tu app se modificó o no es una versión que Google Play reconoce.

Solución

Puedes mostrar el diálogo GET_LICENSED para solicitarle al usuario que adquiera la app genuina desde Google Play. Este único diálogo aborda ambas situaciones:

  • En el caso de un usuario sin licencia, le otorga una licencia de Play. Esto permite que el usuario reciba actualizaciones de la app desde Google Play.
  • En el caso de un usuario con una versión de la app manipulada, se lo guía para que instale la app sin modificar desde Google Play.

Cuando el usuario completa el diálogo, las verificaciones de integridad posteriores devuelven appLicensingVerdict: "LICENSED" y appRecognitionVerdict: "PLAY_RECOGNIZED".

UX de ejemplo

Figura 1: Diálogo de Play GET_LICENSED.

CLOSE_UNKNOWN_ACCESS_RISK (código de tipo 2)

Problema con el veredicto

Cuando environmentDetails.appAccessRiskVerdict.appsDetected contiene "UNKNOWN_CAPTURING" o "UNKNOWN_CONTROLLING", significa que hay apps desconocidas ejecutándose en el dispositivo que podrían estar capturando la pantalla o controlando el dispositivo.

Solución

Puedes mostrar el diálogo CLOSE_UNKNOWN_ACCESS_RISK para solicitarle al usuario que cierre todas las apps desconocidas que podrían estar capturando la pantalla o controlando el dispositivo. Si el usuario presiona el botón Close all, se cierran todas las apps de este tipo.

UX de ejemplo

Figura 2: Es un diálogo para cerrar el riesgo de acceso desconocido.

CLOSE_ALL_ACCESS_RISK (código de tipo 3)

Problema con el veredicto

Cuando environmentDetails.appAccessRiskVerdict.appsDetected contiene alguno de los siguientes elementos: "KNOWN_CAPTURING", "KNOWN_CONTROLLING","UNKNOWN_CAPTURING" o "UNKNOWN_CONTROLLING", significa que hay apps en ejecución en el dispositivo que podrían capturar la pantalla o controlarlo.

Solución

Puedes mostrar el diálogo CLOSE_ALL_ACCESS_RISK para solicitarle al usuario que cierre todas las apps que podrían estar capturando la pantalla o controlando el dispositivo. Si el usuario presiona el botón Close all, se cierran todas las apps de este tipo en el dispositivo.

UX de ejemplo

Figura 3: Diálogo para cerrar todo el riesgo de acceso.