このページでは、完全性判定の結果に関する問題への対処方法について説明します。
完全性トークンがリクエストされたときに、ユーザーに Google Play ダイアログを表示できます。完全性判定の結果に 1 つ以上の問題がある場合、または Integrity API リクエスト中に例外が発生した場合は、ダイアログを表示できます。ダイアログを閉じると、別の完全性トークン リクエストで問題が修正されたことを確認できます。標準リクエストを実行する場合は、トークン プロバイダを再度ウォームアップして、新しい判定結果を取得する必要があります。
判定の問題を解決するために完全性ダイアログをリクエストする
クライアントが完全性トークンをリクエストすると、StandardIntegrityToken(標準 API)と IntegrityTokenResponse(クラシック 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) { // 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); } - ネイティブ- // 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. } 
- ダイアログは提供されたアクティビティの上に表示されます。ユーザーがダイアログを閉じると、タスクはレスポンス コードを返して完了します。 
- (省略可)さらにダイアログを表示するには、別のトークンをリクエストします。標準リクエストを実行する場合は、トークン プロバイダを再度ウォームアップして、新しい判定結果を取得する必要があります。 
クライアント側の例外を修正するために完全性ダイアログをリクエストする
完全性 API リクエストが StandardIntegrityException(標準 API)または IntegrityServiceException(クラシック 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; } 
- 例外が修正可能な場合は、返された例外を使用して - 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); } 
- 返されたレスポンス コードが成功を示している場合、次の完全性トークンのリクエストは例外なく成功します。標準リクエストを実行する場合は、トークン プロバイダを再度ウォームアップして、新しい判定結果を取得する必要があります。 
完全性ダイアログ コード
GET_LICENSED(タイプコード 1)
判定に関する問題
このダイアログは、次の 2 つの問題に適しています。
- 不正アクセス: appLicensingVerdict: "UNLICENSED"。これは、ユーザー アカウントがアプリの利用資格を持っていないことを意味します。ユーザーがアプリをサイドローディングした場合や、Google Play 以外のアプリストアから取得した場合に発生する可能性があります。
- 改ざんされたアプリ: appRecognitionVerdict: "UNRECOGNIZED_VERSION"。これは、アプリのバイナリが改変されているか、Google Play で認識されていないバージョンであることを意味します。
改善
GET_LICENSED ダイアログを表示して、Google Play から正規のアプリを入手するようユーザーに促すことができます。この単一のダイアログで両方のシナリオに対応します。
- ライセンスのないユーザーには、Play ライセンスが付与されます。これにより、ユーザーは Google Play からアプリのアップデートを受け取ることができます。
- 改ざんされたアプリ バージョンを使用しているユーザーに対しては、Google Play から変更されていないアプリをインストールするよう案内します。
ユーザーがダイアログを完了すると、以降の完全性チェックは appLicensingVerdict: "LICENSED" と appRecognitionVerdict: "PLAY_RECOGNIZED" を返します。
UX の例
 
    CLOSE_UNKNOWN_ACCESS_RISK(タイプコード 2)
判定に関する問題
environmentDetails.appAccessRiskVerdict.appsDetected に "UNKNOWN_CAPTURING" または "UNKNOWN_CONTROLLING" が含まれている場合、画面のキャプチャやデバイスの制御を行う可能性のある他のアプリ(Google Play によってインストールされたアプリ、またはデバイスのメーカーによってシステム パーティションにプリロードされたアプリではない)がデバイスで実行されていることを意味します。
改善
CLOSE_UNKNOWN_ACCESS_RISK ダイアログを表示して、画面をキャプチャしたりデバイスを制御したりする可能性のある不明なアプリをすべて閉じるようユーザーに求めることができます。ユーザーが Close all ボタンをタップすると、そのようなアプリがすべて閉じられます。
UX の例
 
    CLOSE_ALL_ACCESS_RISK(タイプコード 3)
判定に関する問題
environmentDetails.appAccessRiskVerdict.appsDetected に "KNOWN_CAPTURING"、"KNOWN_CONTROLLING"、"UNKNOWN_CAPTURING"、"UNKNOWN_CONTROLLING" のいずれかが含まれている場合、デバイスで実行されているアプリが画面をキャプチャしたり、デバイスを制御したりしている可能性があります。
改善
CLOSE_ALL_ACCESS_RISK ダイアログを表示して、画面をキャプチャしたりデバイスを制御したりする可能性のあるすべてのアプリを閉じるようユーザーに求めることができます。ユーザーが Close all ボタンをタップすると、デバイス上のそのようなアプリがすべて閉じられます。
UX の例
 
    GET_INTEGRITY(タイプコード 4)
