Device Trust from Android Enterprise : guide d'intégration

Ce document doit être votre guide principal pour l'utilisation du SDK AMAPI afin de recevoir des signaux de confiance de l'appareil.

Le SDK AMAPI permet à votre application (que nous appelons parfois application "compagnon") d'accéder aux signaux de fiabilité de l'appareil à partir de l'application ADP (Android Device Policy). Votre application peut ensuite utiliser ces signaux pour calculer l'état de fiabilité de l'appareil et appliquer la logique métier de votre choix.

Prérequis

  • L'accès aux signaux de confiance de l'appareil est limité pour empêcher toute utilisation non autorisée. Pour savoir comment faire votre demande, consultez la page Accès aux signaux de confiance des appareils.
  • Android Enterprise recommande d'intégrer la suite d'API Play Integrity à votre application cliente et de consulter le résultat avant de lire et d'utiliser les signaux de confiance de l'appareil. Les appareils qui échouent aux vérifications de l'API Play Integrity ne doivent pas être considérés comme fiables, ni les signaux dérivés de l'appareil utilisé pour déterminer la posture de confiance. Pour en savoir plus, consultez la documentation de Play Integrity.

Intégrer le SDK AMAPI à votre application

Pour accéder aux signaux de confiance de l'appareil, votre application doit s'intégrer au SDK AMAPI. Pour en savoir plus sur cette bibliothèque et sur la façon de l'ajouter à votre application, consultez le guide d'intégration du SDK AMAPI.

Ajouter les autorisations requises

Certains des signaux renvoyés par l'API Device Trust from Android Enterprise exigent que l'application déclare la même autorisation que celle requise pour accéder à ces informations en premier lieu, en particulier :

Signal Autorisation requise
État du réseau ACCESS_NETWORK_STATE
Complexité du verrouillage de l'écran REQUEST_PASSWORD_COMPLEXITY

Si ces autorisations ne sont pas incluses dans le AndroidManifest.xml de l'application, l'API Device Trust from Android Enterprise renverra PERMISSION_ISSUE dans les métadonnées du signal associé :

internalDeviceSettings=DeviceSettings{
  screenLockComplexity=COMPLEXITY_UNSPECIFIED,
  internalScreenLockComplexityMetadata=Metadata{
    dataIssues=[
      DataIssue{
        issueType=PERMISSION_ISSUE,
        issueLevel=WARNING,
        issueDetails=IssueDetailsCase{none}
      }
    ]
  },

Pour en savoir plus, consultez la liste des signaux de confiance des appareils disponibles.

Étapes à suivre pour accéder aux signaux de confiance des appareils

Les applications qui souhaitent accéder aux signaux de confiance de l'appareil doivent vérifier que l'environnement client est à jour et le mettre à jour si nécessaire.

Pour accéder aux signaux de confiance des appareils :

Étapes à suivre pour accéder aux signaux de confiance des appareils

Vérifier l'environnement client

L'exemple de code suivant montre comment utiliser getEnvironment pour lire l'état actuel de l'application ADP. Votre application peut ensuite créer un deviceClient pour accéder aux signaux de confiance des appareils si l'environnement est prêt et à jour (voir Accéder aux signaux de confiance des appareils).

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 = 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)
}

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();

    ImmutableList roles = 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());
}

Si l'application ADP est installée, mais pas à jour, votre application doit appeler prepareEnvironment pour mettre à jour l'application ADP de manière silencieuse, sans intervention de l'utilisateur.

Si l'application ADP n'est pas installée, votre application peut appeler prepareEnvironment pour inviter l'utilisateur à l'installer. Consultez Préparer l'environnement client.

Préparer l'environnement client

  • Si l'application ADP est déjà installée, l'API la mettra à jour en mode silencieux, sans intervention de l'utilisateur.

  • Si l'application ADP n'est pas installée, l'API invite l'utilisateur à accepter l'installation de l'application ADP.

Installer Android Device Policy

Il est possible d'enregistrer un rappel pour surveiller le choix de l'utilisateur. Pour en savoir plus, consultez Suivre l'interaction de l'utilisateur lors de l'installation de l'application ADP.

Nous vous recommandons d'effectuer l'appel prepareEnvironment à partir d'un processus de premier plan, pendant le flux UX d'intégration, afin d'éviter de surprendre l'utilisateur avec la boîte de dialogue modale Installer Android Device Policy. Si l'appel à partir d'un processus de premier plan n'est pas possible, car il s'agit d'un flux Web et que le composant Android n'a pas d'UI, l'appel à partir de l'arrière-plan est autorisé à condition qu'il se produise pendant le flux UX d'intégration.

Une fois l'environnement correctement configuré, il est possible d'accéder aux signaux de confiance de l'appareil. Consultez Accéder aux signaux de confiance des appareils.

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 = 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())
}

Java

try {
    ComponentName myNotificationReceiverService = new ComponentName(
            context,
            MyNotificationReceiverService.class
    );

    ImmutableList roles = 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());
}

Accéder aux signaux de confiance des appareils

Pour accéder aux signaux de confiance de l'appareil qui vous intéressent, vous pouvez utiliser l'instance deviceClient vue à l'étape précédente pour demander l'objet Device.

Kotlin

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())
}

Java

try {
    ListenableFuture deviceFuture =
            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());
}

Suivre l'interaction des utilisateurs lors de l'installation de l'application ADP

Si l'appareil doit installer l'application ADP pendant l'état prepareEnvironment, votre application peut suivre l'interaction de l'utilisateur en implémentant un NotificationReceiverService pour recevoir des notifications en remplaçant 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");
        }
    }
}

Problèmes connus

Il n'y a aucun problème connu pour le moment.