На этой странице описано, как реагировать на решения суда по вопросам этики.
После запроса токена целостности у вас есть возможность отобразить пользователю диалоговое окно Google Play. Вы можете отобразить это диалоговое окно, если возникнет одна или несколько проблем с вердиктом целостности или если во время запроса к API целостности произойдет исключение. После закрытия диалогового окна вы можете убедиться, что проблема решена, отправив еще один запрос на получение токена целостности. При выполнении стандартных запросов необходимо повторно активировать поставщика токенов, чтобы получить новый вердикт.
Запросить диалог проверки целостности данных для исправления ошибки в вердикте.
Когда клиент запрашивает токен целостности, вы можете использовать метод, предлагаемый в StandardIntegrityToken (стандартный API) и IntegrityTokenResponse (классический API): showDialog(Activity activity, int integrityDialogTypeCode) .
Следующие шаги описывают, как использовать API Play Integrity для отображения диалогового окна исправления с помощью кода диалога GET_LICENSED . Другие коды диалогов, которые может запрашивать ваше приложение, перечислены после этого раздела.
Запросите токен целостности у своего приложения и отправьте его на свой сервер. Вы можете использовать стандартный или классический запрос.
Котлин
// 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());
Единство
// 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);
Родной
/// 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));
На вашем сервере расшифруйте токен целостности и проверьте поле
appLicensingVerdict. Оно может выглядеть примерно так:// Licensing issue { ... "accountDetails": { "appLicensingVerdict": "UNLICENSED" } }
Если токен содержит
appLicensingVerdict: "UNLICENSED", отправьте ответ клиенту вашего приложения, запросив отображение диалога лицензирования:Котлин
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; }
Единство
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; }
Родной
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; }
В вашем приложении вызовите
showDialog, используя запрошенный код, полученный с вашего сервера:Котлин
// Show dialog as indicated by the server val showDialogType: Int? = yourServerResponse.integrityDialogTypeCode() if (showDialogType == null) { return } // Create dialog request val dialogRequest = StandardIntegrityDialogRequest.builder() .setActivity(activity) .setTypeCode(showDialogType) .setStandardIntegrityResponse(StandardIntegrityResponse.TokenResponse(token)) .build() // Call showDialog, the dialog will be shown on top of the provided activity // and the task will complete when the dialog is closed. val result: Task<Int> = standardIntegrityManager.showDialog(dialogRequest) // Handle response code, call the Integrity API again to confirm that the // verdict issue has been resolved.
Java
// Show dialog as indicated by the server @Nullable Integer showDialogType = yourServerResponse.integrityDialogTypeCode(); if (showDialogType == null) { return; } // Create dialog request StandardIntegrityDialogRequest dialogRequest = StandardIntegrityDialogRequest.builder() .setActivity(getActivity()) .setTypeCode(showDialogTypeCode) .setStandardIntegrityResponse(new StandardIntegrityResponse.TokenResponse(token)) .build(); // Call showDialog, the dialog will be shown on top of the provided activity // and the task will complete when the dialog is closed. Task<Integer> result = standardIntegrityManager.showDialog(dialogRequest); // Handle response code, call the Integrity API again to confirm that the // verdict issue has been resolved.
Единство
// Initialize the V2 manager. Requires Play Integrity Unity Plugin v2.0.0 or // higher. var standardIntegrityManager = new StandardIntegrityManagerV2(); IEnumerator ShowDialogCoroutine() { int showDialogType = yourServerResponse.IntegrityDialogTypeCode(); PlayAsyncOperation<IntegrityDialogResponseCode, StandardIntegrityError> showDialogTask; using (var activity = UnityPlayerHelper.GetCurrentActivity()) { // Create a dialog request var dialogRequest = new StandardIntegrityDialogRequest(tokenResponse, activity, showDialogType); // Call showDialog with the request, the dialog will be shown on top of the // provided activity and complete when the dialog is closed. showDialogTask = standardIntegrityManager.ShowDialog(dialogRequest); } // Wait for PlayAsyncOperation to complete. yield return showDialogTask; // Handle response code, call the Integrity API again to confirm that the // verdict issue been resolved. }
Unreal Engine
// .h void MyClass::OnShowDialogCompleted( EStandardIntegrityErrorCode Error, EIntegrityDialogResponseCode Response) { // Handle response code, call the Integrity API again to confirm that the // verdict issue has 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); }
Родной
// Show dialog as indicated by the server int show_dialog_type = yourServerResponse.integrityDialogTypeCode(); if(show_dialog_type == 0){ return; } /// Create dialog request StandardIntegrityDialogRequest* dialog_request; StandardIntegrityDialogRequest_create(&dialog_request); StandardIntegrityDialogRequest_setTypeCode(dialog_request, show_dialog_type); StandardIntegrityDialogRequest_setActivity(dialog_request, activity); StandardIntegrityDialogRequest_setStandardIntegrityToken(dialog_request, token_response); /// Call showDialog with the dialog request. The dialog will be shown on top /// of the provided activity and complete when the dialog is closed by the /// user. StandardIntegrityDialogResponse* dialog_response; StandardIntegrityErrorCode error_code = StandardIntegrityManager_showDialog(dialog_request, &dialog_response); /// Use polling to wait for the async operation to complete. Note, the polling /// shouldn't block the thread where the StandardIntegrityManager is running. IntegrityDialogResponseCode response_code = INTEGRITY_DIALOG_RESPONSE_UNKNOWN; while (error_code == STANDARD_INTEGRITY_NO_ERROR) { error_code = StandardIntegrityDialogResponse_getResponseCode(dialog_response, &response_code); if(response_code != INTEGRITY_DIALOG_RESPONSE_UNKNOWN){ break; } } /// Free memory StandardIntegrityDialogRequest_destroy(dialog_request); StandardIntegrityDialogResponse_destroy(dialog_response); /// Handle response code, call the Integrity API again to confirm that the /// verdict issues have been resolved.
Диалоговое окно отображается поверх предоставленного действия. После закрытия пользователем диалогового окна задача завершается с кодом ответа .
(Необязательно) Запросите еще один токен для отображения дальнейших диалогов. При выполнении стандартных запросов необходимо повторно активировать поставщика токенов для получения нового результата.
Запросить диалог проверки целостности данных для устранения ошибки на стороне клиента.
Если запрос к Integrity API завершается с ошибкой StandardIntegrityException (стандартный API) или IntegrityServiceException (классический API), и эту ошибку можно исправить, вы можете использовать диалоговые окна GET_INTEGRITY или GET_STRONG_INTEGRITY для устранения ошибки.
Следующие шаги описывают, как использовать диалоговое окно GET_INTEGRITY для исправления ошибки на стороне клиента, о которой сообщает API Integrity.
Убедитесь, что исключение, возвращаемое запросом к Integrity API, подлежит устранению.
Котлин
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; }
Единство
private bool IsExceptionRemediable(StandardIntegrityError error) { // Check if the operation resulted in a remediable error if (error != null && error.ErrorCode != StandardIntegrityErrorCode.NoError && error.IsRemediable) { return true; } return false; }
Родной
bool IsErrorRemediable(StandardIntegrityToken* token) { /// Check if the error associated with the token is remediable bool isRemediable = false; if(StandardIntegrityToken_getIsRemediable(response, &isRemediable) == STANDARD_INTEGRITY_NO_ERROR){ return isRemediable; } return false; }
Если исключение устранимо, запросите диалоговое окно
GET_INTEGRITY, используя возвращенное исключение. Диалоговое окно отобразится поверх указанного действия, и возвращенная задача завершится с кодом ответа после того, как пользователь закроет диалоговое окно.Котлин
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); }
Единство
// Initialize the V2 manager. Requires Play Integrity Unity Plugin v2.0.0 or // higher. var standardIntegrityManager = new StandardIntegrityManagerV2(); IEnumerator ShowDialogToFixError(StandardIntegrityError error) { PlayAsyncOperation<IntegrityDialogResponseCode, StandardIntegrityError> showDialogTask; using (var activity = UnityPlayerHelper.GetCurrentActivity()) { // Create a dialog request using the error var dialogRequest = new StandardIntegrityDialogRequest(error, activity, GET_INTEGRITY_DIALOG); // Request dialog showDialogTask = standardIntegrityManager.ShowDialog(dialogRequest); } // Wait for PlayAsyncOperation to complete. yield return showDialogTask; }
Родной
private void showDialogToFixError(StandardIntegrityToken* token) { /// If the token request failed, and the underlying error is not fixable /// then return early if(isErrorRemediable(token)) { return; } /// Create dialog request StandardIntegrityDialogRequest* dialog_request; StandardIntegrityDialogRequest_create(&dialog_request); StandardIntegrityDialogRequest_setTypeCode(dialog_request, kGetIntegrityDialogTypeCode); StandardIntegrityDialogRequest_setActivity(dialog_request, activity); StandardIntegrityDialogRequest_setStandardIntegrityToken(dialog_request, token_response); /// Call showDialog with the dialog request. The dialog will be shown on /// top of the provided activity and complete when the dialog is closed by /// the user. StandardIntegrityDialogResponse* dialog_response; StandardIntegrityErrorCode error_code = StandardIntegrityManager_showDialog(dialog_request, &dialog_response); /// Use polling to wait for the async operation to complete. /// Note, the polling shouldn't block the thread where the /// StandardIntegrityManager is running. IntegrityDialogResponseCode response_code = INTEGRITY_DIALOG_RESPONSE_UNKNOWN; while (error_code == STANDARD_INTEGRITY_NO_ERROR) { error_code = StandardIntegrityDialogResponse_getResponseCode(response, &response_code); if(response_code != INTEGRITY_DIALOG_RESPONSE_UNKNOWN){ break; } } /// Free memory StandardIntegrityDialogRequest_destroy(dialog_request); StandardIntegrityDialogResponse_destroy(dialog_response); }
Если полученный код ответа указывает на успех, следующий запрос на получение токена целостности должен пройти успешно без каких-либо исключений. При выполнении стандартных запросов необходимо повторно активировать поставщика токенов для получения нового результата.
Коды диалога целостности
GET_LICENSED (Код типа 1)
Вопрос о вынесении вердикта
Данный диалог уместен для обсуждения двух вопросов:
- Несанкционированный доступ :
appLicensingVerdict: "UNLICENSED". Это означает, что у учетной записи пользователя нет прав на использование вашего приложения, что может произойти, если пользователь установил его из стороннего источника или приобрел его в другом магазине приложений, отличном от Google Play. - Приложение изменено :
appRecognitionVerdict: "UNRECOGNIZED_VERSION". Это означает, что исполняемый файл вашего приложения был изменен или не является версией, распознаваемой Google Play.
Очистка
Вы можете отобразить диалоговое окно GET_LICENSED , чтобы предложить пользователю загрузить подлинное приложение из Google Play. Это единственное диалоговое окно решает обе задачи:
- Для пользователей без лицензии это предоставляет лицензию Play. Это позволяет пользователю получать обновления приложения из Google Play.
- Пользователю, у которого установлена модифицированная версия приложения, предлагается установить неизмененное приложение из Google Play.
После завершения диалога пользователем последующие проверки целостности возвращают значения appLicensingVerdict: "LICENSED" и appRecognitionVerdict: "PLAY_RECOGNIZED" .
Пример UX

CLOSE_UNKNOWN_ACCESS_RISK (Код типа 2)
Вопрос о вынесении вердикта
Если в environmentDetails.appAccessRiskVerdict.appsDetected содержится "UNKNOWN_CAPTURING" или "UNKNOWN_CONTROLLING" , это означает, что на устройстве работают другие приложения (не установленные Google Play или предустановленные производителем устройства в системном разделе), которые могут захватывать экран или управлять устройством.
Очистка
Вы можете отобразить диалоговое окно CLOSE_UNKNOWN_ACCESS_RISK , чтобы предложить пользователю закрыть все неизвестные приложения, которые могут делать снимки экрана или управлять устройством. Если пользователь нажмет кнопку Close all , все такие приложения будут закрыты.
Пример UX

CLOSE_ALL_ACCESS_RISK (Код типа 3)
Вопрос о вынесении вердикта
Если в environmentDetails.appAccessRiskVerdict.appsDetected содержится любое из значений "KNOWN_CAPTURING" , "KNOWN_CONTROLLING" , "UNKNOWN_CAPTURING" или "UNKNOWN_CONTROLLING" , это означает, что на устройстве запущены приложения, которые могут захватывать экран или управлять устройством.
Очистка
Вы можете отобразить диалоговое окно CLOSE_ALL_ACCESS_RISK , чтобы предложить пользователю закрыть все приложения, которые могут делать снимки экрана или управлять устройством. Если пользователь нажмет кнопку Close all , все такие приложения будут закрыты на устройстве.
Пример UX

GET_INTEGRITY (Код типа 4)
Вопрос о вынесении вердикта
Данный диалог подходит для обсуждения следующих вопросов:
Слабая целостность устройства : Если
deviceRecognitionVerdictне содержитMEETS_DEVICE_INTEGRITY, устройство может не являться подлинным и сертифицированным устройством Android. Это может произойти, например, если загрузчик устройства разблокирован или загруженная ОС Android не является сертифицированным образом производителя.Несанкционированный доступ :
appLicensingVerdict: "UNLICENSED". Это означает, что у учетной записи пользователя нет прав на использование вашего приложения, что может произойти, если пользователь установил его из стороннего источника или приобрел его в другом магазине приложений, отличном от Google Play.Приложение изменено :
appRecognitionVerdict: "UNRECOGNIZED_VERSION". Это означает, что исполняемый файл вашего приложения был изменен или не является версией, распознаваемой Google Play.Исключения на стороне клиента : Когда во время запроса к Integrity API возникает исправимое исключение. Исправленные исключения — это исключения Integrity API с кодами ошибок, такими как
PLAY_SERVICES_VERSION_OUTDATED,NETWORK_ERROR,PLAY_SERVICES_NOT_FOUNDи т. д. Вы можете использовать методexception.isRemediable()для проверки того, можно ли исправить исключение с помощью диалогового окна.
Очистка
Диалоговое окно GET_INTEGRITY разработано для упрощения работы пользователя за счет обработки нескольких этапов устранения неполадок в рамках единого непрерывного процесса. Это избавляет пользователя от необходимости взаимодействовать с несколькими отдельными диалоговыми окнами для решения различных проблем.
При запросе диалога система автоматически определяет, какие из целевых проблем, указанных в вердикте, присутствуют, и предоставляет соответствующие шаги по их устранению. Это означает, что один запрос диалога может решить несколько проблем одновременно, включая:
- Целостность устройства : Если обнаружена проблема с целостностью устройства, диалоговое окно предложит пользователю улучшить состояние безопасности устройства, чтобы оно соответствовало требованиям для получения вердикта
MEETS_DEVICE_INTEGRITY. - Проверка целостности приложения : если будут обнаружены такие проблемы, как несанкционированный доступ или изменение приложения, диалоговое окно предложит пользователям загрузить приложение из Play Store для их устранения.
- Исключения на стороне клиента : диалоговое окно проверяет и пытается устранить любые скрытые проблемы, вызвавшие исключение Integrity API. Например, оно может предложить пользователю обновить устаревшую версию сервисов Google Play.
Пример UX

GET_STRONG_INTEGRITY (Код типа 5)
Вопрос о вынесении вердикта
Это диалоговое окно предназначено для устранения всех тех же проблем, что и GET_INTEGRITY , с дополнительной возможностью исправления проблем, препятствующих получению устройством вердикта MEETS_STRONG_INTEGRITY , а также исправления проблем с вердиктами Play Protect.
Очистка
GET_STRONG_INTEGRITY разработан для упрощения работы пользователя за счет обработки нескольких этапов устранения неполадок в рамках единого непрерывного процесса. Диалоговое окно автоматически проверяет наличие и устраняет применимые проблемы целостности, включая:
- Целостность устройства : Если обнаружена проблема с целостностью устройства, диалоговое окно предложит пользователю улучшить состояние безопасности устройства, чтобы оно соответствовало требованиям для получения вердикта
MEETS_STRONG_INTEGRITY. Статус защиты воспроизведения : Если
playProtectVerdictуказывает на проблему, диалоговое окно подскажет пользователю, как ее исправить:- Если функция Play Protect отключена (
playProtectVerdict == POSSIBLE_RISK), в диалоговом окне пользователю будет предложено включить её и выполнить сканирование всех приложений на устройстве. - Если будут обнаружены вредоносные приложения (
playProtectVerdict == MEDIUM_RISKилиHIGH_RISK), в диалоговом окне пользователю будет предложено удалить их с помощью Google Play Protect.
- Если функция Play Protect отключена (
Проверка целостности приложения : если будут обнаружены такие проблемы, как несанкционированный доступ или изменение приложения, в диалоговом окне пользователю будет предложено загрузить приложение из Play Store для устранения проблемы.
Исключения на стороне клиента : Диалоговое окно также пытается устранить любые основные проблемы, вызвавшие исключение Integrity API. Например, оно может предложить пользователю включить сервисы Google Play, если они отключены. Исправимые исключения — это исключения Integrity API с кодами ошибок, такими как
PLAY_SERVICES_VERSION_OUTDATED,NETWORK_ERRORилиPLAY_SERVICES_NOT_FOUND. Вы можете использовать методexception.isRemediable(), чтобы проверить, можно ли исправить ошибку с помощью диалогового окна.
Пример UX
