Ten dokument powinien być Twoim głównym przewodnikiem po korzystaniu z pakietu AMAPI SDK do odbierania sygnałów zaufania urządzenia.
Pakiet AMAPI SDK umożliwia aplikacji dostęp do sygnałów zaufania urządzenia z aplikacji Android Device Policy (ADP). Aplikacja może następnie używać tych sygnałów do obliczania stanu zaufania urządzenia i wdrażania wybranej logiki biznesowej.
Wymagania wstępne
- Twoja aplikacja musi mieć uprawnienia do odczytu sygnałów zaufania urządzenia:
- Aplikacje towarzyszące na urządzeniach zarządzanych przez AMAPI: aplikacje, którym w zasadach przypisano rolę
COMPANION_APP, mogą uzyskiwać dostęp do sygnałów zaufania urządzenia bez konieczności umieszczania ich na liście dozwolonych na w pełni zarządzanych urządzeniach lub w profilu służbowym (z dostępem ograniczonym do sygnałów z profilu służbowego). Więcej informacji znajdziesz w artykułach Zarządzanie rolami aplikacji, Konfiguracja zasad i Uzyskiwanie dostępu do sygnałów z aplikacji towarzyszącej. - Wszystkie inne aplikacje i środowiska: dostęp do sygnałów zaufania urządzenia z profilu osobistego, na urządzeniu niezarządzanym, na urządzeniu zarządzanym przez niestandardowy kontroler zasad urządzeń lub bez roli
COMPANION_APPwymaga, aby aplikacja znajdowała się na liście dozwolonych. Informacje o tym, jak się zgłosić, znajdziesz na stronie Dostęp do sygnałów zaufania urządzenia.
- Aplikacje towarzyszące na urządzeniach zarządzanych przez AMAPI: aplikacje, którym w zasadach przypisano rolę
- Android Enterprise zaleca zintegrowanie pakietu interfejsów Play Integrity API z aplikacją kliencką i odwoływanie się do wyniku przed odczytaniem sygnałów zaufania urządzenia i poleganiem na nich. Urządzenia, które nie przejdą testów interfejsu Play Integrity API, nie powinny być uznawane za zaufane. To samo dotyczy sygnałów pochodzących z urządzenia, które są używane do określania stanu zaufania. Więcej informacji znajdziesz w dokumentacji Play Integrity.
Integracja z pakietem AMAPI SDK w aplikacji
Aby uzyskać dostęp do sygnałów zaufania urządzenia, aplikacja musi zostać zintegrowana z pakietem SDK AMAPI. Więcej informacji o tej bibliotece i sposobie dodawania jej do aplikacji znajdziesz w przewodniku po integracji pakietu AMAPI SDK.
Dodawanie wymaganych uprawnień
Niektóre sygnały zwracane przez interfejs Device Trust od Android Enterprise API wymagają, aby aplikacja deklarowała te same uprawnienia, które byłyby wymagane do uzyskania dostępu do tych informacji, w szczególności:
| Sygnał | Wymagane uprawnienie |
|---|---|
| Stan sieci | ACCESS_NETWORK_STATE |
| Złożoność blokady ekranu | REQUEST_PASSWORD_COMPLEXITY |
Jeśli te uprawnienia nie są uwzględnione w pliku AndroidManifest.xml aplikacji, interfejs Device Trust od Android Enterprise API zwróci wartość PERMISSION_ISSUE w metadanych powiązanego sygnału:
internalDeviceSettings=DeviceSettings{
screenLockComplexity=COMPLEXITY_UNSPECIFIED,
internalScreenLockComplexityMetadata=Metadata{
dataIssues=[
DataIssue{
issueType=PERMISSION_ISSUE,
issueLevel=WARNING,
issueDetails=IssueDetailsCase{none}
}
]
},
Więcej informacji znajdziesz na liście dostępnych sygnałów Device Trust.
Uzyskiwanie dostępu do sygnałów zaufania urządzenia
Aplikacje z listy dozwolonych (w tym aplikacje działające w profilu osobistym lub na urządzeniu niezarządzanym) muszą sprawdzić, czy środowisko klienta jest aktualne, i w razie potrzeby przygotować je przed wysłaniem żądania sygnałów.
Aby aplikacje z listy dozwolonych mogły uzyskać dostęp do sygnałów zaufania urządzenia, wykonaj te czynności:
- Weryfikowanie środowiska klienta
- Przygotowywanie środowiska klienta
- Dostęp do sygnałów zaufania urządzenia

Weryfikacja środowiska klienta
Poniższy przykładowy kod pokazuje, jak używać getEnvironment do odczytywania bieżącego stanu aplikacji ADP. Twoja aplikacja może następnie używać zapisanej w pamięci podręcznej instancji deviceClient, aby uzyskać dostęp do sygnałów zaufania urządzenia, jeśli środowisko jest gotowe i aktualne (patrz Uzyskiwanie dostępu do sygnałów zaufania urządzenia).
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()); }
Jeśli aplikacja ADP jest zainstalowana, ale nie jest aktualna, Twoja aplikacja powinna wywołać funkcję prepareEnvironment, aby dyskretnie zaktualizować aplikację ADP bez interwencji użytkownika.
Jeśli aplikacja ADP nie jest zainstalowana, Twoja aplikacja może wywołać prepareEnvironment, aby poprosić użytkownika o jej zainstalowanie. Więcej informacji znajdziesz w sekcji Przygotowywanie środowiska klienta.
Przygotowywanie środowiska klienta
Jeśli aplikacja ADP jest już zainstalowana, interfejs API zaktualizuje ją bez interwencji użytkownika.
Jeśli aplikacja ADP nie jest zainstalowana, interfejs API poprosi użytkownika o zaakceptowanie jej instalacji.
Można zarejestrować wywołanie zwrotne, aby monitorować wybór użytkownika. Więcej informacji znajdziesz w artykule Śledzenie interakcji użytkownika podczas instalacji aplikacji ADP.
Zalecamy, aby wywołanie prepareEnvironment było wykonywane z procesu na pierwszym planie podczas procesu UX wprowadzania, aby nie zaskoczyć użytkownika oknem modalnym Zainstaluj zasady dotyczące urządzeń z Androidem.
Jeśli wywołanie z procesu na pierwszym planie nie jest możliwe, ponieważ jest to proces internetowy, a komponent Androida nie ma interfejsu, wywołanie z tła jest dozwolone, pod warunkiem że następuje podczas procesu wprowadzania UX.
Po prawidłowym skonfigurowaniu środowiska można uzyskać dostęp do sygnałów zaufania urządzenia. Zobacz Uzyskiwanie dostępu do sygnałów zaufania urządzenia.
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()); }
Dostęp do sygnałów zaufania urządzenia
Rekomendacja dotycząca korzystania z DeviceClient
Utworzenie instancji deviceClient (lub EnvironmentClient) powoduje ustanowienie kanału komunikacyjnego z aplikacją Android Device Policy (ADP), który automatycznie się rozłącza, gdy jest bezczynny. Instancje klienta nie udostępniają jawnej metody wyłączania, więc tworzenie i odrzucanie wielu instancji pozostawia osierocone kanały do usunięcia przez moduł odśmiecania pamięci.
Aby zoptymalizować wydajność i uniknąć osieroconych kanałów, utwórz pojedynczą instancję DeviceClient w zakresie aplikacji za pomocą DeviceClientFactory.create(context.applicationContext) i zapisz ją w pamięci podręcznej do ponownego użycia w aplikacji – na przykład jako singleton w zakresie aplikacji lub za pomocą wstrzykiwania zależności.
Aby uzyskać dostęp do interesujących Cię sygnałów zaufania do urządzenia, użyj instancji deviceClient z pamięci podręcznej i poproś o obiekt 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());
Sygnały dostępu z aplikacji towarzyszącej
Na w pełni zarządzanych urządzeniach lub w profilu służbowym zarządzanym za pomocą interfejsu Android Management API (AMAPI) aplikacja z przypisaną rolą COMPANION_APP może odczytywać sygnały Device Trust bez konieczności rejestrowania na liście dozwolonych.
Gdy aplikacja jest wywoływana z profilu służbowego (na urządzeniu należącym do firmy lub na urządzeniu osobistym), musi działać w profilu służbowym, a dostęp jest ograniczony do sygnałów z profilu służbowego (np. stan zarządzania, złożoność blokady ekranu i zainstalowane aplikacje odzwierciedlają tylko profil służbowy).
1. Przypisywanie roli COMPANION_APP w zasadzie
Skonfiguruj zasady EMM, aby przyznać uprawnienie roleType: "COMPANION_APP" aplikacji na liście zasad applications:
- Aby dowiedzieć się, jak skonfigurować plik manifestu aplikacji i opcjonalnie nasłuchiwać przypisań ról, zapoznaj się z artykułem Zarządzanie rolami aplikacji.
- Składnię konfiguracji zasad znajdziesz w artykule na temat konfiguracji zasad.
2. Odczytywanie sygnałów bezpośrednio za pomocą DeviceClient
Utwórz i zapisz w pamięci podręcznej pojedynczą instancję deviceClient w zakresie aplikacji, używając DeviceClientFactory.create (patrz zalecenia dotyczące korzystania z DeviceClient), a następnie poproś bezpośrednio o obiekt 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()); } }
Obsługuj błędy
Jeśli aplikacja wysyła żądanie zaufania urządzenia, a wywołanie się nie powiedzie, aplikacja otrzyma wyjątek. Te wyjątki mogą wystąpić z różnych powodów, takich jak:
SecurityException– Twoja aplikacja nie ma uprawnień do wysyłania żądania (np. nie jest zarejestrowana na liście dozwolonych, gdy jest to wymagane, nie ma przypisanej roliCOMPANION_APPna urządzeniu zarządzanym przez AMAPI lub wywołałaprepareEnvironmentjako aplikacja towarzysząca, która nie znajduje się na liście dozwolonych). Zobacz dostęp do sygnałów zaufania urządzenia i dostęp do sygnałów z aplikacji towarzyszącej.- Podklasa
AmapiSdkException– ADP jest niedostępna lub niegotowa. Może to być spowodowane stanem przejściowym. Spróbuj ponownie nawiązać połączenie.
Strategie ponawiania
W przypadku wyjątków spowodowanych stanem przejściowym wdróż strategię ponawiania prób ze wzrastającym czasem do ponowienia. Po pierwszym niepowodzeniu odczekaj 5 sekund przed ponowną próbą.
Wdróż strategię ponawiania z maksymalną liczbą prób jako warunkiem zakończenia, stosując za każdym razem wykładniczo zwiększane opóźnienie (10 s, 20 s itd.).
Śledzenie interakcji użytkownika podczas instalacji aplikacji ADP
Jeśli urządzenie musi zainstalować aplikację ADP podczas prepareEnvironment, Twoja aplikacja może śledzić interakcje użytkownika, implementując NotificationReceiverService, aby otrzymywać powiadomienia zastępujące 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"); } } }
Znane problemy
Obecnie nie ma żadnych znanych problemów.