Okna działań naprawczych

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

Po wysłaniu żądania tokena integralności możesz wyświetlić użytkownikowi okno Google Play. Możesz wyświetlić okno, gdy wystąpią co najmniej 1 problem z wynikiem oceny integralności lub wyjątek podczas żądania interfejsu Integrity API. Po zamknięciu okna możesz sprawdzić, czy problem został rozwiązany, wysyłając kolejne żądanie tokena integralności. Jeśli wysyłasz standardowe żądania, musisz ponownie rozgrzać dostawcę tokenów, aby uzyskać nową decyzję.

Prośba o wyświetlenie okna integralności w celu rozwiązania problemu z oceną

Gdy klient zażąda tokena integralności, możesz użyć metody oferowanej w interfejsach StandardIntegrityToken (standardowy interfejs API) i IntegrityTokenResponse (klasyczny interfejs API): showDialog(Activity activity, int integrityDialogTypeCode).

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

  1. Wyślij z aplikacji prośbę o token integralności i prześ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); 

    Rodzimy użytkownik

    /// 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. Odszyfruj token integralności na serwerze i sprawdź pole appLicensingVerdict. Może to wyglądać mniej więcej tak:

    // Licensing issue
    {
      ...
      "accountDetails": {
          "appLicensingVerdict": "UNLICENSED"
      }
    }
  3. Jeśli token zawiera znak appLicensingVerdict: "UNLICENSED", odpowiedz klientowi aplikacji, prosząc go o wyświetlenie okna licencji:

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

    Rodzimy użytkownik

    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 funkcję showDialog z kodem pobranym z serwera:

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

    Rodzimy użytkownik

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

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

Wysyłanie prośby o wyświetlenie okna ochrony integralności w celu naprawienia wyjątku po stronie klienta

Jeśli żądanie do interfejsu Integrity API zakończy się niepowodzeniem z błędem StandardIntegrityException (standardowy interfejs API) lub IntegrityServiceException (klasyczny interfejs API) i wyjątek można naprawić, możesz użyć okna GET_INTEGRITY lub GET_STRONG_INTEGRITY, aby rozwiązać problem.

Z tych instrukcji dowiesz się, jak za pomocą okna GET_INTEGRITY naprawić błąd po stronie klienta, który można naprawić i który został zgłoszony przez interfejs Integrity API.

  1. Sprawdź, czy wyjątek zwrócony przez żądanie 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;
    }
     
  2. Jeśli wyjątek można naprawić, poproś o wyświetlenie GET_INTEGRITYokna dialogowego za pomocą zwróconego wyjątku. Okno dialogowe zostanie wyświetlone nad podaną aktywnością, a zwrócone zadanie zakończy się 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);
    }  
  3. Jeśli zwrócony kod odpowiedzi wskazuje powodzenie, kolejne żądanie tokena integralności powinno się powieść bez żadnych wyjątków. Jeśli wysyłasz standardowe żądania, musisz ponownie rozgrzać dostawcę tokenów, aby uzyskać nową decyzję.

Kody okna integralności

GET_LICENSED (typ kodu 1)

Problem z oceną

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

  • Nieautoryzowany dostęp: appLicensingVerdict: "UNLICENSED" Oznacza to, że konto użytkownika nie ma uprawnień do korzystania z aplikacji. Może się tak zdarzyć, jeśli użytkownik zainstalował ją z innego urządzenia lub pozyskał ją z innego sklepu z aplikacjami niż Google Play.
  • Zmodyfikowana aplikacja: appRecognitionVerdict: "UNRECOGNIZED_VERSION". Oznacza to, że plik binarny aplikacji został zmodyfikowany lub nie jest wersją rozpoznawaną przez Google Play.

Działania naprawcze

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

  • W przypadku użytkownika bez licencji przyznaje mu licencję Google Play. Dzięki temu użytkownik będzie otrzymywać aktualizacje aplikacji z Google Play.
  • W przypadku użytkownika, który ma zmodyfikowaną wersję aplikacji, wyświetla instrukcje instalacji niezmodyfikowanej aplikacji z Google Play.

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

