本文档应作为您使用 AMAPI SDK 接收设备信任信号的主要指南。
AMAPI SDK 可让您的应用(我们有时也称之为“配套”应用)访问 ADP(Android 设备政策)应用中的设备信任信号。然后,您的应用可以使用这些信号计算设备的信任状态,并根据所选的业务逻辑执行操作。
前提条件
- 设备搭载 Android 9 或更高版本。
- 为防止未经授权的使用,我们对该 API 的访问权限进行了限制。如需详细了解受支持的用例以及如何申请,请参阅:API 访问权限。您的请求获得批准后,注册 API 访问权限部分会详细介绍相关技术要求。
- Android Enterprise 建议您将 Play Integrity 套件 API 集成到客户端应用中,并在读取设备信任信号并依赖于这些信号之前,先参考结果。不应信任未通过 Play Integrity API 检查的设备,也不应信任从设备派生出来用于确定信任状态的信号。如需了解详情,请参阅 Play Integrity 文档。
注册以获取 API 访问权限
- 使用您拥有的现有 Cloud 项目,然后从第 2 步开始操作;或者创建新的 Cloud Platform 项目:
- 在 Google Cloud 控制台中,前往“项目”页面
- 点击
NEW PROJECT
- 记下项目 ID,您在注册应用时需要用到此 ID。
- 为您的项目启用 Android Management API。
- 在 Google Cloud 控制台中,选择要用于 AMAPI SDK 集成的项目;可以是现有项目,也可以是第 1 步中创建的项目。
- 在 API 库中搜索并选择 Android Management API
- 前往“API 库”页面
- 点击
Enable
- 请使用反馈表单提供以下详细信息。如果您想使用不同的软件包名称 / 签名测试发布 build 和调试 build,也可以共享多个软件包名称。
package_name
:要集成 AMAPI SDK 的应用的软件包名称。certificate_hash_sha256
:应用签名证书的 SHA256 哈希值project_id
:在第 1 步中创建的 Cloud 项目的 ID。
示例数据
package_name = 'com.abc.xyz'
certificate_hash_sha256 = '03675ac53ff9cd1535ccc7dfcdfa2c458c5218371f418dc136f2d19ac1fbe8a5'
project_id = "example-id"
在应用中与 AMAPI SDK 集成
如需访问设备信任信号,您的应用必须与 AMAPI SDK 集成。如需详细了解此库以及如何将其添加到应用中,请参阅 AMAPI SDK 集成指南。
添加所需权限
从 Device Trust from 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
来访问设备信任信号(请参阅访问设备信任信号)。
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 应用已安装但不是最新版本,您的应用应调用 prepareEnvironment
以在不干扰用户的情况下静默更新 ADP 应用。
如果未安装 ADP 应用,您的应用可以调用 prepareEnvironment
以提示用户安装 ADP 应用。请参阅准备客户端环境。
准备客户端环境
如果 ADP 应用已安装,该 API 将静默更新该应用,无需用户干预。
如果未安装 ADP 应用,API 会提示用户接受安装 ADP 应用。
您可以注册回调来监控用户的选择。如需了解详情,请参阅跟踪 ADP 应用安装期间的用户互动。
我们建议在初始配置用户体验流程中通过前台进程执行 prepareEnvironment
调用,以免用户意外看到 Install Android Device Policy 模态对话框。如果由于这是 Web 流程且 Android 组件没有界面而无法从前台进程进行调用,则允许从后台进行调用,但要求是在初始配置用户体验流程中进行调用。
正确设置环境后,即可访问设备信任信号。请参阅访问设备信任信号。
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
对象。
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 应用,您的应用可以跟踪用户注册服务以接收通知的互动:
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"); } } }
已知问题
目前,如果不启用应用缩减功能,就无法使用 JaCoCo 进行覆盖率测试:
debug {
// Set to true generates an exception when minify is false
enableAndroidTestCoverage = false
enableUnitTestCoverage = true
// Enable to make the library work with JaCoCo
isMinifyEnabled = false
}