이 페이지에서는 무결성 확인 결과 문제를 처리하는 방법을 설명합니다.
무결성 토큰을 요청한 후 사용자에게 Google Play 대화상자를 표시할 수 있습니다. 무결성 확인 결과에 하나 이상의 문제가 있거나 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 }
자바
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) }
자바
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)
확인 결과 문제
이 대화상자는 다음 두 가지 문제에 적합합니다.
- 무단 액세스:
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
가 포함되지 않은 경우 기기가 정품 Play 프로텍트 인증 Android 기반 기기가 아닐 수 있습니다. 예를 들어 기기의 부트로더가 잠금 해제되었거나 로드된 Android OS가 인증된 제조업체 이미지가 아닌 경우 이러한 상황이 발생할 수 있습니다.무단 액세스:
appLicensingVerdict: "UNLICENSED"
. 이는 사용자 계정에 앱에 대한 사용 권한이 없음을 의미하며, 사용자가 앱을 사이드로드했거나 Google Play가 아닌 다른 앱 스토어에서 앱을 획득한 경우에 발생할 수 있습니다.조작된 앱:
appRecognitionVerdict: "UNRECOGNIZED_VERSION"
이는 앱의 바이너리가 수정되었거나 Google Play에서 인식하는 버전이 아님을 의미합니다.클라이언트 측 예외: 무결성 API 요청 중에 해결 가능한 예외가 발생합니다. 해결 가능한 예외는
PLAY_SERVICES_VERSION_OUTDATED
,NETWORK_ERROR
,PLAY_SERVICES_NOT_FOUND
등의 오류 코드가 있는 Integrity API 예외입니다.exception.isRemediable()
메서드를 사용하여 대화상자로 예외를 수정할 수 있는지 확인할 수 있습니다.
해결
GET_INTEGRITY
대화상자는 단일 연속 흐름 내에서 여러 수정 단계를 처리하여 사용자의 환경을 간소화하도록 설계되었습니다. 이렇게 하면 사용자가 여러 개별 대화상자와 상호작용하여 여러 문제를 수정하지 않아도 됩니다.
대화상자를 요청하면 타겟팅된 평결 문제 중 어떤 문제가 있는지 자동으로 감지하고 적절한 해결 단계를 제공합니다. 즉, 단일 대화상자 요청으로 다음을 비롯한 여러 문제를 한 번에 해결할 수 있습니다.
- 기기 무결성: 기기 무결성 문제가 감지되면 대화상자에서 사용자에게
MEETS_DEVICE_INTEGRITY
확인 결과의 요구사항을 충족하도록 기기의 보안 상태를 개선하도록 안내합니다. - 앱 무결성: 무단 액세스 또는 앱 조작과 같은 문제가 감지되면 대화상자에서 사용자에게 Play 스토어에서 앱을 획득하여 문제를 해결하도록 안내합니다.
- 클라이언트 측 예외: 대화상자에서 Integrity API 예외를 일으킨 기본 문제를 확인하고 해결하려고 시도합니다. 예를 들어 오래된 버전의 Google Play 서비스를 업데이트하라는 메시지가 사용자에게 표시될 수 있습니다.
UX 예시

GET_STRONG_INTEGRITY (유형 코드 5)
확인 결과 문제
이 대화상자는 GET_INTEGRITY에서 다루는 모든 동일한 문제를 해결하도록 설계되었으며, 기기에서 MEETS_STRONG_INTEGRITY
평결을 수신하지 못하도록 하는 문제를 해결하고 Play 프로텍트 평결 문제를 해결하는 기능이 추가되었습니다.
해결
GET_STRONG_INTEGRITY
는 단일 연속 흐름 내에서 여러 수정 단계를 처리하여 사용자의 환경을 간소화하도록 설계되었습니다. 대화상자에서는 다음을 비롯한 주소에 적용 가능한 무결성 문제를 자동으로 확인합니다.
- 기기 무결성: 기기 무결성 문제가 감지되면 대화상자에서 사용자에게
MEETS_STRONG_INTEGRITY
확인 결과의 요구사항을 충족하도록 기기의 보안 상태를 개선하도록 안내합니다. Play 프로텍트 상태:
playProtectVerdict
에 문제가 표시되면 대화상자에서 사용자에게 문제 해결 방법을 안내합니다.- Play 프로텍트가 사용 중지된 경우 (
playProtectVerdict == POSSIBLE_RISK
) 대화상자에서 사용자에게 사용 설정하고 기기의 모든 앱을 검사하라는 메시지를 표시합니다. - 유해한 앱이 감지되면 (
playProtectVerdict == MEDIUM_RISK
또는HIGH_RISK
) 대화상자에서 Google Play 프로텍트를 사용하여 앱을 제거하도록 안내합니다.
- Play 프로텍트가 사용 중지된 경우 (
앱 무결성: 무단 액세스 또는 앱 조작과 같은 문제가 감지되면 대화상자에서 사용자에게 Play 스토어에서 앱을 획득하여 문제를 해결하라는 메시지를 표시합니다.
클라이언트 측 예외: 대화상자에서는 무결성 API 예외를 일으킨 기본 문제도 해결하려고 시도합니다. 예를 들어 Google Play 서비스가 사용 중지된 것으로 확인되면 사용자에게 사용 설정하라는 메시지가 표시될 수 있습니다. 수정 가능한 예외는
PLAY_SERVICES_VERSION_OUTDATED
,NETWORK_ERROR
,PLAY_SERVICES_NOT_FOUND
과 같은 오류 코드가 있는 Integrity API 예외입니다.exception.isRemediable()
메서드를 사용하여 대화상자로 오류를 수정할 수 있는지 확인할 수 있습니다.
UX 예시
