Okna działań naprawczych

Na tej stronie opisujemy, jak rozwiązywać problemy z ocenami integralności.

Po przesłaniu prośby o token integralności możesz wyświetlić użytkownikowi okno Google Play. Okno możesz wyświetlić, gdy występuje co najmniej 1 problem z oceną integralności lub gdy podczas żądania do interfejsu Integrity API wystąpił wyjątek. Po zamknięciu okna możesz sprawdzić, czy problem został rozwiązany, wysyłając kolejną prośbę o token integralności. Jeśli wysyłasz standardowe żądania, musisz ponownie rozgrzać dostawcę tokenów, aby uzyskać nową ocenę.

Wyświetlanie okna integralności w celu rozwiązania problemu z werdyktem

Gdy klient prosi o token integralności, możesz użyć metody oferowanej w StandardIntegrityToken (standardowy interfejs API) i IntegrityTokenResponse (klasyczny interfejs API): showDialog(Activity activity, int integrityDialogTypeCode).

Poniżej opisujemy, jak za pomocą interfejsu Play Integrity API wyświetlić okno na temat działań naprawczych przy użyciu kodu okna GET_LICENSED. Inne kody okien, o które może prosić aplikacja, są wymienione po tej sekcji.

  1. Poproś o token integralności z aplikacji i wyślij go na serwer. Możesz użyć żądania standardowego lub klasycznego.

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

    Natywna

    /// 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. Na serwerze odszyfruj token integralności i sprawdź pole appLicensingVerdict. Może to wyglądać tak:

    // Licensing issue
    {
        ...
        "accountDetails": {
            "appLicensingVerdict": "UNLICENSED"
        }
    }
    
  3. Jeśli token zawiera appLicensingVerdict: "UNLICENSED", wyślij odpowiedź do klienta aplikacji z prośbą o wyświetlenie okna licencjonowania:

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

    Natywna

    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. W aplikacji wywołaj showDialog z kodem żądania pobranym z serwera:

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

    Natywna

    // 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. Okno jest wyświetlane nad podaną aktywnością. Gdy użytkownik zamknie okno, zadanie zostanie zakończone z kodem odpowiedzi.

  6. (Opcjonalnie) Poproś o kolejny token, aby wyświetlić dodatkowe okna. Jeśli wysyłasz żądania standardowe, musisz ponownie rozgrzać dostawcę tokenów, aby uzyskać nową decyzję.

Wyświetlanie okna integralności w celu naprawienia wyjątku po stronie klienta

Jeśli żądanie do interfejsu Integrity API nie powiedzie się i zostanie zwrócony błąd StandardIntegrityException (standardowy interfejs API) lub IntegrityServiceException (klasyczny interfejs API), a wyjątek można naprawić, możesz użyć okna GET_INTEGRITY lub GET_STRONG_INTEGRITY, aby naprawić błąd.

Poniżej opisujemy, jak za pomocą okna GET_INTEGRITY naprawić błąd po stronie klienta zgłoszony przez interfejs Integrity API.

  1. Sprawdź, czy wyjątek zwrócony przez żądanie do interfejsu Integrity API można naprawić.

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

    Natywna

    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. Jeśli wyjątek można naprawić, poproś o okno GET_INTEGRITY, używając zwróconego wyjątku. Okno zostanie wyświetlone nad podaną aktywnością, a zwrócone zadanie zostanie zakończone kodem odpowiedzi po zamknięciu okna przez użytkownika.

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

    Natywna

    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. Jeśli zwrócony kod odpowiedzi wskazuje na powodzenie, następne żądanie tokena integralności powinno zostać zrealizowane bez żadnych wyjątków. Jeśli wysyłasz żądania standardowe, musisz ponownie rozgrzać dostawcę tokenów, aby uzyskać nową ocenę.

Kody okien integralności

GET_LICENSED (kod typu 1)

Problem z werdyktem

To okno jest odpowiednie w przypadku 2 problemów:

  • Nieautoryzowany dostęp: appLicensingVerdict: "UNLICENSED". Oznacza to, że konto użytkownika nie ma uprawnień do korzystania z Twojej aplikacji, co może się zdarzyć, jeśli użytkownik zainstalował ją z nieoficjalnego źródła lub pobrał ją ze sklepu z aplikacjami innego niż Google Play.
  • Zmodyfikowana aplikacja: appRecognitionVerdict: "UNRECOGNIZED_VERSION". Oznacza to, że plik binarny aplikacji został zmodyfikowany lub nie jest wersją rozpoznawaną przez Google Play.

