Диалоговые окна для устранения неполадок

На этой странице описано, как устранять проблемы, обнаруженные с помощью оценок целостности.

После запроса токена целостности вы можете вывести диалоговое окно Google Play, Вы можете показывать диалоговое окно, если оценка целостности указывает на наличие одной или нескольких проблем или если во время запроса к Integrity API возникло исключение. После закрытия окна вы можете отправить ещё один запрос токена целостности, чтобы проверить, была ли исправлена проблема. Если вы отправляете стандартные запросы, вам нужно снова выполнить прогрев поставщика токенов, чтобы получить новую оценку.

Как запросить показ диалогового окна для устранения проблемы с оценкой целостности

Когда клиент запрашивает токен целостности, вы можете использовать метод, предлагаемый в StandardIntegrityToken (стандартный API) и IntegrityTokenResponse (классический API): showDialog(Activity activity, int integrityDialogTypeCode).

Ниже описано, как с помощью Play Integrity API и кода GET_LICENSED показывать диалоговое окно. Другие коды диалоговых окон, которые может запрашивать приложение, перечислены в следующем разделе.

  1. Запросите токен целостности из приложения и отправьте его на сервер. Вы можете использовать стандартный или классический запрос.

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

    Нативный код

    /// 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. Расшифруйте токен целостности на сервере и проверьте поле appLicensingVerdict. Вот как это может выглядеть:

    // Licensing issue
    {
        ...
        "accountDetails": {
            "appLicensingVerdict": "UNLICENSED"
        }
    }
    
  3. Если токен содержит appLicensingVerdict: "UNLICENSED", отправьте ответ клиенту приложения с запросом на показ диалогового окна лицензирования:

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

    Нативный код

    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. В приложении вызовите showDialog с кодом, полученным от сервера:

    Kotlin

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

    Unity

    // 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.
    
  5. Диалоговое окно показывается поверх указанного действия. Когда пользователь закроет диалоговое окно, задача завершится с кодом ответа.

  6. При необходимости запросите другой токен, чтобы показать другие диалоговые окна. Если вы используете стандартные запросы, то для получения новой оценки вам нужно снова подготовить поставщика токенов.

Запросите диалоговое окно Integrity API, чтобы исправить исключение на стороне клиента

Если запрос к Integrity API не удается выполнить и возвращается ошибка StandardIntegrityException (Standard API) или IntegrityServiceException (Classic API), которую можно устранить, вы можете использовать диалоговое окно GET_INTEGRITY или GET_STRONG_INTEGRITY.

Ниже описано, как использовать диалоговое окно GET_INTEGRITY, чтобы устранить ошибку на стороне клиента, о которой сообщает Integrity API.

  1. Убедитесь, что исключение, возвращенное из запроса Integrity API, можно исправить.

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

    Unity

    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;
    }
    
  2. Если исключение можно устранить, запросите диалоговое окно GET_INTEGRITY, используя возвращенное исключение. Диалоговое окно будет показано поверх указанного действия, а возвращенная задача завершится с кодом ответа после того, как пользователь закроет диалоговое окно.

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

    Unity

    // 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);
    
    }
    
  3. Если возвращенный код ответа указывает на успешное выполнение, следующий запрос токена целостности должен пройти без исключений. Если вы отправляете стандартные запросы, вам нужно снова подготовить поставщика токенов, чтобы получить новую оценку.

Коды диалоговых окон Integrity API

GET_LICENSED (код типа 1)

Проблема с оценкой

Это диалоговое окно подходит для двух проблем:

  • Несанкционированный доступ. appLicensingVerdict: "UNLICENSED". Это означает, что у аккаунта пользователя нет права на ваше приложение. Такое может произойти, если пользователь установил его из неизвестного источника или приобрел в другом магазине приложений, а не в Google Play.
  • Приложение, в которое были внесены изменения: appRecognitionVerdict: "UNRECOGNIZED_VERSION". Это означает, что исполняемый файл приложения был изменен или его версия не подтверждена Google Play.

Как устранить проблему

Вы можете показать диалоговое окно GET_LICENSED, чтобы предложить пользователю скачать подлинное приложение из Google Play. В этом диалоговом окне рассматриваются оба сценария:

  • Если у пользователя нет лицензии, он получит лицензию Google Play. Это позволяет пользователю получать обновления приложений из Google Play.
  • Если у пользователя установлена измененная версия приложения, ему будет предложено установить оригинальную версию из Google Play.

Когда пользователь выполнит действия в диалоговом окне, последующие проверки целостности будут возвращать appLicensingVerdict: "LICENSED" и appRecognitionVerdict: "PLAY_RECOGNIZED".