Przykładowy UX

Rysunek 1. Okno GET_LICENSED Play.

CLOSE_UNKNOWN_ACCESS_RISK (kod typu 2)

Problem z oceną

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.

Działania naprawcze

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 nieznanego ryzyka dostępu.

CLOSE_ALL_ACCESS_RISK (Type Code 3)

Problem z oceną

Gdy environmentDetails.appAccessRiskVerdict.appsDetected zawiera którykolwiek z tych sygnałów: "KNOWN_CAPTURING", "KNOWN_CONTROLLING", "UNKNOWN_CAPTURING" lub "UNKNOWN_CONTROLLING", oznacza to, że na urządzeniu są uruchomione aplikacje, które mogą przechwytywać ekran lub sterować urządzeniem.

Działania naprawcze

Możesz wyświetlić 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 ryzyk związanych z dostępem.

GET_INTEGRITY (kod typu 4)

Problem z oceną

To okno dialogowe jest odpowiednie w przypadku każdego z 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ć na przykład wtedy, gdy 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. Może się tak zdarzyć, jeśli użytkownik zainstalował ją z zewnętrznego źródła lub pobrał z innego sklepu z aplikacjami 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 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. Za pomocą metody exception.isRemediable() możesz sprawdzić, czy wyjątek można naprawić za pomocą okna.

Działania naprawcze

Okno GET_INTEGRITY zostało zaprojektowane tak, aby uprościć obsługę przez użytkownika, ponieważ umożliwia wykonanie wielu czynności naprawczych w ramach jednego, ciągłego procesu. Dzięki temu użytkownik nie musi wchodzić w interakcję z wieloma oddzielnymi oknami, aby rozwiązać różne problemy.

Gdy poprosisz o wyświetlenie okna, automatycznie wykryje ono, które z problemów z decyzją o kierowaniu są obecne, i poda odpowiednie kroki naprawcze. Oznacza to, że jedno żądanie dialogu może rozwiązywać jednocześnie wiele problemów, w tym:

  • Integralność urządzenia: jeśli zostanie wykryty problem z integralnością urządzenia, okno dialogowe pomoże użytkownikowi poprawić stan bezpieczeństwa urządzenia, aby spełniało ono wymagania dotyczące wyniku MEETS_DEVICE_INTEGRITY.
  • Integralność aplikacji: jeśli zostaną wykryte problemy, takie jak nieautoryzowany dostęp lub manipulowanie aplikacją, okno przekieruje użytkowników do Sklepu Play, aby mogli pobrać aplikację i rozwiązać te problemy.
.
  • Wyjątki po stronie klienta: okno dialogowe 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 oceną

To okno ma na celu rozwiązanie wszystkich problemów, które rozwiązuje interfejs GET_INTEGRITY, a także problemów, które uniemożliwiają urządzeniu otrzymanie wyniku MEETS_STRONG_INTEGRITY, oraz problemów z wynikiem Play Protect.

Działania naprawcze

GET_STRONG_INTEGRITY ma na celu usprawnienie obsługi przez użytkownika poprzez wykonywanie wielu czynności naprawczych w ramach jednego, ciągłego procesu. W oknie dialogowym automatycznie sprawdzane są 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 bezpieczeństwa urządzenia, aby spełniało ono wymagania dotyczące wyniku MEETS_STRONG_INTEGRITY.
  • Stan ochrony w Google Play: jeśli playProtectVerdict wskazuje problem, okno dialogowe pomoże użytkownikowi go rozwiązać:

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

  • Wyjątki po stronie klienta: okno dialogowe próbuje też rozwiązać wszelkie problemy, które spowodowały wyjątek interfejsu Integrity API. Na przykład może wyświetlić użytkownikowi prośbę o włączenie Usług Google Play, jeśli okaże się, że 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. Za pomocą metody exception.isRemediable() możesz sprawdzić, czy błąd można naprawić za pomocą okna.

Przykładowy UX

Rysunek 5. Okno GET_STRONG_INTEGRITY z informacją o aktualizowaniu Usług Play.