En esta página, se describe cómo abordar problemas relacionados con los veredictos de integridad.
Después de solicitar 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 o si se produjo una excepción durante una solicitud a la API de Integrity. Una vez que se cierra el diálogo, puedes verificar que el problema se haya solucionado con otra solicitud de token de integridad. Si realizas solicitudes estándar, debes volver a preparar el proveedor de tokens para obtener un veredicto nuevo.
Cómo solicitar un diálogo de integridad para corregir un problema de veredicto
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 de diálogo GET_LICENSED
.
Después de esta sección, se enumeran otros códigos de diálogo que tu app puede solicitar.
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));
En tu servidor, desencripta el token de integridad y verifica el campo
appLicensingVerdict
. Tal vez se vería así:// Licensing issue { ... "accountDetails": { "appLicensingVerdict": "UNLICENSED" } }
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; }
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. }
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.
(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ómo solicitar un diálogo de integridad para corregir una excepción del cliente
Si una solicitud a la API de Integrity falla con un StandardIntegrityException
(API estándar) o IntegrityServiceException
(API clásica) y la excepción se puede corregir, puedes usar los diálogos GET_INTEGRITY
o GET_STRONG_INTEGRITY
para corregir el error.
En los siguientes pasos, se describe cómo puedes usar el diálogo GET_INTEGRITY
para corregir un error del cliente que se puede solucionar y que informó la API de Integrity.
Comprueba que la excepción que se devolvió de una solicitud a la API de Integrity se pueda corregir.
Kotlin
private fun isExceptionRemediable(exception: ExecutionException): Boolean { val cause = exception.cause if (cause is StandardIntegrityException && cause.isRemediable) { return true } return false }
Java
private boolean isExceptionRemediable(ExecutionException exception) { Throwable cause = exception.getCause(); if (cause instanceof StandardIntegrityException integrityException && integrityException.isRemediable()) { return true; } return false; }
Si la excepción se puede corregir, solicita el diálogo
GET_INTEGRITY
con la excepción devuelta. El diálogo se mostrará sobre la actividad proporcionada y la tarea devuelta se completará con un código de respuesta después de que el usuario cierre el diálogo.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
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); }
Si el código de respuesta devuelto indica que la operación se realizó correctamente, la siguiente solicitud de un token de integridad debería completarse sin excepciones. 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

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 otras apps (que Google Play no instaló o que el fabricante del dispositivo no precargó en la partición del sistema) que se ejecutan en el dispositivo y que podrían capturar la pantalla o controlar 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 ese tipo.
UX de ejemplo

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 controlar el dispositivo.
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

GET_INTEGRITY (código de tipo 4)
Problema con el veredicto
Este diálogo es adecuado para cualquiera de los siguientes problemas:
Integridad del dispositivo débil: Cuando
deviceRecognitionVerdict
no contieneMEETS_DEVICE_INTEGRITY
, es posible que el dispositivo no sea un dispositivo genuino con tecnología de Android y certificación de Play Protect. Esto puede suceder, por ejemplo, si el bootloader del dispositivo está desbloqueado o si el SO Android cargado no es una imagen certificada del fabricante.Acceso no autorizado:
appLicensingVerdict: "UNLICENSED"
. Esto significa que la cuenta de usuario no tiene derecho a tu app, lo que puede ocurrir si el usuario la transfirió de forma lateral o la adquirió en una tienda de aplicaciones diferente de 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.Excepciones del cliente: Se producen cuando ocurre una excepción remediable durante una solicitud a la API de Integrity. Las excepciones subsanables son excepciones de la API de Integrity con códigos de error como
PLAY_SERVICES_VERSION_OUTDATED
,NETWORK_ERROR
,PLAY_SERVICES_NOT_FOUND
, etcétera. Puedes usar el métodoexception.isRemediable()
para verificar si el diálogo puede corregir una excepción.
Solución
El diálogo de GET_INTEGRITY
está diseñado para optimizar la experiencia del usuario controlando varios pasos de corrección en un solo flujo continuo. Esto evita que el usuario tenga que interactuar con varios diálogos separados para solucionar diferentes problemas.
Cuando solicitas el diálogo, se detecta automáticamente cuáles de los problemas de veredicto objetivo están presentes y se proporcionan los pasos de corrección adecuados. Esto significa que una sola solicitud de diálogo puede abordar varios problemas a la vez, incluidos los siguientes:
- Integridad del dispositivo: Si se detecta un problema de integridad del dispositivo, el diálogo guiará al usuario para que mejore el estado de seguridad del dispositivo y cumpla con los requisitos para obtener un veredicto de
MEETS_DEVICE_INTEGRITY
. - Integridad de la app: Si se detectan problemas como acceso no autorizado o manipulación de la app, el diálogo dirigirá a los usuarios a adquirir la app desde Play Store para corregirlos.
- Excepciones del cliente: El diálogo verifica y trata de resolver cualquier problema subyacente que haya causado una excepción de la API de Integrity. Por ejemplo, podría solicitarle al usuario que actualice una versión desactualizada de los Servicios de Google Play.
UX de ejemplo

GET_STRONG_INTEGRITY (código de tipo 5)
Problema con el veredicto
Este diálogo está diseñado para corregir todos los problemas que aborda GET_INTEGRITY, con la capacidad adicional de solucionar problemas que impiden que un dispositivo reciba un veredicto de MEETS_STRONG_INTEGRITY
y solucionar problemas de veredictos de Play Protect.
Solución
GET_STRONG_INTEGRITY
está diseñado para optimizar la experiencia del usuario, ya que controla varios pasos de corrección en un solo flujo continuo. El diálogo automáticamente verifica si hay problemas de integridad aplicables a una dirección, incluidos los siguientes:
- Integridad del dispositivo: Si se detecta un problema de integridad del dispositivo, el diálogo guiará al usuario para que mejore el estado de seguridad del dispositivo y cumpla con los requisitos para obtener un veredicto de
MEETS_STRONG_INTEGRITY
. Estado de Play Protect: Si el
playProtectVerdict
indica un problema, el diálogo guiará al usuario para que lo solucione:- Si Play Protect está inhabilitado (
playProtectVerdict == POSSIBLE_RISK
), el diálogo le pedirá al usuario que lo habilite y que realice un análisis de todas las apps del dispositivo. - Si se detectan apps dañinas (
playProtectVerdict == MEDIUM_RISK
oHIGH_RISK
), el diálogo dirigirá al usuario para que las desinstale con Google Play Protect.
- Si Play Protect está inhabilitado (
Integridad de la app: Si se detectan problemas como acceso no autorizado o manipulación de la app, el diálogo le solicitará al usuario que adquiera la app desde Play Store para solucionar el problema.
Excepciones del cliente: El diálogo también intenta resolver cualquier problema subyacente que haya causado una excepción de la API de Integrity. Por ejemplo, puede solicitarle al usuario que habilite los Servicios de Google Play si se detecta que están inhabilitados. Las excepciones corregibles son excepciones de la API de Integrity con códigos de error como
PLAY_SERVICES_VERSION_OUTDATED
,NETWORK_ERROR
oPLAY_SERVICES_NOT_FOUND
. Puedes usar el métodoexception.isRemediable()
para verificar si el diálogo puede corregir un error.
UX de ejemplo
