Trang này mô tả cách xử lý các vấn đề liên quan đến kết quả về tính toàn vẹn.
Sau khi yêu cầu mã thông báo về tính toàn vẹn, bạn có thể cho người dùng thấy hộp thoại của Google Play. Bạn có thể cho người dùng thấy hộp thoại khi gặp phải một hoặc nhiều vấn đề liên quan đến kết quả về tính toàn vẹn hoặc nếu xảy ra trường hợp ngoại lệ trong yêu cầu API Tính toàn vẹn. Sau khi đóng hộp thoại, bạn có thể xác minh xem vấn đề đã được khắc phục chưa bằng cách gửi một yêu cầu khác về mã thông báo về tính toàn vẹn. Nếu thực hiện yêu cầu chuẩn, bạn cần khởi động lại trình cung cấp mã thông báo để nhận kết quả mới.
Yêu cầu hộp thoại về tính toàn vẹn để khắc phục vấn đề về kết quả
Khi ứng dụng khách yêu cầu mã thông báo về tính toàn vẹn, bạn có thể sử dụng phương thức mà chúng tôi cung cấp trong
StandardIntegrityToken (API Chuẩn) và
IntegrityTokenResponse (API Kiểu cũ):
showDialog(Activity activity, int integrityDialogTypeCode).
Các bước sau đây sẽ trình bày cách sử dụng Play Integrity API để hiện hộp thoại khắc phục bằng mã hộp thoại GET_LICENSED.
Các mã hộp thoại khác mà ứng dụng của bạn có thể yêu cầu được liệt kê sau phần này.
Yêu cầu mã thông báo về tính toàn vẹn từ ứng dụng rồi gửi mã thông báo này đến máy chủ của bạn. Bạn có thể sử dụng yêu cầu Chuẩn hoặc Kiểu cũ.
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);
Mã gốc
/// 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));
Trên máy chủ của bạn, hãy giải mã mã thông báo về tính toàn vẹn rồi kiểm tra trường
appLicensingVerdict. Mã sẽ có dạng như sau:// Licensing issue { ... "accountDetails": { "appLicensingVerdict": "UNLICENSED" } }
Nếu mã thông báo chứa
appLicensingVerdict: "UNLICENSED", hãy phản hồi ứng dụng khách của bạn và yêu cầu hiện hộp thoại cấp phép: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; }
Mã gốc
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; }
Trên ứng dụng, hãy gọi
showDialogbằng mã bắt buộc được truy xuất từ máy chủ: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); }
Mã gốc
// 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.
Hộp thoại sẽ xuất hiện bên trên hoạt động được cung cấp. Khi người dùng đóng hộp thoại, Tác vụ sẽ hoàn tất bằng một mã phản hồi.
(Không bắt buộc) Hãy yêu cầu mã thông báo khác để hiện thêm hộp thoại. Nếu thực hiện yêu cầu chuẩn, bạn cần khởi động lại trình cung cấp mã thông báo để nhận kết quả mới.
Yêu cầu hộp thoại về tính toàn vẹn để khắc phục trường hợp ngoại lệ ở phía ứng dụng khách
Nếu yêu cầu API Tính toàn vẹn không thành công với StandardIntegrityException
(API Chuẩn) hoặc IntegrityServiceException (API Kiểu cũ) và trường hợp ngoại lệ đó có thể khắc phục được, thì bạn có thể sử dụng hộp thoại GET_INTEGRITY hoặc
GET_STRONG_INTEGRITY để khắc phục lỗi.
Các bước sau đây sẽ trình bày cách sử dụng GET_INTEGRITY hộp thoại để khắc phục lỗi ở phía máy khách có thể khắc phục được do Integrity API báo cáo.
Kiểm tra để đảm bảo rằng trường hợp ngoại lệ được trả về từ yêu cầu API Tính toàn vẹn có thể khắc phục được.
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; }
Mã gốc
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; }
Nếu trường hợp ngoại lệ có thể khắc phục được, hãy yêu cầu hộp thoại
GET_INTEGRITYbằng trường hợp ngoại lệ được trả về. Hộp thoại sẽ xuất hiện trên hoạt động được cung cấp và Tác vụ được trả về sẽ hoàn tất bằng một mã phản hồi sau khi người dùng đóng hộp thoại.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; }
Mã gốc
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); }
Nếu mã phản hồi được trả về cho biết thành công, thì yêu cầu tiếp theo về mã thông báo về tính toàn vẹn sẽ thành công mà không có trường hợp ngoại lệ nào. Nếu thực hiện yêu cầu chuẩn, bạn cần khởi động lại trình cung cấp mã thông báo để nhận kết quả mới.
Mã hộp thoại về tính toàn vẹn
GET_LICENSED (Mã loại 1)
Vấn đề về kết quả kiểm tra
Hộp thoại này phù hợp với 2 vấn đề:
- Truy cập trái phép:
appLicensingVerdict: "UNLICENSED". Điều này có nghĩa là tài khoản người dùng không có quyền đối với ứng dụng của bạn. Tình trạng này có thể xảy ra nếu người dùng tải ứng dụng xuống thiết bị theo cách thủ công hoặc tải ứng dụng xuống từ một cửa hàng ứng dụng khác ngoài Google Play. - Ứng dụng bị can thiệp:
appRecognitionVerdict: "UNRECOGNIZED_VERSION". Điều này có nghĩa là tệp nhị phân của ứng dụng đã bị sửa đổi hoặc không phải là phiên bản được Google Play nhận dạng.
Cách khắc phục
Bạn có thể cho người dùng thấy hộp thoại GET_LICENSED để nhắc họ tải ứng dụng chính hãng xuống từ Google Play. Hộp thoại duy nhất này giải quyết cả 2 trường hợp:
- Đối với người dùng không có giấy phép, hộp thoại này sẽ cấp cho họ giấy phép Play. Điều này cho phép người dùng nhận bản cập nhật ứng dụng từ Google Play.
- Đối với người dùng có phiên bản ứng dụng bị can thiệp, hộp thoại này sẽ hướng dẫn họ cài đặt ứng dụng chưa bị sửa đổi từ Google Play.
Khi người dùng hoàn tất hộp thoại, các lượt kiểm tra tính toàn vẹn tiếp theo sẽ trả về
appLicensingVerdict: "LICENSED" và appRecognitionVerdict: "PLAY_RECOGNIZED".
Ví dụ về trải nghiệm người dùng
CLOSE_UNKNOWN_ACCESS_RISK (Mã loại 2)
Vấn đề về kết quả kiểm tra
Khi environmentDetails.appAccessRiskVerdict.appsDetected chứa
"UNKNOWN_CAPTURING" hoặc "UNKNOWN_CONTROLLING", điều này có nghĩa là có các ứng dụng khác
(không phải do Google Play cài đặt hoặc không phải do nhà sản xuất thiết bị tải trước lên phân vùng hệ thống)
đang chạy trên thiết bị có thể ghi lại màn hình hoặc
kiểm soát thiết bị.
Cách khắc phục
Bạn có thể cho người dùng thấy hộp thoại CLOSE_UNKNOWN_ACCESS_RISK để nhắc họ đóng tất cả ứng dụng không xác định có thể ghi lại màn hình hoặc kiểm soát thiết bị.
Nếu người dùng nhấn vào nút Close all (Đóng tất cả), thì tất cả các ứng dụng như vậy sẽ bị đóng.
Ví dụ về trải nghiệm người dùng
CLOSE_ALL_ACCESS_RISK (Mã loại 3)
Vấn đề về kết quả kiểm tra
Khi environmentDetails.appAccessRiskVerdict.appsDetected chứa bất kỳ giá trị nào trong số
"KNOWN_CAPTURING", "KNOWN_CONTROLLING","UNKNOWN_CAPTURING" hoặc
"UNKNOWN_CONTROLLING", điều này có nghĩa là có các ứng dụng đang chạy trên thiết bị có thể
ghi lại màn hình hoặc kiểm soát thiết bị.
Cách khắc phục
Bạn có thể cho người dùng thấy hộp thoại CLOSE_ALL_ACCESS_RISK để nhắc họ đóng tất cả ứng dụng có thể ghi lại màn hình hoặc kiểm soát thiết bị. Nếu người dùng nhấn vào nút Close all (Đóng tất cả), thì tất cả các ứng dụng như vậy sẽ bị đóng trên thiết bị.
Ví dụ về trải nghiệm người dùng
GET_INTEGRITY (Mã loại 4)
Vấn đề về kết quả kiểm tra
Hộp thoại này phù hợp với bất kỳ vấn đề nào sau đây:
Tính toàn vẹn của thiết bị yếu: Khi
deviceRecognitionVerdictkhông chứaMEETS_DEVICE_INTEGRITY, thiết bị có thể không phải là thiết bị Android chính hãng và được chứng nhận. Tình trạng này có thể xảy ra, chẳng hạn như nếu trình tải khởi động của thiết bị bị mở khoá hoặc hệ điều hành Android đã tải không phải là hình ảnh của nhà sản xuất được chứng nhận.Truy cập trái phép:
appLicensingVerdict: "UNLICENSED". Điều này có nghĩa là tài khoản người dùng không có quyền đối với ứng dụng của bạn. Tình trạng này có thể xảy ra nếu người dùng tải ứng dụng xuống thiết bị theo cách thủ công hoặc tải ứng dụng xuống từ một cửa hàng ứng dụng khác ngoài Google Play.Ứng dụng bị can thiệp:
appRecognitionVerdict: "UNRECOGNIZED_VERSION". Điều này có nghĩa là tệp nhị phân của ứng dụng đã bị sửa đổi hoặc không phải là phiên bản được Google Play nhận dạng.Trường hợp ngoại lệ ở phía ứng dụng khách: Khi xảy ra trường hợp ngoại lệ có thể khắc phục được trong yêu cầu API Tính toàn vẹn. Trường hợp ngoại lệ có thể khắc phục được là trường hợp ngoại lệ của Integrity API có mã lỗi như
PLAY_SERVICES_VERSION_OUTDATED,NETWORK_ERROR,PLAY_SERVICES_NOT_FOUND, v.v. Bạn có thể sử dụng phương thứcexception.isRemediable()để kiểm tra xem hộp thoại có thể khắc phục lỗi hay không.
Cách khắc phục
Hộp thoại GET_INTEGRITY được thiết kế để đơn giản hoá trải nghiệm của người dùng bằng cách xử lý nhiều bước khắc phục trong một quy trình liên tục duy nhất. Điều này giúp người dùng không phải tương tác với nhiều hộp thoại riêng biệt để khắc phục các vấn đề khác nhau.
Khi bạn yêu cầu hộp thoại, hộp thoại này sẽ tự động phát hiện vấn đề nào về kết quả mục tiêu đang xảy ra và cung cấp các bước khắc phục phù hợp. Điều này có nghĩa là một yêu cầu hộp thoại có thể giải quyết nhiều vấn đề cùng một lúc, bao gồm:
- Tính toàn vẹn của thiết bị: Nếu phát hiện thấy vấn đề về tính toàn vẹn của thiết bị, hộp thoại sẽ
hướng dẫn người dùng cải thiện trạng thái bảo mật của thiết bị để đáp ứng các
yêu cầu đối với kết quả
MEETS_DEVICE_INTEGRITY. - Tính toàn vẹn của ứng dụng: Nếu phát hiện thấy các vấn đề như truy cập trái phép hoặc can thiệp vào ứng dụng, hộp thoại sẽ hướng dẫn người dùng tải ứng dụng xuống từ Cửa hàng Play để khắc phục các vấn đề đó.
- Trường hợp ngoại lệ ở phía máy khách: Hộp thoại này kiểm tra và cố gắng giải quyết mọi vấn đề cơ bản gây ra trường hợp ngoại lệ của Integrity API. Ví dụ: hộp thoại này có thể nhắc người dùng cập nhật phiên bản Dịch vụ Google Play đã lỗi thời.
Ví dụ về trải nghiệm người dùng
GET_STRONG_INTEGRITY (Mã loại 5)
Vấn đề về kết quả kiểm tra
Hộp thoại này được thiết kế để khắc phục tất cả các vấn đề mà
GET_INTEGRITY
giải quyết, đồng thời có thêm khả năng khắc phục các vấn đề khiến thiết bị
không nhận được kết quả MEETS_STRONG_INTEGRITY và khắc phục các vấn đề về kết quả của Play Protect.
Cách khắc phục
GET_STRONG_INTEGRITY được thiết kế để đơn giản hoá trải nghiệm của người dùng bằng cách xử lý nhiều bước khắc phục trong một quy trình liên tục duy nhất. Hộp thoại này tự động kiểm tra các vấn đề về tính toàn vẹn có thể áp dụng cho địa chỉ, bao gồm:
- Tính toàn vẹn của thiết bị: Nếu phát hiện thấy vấn đề về tính toàn vẹn của thiết bị, hộp thoại sẽ
hướng dẫn người dùng cải thiện trạng thái bảo mật của thiết bị để đáp ứng các
yêu cầu đối với kết quả
MEETS_STRONG_INTEGRITY. Trạng thái của Play Protect: Nếu
playProtectVerdictcho biết có vấn đề, hộp thoại sẽ hướng dẫn người dùng khắc phục vấn đề đó:- Nếu Play Protect bị tắt (
playProtectVerdict == POSSIBLE_RISK), hộp thoại sẽ nhắc người dùng bật tính năng này và quét tất cả ứng dụng trên thiết bị. - Nếu phát hiện thấy các ứng dụng có hại (
playProtectVerdict == MEDIUM_RISKhoặcHIGH_RISK), hộp thoại sẽ hướng dẫn người dùng gỡ cài đặt các ứng dụng đó bằng Google Play Protect.
- Nếu Play Protect bị tắt (
Tính toàn vẹn của ứng dụng: Nếu phát hiện thấy các vấn đề như truy cập trái phép hoặc can thiệp vào ứng dụng, hộp thoại sẽ nhắc người dùng tải ứng dụng xuống từ Cửa hàng Play để khắc phục vấn đề.
Trường hợp ngoại lệ ở phía máy khách: Hộp thoại này cũng cố gắng giải quyết mọi vấn đề cơ bản gây ra trường hợp ngoại lệ của Integrity API. Ví dụ: hộp thoại này có thể nhắc người dùng bật Dịch vụ Google Play nếu phát hiện thấy tính năng này bị tắt. Trường hợp ngoại lệ có thể khắc phục được là trường hợp ngoại lệ của Integrity API có mã lỗi như
PLAY_SERVICES_VERSION_OUTDATED,NETWORK_ERRORhoặcPLAY_SERVICES_NOT_FOUND. Bạn có thể sử dụng phương thứcexception.isRemediable()để kiểm tra xem hộp thoại có thể khắc phục lỗi hay không.
Ví dụ về trải nghiệm người dùng