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.
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));Na serwerze odszyfruj token integralności i sprawdź pole
appLicensingVerdict. Może to wyglądać tak:// Licensing issue { ... "accountDetails": { "appLicensingVerdict": "UNLICENSED" } }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; }W aplikacji wywołaj
showDialogz 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.Okno jest wyświetlane nad podaną aktywnością. Gdy użytkownik zamknie okno, zadanie zostanie zakończone z kodem odpowiedzi.
(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.
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; }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); }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
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
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
GET_INTEGRITY (kod typu 4)
Problem z werdyktem
To okno dialogowe jest odpowiednie w przypadku tych problemów:
Słaba integralność urządzenia: gdy
deviceRecognitionVerdictnie zawieraMEETS_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_FOUNDitp. Możesz użyć metodyexception.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
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
playProtectVerdictwskazuje 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_RISKlubHIGH_RISK), w oknie pojawi się prośba o odinstalowanie ich za pomocą Google Play Protect.
- Jeśli usługa Play Protect jest wyłączona (
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_ERRORlubPLAY_SERVICES_NOT_FOUND. Możesz użyć metodyexception.isRemediable(), aby sprawdzić, czy błąd można naprawić za pomocą okna.
Przykładowy UX