判定に関する問題
このダイアログは、次のいずれかの問題に適しています。
- デバイスの完全性が低い: - deviceRecognitionVerdictに- MEETS_DEVICE_INTEGRITYが含まれていない場合、デバイスが正規の認定済み Android デバイスでない可能性があります。たとえば、デバイスのブートローダーがロック解除されている場合や、読み込まれた Android OS が認定メーカーのイメージでない場合などに、このエラーが発生することがあります。
- 不正アクセス: - appLicensingVerdict: "UNLICENSED"。これは、ユーザー アカウントがアプリの利用資格を持っていないことを意味します。これは、ユーザーがアプリをサイドローディングした場合や、Google Play 以外のアプリストアから取得した場合に発生する可能性があります。
- 改ざんされたアプリ: - appRecognitionVerdict: "UNRECOGNIZED_VERSION"。これは、アプリのバイナリが変更されているか、Google Play で認識されているバージョンではないことを意味します。
- クライアント側の例外: Integrity API リクエスト中に修正可能な例外が発生した場合。修復可能な例外は、 - PLAY_SERVICES_VERSION_OUTDATED、- NETWORK_ERROR、- PLAY_SERVICES_NOT_FOUNDなどのエラーコードを含む Integrity API の例外です。- exception.isRemediable()メソッドを使用して、例外がダイアログで修正可能かどうかを確認できます。
改善
GET_INTEGRITY ダイアログは、単一の継続的なフロー内で複数の修復手順を処理することで、ユーザー エクスペリエンスを効率化するように設計されています。これにより、ユーザーはさまざまな問題を解決するために複数の個別のダイアログを操作する必要がなくなります。
ダイアログをリクエストすると、ターゲットの判定問題のうちどれが存在するかを自動的に検出し、適切な修正手順を提供します。つまり、1 つのダイアログ リクエストで、次のような複数の問題を一度に解決できます。
- デバイスの完全性: デバイスの完全性に関する問題が検出された場合、ダイアログには、デバイスのセキュリティ ステータスを改善して MEETS_DEVICE_INTEGRITY判定の要件を満たすための手順が表示されます。
- アプリの完全性: 不正アクセスやアプリの改ざんなどの問題が検出された場合、ダイアログには、問題を解決するために Google Play ストアからアプリを取得するようユーザーに促すメッセージが表示されます。
- クライアント側の例外: ダイアログで、Integrity API の例外を引き起こした根本的な問題を確認し、解決を試みます。たとえば、古いバージョンの Google Play 開発者サービスを更新するようユーザーに求めることがあります。
UX の例
 
    GET_STRONG_INTEGRITY(タイプコード 5)
判定に関する問題
このダイアログは、GET_INTEGRITY が対処するすべての同じ問題を解決するように設計されています。さらに、デバイスが MEETS_STRONG_INTEGRITY 判定を受け取れない問題を解決し、Google Play プロテクトの判定の問題を解決する機能も追加されています。
改善
GET_STRONG_INTEGRITY は、単一の継続的なフロー内で複数の修復手順を処理することで、ユーザー エクスペリエンスを合理化するように設計されています。ダイアログでは、次のような住所に適用可能な整合性に関する問題が自動的にチェックされます。
- デバイスの完全性: デバイスの完全性に関する問題が検出された場合、ダイアログには、デバイスのセキュリティ ステータスを改善して MEETS_STRONG_INTEGRITY判定の要件を満たすための手順が表示されます。
- Play プロテクトのステータス: - playProtectVerdictに問題が示されている場合、ダイアログに問題の解決方法が表示されます。- Play プロテクトが無効になっている場合(playProtectVerdict == POSSIBLE_RISK)、ダイアログで有効にしてデバイス上のすべてのアプリをスキャンするようユーザーに求められます。
- 有害なアプリが検出された場合(playProtectVerdict == MEDIUM_RISKまたはHIGH_RISK)、ダイアログで Google Play プロテクトを使用してアンインストールするようユーザーに指示します。
 
- Play プロテクトが無効になっている場合(
- アプリの完全性: 不正アクセスやアプリの改ざんなどの問題が検出された場合、問題を解決するために Google Play ストアからアプリを入手するようユーザーに促すダイアログが表示されます。 
- クライアント側の例外: このダイアログでは、Integrity API の例外を引き起こした根本的な問題の解決も試みます。たとえば、Google Play 開発者サービスが無効になっていることが判明した場合、ユーザーに有効にするよう求めるメッセージが表示されることがあります。修復可能な例外は、 - PLAY_SERVICES_VERSION_OUTDATED、- NETWORK_ERROR、- PLAY_SERVICES_NOT_FOUNDなどのエラーコードを含む Integrity API の例外です。- exception.isRemediable()メソッドを使用して、ダイアログでエラーを修正できるかどうかを確認できます。
UX の例
 
    