בדף הזה מוסבר איך לטפל בבעיות שקשורות להחלטות לגבי תקינות.
אחרי שמתקבלת בקשה לאסימון תקינות, אפשר להציג למשתמש תיבת דו-שיח של Google Play. אפשר להציג את תיבת הדו-שיח כשיש בעיה אחת או יותר בהחלטה לגבי התקינות, או אם מתרחש חריג במהלך בקשת API ל-Integrity API. אחרי שסוגרים את תיבת הדו-שיח, אפשר לוודא שהבעיה נפתרה באמצעות בקשה נוספת של אסימון תקינות. אם אתם שולחים בקשות רגילות, אתם צריכים להכין שוב את ספק הטוקנים כדי לקבל החלטה חדשה.
בקשה להצגת תיבת דו-שיח של תקינות כדי לפתור בעיה בקביעה
כשהלקוח מבקש אסימון תקינות, אתם יכולים להשתמש ב-method שמוצע ב-StandardIntegrityToken (Standard API) וב-IntegrityTokenResponse (Classic API): showDialog(Activity activity, int integrityDialogTypeCode).
בשלבים הבאים מוסבר איך להשתמש ב-Play Integrity API כדי להציג תיבת דו-שיח לתיקון שגיאות באמצעות קוד תיבת הדו-שיח GET_LICENSED.
אחרי הקטע הזה מפורטים קודים אחרים של תיבות דו-שיח שהאפליקציה יכולה לבקש.
מבקשים טוקן תקינות מהאפליקציה ושולחים את הטוקן לשרת. אפשר להשתמש בבקשה רגילה או בבקשה קלאסית.
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));בשרת, מפענחים את אסימון התקינות ובודקים את השדה
appLicensingVerdict. הוא יכול להיראות בערך כך:// Licensing issue { ... "accountDetails": { "appLicensingVerdict": "UNLICENSED" } }אם האסימון מכיל את הערך
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; }באפליקציה, מפעילים את
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.תיבת הדו-שיח מוצגת מעל הפעילות שסופקה. כשהמשתמש סוגר את תיבת הדו-שיח, המשימה מסתיימת עם קוד תגובה.
(אופציונלי) מבקשים אסימון נוסף כדי להציג תיבות דו-שיח נוספות. אם אתם שולחים בקשות רגילות, אתם צריכים להפעיל מחדש את ספק הטוקנים כדי לקבל החלטה חדשה.
בקשה להצגת תיבת דו-שיח של תקינות כדי לתקן חריגה בצד הלקוח
אם בקשת Integrity API נכשלת עם StandardIntegrityException (Standard API) או IntegrityServiceException (Classic API) ואפשר לתקן את החריגה, אתם יכולים להשתמש בתיבות הדו-שיח GET_INTEGRITY או GET_STRONG_INTEGRITY כדי לתקן את השגיאה.
בשלבים הבאים מוסבר איך להשתמש בתיבת הדו-שיח GET_INTEGRITY כדי לתקן שגיאה בצד הלקוח שניתן לתקן, שדווחה על ידי Integrity API.
בודקים אם אפשר לתקן את החריגה שהוחזרה מבקשת 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; }אם אפשר לתקן את החריגה, מבקשים את תיבת הדו-שיח
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); }אם קוד התגובה שהוחזר מציין הצלחה, הבקשה הבאה לאסימון תקינות אמורה להצליח בלי חריגות. אם אתם שולחים בקשות רגילות, אתם צריכים להכין שוב את ספק הטוקנים כדי לקבל פסיקה חדשה.
קודים של תיבות דו-שיח של Integrity
GET_LICENSED (קוד סוג 1)
בעיה בתוצאה
תיבת הדו-שיח הזו מתאימה לשתי בעיות:
- גישה לא מורשית:
appLicensingVerdict: "UNLICENSED". זה אומר שלחשבון המשתמש אין הרשאה לאפליקציה, וזה יכול לקרות אם המשתמש הוריד אותה ממקור לא רשמי או מחנות אפליקציות אחרת, לא מ-Google Play. - אפליקציה שבוצעו בה שינויים:
appRecognitionVerdict: "UNRECOGNIZED_VERSION". זה אומר שהקובץ הבינארי של האפליקציה עבר שינוי או שהגרסה לא מזוהה על ידי Google Play.
תיקון שגיאות
אתם יכולים להציג את תיבת הדו-שיח של GET_LICENSED כדי לבקש מהמשתמש להוריד את האפליקציה המקורית מ-Google Play. תיבת הדו-שיח הזו מתייחסת לשני התרחישים:
- למשתמש ללא רישיון, הפעולה הזו מעניקה רישיון ל-Play. כך המשתמש יכול לקבל עדכונים לאפליקציה מ-Google Play.
- אם למשתמש יש גרסה מזויפת של האפליקציה, הוא יקבל הנחיות להתקנת האפליקציה המקורית מ-Google Play.
כשהמשתמש משלים את תיבת הדו-שיח, בדיקות התקינות הבאות מחזירות את הערכים appLicensingVerdict: "LICENSED" ו-appRecognitionVerdict: "PLAY_RECOGNIZED".
דוגמה לחוויית משתמש
CLOSE_UNKNOWN_ACCESS_RISK (קוד סוג 2)
בעיה בתוצאה
כשהערך של environmentDetails.appAccessRiskVerdict.appsDetected הוא "UNKNOWN_CAPTURING" או "UNKNOWN_CONTROLLING", זה אומר שיש אפליקציות אחרות (שלא הותקנו על ידי Google Play או נטענו מראש במחיצת המערכת על ידי יצרן המכשיר) שפועלות במכשיר ויכולות לצלם את המסך או לשלוט במכשיר.
תיקון שגיאות
אפשר להציג את תיבת הדו-שיח CLOSE_UNKNOWN_ACCESS_RISK כדי לבקש מהמשתמש לסגור את כל האפליקציות הלא מוכרות שעלולות לתעד את המסך או לשלוט במכשיר.
אם המשתמש לוחץ על הלחצן Close all, כל האפליקציות האלה נסגרות.
דוגמה לחוויית משתמש
CLOSE_ALL_ACCESS_RISK (קוד סוג 3)
בעיה בתוצאה
אם environmentDetails.appAccessRiskVerdict.appsDetected מכיל את הערכים "KNOWN_CAPTURING", "KNOWN_CONTROLLING", "UNKNOWN_CAPTURING" או "UNKNOWN_CONTROLLING", זה אומר שיש אפליקציות שפועלות במכשיר ויכולות לצלם את המסך או לשלוט במכשיר.
תיקון שגיאות
אפשר להציג את תיבת הדו-שיח CLOSE_ALL_ACCESS_RISK כדי לבקש מהמשתמש לסגור את כל האפליקציות שיכולות לצלם את המסך או לשלוט במכשיר. אם המשתמש מקיש על הלחצן Close all, כל האפליקציות האלה נסגרות במכשיר.
דוגמה לחוויית משתמש
GET_INTEGRITY (קוד סוג 4)
בעיה בתוצאה
תיבת הדו-שיח הזו מתאימה לכל אחת מהבעיות הבאות:
תקינות מכשיר חלשה: אם
deviceRecognitionVerdictלא מכיל אתMEETS_DEVICE_INTEGRITY, יכול להיות שהמכשיר לא מקורי ולא מאושר עם מערכת הפעלה Android. מצב כזה יכול לקרות, למשל, אם תוכנת האתחול של המכשיר פתוחה או אם מערכת ההפעלה של Android שנטענה היא לא קובץ אימג' של יצרן מאושר.גישה לא מורשית:
appLicensingVerdict: "UNLICENSED". זה אומר שלחשבון המשתמש אין הרשאה לאפליקציה, וזה יכול לקרות אם המשתמש הוריד אותה ממקור לא רשמי או מחנות אפליקציות אחרת, לא מ-Google Play.אפליקציה שבוצעו בה שינויים:
appRecognitionVerdict: "UNRECOGNIZED_VERSION". זה אומר שהקובץ הבינארי של האפליקציה עבר שינוי או שהגרסה שלו לא מזוהה על ידי Google Play.חריגים בצד הלקוח: כשמתרחש חריג שניתן לתיקון במהלך בקשת API ל-Integrity API. חריגים שניתן לתקן הם חריגים של Integrity API עם קודי שגיאה כמו
PLAY_SERVICES_VERSION_OUTDATED,NETWORK_ERROR,PLAY_SERVICES_NOT_FOUNDוכו'. אתם יכולים להשתמש ב-methodexception.isRemediable()כדי לבדוק אם אפשר לתקן חריג באמצעות תיבת הדו-שיח.
תיקון שגיאות
תיבת הדו-שיח GET_INTEGRITY נועדה לייעל את חוויית המשתמש על ידי טיפול בכמה שלבי תיקון בתהליך אחד רציף. כך המשתמש לא צריך לקיים אינטראקציה עם כמה תיבות דו-שיח נפרדות כדי לפתור בעיות שונות.
כשמבקשים את תיבת הדו-שיח, היא מזהה באופן אוטומטי אילו מהבעיות שקשורות לתוצאות הניתוח קיימות, ומספקת את השלבים המתאימים לתיקון הבעיות. כלומר, בקשת תיבת דו-שיח אחת יכולה לטפל בכמה בעיות בו-זמנית, כולל:
- תקינות המכשיר: אם זוהתה בעיה בתקינות המכשיר, תיבת הדו-שיח תנחה את המשתמש לשפר את סטטוס האבטחה של המכשיר כדי לעמוד בדרישות לקבלת קביעה מסוג
MEETS_DEVICE_INTEGRITY. - תקינות האפליקציה: אם מזוהות בעיות כמו גישה לא מורשית או שינוי לא מורשה של האפליקציה, תיבת הדו-שיח תנחה את המשתמשים להוריד את האפליקציה מחנות Play כדי לפתור את הבעיות.
- חריגים בצד הלקוח: תיבת הדו-שיח בודקת אם יש בעיות בסיסיות שגרמו לחריג ב-Integrity API, ומנסה לפתור אותן. לדוגמה, יכול להיות שהיא תבקש מהמשתמש לעדכן גרסה ישנה של Google Play Services.
דוגמה לחוויית משתמש
GET_STRONG_INTEGRITY (קוד סוג 5)
בעיה בתוצאה
תיבת הדו-שיח הזו נועדה לפתור את כל הבעיות ש-GET_INTEGRITY מטפל בהן, וגם בעיות שמונעות ממכשיר לקבל את ההחלטה MEETS_STRONG_INTEGRITY ובעיות בהחלטות של Play Protect.
תיקון שגיאות
GET_STRONG_INTEGRITY נועד לייעל את חוויית המשתמש על ידי טיפול בכמה שלבי תיקון בתהליך אחד רציף. בתיבת הדו-שיח מתבצעת בדיקה אוטומטית של בעיות תקינות שרלוונטיות לכתובת, כולל:
- תקינות המכשיר: אם זוהתה בעיה בתקינות המכשיר, תיבת הדו-שיח תנחה את המשתמש לשפר את סטטוס האבטחה של המכשיר כדי לעמוד בדרישות לקבלת קביעה מסוג
MEETS_STRONG_INTEGRITY. סטטוס Play Protect: אם
playProtectVerdictמציין שיש בעיה, תיבת הדו-שיח תנחה את המשתמש איך לתקן אותה:- אם Play Protect מושבת (
playProtectVerdict == POSSIBLE_RISK), בתיבת הדו-שיח המשתמש יתבקש להפעיל אותו ולבצע סריקה של כל האפליקציות במכשיר. - אם מזוהות אפליקציות מזיקות (
playProtectVerdict == MEDIUM_RISKאוHIGH_RISK), תיבת הדו-שיח תנחה את המשתמש להסיר אותן באמצעות Google Play Protect.
- אם Play Protect מושבת (
תקינות האפליקציה: אם מזוהות בעיות כמו גישה לא מורשית או שינוי לא מורשה של האפליקציה, תיבת הדו-שיח תבקש מהמשתמש להוריד את האפליקציה מחנות Play כדי לפתור את הבעיה.
חריגים בצד הלקוח: תיבת הדו-שיח גם מנסה לפתור את הבעיות הבסיסיות שגרמו לחריג ב-Integrity API. לדוגמה, אם Google Play Services מושבת, יכול להיות שהיא תבקש מהמשתמש להפעיל אותו. חריגות שניתן לתקן הן חריגות של Integrity API עם קודי שגיאה כמו
PLAY_SERVICES_VERSION_OUTDATED,NETWORK_ERRORאוPLAY_SERVICES_NOT_FOUND. אפשר להשתמש ב-methodexception.isRemediable()כדי לבדוק אם אפשר לתקן שגיאה באמצעות תיבת הדו-שיח.
דוגמה לחוויית משתמש