Dieses Dokument ist Ihr primärer Leitfaden für die Verwendung des AMAPI SDK zum Empfangen von Signalen zur Gerätevertrauenswürdigkeit.
Mit dem AMAPI SDK kann Ihre Anwendung auf Device-Trust-Signale der Android Device Policy-App (ADP) zugreifen. Ihre App kann diese Signale dann verwenden, um den Vertrauensstatus des Geräts zu berechnen und die gewünschte Geschäftslogik anzuwenden.
Vorbereitung
- Ihre Anwendung muss autorisiert sein, Device-Trust-Signale zu lesen:
- Companion-Apps auf AMAPI-verwalteten Geräten: Anwendungen, denen in der Richtlinie die Rolle
COMPANION_APPzugewiesen ist, können auf Vertrauenssignale des Geräts zugreifen, ohne auf vollständig verwalteten Geräten oder in einem Arbeitsprofil auf der Zulassungsliste zu stehen. Der Zugriff ist dabei auf Signale aus dem Arbeitsprofil beschränkt. Weitere Informationen finden Sie unter Anwendungsrollen verwalten, Richtlinienkonfiguration und Auf Signale von einer Companion-App zugreifen. - Alle anderen Anwendungen und Umgebungen: Für den Zugriff auf Vertrauenssignale von Geräten über ein privates Profil, auf einem nicht verwalteten Gerät, auf einem von einem benutzerdefinierten DPC verwalteten Gerät oder ohne die Rolle
COMPANION_APPmuss Ihre Anwendung auf der Zulassungsliste stehen. Informationen zur Beantragung finden Sie auf der Seite Zugriff auf Device-Trust-Signale.
- Companion-Apps auf AMAPI-verwalteten Geräten: Anwendungen, denen in der Richtlinie die Rolle
- Android Enterprise empfiehlt, die Play Integrity-APIs in Ihre Clientanwendung zu integrieren und das Ergebnis zu prüfen, bevor Sie die Vertrauenssignale des Geräts lesen und sich darauf verlassen. Geräten, die die Play Integrity API-Prüfungen nicht bestehen, sollte nicht vertraut werden. Das gilt auch für Signale, die vom Gerät stammen und zur Bestimmung des Vertrauensstatus verwendet werden. Weitere Informationen finden Sie in der Dokumentation zu Play Integrity.
AMAPI SDK in Ihre Anwendung einbinden
Damit Ihre Anwendung auf die Signale zur Gerätevertrauenswürdigkeit zugreifen kann, muss sie in das AMAPI SDK eingebunden werden. Weitere Informationen zu dieser Bibliothek und dazu, wie Sie sie Ihrer Anwendung hinzufügen, finden Sie im Integrationsleitfaden für das AMAPI SDK.
Erforderliche Berechtigungen hinzufügen
Für einige der von der Device Trust from Android Enterprise API zurückgegebenen Signale muss die App dieselbe Berechtigung deklarieren, die für den Zugriff auf diese Informationen erforderlich wäre, insbesondere:
| Signal | Erforderliche Berechtigung |
|---|---|
| Netzwerkstatus | ACCESS_NETWORK_STATE |
| Komplexität der Displaysperre | REQUEST_PASSWORD_COMPLEXITY |
Wenn diese Berechtigungen nicht in der AndroidManifest.xml der App enthalten sind, gibt die Device Trust from Android Enterprise API in den Metadaten des zugehörigen Signals PERMISSION_ISSUE zurück:
internalDeviceSettings=DeviceSettings{
screenLockComplexity=COMPLEXITY_UNSPECIFIED,
internalScreenLockComplexityMetadata=Metadata{
dataIssues=[
DataIssue{
issueType=PERMISSION_ISSUE,
issueLevel=WARNING,
issueDetails=IssueDetailsCase{none}
}
]
},
Weitere Informationen finden Sie in der Liste der verfügbaren Device Trust-Signale.
Schritte zum Zugriff auf die Device-Trust-Signale
Anwendungen auf der Zulassungsliste (einschließlich Apps, die in einem privaten Profil oder auf einem nicht verwalteten Gerät ausgeführt werden) müssen prüfen, ob die Clientumgebung auf dem neuesten Stand ist, und sie gegebenenfalls vorbereiten, bevor Signale angefordert werden.
So können Anwendungen auf der Zulassungsliste auf die Device-Trust-Signale zugreifen:

Clientumgebung prüfen
Im folgenden Codebeispiel wird gezeigt, wie Sie mit getEnvironment den aktuellen Status der ADP-App lesen. Ihre Anwendung kann dann eine im Cache gespeicherte deviceClient-Instanz verwenden, um auf die Device-Trust-Signale zuzugreifen, wenn die Umgebung bereit und auf dem neuesten Stand ist (siehe Auf Device-Trust-Signale zugreifen).
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()); }
Wenn die ADP-App installiert, aber nicht auf dem neuesten Stand ist, sollte deine Anwendung prepareEnvironment aufrufen, um die ADP-App ohne Nutzerinteraktion im Hintergrund zu aktualisieren.
Wenn die ADP-App nicht installiert ist, kann Ihre Anwendung prepareEnvironment aufrufen, um den Nutzer zur Installation der ADP-App aufzufordern. Weitere Informationen finden Sie unter Clientumgebung vorbereiten.
Clientumgebung vorbereiten
Wenn die ADP-App bereits installiert ist, wird sie von der API ohne Eingreifen des Nutzers im Hintergrund aktualisiert.
Wenn die ADP-App nicht installiert ist, wird der Nutzer von der API aufgefordert, die Installation der ADP-App zu akzeptieren.
Sie können einen Callback registrieren, um die Nutzerauswahl zu überwachen. Weitere Informationen finden Sie unter Nutzerinteraktion während der Installation der ADP-App erfassen.
Wir empfehlen, den Aufruf prepareEnvironment während des Onboarding-UX-Prozesses über einen Vordergrundprozess auszuführen, damit der Nutzer nicht von dem modalen Dialogfeld Android Device Policy installieren überrascht wird.
Wenn ein Aufruf aus einem Vordergrundprozess nicht möglich ist, weil es sich um einen Web-Flow handelt und die Android-Komponente keine Benutzeroberfläche hat, ist ein Aufruf aus dem Hintergrund zulässig, sofern dies während des Onboarding-UX-Flows geschieht.
Sobald die Umgebung korrekt eingerichtet ist, können Sie auf die Device-Trust-Signale zugreifen. Weitere Informationen finden Sie unter Auf Device-Trust-Signale zugreifen.
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()); }
Auf Device-Trust-Signale zugreifen
Empfehlung zur DeviceClient-Nutzung
Wenn Sie eine deviceClient-Instanz (oder eine EnvironmentClient-Instanz) erstellen, wird ein Kommunikationskanal mit der Android-Geräterichtlinien-App (ADP) eingerichtet, der automatisch getrennt wird, wenn er inaktiv ist. Da Clientinstanzen keine explizite Methode zum Herunterfahren bereitstellen, werden beim Erstellen und Verwerfen mehrerer Instanzen verwaiste Kanäle hinterlassen, die von der automatischen Speicherbereinigung entfernt werden müssen.
Um die Leistung zu optimieren und verwaiste Kanäle zu vermeiden, erstellen Sie eine einzelne Instanz von DeviceClient im Anwendungsbereich mit DeviceClientFactory.create(context.applicationContext) und speichern Sie sie im Cache, um sie in der gesamten Anwendung wiederzuverwenden, z. B. als Singleton im Anwendungsbereich oder mithilfe von Dependency Injection.
Wenn Sie auf die gewünschten Device-Trust-Signale zugreifen möchten, fordern Sie mit Ihrer im Cache gespeicherten deviceClient-Instanz das Device-Objekt an:
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());
Zugriffssignale von einer Companion-App
Auf vollständig verwalteten Geräten oder in einem Arbeitsprofil, das mit der Android Management API (AMAPI) verwaltet wird, kann eine Anwendung, der die Rolle COMPANION_APP zugewiesen ist, Device-Trust-Signale lesen, ohne auf der Zulassungsliste registriert zu sein.
Wenn Ihre App innerhalb eines Arbeitsprofils (entweder auf einem unternehmenseigenen oder einem privaten Gerät) aufgerufen wird, muss sie innerhalb des Arbeitsprofils ausgeführt werden. Der Zugriff ist auf Signale aus dem Arbeitsprofil beschränkt (z. B. spiegeln der Verwaltungsstatus, die Komplexität der Displaysperre und die installierten Anwendungen nur das Arbeitsprofil wider).
1. Weisen Sie die Rolle COMPANION_APP in der Richtlinie zu.
Konfigurieren Sie Ihre EMM-Richtlinie so, dass Ihrer Anwendung in der Richtlinienliste applications die Berechtigung roleType: "COMPANION_APP" gewährt wird:
- Informationen zum Konfigurieren des Manifests Ihrer Anwendung und optional zum Überwachen von Rollenzuweisungen finden Sie unter Anwendungsrollen verwalten.
- Informationen zur Syntax der Richtlinienkonfiguration finden Sie unter Richtlinienkonfiguration.
2. Signale direkt mit DeviceClient lesen
Erstellen Sie eine einzelne deviceClient-Instanz im Anwendungsbereich mit DeviceClientFactory.create (siehe Empfehlung zur Verwendung von DeviceClient) und speichern Sie sie im Cache. Fordern Sie dann das Device-Objekt direkt an:
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()); } }
Fehler verarbeiten
Wenn Ihre App eine Anfrage zur Gerätevertrauenswürdigkeit stellt und der Aufruf fehlschlägt, erhält Ihre App eine Ausnahme. Diese Ausnahmen können verschiedene Ursachen haben, z. B.:
SecurityException: Ihre App ist nicht berechtigt, die Anfrage zu stellen. Beispielsweise ist Ihre App nicht auf der Zulassungsliste registriert, wenn dies erforderlich ist, ihr ist nicht die RolleCOMPANION_APPauf einem von der AMAPI verwalteten Gerät zugewiesen oder sie hatprepareEnvironmentals Companion-App aufgerufen, die nicht auf der Zulassungsliste steht. Weitere Informationen finden Sie unter Zugriff auf Device-Trust-Signale und Zugriff auf Signale über eine Companion-App.- Eine Unterklasse von
AmapiSdkException– ADP ist nicht vorhanden oder nicht bereit. Dies kann durch einen vorübergehenden Zustand verursacht werden. Sie sollten den Aufruf wiederholen.
Wiederholungsstrategien
Implementieren Sie für Ausnahmen, die auf einen vorübergehenden Zustand zurückzuführen sind, eine Wiederholungsstrategie mit exponentiellem Backoff. Nach dem ersten Fehler wird mit einer anfänglichen Verzögerung von 5 Sekunden begonnen, bevor der Vorgang wiederholt wird.
Implementieren Sie eine Wiederholungsstrategie mit einer maximalen Anzahl von Versuchen als Abbruchbedingung und einer exponentiell zunehmenden Verzögerung (10 s, 20 s usw.).
Nutzerinteraktion während der Installation der ADP-App erfassen
Wenn die ADP-App während des prepareEnvironment auf dem Gerät installiert werden muss, kann Ihre Anwendung die Nutzerinteraktion verfolgen, indem sie einen NotificationReceiverService implementiert, um Benachrichtigungen zu erhalten, die getPrepareEnvironmentListener überschreiben:
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"); } } }
Bekannte Probleme
Derzeit sind keine Probleme bekannt.