이 문서는 기기 신뢰 신호를 수신하기 위한 목적으로 AMAPI SDK를 사용하는 데 있어 기본 가이드가 되어야 합니다.
AMAPI SDK를 사용하면 애플리케이션 (때로는 '동반' 앱이라고도 함)이 ADP (Android 기기 정책) 앱의 기기 신뢰 신호에 액세스할 수 있습니다. 그러면 앱이 이러한 신호를 사용하여 기기의 신뢰 상태를 계산하고 선택한 비즈니스 로직을 실행할 수 있습니다.
기본 요건
- 무단 사용을 방지하기 위해 기기 신뢰 신호에 대한 액세스가 제한됩니다. 신청 방법에 관한 자세한 내용은 기기 신뢰 신호 액세스 페이지를 참고하세요.
- Android Enterprise에서는 기기 신뢰 신호를 읽고 이에 의존하기 전에 Play Integrity API 모음을 클라이언트 애플리케이션에 통합하고 결과를 참조할 것을 권장합니다. Play Integrity API 검사에 실패한 기기나 신뢰 태도 결정을 내리는 데 사용된 기기에서 파생된 신호는 신뢰할 수 없습니다. 자세한 내용은 Play Integrity 문서를 참고하세요.
애플리케이션에서 AMAPI SDK와 통합
애플리케이션이 기기 신뢰 신호에 액세스하려면 AMAPI SDK와 통합해야 합니다. 이 라이브러리 및 애플리케이션에 추가하는 방법에 관한 자세한 내용은 AMAPI SDK 통합 가이드를 참고하세요.
필수 권한 추가
Android Enterprise API의 기기 신뢰에서 반환되는 일부 신호는 앱이 이 정보에 액세스하는 데 필요한 것과 동일한 권한을 선언해야 합니다. 특히 다음이 해당됩니다.
신호 | 필요한 권한 |
---|---|
네트워크 상태 | ACCESS_NETWORK_STATE |
화면 잠금 복잡도 | REQUEST_PASSWORD_COMPLEXITY |
이러한 권한이 앱의 AndroidManifest.xml
에 포함되지 않으면 Android Enterprise API의 기기 신뢰가 관련 신호의 메타데이터에서 PERMISSION_ISSUE
을 반환합니다.
internalDeviceSettings=DeviceSettings{
screenLockComplexity=COMPLEXITY_UNSPECIFIED,
internalScreenLockComplexityMetadata=Metadata{
dataIssues=[
DataIssue{
issueType=PERMISSION_ISSUE,
issueLevel=WARNING,
issueDetails=IssueDetailsCase{none}
}
]
},
자세한 내용은 사용 가능한 기기 신뢰 신호 목록을 참고하세요.
기기 신뢰 신호에 액세스하는 단계
기기 신뢰 신호에 액세스하려는 애플리케이션은 클라이언트 환경이 최신인지 확인하고 필요한 경우 업데이트해야 합니다.
기기 트러스트 신호에 액세스하는 단계는 다음과 같습니다.
클라이언트 환경 확인
다음 코드 예에서는 getEnvironment
를 사용하여 ADP 앱의 현재 상태를 읽는 방법을 보여줍니다. 그러면 환경이 준비되고 최신 상태인 경우 애플리케이션에서 deviceClient
를 만들어 기기 신뢰 신호에 액세스할 수 있습니다 (기기 신뢰 신호 액세스 참고).
Kotlin
import com.google.android.managementapi.common.model.Role import com.google.android.managementapi.device.DeviceClient import com.google.android.managementapi.device.DeviceClientFactory import com.google.android.managementapi.device.model.GetDeviceRequest import com.google.android.managementapi.environment.EnvironmentClient import com.google.android.managementapi.environment.EnvironmentClientFactory import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.INSTALLED import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.NOT_INSTALLED import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.READY import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.Version.UP_TO_DATE import com.google.android.managementapi.environment.model.GetEnvironmentRequest import com.google.android.managementapi.environment.model.PrepareEnvironmentRequest try { val context = applicationContext val roles = listOf(Role.builder().setRoleType(Role.RoleType.IDENTITY_PROVIDER).build()) val request = GetEnvironmentRequest.builder().setRoles(roles).build() val environmentClient = EnvironmentClientFactory.create(context) val environmentResponse = environmentClient.getEnvironment(request) if (environmentResponse.hasAndroidDevicePolicyEnvironment()) { val adpEnvironment = environmentResponse.androidDevicePolicyEnvironment if (adpEnvironment.state == READY && adpEnvironment.version == UP_TO_DATE) { // AMAPI Environment State OK, Version OK. Requesting Device signals.. checkDevice(deviceClient = DeviceClientFactory.create(context)) } else if (adpEnvironment.state == INSTALLED) { // prepareEnvironment should be called, calling // prepareEnvironment won't show the UI prepareEnvironment(context, environmentClient) } else if (adpEnvironment.state == NOT_INSTALLED) { // prepareEnvironment should be called, calling // prepareEnvironment will show the UI prepareEnvironment(context, environmentClient) } } } catch (e: Exception) { Log.e(TAG, "Exception", e) }
자바
import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.INSTALLED; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.NOT_INSTALLED; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.READY; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.Version.UP_TO_DATE; import com.google.android.managementapi.common.model.Role; import com.google.android.managementapi.device.DeviceClient; import com.google.android.managementapi.device.DeviceClientFactory; import com.google.android.managementapi.device.model.Device; import com.google.android.managementapi.device.model.GetDeviceRequest; import com.google.android.managementapi.environment.EnvironmentClient; import com.google.android.managementapi.environment.EnvironmentClientFactory; import com.google.android.managementapi.environment.model.Environment; import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment; import com.google.android.managementapi.environment.model.GetEnvironmentRequest; import com.google.android.managementapi.environment.model.PrepareEnvironmentRequest; import com.google.android.managementapi.environment.model.PrepareEnvironmentResponse; try { Context context = getApplicationContext(); ImmutableListroles = new ImmutableList.Builder () .add(Role.builder() .setRoleType(Role.RoleType.IDENTITY_PROVIDER) .build()) .build(); EnvironmentClient environmentClient = EnvironmentClientFactory.create(context); GetEnvironmentRequest request = GetEnvironmentRequest.builder() .setRoles(roles) .build(); ListenableFuture getEnvironmentFuture = environmentClient.getEnvironmentAsync(request); Futures.addCallback(getEnvironmentFuture, new FutureCallback<>() { @Override public void onSuccess(Environment environment) { AndroidDevicePolicyEnvironment adpEnvironment = environment.getAndroidDevicePolicyEnvironment(); AndroidDevicePolicyEnvironment.State state = adpEnvironment.getState(); AndroidDevicePolicyEnvironment.Version version = adpEnvironment.getVersion(); if (state == READY && version == UP_TO_DATE) { // AMAPI Environment State OK, Version OK. Requesting Device signals.. DeviceClient deviceClient = DeviceClientFactory.create(context); checkDevice(deviceClient); } else if (state == INSTALLED) { // prepareEnvironment should be called, calling // prepareEnvironment won't show the UI prepareEnvironment(context, environmentClient); } else if (state == NOT_INSTALLED) { // prepareEnvironment should be called, calling // prepareEnvironment will show the UI prepareEnvironment(context, environmentClient); } } @Override public void onFailure(Throwable t) { Log.d(TAG, t.toString()); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.d(TAG, e.toString()); }
ADP 앱이 설치되었지만 최신 버전이 아닌 경우 애플리케이션은 사용자 개입 없이 ADP 앱을 자동으로 업데이트하기 위해 prepareEnvironment
를 호출해야 합니다.
ADP 앱이 설치되어 있지 않으면 애플리케이션에서 prepareEnvironment
를 호출하여 사용자에게 ADP 앱을 설치하라는 메시지를 표시할 수 있습니다. 클라이언트 환경 준비를 참고하세요.
클라이언트 환경 준비
ADP 앱이 이미 설치되어 있으면 API가 사용자 개입 없이 자동으로 업데이트합니다.
ADP 앱이 설치되어 있지 않으면 API에서 사용자에게 ADP 앱 설치를 수락하라는 메시지를 표시합니다.
사용자 선택을 모니터링하는 콜백을 등록할 수 있습니다. 자세한 내용은 ADP 앱 설치 중 사용자 상호작용 추적을 참고하세요.
Android Device Policy 설치 모달 대화상자로 사용자를 놀라게 하지 않으려면 온보딩 UX 흐름 중에 포그라운드 프로세스에서 prepareEnvironment
호출을 실행하는 것이 좋습니다.
포그라운드 프로세스에서 호출할 수 없는 경우(웹 흐름이고 Android 구성요소에 UI가 없기 때문) 온보딩 UX 흐름 중에 발생하는 경우 백그라운드에서 호출이 허용됩니다.
환경이 올바르게 설정되면 기기 신뢰 신호에 액세스할 수 있습니다. 기기 신뢰 신호 액세스를 참고하세요.
Kotlin
try { val myNotificationReceiverService = ComponentName( context, MyNotificationReceiverService::class.java ) val roles = listOf(Role.builder().setRoleType(Role.RoleType.IDENTITY_PROVIDER).build()) val request = PrepareEnvironmentRequest.builder().setRoles(roles).build() val response = environmentClient.prepareEnvironment(request, myNotificationReceiverService) val environment = response.environment val adpEnvironment = environment.androidDevicePolicyEnvironment val state = adpEnvironment.state val version = adpEnvironment.version if (state == READY && version == UP_TO_DATE) { // Environment is prepared, access device posture signals using // DeviceClient. checkDevice(deviceClient = DeviceClientFactory.create(context)) } else { // The prepareEnvironment call failed to prepare Log.w( TAG, "AMAPI environment was not ready: " + state + " - " + version ) } } catch (e: java.lang.Exception) { Log.d(TAG, e.toString()) }
자바
try { ComponentName myNotificationReceiverService = new ComponentName( context, MyNotificationReceiverService.class ); ImmutableListroles = new ImmutableList.Builder () .add(Role.builder() .setRoleType(Role.RoleType.IDENTITY_PROVIDER) .build()) .build(); PrepareEnvironmentRequest request = PrepareEnvironmentRequest.builder() .setRoles(roles) .build(); ListenableFuture environmentFuture = environmentClient.prepareEnvironmentAsync( request, myNotificationReceiverService ); Futures.addCallback(environmentFuture, new FutureCallback<>() { @Override public void onSuccess(PrepareEnvironmentResponse response) { Environment environment = response.getEnvironment(); AndroidDevicePolicyEnvironment adpEnvironment = environment.getAndroidDevicePolicyEnvironment(); AndroidDevicePolicyEnvironment.State state = adpEnvironment.getState(); AndroidDevicePolicyEnvironment.Version version = adpEnvironment.getVersion(); if (state == READY && version == UP_TO_DATE) { // AMAPI Environment State OK, Version OK. Requesting Device signals.. DeviceClient deviceClient = DeviceClientFactory.create(context); checkDevice(deviceClient); } else { // The prepareEnvironment call failed to prepare Log.w( TAG, "AMAPI environment was not ready: " + adpEnvironment.getState() + " - " + adpEnvironment.getVersion() ); } } @Override public void onFailure(@NonNull Throwable t) { // Handle the error Log.d(TAG, "AMAPI response did not contain an ADP environment"); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.d(TAG, e.toString()); }
기기 트러스트 신호 액세스
관심 있는 기기 신뢰 신호에 액세스하려면 이전 단계에서 본 deviceClient
인스턴스를 사용하여 Device
객체를 요청하면 됩니다.
Kotlin
try { kotlin.runCatching { deviceClient.getDeviceAwait(GetDeviceRequest.getDefaultInstance()) }.onFailure { t -> Log.d(TAG, t.toString()) }.onSuccess { device -> // Access device posture signals available in device val deviceString = device.toString() Log.d(TAG, deviceString) } } catch (e: java.lang.Exception) { Log.d(TAG, e.toString()) }
자바
try { ListenableFuturedeviceFuture = deviceClient.getDevice(GetDeviceRequest.getDefaultInstance()); Futures.addCallback(deviceFuture, new FutureCallback () { @Override public void onSuccess(Device device) { // Access device posture signals available in device String deviceString = device.toString(); Log.d(TAG, deviceString); } @Override public void onFailure(Throwable t) { Log.d(TAG, Log.d(TAG, t.toString()); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.d(TAG, e.toString()); }
ADP 앱 설치 중 사용자 상호작용 추적
기기에서 prepareEnvironment
중에 ADP 앱을 설치해야 하는 경우 애플리케이션은 getPrepareEnvironmentListener
를 재정의하는 알림을 수신하기 위해 NotificationReceiverService
를 구현하여 사용자 상호작용을 추적할 수 있습니다.
Kotlin
import android.util.Log import com.google.android.managementapi.environment.EnvironmentListener import com.google.android.managementapi.environment.model.EnvironmentEvent.EventCase.Kind.ANDROID_DEVICE_POLICY_INSTALL_CONSENT_ACCEPTED import com.google.android.managementapi.environment.model.EnvironmentEvent import com.google.android.managementapi.notification.NotificationReceiverService class MyNotificationReceiverService : NotificationReceiverService() { override fun getPrepareEnvironmentListener(): EnvironmentListener { return MyEnvironmentListener() } } class MyEnvironmentListener : EnvironmentListener { override fun onEnvironmentEvent( event: EnvironmentEvent ) { if (event.event.kind == ANDROID_DEVICE_POLICY_INSTALL_CONSENT_ACCEPTED) { Log.d(TAG, "User provided install consent") } else { Log.d(TAG, "User rejected install consent") } } companion object { private val TAG: String = MyEnvironmentListener::class.java.simpleName } }
자바
import static com.google.android.managementapi.environment.model.EnvironmentEvent.EventCase.Kind.ANDROID_DEVICE_POLICY_INSTALL_CONSENT_ACCEPTED; import android.util.Log; import androidx.annotation.NonNull; import com.google.android.managementapi.environment.EnvironmentListener; import com.google.android.managementapi.environment.model.EnvironmentEvent; import com.google.android.managementapi.notification.NotificationReceiverService; class MyNotificationReceiverService extends NotificationReceiverService { @NonNull @Override protected EnvironmentListener getPrepareEnvironmentListener() { return new MyEnvironmentListener(); } } class MyEnvironmentListener implements EnvironmentListener { final private String TAG = MyEnvironmentListener.class.getSimpleName(); @Override public void onEnvironmentEvent(EnvironmentEvent event) { if (event.getEvent().getKind() == ANDROID_DEVICE_POLICY_INSTALL_CONSENT_ACCEPTED) { Log.d(TAG, "User provided install consent"); } else { Log.d(TAG, "User rejected install consent"); } } }
알려진 문제
현재 알려진 문제가 없습니다.