本文档应作为您使用 AMAPI SDK 接收设备信任信号的主要指南。
AMAPI SDK 使您的应用能够从 Android 设备政策 (ADP) 应用访问设备信任信号。然后,您的应用可以使用这些信号来计算设备的信任状态,并根据需要执行业务逻辑。
前提条件
- 您的应用必须获得授权才能读取设备信任信号:
- AMAPI 管理的设备上的配套应用:在政策中分配了
COMPANION_APP角色的应用可以访问设备信任信号,而无需在全托管式设备上或从工作资料内部(访问权限仅限于来自工作资料的信号)列入许可清单。如需了解详情,请参阅管理应用角色、政策配置和从配套应用访问信号。 - 所有其他应用和环境:如需在个人资料中、在非受管设备上、在由自定义 DPC 管理的设备上或在没有
COMPANION_APP角色的情况下访问设备信任信号,您的应用必须在许可清单中。如需了解如何申请,请前往设备信任信号访问权限页面。
- AMAPI 管理的设备上的配套应用:在政策中分配了
- Android Enterprise 建议您将 Play Integrity API 套件集成到客户端应用中,并在读取和依赖设备信任信号之前参考结果。未通过 Play Integrity API 检查的设备不应被信任,也不应使用从该设备获取的任何信号来确定信任状态。如需了解详情,请参阅 Play Integrity 的文档。
在应用中集成 AMAPI SDK
如需访问设备信任信号,您的应用必须与 AMAPI SDK 集成。如需详细了解此库以及如何将其添加到应用中,请参阅 AMAPI SDK 集成指南。
添加所需权限
Device Trust from Android Enterprise API 返回的部分信号要求应用声明访问这些信息所需的相同权限,具体如下:
| 信号 | 所需权限 |
|---|---|
| 网络状态 | ACCESS_NETWORK_STATE |
| 屏锁复杂度 | REQUEST_PASSWORD_COMPLEXITY |
如果应用的 AndroidManifest.xml 中未包含这些权限,Android Enterprise API 的 Device Trust 将在相关信号的元数据中返回 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) } 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.. 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 调用,以免安装 Android 设备政策 模态对话框让用户感到意外。如果无法从前台进程调用(因为这是 Web 流程,并且 Android 组件没有界面),则允许从后台调用,但要求在初始配置用户体验流程中进行调用。
正确设置环境后,便可以访问设备信任信号。请参阅访问设备信任信号。
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) } 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.. 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 使用建议
创建 deviceClient 实例(或 EnvironmentClient 实例)会与 Android Device Policy (ADP) 应用建立通信通道,该通道在空闲时会自动解除绑定。由于客户端实例不公开显式关停方法,因此创建和舍弃多个实例会留下孤立的通道,这些通道会被垃圾回收。
为了优化性能并避免孤立通道,请使用 DeviceClientFactory.create(context.applicationContext) 在应用范围内创建 DeviceClient 的单个实例,并将其缓存以在整个应用中重复使用,例如作为应用范围内的单例或使用依赖项注入。
如需访问您感兴趣的设备信任信号,请使用缓存的 deviceClient 实例请求 Device 对象:
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) }
Java
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, t.toString()); } }, MoreExecutors.directExecutor());
从配套应用访问信号
在全托管式设备上,或在通过 Android Management API (AMAPI) 管理的工作资料中,分配了 COMPANION_APP 角色的应用无需在许可清单中注册即可读取设备信任信号。
从工作资料(在公司自有设备或个人设备上)内部调用时,您的应用必须在工作资料内部运行,并且访问权限仅限于来自工作资料的信号(例如,管理状态、屏幕锁定复杂度和已安装的应用仅反映工作资料)。
1. 在政策中分配 COMPANION_APP 角色
配置 EMM 政策,以在 applications 政策列表中向您的应用授予 roleType: "COMPANION_APP":
2. 直接使用 DeviceClient 读取信号
使用 DeviceClientFactory.create 在应用范围内创建并缓存单个 deviceClient 实例(请参阅 DeviceClient 使用建议),然后直接请求 Device 对象:
Kotlin
import android.content.Context import android.util.Log import com.google.android.managementapi.device.DeviceClient import com.google.android.managementapi.device.DeviceClientFactory import com.google.android.managementapi.device.model.GetDeviceRequest // Instantiate once at application scope (for example, using dependency injection) // to reuse a single DeviceClient instance across your app. class DeviceTrustRepository(context: Context) { private val deviceClient: DeviceClient = DeviceClientFactory.create(context.applicationContext) suspend fun readSignalsFromCompanionApp() { runCatching { deviceClient.getDeviceAwait(GetDeviceRequest.getDefaultInstance()) }.onSuccess { device -> Log.d(TAG, "Device signals: $device") }.onFailure { t -> Log.e(TAG, "Failed to read device signals", t) } } companion object { private const val TAG = "DeviceTrustRepository" } }
Java
import android.content.Context; import android.util.Log; 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.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; // Instantiate once at application scope (for example, using dependency injection) // to reuse a single DeviceClient instance across your app. public class DeviceTrustRepository { private static final String TAG = "DeviceTrustRepository"; private final DeviceClient deviceClient; public DeviceTrustRepository(Context context) { this.deviceClient = DeviceClientFactory.create(context.getApplicationContext()); } public void readSignalsFromCompanionApp() { ListenableFuturedeviceFuture = deviceClient.getDevice(GetDeviceRequest.getDefaultInstance()); Futures.addCallback(deviceFuture, new FutureCallback () { @Override public void onSuccess(Device device) { Log.d(TAG, "Device signals: " + device); } @Override public void onFailure(Throwable t) { Log.e(TAG, "Failed to read device signals", t); } }, MoreExecutors.directExecutor()); } }
处理错误
如果您的应用发出设备信任请求,但调用失败,则应用会收到异常。这些异常可能由多种原因导致,例如:
SecurityException- 您的应用无权发出请求(例如,您的应用未在许可清单中注册 [如果需要]、未在 AMAPI 管理的设备上分配COMPANION_APP角色,或作为不在许可清单中的配套应用调用了prepareEnvironment)。请参阅设备信任信号访问权限和从配套应用访问信号。AmapiSdkException的子类 - ADP 不存在或未准备就绪。这可能是由暂时性情况引起的,您应该重试调用。
重试策略
对于因暂时性情况导致的异常,请使用指数退避算法实现重试策略。第一次失败后,先延迟 5 秒钟,然后再重试。
实现重试策略,将尝试次数上限作为退出条件,每次都会以指数方式增加延迟时间(10 秒、20 秒等)。
跟踪 ADP 应用安装期间的用户互动
如果设备需要在 prepareEnvironment 期间安装 ADP 应用,您的应用可以通过实现 NotificationReceiverService 来跟踪用户互动,以便接收替换 getPrepareEnvironmentListener 的通知:
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"); } } }
已知问题
目前没有已知问题。