Questo documento deve essere la tua guida principale per l'utilizzo dell'SDK AMAPI ai fini della ricezione degli indicatori di attendibilità del dispositivo.
L'SDK AMAPI consente alla tua applicazione di accedere agli indicatori di attendibilità del dispositivo dall'app Android Device Policy (ADP). La tua app può quindi utilizzare questi indicatori per calcolare lo stato di attendibilità del dispositivo e applicare la logica di business scelta.
Prerequisiti
- La tua applicazione deve essere autorizzata a leggere gli indicatori di attendibilità del dispositivo:
- App complementari sui dispositivi gestiti da AMAPI: le applicazioni a cui è assegnato il ruolo
COMPANION_APPnella policy possono accedere agli indicatori di attendibilità del dispositivo senza essere incluse nella lista consentita sui dispositivi completamente gestiti o da un profilo di lavoro (con accesso limitato agli indicatori del profilo di lavoro). Per maggiori dettagli, consulta Gestisci i ruoli delle applicazioni, Configurazione delle policy e Accedi agli indicatori da un'app complementare. - Tutte le altre applicazioni e ambienti: l'accesso agli indicatori di attendibilità del dispositivo
da un profilo personale, su un dispositivo non gestito, su un dispositivo
gestito da un DPC personalizzato o senza il ruolo
COMPANION_APPrichiede che l'applicazione sia inclusa nella lista consentita. Per informazioni su come fare domanda, vai alla pagina Accesso agli indicatori di attendibilità dei dispositivi.
- App complementari sui dispositivi gestiti da AMAPI: le applicazioni a cui è assegnato il ruolo
- Android Enterprise consiglia di integrare la suite di API Play Integrity nella tua applicazione client e di fare riferimento al risultato prima di leggere e fare affidamento sugli indicatori di attendibilità del dispositivo. I dispositivi che non superano i controlli dell'API Play Integrity non devono essere considerati attendibili, né devono esserlo gli indicatori derivati dal dispositivo utilizzati per determinare la postura di attendibilità. Per maggiori dettagli, puoi consultare la documentazione di Play Integrity.
Integra l'SDK AMAPI nella tua applicazione
Per accedere agli indicatori di attendibilità del dispositivo, l'applicazione deve essere integrata con l'SDK AMAPI. Puoi trovare ulteriori informazioni su questa libreria e su come aggiungerla alla tua applicazione nella guida all'integrazione dell'SDK AMAPI.
Aggiungi le autorizzazioni richieste
Alcuni degli indicatori restituiti dall'API Device Trust di Android Enterprise richiedono che l'app dichiari la stessa autorizzazione che sarebbe necessaria per accedere a queste informazioni, in particolare:
| Indicatore | Autorizzazione obbligatoria |
|---|---|
| Stato della rete | ACCESS_NETWORK_STATE |
| Complessità del blocco schermo | REQUEST_PASSWORD_COMPLEXITY |
Se queste autorizzazioni non sono incluse nel file AndroidManifest.xml dell'app, l'API Device Trust di Android Enterprise restituirà PERMISSION_ISSUE nei metadati dell'indicatore correlato:
internalDeviceSettings=DeviceSettings{
screenLockComplexity=COMPLEXITY_UNSPECIFIED,
internalScreenLockComplexityMetadata=Metadata{
dataIssues=[
DataIssue{
issueType=PERMISSION_ISSUE,
issueLevel=WARNING,
issueDetails=IssueDetailsCase{none}
}
]
},
Per maggiori dettagli, consulta l'elenco degli indicatori di attendibilità dei dispositivi disponibili.
Passaggi per accedere agli indicatori di attendibilità del dispositivo
Le applicazioni incluse nella lista consentita (incluse le app in esecuzione all'interno di un profilo personale o su un dispositivo non gestito) devono verificare che l'ambiente client sia aggiornato e prepararlo, se necessario, prima di richiedere gli indicatori.
I passaggi per consentire alle applicazioni nella lista consentita di accedere agli indicatori di attendibilità del dispositivo sono:
- Verifica l'ambiente client
- Prepara l'ambiente client
- Accedi agli indicatori di attendibilità del dispositivo

Verifica l'ambiente client
Il seguente esempio di codice mostra come utilizzare getEnvironment per leggere lo stato attuale dell'app ADP. L'applicazione può quindi utilizzare un'istanza deviceClient memorizzata nella cache per accedere agli indicatori di attendibilità del dispositivo se l'ambiente è pronto e aggiornato (consulta Accedi agli indicatori di attendibilità del dispositivo).
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()); }
Se l'app ADP è installata, ma non è aggiornata, l'applicazione deve chiamare
prepareEnvironment per aggiornare l'app ADP in modalità invisibile senza l'intervento dell'utente.
Se l'app ADP non è installata, la tua applicazione può chiamare prepareEnvironment
per chiedere all'utente di installare l'app ADP. Consulta
Prepara l'ambiente client.
Prepara l'ambiente client
Se l'app ADP è già installata, l'API la aggiornerà automaticamente senza l'intervento dell'utente.
Se l'app ADP non è installata, l'API chiederà all'utente di accettare l'installazione dell'app ADP.
È possibile registrare un callback per monitorare la scelta dell'utente. Per ulteriori dettagli, consulta Monitora l'interazione dell'utente durante l'installazione dell'app ADP.
Ti consigliamo di effettuare la chiamata prepareEnvironment da un processo in primo piano, durante il flusso UX di onboarding, per evitare di sorprendere l'utente con la finestra di dialogo modale Installa Android Device Policy.
Se non è possibile chiamare da un processo in primo piano, perché si tratta di un flusso web e il componente Android non ha un'UI, è consentito chiamare dallo sfondo, a condizione che ciò avvenga durante il flusso UX di onboarding.
Una volta configurato correttamente l'ambiente, è possibile accedere agli indicatori di attendibilità del dispositivo. Consulta la sezione Accedi agli indicatori di attendibilità del dispositivo.
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()); }
Accedi agli indicatori di attendibilità del dispositivo
Suggerimento sull'utilizzo di DeviceClient
La creazione di un'istanza deviceClient (o un'istanza EnvironmentClient) stabilisce un canale di comunicazione con l'app Android Device Policy (ADP) che si scollega automaticamente quando è inattiva. Poiché le istanze client non espongono un metodo di arresto esplicito, la creazione e l'eliminazione di più istanze lasciano canali orfani che devono essere sottoposti a garbage collection.
Per ottimizzare le prestazioni ed evitare canali orfani, crea una singola istanza di DeviceClient nell'ambito dell'applicazione utilizzando DeviceClientFactory.create(context.applicationContext) e memorizzala nella cache per riutilizzarla in tutta l'applicazione, ad esempio come singleton con ambito dell'applicazione o utilizzando l'inserimento delle dipendenze.
Per accedere agli indicatori di attendibilità del dispositivo che ti interessano, utilizza l'istanza deviceClient memorizzata nella cache per richiedere l'oggetto 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());
Accedi agli indicatori da un'app complementare
Sui dispositivi completamente gestiti o all'interno di un profilo di lavoro gestito con l'API Android Management (AMAPI), un'applicazione a cui è assegnato il ruolo COMPANION_APP può leggere gli indicatori di attendibilità del dispositivo senza essere registrata nella lista consentita.
Se viene chiamata dall'interno di un profilo di lavoro (su un dispositivo di proprietà dell'azienda o personale), l'app deve essere eseguita all'interno del profilo di lavoro e l'accesso è limitato agli indicatori del profilo di lavoro (ad esempio, lo stato di gestione, la complessità del blocco schermo e le applicazioni installate riflettono solo il profilo di lavoro).
1. Assegna il ruolo COMPANION_APP nella policy
Configura la policy EMM per concedere roleType: "COMPANION_APP" alla tua applicazione nell'elenco delle policy applications:
- Per configurare il manifest dell'app e, facoltativamente, ascoltare le assegnazioni dei ruoli, consulta Gestisci i ruoli dell'applicazione.
- Per la sintassi di configurazione delle policy, consulta Configurazione delle policy.
2. Leggi gli indicatori direttamente con DeviceClient
Crea e memorizza nella cache una singola istanza deviceClient nell'ambito dell'applicazione utilizzando
DeviceClientFactory.create (consulta
Suggerimento per l'utilizzo di DeviceClient), quindi
richiedi direttamente l'oggetto 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()); } }
Gestisci gli errori
Se la tua app effettua una richiesta di attendibilità del dispositivo e la chiamata non va a buon fine, l'app riceve un'eccezione. Queste eccezioni possono verificarsi per vari motivi, ad esempio:
SecurityException: la tua app non è autorizzata a effettuare la richiesta (ad esempio, non è registrata nella lista consentita quando richiesto, non le è stato assegnato il ruoloCOMPANION_APPsu un dispositivo gestito da AMAPI o ha chiamatoprepareEnvironmentcome app complementare non inclusa nella lista consentita). Consulta Accesso agli indicatori di attendibilità del dispositivo e Accedi agli indicatori da un'app complementare.- Una sottoclasse di
AmapiSdkException- ADP non è presente o non è pronta. Questo errore può essere causato da una condizione temporanea, quindi devi riprovare a effettuare la chiamata.
Strategie di ripetizione dei tentativi
Per le eccezioni dovute a una condizione temporanea, implementa una strategia di nuovo tentativo con un backoff esponenziale. Dopo il primo errore, inizia con un ritardo iniziale di 5 secondi prima di riprovare.
Implementa una strategia di nuovi tentativi con un numero massimo di tentativi come condizione di uscita utilizzando un ritardo aumentato in modo esponenziale ogni volta (10 secondi, 20 secondi e così via).
Monitora l'interazione dell'utente durante l'installazione dell'app ADP
Se il dispositivo deve installare l'app ADP durante la prepareEnvironment, la tua applicazione può monitorare l'interazione dell'utente implementando un NotificationReceiverService per ricevere notifiche che eseguono l'override di 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"); } } }
Problemi noti
Al momento non sono presenti problemi noti.