Jakie podjąć działania

Możesz wyświetlić okno GET_LICENSED, aby poprosić użytkownika o pobranie oryginalnej aplikacji z Google Play. To jedno okno dialogowe obejmuje oba scenariusze:

  • W przypadku użytkownika bez licencji przyznaje mu licencję na Google Play. Dzięki temu użytkownik będzie otrzymywać aktualizacje aplikacji z Google Play.
  • Użytkownikowi, który ma zmodyfikowaną wersję aplikacji, pomaga zainstalować niezmodyfikowaną wersję z Google Play.

Gdy użytkownik zamknie okno, kolejne testy integralności zwrócą wartości appLicensingVerdict: "LICENSED" i appRecognitionVerdict: "PLAY_RECOGNIZED".

Przykładowy UX

Rysunek 1. Okno GET_LICENSED w Google Play.

CLOSE_UNKNOWN_ACCESS_RISK (kod typu 2)

Problem z werdyktem

Gdy environmentDetails.appAccessRiskVerdict.appsDetected zawiera "UNKNOWN_CAPTURING" lub "UNKNOWN_CONTROLLING", oznacza to, że na urządzeniu działają inne aplikacje (niezainstalowane przez Google Play ani niezaładowane wstępnie na partycji systemowej przez producenta urządzenia), które mogą przechwytywać ekran lub sterować urządzeniem.

Jakie podjąć działania

Możesz wyświetlić okno CLOSE_UNKNOWN_ACCESS_RISK, aby poprosić użytkownika o zamknięcie wszystkich nieznanych aplikacji, które mogą przechwytywać ekran lub sterować urządzeniem. Jeśli użytkownik kliknie przycisk Close all, wszystkie takie aplikacje zostaną zamknięte.

Przykładowy UX

Rysunek 2. Okno zamykania ryzyka nieznanego dostępu.

CLOSE_ALL_ACCESS_RISK (kod typu 3)

Problem z werdyktem

Gdy environmentDetails.appAccessRiskVerdict.appsDetected zawiera dowolną z tych wartości: "KNOWN_CAPTURING", "KNOWN_CONTROLLING", "UNKNOWN_CAPTURING" lub "UNKNOWN_CONTROLLING", oznacza to, że na urządzeniu działają aplikacje, które mogą przechwytywać ekran lub sterować urządzeniem.

Jakie podjąć działania

Możesz wyświetlić okno CLOSE_ALL_ACCESS_RISK, aby poprosić użytkownika o zamknięcie wszystkich aplikacji, które mogą przechwytywać ekran lub sterować urządzeniem. Jeśli użytkownik kliknie przycisk Close all, wszystkie takie aplikacje zostaną zamknięte na urządzeniu.

Przykładowy UX

Rysunek 3. Okno zamykania wszystkich zagrożeń związanych z dostępem.

GET_INTEGRITY (kod typu 4)

Problem z werdyktem

To okno dialogowe jest odpowiednie w przypadku tych problemów:

  • Słaba integralność urządzenia: gdy deviceRecognitionVerdict nie zawiera MEETS_DEVICE_INTEGRITY, urządzenie może nie być oryginalnym i certyfikowanym urządzeniem z Androidem. Może się tak zdarzyć, jeśli na przykład program rozruchowy urządzenia jest odblokowany lub załadowany system operacyjny Android nie jest certyfikowanym obrazem producenta.

  • Nieautoryzowany dostęp: appLicensingVerdict: "UNLICENSED". Oznacza to, że konto użytkownika nie ma uprawnień do korzystania z Twojej aplikacji, co może się zdarzyć, jeśli użytkownik zainstalował ją z nieoficjalnego źródła lub pobrał ją ze sklepu z aplikacjami innego niż Google Play.

  • Zmodyfikowana aplikacja: appRecognitionVerdict: "UNRECOGNIZED_VERSION". Oznacza to, że plik binarny aplikacji został zmodyfikowany lub nie jest wersją rozpoznawaną przez Google Play.

  • Wyjątki po stronie klienta: gdy podczas żądania do interfejsu Integrity API wystąpi wyjątek, który można naprawić. Wyjątki, które można naprawić, to wyjątki interfejsu Integrity API z kodami błędów takimi jak PLAY_SERVICES_VERSION_OUTDATED, NETWORK_ERROR, PLAY_SERVICES_NOT_FOUND itp. Możesz użyć metody exception.isRemediable(), aby sprawdzić, czy wyjątek można naprawić za pomocą okna.