Пример UX

Рисунок 1. Диалоговое окно GET_LICENSED в Google Play.

CLOSE_UNKNOWN_ACCESS_RISK (код типа 2)

Проблема с оценкой

Если в поле environmentDetails.appAccessRiskVerdict.appsDetected указано значение "UNKNOWN_CAPTURING" или "UNKNOWN_CONTROLLING", значит на устройстве запущены другие приложения, которые могут делать снимки экрана или управлять устройством. Эти приложения не были установлены через Google Play или предварительно загружены в системный раздел производителем устройства.

Как устранить проблему

Вы можете показать диалоговое окно CLOSE_UNKNOWN_ACCESS_RISK, чтобы предложить пользователю закрыть все неизвестные приложения, которые могут записывать экран или управлять устройством. Если пользователь нажмет кнопку Close all, все такие приложения будут закрыты.

Пример UX

Рисунок 2. Диалоговое окно для закрытия неизвестного риска доступа.

CLOSE_ALL_ACCESS_RISK (код типа 3)

Проблема с оценкой

Если environmentDetails.appAccessRiskVerdict.appsDetected содержит "KNOWN_CAPTURING", "KNOWN_CONTROLLING", "UNKNOWN_CAPTURING" или "UNKNOWN_CONTROLLING", значит на устройстве запущены приложения, которые могут записывать экран или управлять устройством.

Как устранить проблему

Вы можете показать диалоговое окно CLOSE_ALL_ACCESS_RISK, чтобы предложить пользователю закрыть все приложения, которые могут записывать экран или управлять устройством. Если пользователь нажмет кнопку Close all, все такие приложения будут закрыты.

Пример UX

Рисунок 3. Диалоговое окно для закрытия всех рисков доступа.

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.
  • Целостность приложения. Если обнаружены проблемы, например несанкционированный доступ или вмешательство в работу приложения, пользователям будет предложено скачать приложение из Google Play, чтобы устранить неполадки.
  • Исключения на стороне клиента. Диалоговое окно проверяет и пытается устранить любые основные проблемы, которые вызвали исключение Integrity API. Например, пользователю может быть предложено обновить устаревшую версию сервисов Google Play.

Пример UX

Рисунок 4. GET_INTEGRITY dialog network error remediation flow

GET_STRONG_INTEGRITY (код типа 5)

Проблема с оценкой

Это диалоговое окно предназначено для устранения всех проблем, которые решает GET_INTEGRITY, а также неполадок, из-за которых устройство не может получить оценку MEETS_STRONG_INTEGRITY, и проблем с оценкой Play Защиты.

Как устранить проблему

GET_STRONG_INTEGRITY позволяет упростить работу пользователя, объединяя несколько шагов по устранению неполадок в один непрерывный процесс. В диалоговом окне автоматически проверяются проблемы с целостностью адреса, в том числе:

  • Целостность устройства. Если обнаружена проблема с целостностью устройства, в диалоговом окне пользователю будет предложено повысить уровень безопасности устройства, чтобы оно соответствовало требованиям для оценки MEETS_STRONG_INTEGRITY.
  • Статус Play Защиты. Если playProtectVerdict указывает на проблему, в диалоговом окне будут инструкции по ее устранению.

    • Если Play Защита отключена (playProtectVerdict == POSSIBLE_RISK), в диалоговом окне пользователю будет предложено включить ее и выполнить проверку всех приложений на устройстве.
    • Если обнаружены вредоносные приложения (playProtectVerdict == MEDIUM_RISK или HIGH_RISK), в диалоговом окне пользователю будет предложено удалить их с помощью Google Play Защиты.
  • Целостность приложения. Если обнаружены такие проблемы, как несанкционированный доступ или вмешательство в работу приложения, пользователю будет предложено скачать приложение из Google Play.

  • Исключения на стороне клиента. Диалоговое окно также пытается устранить основные проблемы, которые вызвали исключение Integrity API. Например, если сервисы Google Play отключены, пользователю будет предложено включить их. К исключениям, которые можно устранить, относятся исключения Integrity API с кодами ошибок, например PLAY_SERVICES_VERSION_OUTDATED, NETWORK_ERROR или PLAY_SERVICES_NOT_FOUND. Чтобы проверить, можно ли устранить ошибку с помощью диалогового окна, используйте метод exception.isRemediable().

Пример UX

Рисунок 5. Диалоговое окно GET_STRONG_INTEGRITY, в котором обновляются сервисы Google Play.