このドキュメントは、デバイスの信頼シグナルを受信することを目的とした AMAPI SDK の使用に関する主要なガイドです。
AMAPI SDK を使用すると、アプリケーション(「コンパニオン」アプリと呼ばれることもあります)が ADP(Android Device Policy)アプリからデバイスの信頼シグナルにアクセスできます。アプリはこれらのシグナルを使用してデバイスの信頼状態を計算し、選択したビジネス ロジックを実行できます。
前提条件
- デバイスのトラスト シグナルへのアクセスは、不正使用を防ぐために制限されています。申請方法については、デバイス トラスト シグナルへのアクセスのページをご覧ください。
- 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 に含まれていない場合、Device Trust from 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) }
Java
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 アプリがインストールされているが最新版ではない場合、アプリケーションは prepareEnvironment を呼び出して、ユーザーの操作なしで ADP アプリをサイレント アップデートする必要があります。
ADP アプリがインストールされていない場合、アプリケーションは prepareEnvironment を呼び出して、ユーザーに ADP アプリのインストールを促すことができます。クライアント環境を準備するをご覧ください。
クライアント環境を準備する
ADP アプリがすでにインストールされている場合、API はユーザーの操作なしで自動的に更新します。
ADP アプリがインストールされていない場合、API はユーザーに ADP アプリのインストールを許可するよう求めます。
コールバックを登録して、ユーザーの選択をモニタリングできます。詳しくは、ADP アプリのインストール中のユーザー操作をトラッキングするをご覧ください。
prepareEnvironment 呼び出しは、オンボーディング UX フローの間にフォアグラウンド プロセスから行うことをおすすめします。これにより、Android デバイス ポリシーをインストール モーダル ダイアログがユーザーに表示されるのを防ぐことができます。フォアグラウンド プロセスからの呼び出しが不可能な場合(ウェブフローであり、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()) }
Java
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()) }
Java
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()); }
エラーを処理する
アプリがデバイス信頼リクエストを行い、呼び出しが失敗すると、アプリは例外を受け取ります。このような例外は、次のようなさまざまな理由で発生する可能性があります。
SecurityException- デバイス トラスト シグナルにアクセスするための登録がアプリにありません。デバイス トラスト シグナルへのアクセスをご覧ください。AmapiSdkExceptionのサブクラス - ADP が存在しないか、準備ができていません。これは一時的な事象が原因である可能性があるため、呼び出しを再試行する必要があります。
再試行手段
一時的な状態による例外については、指数バックオフを使用して再試行戦略を実装します。最初に失敗した後、最初に 5 秒遅延してから再試行します。
試行のたびに指数関数的に増加する遅延(10 秒、20 秒など)を使用して、終了条件として最大試行回数を設定した再試行手段をおすすめします。
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 } }
Java
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"); } } }
既知の問題
現時点では、報告されている問題はありません。