Jakie podjąć działania

Okno dialogowe GET_INTEGRITY zostało zaprojektowane tak, aby uprościć obsługę użytkownika przez wykonywanie wielu działań naprawczych w ramach jednego, ciągłego procesu. Dzięki temu użytkownik nie musi wchodzić w interakcję z wieloma osobnymi oknami, aby rozwiązać różne problemy.

Gdy poprosisz o wyświetlenie okna, automatycznie wykryje ono, które z docelowych problemów z werdyktem występują, i poda odpowiednie kroki naprawcze. Oznacza to, że pojedyncza prośba o okno może dotyczyć wielu problemów naraz, w tym:

  • Integralność urządzenia: jeśli zostanie wykryty problem z integralnością urządzenia, okno dialogowe pomoże użytkownikowi poprawić stan zabezpieczeń urządzenia, aby spełniało ono wymagania dotyczące werdyktu MEETS_DEVICE_INTEGRITY.
  • Integralność aplikacji: jeśli zostaną wykryte problemy, takie jak nieautoryzowany dostęp lub modyfikowanie aplikacji, okno dialogowe przekieruje użytkowników do Sklepu Play, aby mogli je rozwiązać.
  • Wyjątki po stronie klienta: okno sprawdza i próbuje rozwiązać wszelkie problemy, które spowodowały wyjątek interfejsu Integrity API. Może na przykład poprosić użytkownika o zaktualizowanie nieaktualnej wersji Usług Google Play.

Przykładowy UX

Rysunek 4. Proces naprawy błędu sieci w oknie GET_INTEGRITY

GET_STRONG_INTEGRITY (kod typu 5)

Problem z werdyktem

To okno służy do rozwiązywania wszystkich problemów, które rozwiązuje GET_INTEGRITY, a dodatkowo umożliwia rozwiązywanie problemów, które uniemożliwiają urządzeniu otrzymanie oceny MEETS_STRONG_INTEGRITY, oraz problemów z oceną Play Protect.

Jakie podjąć działania

GET_STRONG_INTEGRITY ma na celu usprawnienie procesu dla użytkownika dzięki obsłudze wielu działań naprawczych w ramach jednego, ciągłego procesu. Okno automatycznie sprawdza, czy nie występują problemy z integralnością adresu, w tym:

  • Integralność urządzenia: jeśli zostanie wykryty problem z integralnością urządzenia, okno dialogowe pomoże użytkownikowi poprawić stan zabezpieczeń urządzenia, aby spełniało ono wymagania dotyczące werdyktu MEETS_STRONG_INTEGRITY.
  • Stan Play Protect: jeśli playProtectVerdict wskazuje problem, okno dialogowe pomoże użytkownikowi go rozwiązać:

    • Jeśli usługa Play Protect jest wyłączona (playProtectVerdict == POSSIBLE_RISK), okno dialogowe poprosi użytkownika o jej włączenie i przeprowadzenie skanowania wszystkich aplikacji na urządzeniu.
    • Jeśli zostaną wykryte szkodliwe aplikacje (playProtectVerdict == MEDIUM_RISK lub HIGH_RISK), w oknie pojawi się prośba o odinstalowanie ich za pomocą Google Play Protect.
  • Integralność aplikacji: jeśli zostaną wykryte problemy, takie jak nieautoryzowany dostęp lub modyfikowanie aplikacji, okno poprosi użytkownika o pobranie aplikacji ze Sklepu Play, aby rozwiązać problem.

  • Wyjątki po stronie klienta: okno próbuje też rozwiązać wszelkie problemy, które spowodowały wyjątek interfejsu Integrity API. Może na przykład poprosić użytkownika o włączenie usług Google Play, jeśli są wyłączone. Wyjątki, które można naprawić, to wyjątki interfejsu Integrity API z kodami błędów takimi jak PLAY_SERVICES_VERSION_OUTDATED, NETWORK_ERROR lub PLAY_SERVICES_NOT_FOUND. Możesz użyć metody exception.isRemediable(), aby sprawdzić, czy błąd można naprawić za pomocą okna.

Przykładowy UX

Rysunek 5. Okno GET_STRONG_INTEGRITY aktualizujące Usługi Google Play.