Livre de recettes sur les appareils dédiés

Ce livre de recettes aide les développeurs et les intégrateurs système à améliorer leur solution d'appareil dédié. Suivez nos recettes pratiques pour trouver des solutions aux comportements liés aux appareils dédiés. Ce livre de recettes fonctionne mieux pour les développeurs qui disposent déjà d'une application d'appareil dédiée. Si vous débutez, consultez la présentation des appareils dédiés.

Applications Home personnalisées

Ces recettes sont utiles si vous développez une application pour remplacer l'écran d'accueil et le lanceur d'applications Android.

Devenez l'appli d'accueil

Vous pouvez définir votre application comme application d'accueil de l'appareil afin qu'elle se lance automatiquement au démarrage de l'appareil. Vous pouvez également activer le bouton Accueil pour mettre au premier plan l'application que vous avez ajoutée à la liste d'autorisation en mode tâches verrouillées.

Toutes les applications d'accueil gèrent la catégorie d'intent CATEGORY_HOME, ce qui permet au système de reconnaître une application d'accueil. Pour devenir l'application d'accueil par défaut, définissez l'une des activités de votre application en tant que gestionnaire d'intent Home préféré, en appelant DevicePolicyManager.addPersistentPreferredActivity() comme illustré dans l'exemple suivant:

Kotlin

// Create an intent filter to specify the Home category.
val filter = IntentFilter(Intent.ACTION_MAIN)
filter.addCategory(Intent.CATEGORY_HOME)
filter.addCategory(Intent.CATEGORY_DEFAULT)

// Set the activity as the preferred option for the device.
val activity = ComponentName(context, KioskModeActivity::class.java)
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE)
        as DevicePolicyManager
dpm.addPersistentPreferredActivity(adminName, filter, activity)

Java

// Create an intent filter to specify the Home category.
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);

// Set the activity as the preferred option for the device.
ComponentName activity = new ComponentName(context, KioskModeActivity.class);
DevicePolicyManager dpm =
    (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
dpm.addPersistentPreferredActivity(adminName, filter, activity);

Vous devez toujours déclarer le filtre d'intent dans le fichier manifeste de votre application, comme indiqué dans l'extrait de code XML suivant:

<activity
        android:name=".KioskModeActivity"
        android:label="@string/kiosk_mode"
        android:launchMode="singleInstance"
        android:excludeFromRecents="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.HOME"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

En règle générale, vous ne souhaitez pas que votre application de lancement s'affiche sur l'écran "Aperçu". Toutefois, vous n'avez pas besoin d'ajouter excludeFromRecents à la déclaration d'activité, car le lanceur d'applications Android masque l'activité lancée initialement lorsque le système s'exécute en mode tâches verrouillées.

Afficher les tâches distinctes

FLAG_ACTIVITY_NEW_TASK peut être un indicateur utile pour les applications de type lanceur d'applications, car chaque nouvelle tâche apparaît en tant qu'élément distinct sur l'écran "Overview" (Aperçu). Pour en savoir plus sur les tâches de l'écran "Aperçu", consultez la section Écran "Recents" (Éléments récents).

Kiosques publics

Ces recettes sont idéales pour les appareils non surveillés dans les espaces publics, mais peuvent également aider de nombreux utilisateurs d'appareils dédiés à se concentrer sur leurs tâches.

Verrouiller l'appareil

Pour vous assurer que les appareils sont utilisés à bon escient, vous pouvez ajouter les restrictions d'utilisateur répertoriées dans le tableau 1.

Tableau 1. Restrictions utilisateur applicables aux appareils en mode kiosque
Restriction d'utilisateur Description
DISALLOW_FACTORY_RESET Empêche l'utilisateur de rétablir la configuration d'usine de l'appareil. Les administrateurs d'appareils entièrement gérés et l'utilisateur principal peuvent définir cette restriction.
DISALLOW_SAFE_BOOT Empêche l'utilisateur de démarrer l'appareil en mode sans échec, ce qui empêche le système de lancer automatiquement votre application. Les administrateurs d'appareils entièrement gérés et l'utilisateur principal peuvent définir cette restriction.
DISALLOW_MOUNT_PHYSICAL_MEDIA Empêche l'utilisateur de l'appareil d'installer des volumes de stockage qu'il pourrait associer à l'appareil. Les administrateurs d'appareils entièrement gérés et l'utilisateur principal peuvent définir cette restriction.
DISALLOW_ADJUST_VOLUME Coupe le son de l'appareil et empêche l'utilisateur de modifier le volume sonore et les paramètres du vibreur. Vérifiez que votre kiosque n'a pas besoin de contenu audio pour la lecture de contenus multimédias ou les fonctionnalités d'accessibilité. Les administrateurs d'appareils entièrement gérés, l'utilisateur principal, les utilisateurs secondaires et les profils professionnels peuvent définir cette restriction.
DISALLOW_ADD_USER Empêche l'utilisateur de l'appareil d'ajouter des utilisateurs, tels que des utilisateurs secondaires ou restreints. Le système ajoute automatiquement cette restriction d'utilisateur aux appareils entièrement gérés, mais elle a peut-être été effacée. Les administrateurs d'appareils entièrement gérés et l'utilisateur principal peuvent définir cette restriction.

L'extrait de code suivant montre comment définir les restrictions:

Kotlin

// If the system is running in lock task mode, set the user restrictions
// for a kiosk after launching the activity.
arrayOf(
        UserManager.DISALLOW_FACTORY_RESET,
        UserManager.DISALLOW_SAFE_BOOT,
        UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
        UserManager.DISALLOW_ADJUST_VOLUME,
        UserManager.DISALLOW_ADD_USER).forEach { dpm.addUserRestriction(adminName, it) }

Java

// If the system is running in lock task mode, set the user restrictions
// for a kiosk after launching the activity.
String[] restrictions = {
    UserManager.DISALLOW_FACTORY_RESET,
    UserManager.DISALLOW_SAFE_BOOT,
    UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
    UserManager.DISALLOW_ADJUST_VOLUME,
    UserManager.DISALLOW_ADD_USER};

for (String restriction: restrictions) dpm.addUserRestriction(adminName, restriction);

Vous pouvez supprimer ces restrictions lorsque votre application est en mode administrateur, afin qu'un administrateur informatique puisse toujours utiliser ces fonctionnalités pour la maintenance des appareils. Pour supprimer la restriction, appelez DevicePolicyManager.clearUserRestriction().

Supprimer les boîtes de dialogue d'erreur

Dans certains environnements, tels que les démonstrations en magasin ou l'affichage d'informations publiques, vous ne souhaitez peut-être pas afficher de boîtes de dialogue d'erreur aux utilisateurs. Sous Android 9.0 (niveau d'API 28) ou version ultérieure, vous pouvez supprimer les boîtes de dialogue d'erreur système pour les applications qui ont planté ou ne répondent pas en ajoutant la restriction utilisateur DISALLOW_SYSTEM_ERROR_DIALOGS. Le système redémarre les applications qui ne répondent pas comme si l'utilisateur de l'appareil fermait l'application à partir de la boîte de dialogue. L'exemple suivant montre comment procéder:

Kotlin

override fun onEnabled(context: Context, intent: Intent) {
    val dpm = getManager(context)
    val adminName = getWho(context)

    dpm.addUserRestriction(adminName, UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS)
}

Java

public void onEnabled(Context context, Intent intent) {
  DevicePolicyManager dpm = getManager(context);
  ComponentName adminName = getWho(context);

  dpm.addUserRestriction(adminName, UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS);
}

Si un administrateur de l'utilisateur principal ou secondaire définit cette restriction, le système supprime les boîtes de dialogue d'erreur uniquement pour cet utilisateur. Si un administrateur d'un appareil entièrement géré définit cette restriction, le système supprime les boîtes de dialogue pour tous les utilisateurs.

Gardez l'écran allumé

Si vous créez un kiosque, vous pouvez arrêter un appareil de se mettre en veille lorsqu'il exécute l'activité de votre application. Ajoutez l'indicateur de mise en page FLAG_KEEP_SCREEN_ON à la fenêtre de votre application, comme indiqué dans l'exemple suivant:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // Keep the screen on and bright while this kiosk activity is running.
    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // Keep the screen on and bright while this kiosk activity is running.
  getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}

Nous vous conseillons de vérifier que l'appareil est branché sur un chargeur CA, USB ou sans fil. Inscrivez-vous aux annonces de changement de batterie et utilisez les valeurs BatteryManager pour découvrir l'état de charge. Vous pouvez même envoyer des alertes à distance à un administrateur informatique si l'appareil est débranché. Pour obtenir des instructions détaillées, consultez Surveiller l'état de la batterie et la charge.

Vous pouvez également définir le paramètre général STAY_ON_WHILE_PLUGGED_IN pour que l'appareil reste activé lorsqu'il est connecté à une source d'alimentation. Les administrateurs d'appareils entièrement gérés équipés d'Android 6.0 (niveau d'API 23) ou version ultérieure peuvent appeler DevicePolicyManager.setGlobalSetting(), comme illustré dans l'exemple suivant:

Kotlin

val pluggedInto = BatteryManager.BATTERY_PLUGGED_AC or
        BatteryManager.BATTERY_PLUGGED_USB or
        BatteryManager.BATTERY_PLUGGED_WIRELESS
dpm.setGlobalSetting(adminName,
        Settings.Global.STAY_ON_WHILE_PLUGGED_IN, pluggedInto.toString())

Java

int pluggedInto = BatteryManager.BATTERY_PLUGGED_AC |
    BatteryManager.BATTERY_PLUGGED_USB |
    BatteryManager.BATTERY_PLUGGED_WIRELESS;
dpm.setGlobalSetting( adminName,
    Settings.Global.STAY_ON_WHILE_PLUGGED_IN, String.valueOf(pluggedInto));

Packages d'applis

Cette section contient des recettes pour installer efficacement des applications sur des appareils dédiés.

Mettre en cache les packages d'applications

Si les utilisateurs d'un appareil partagé partagent tous un ensemble commun d'applications, il est judicieux d'éviter de télécharger des applications dans la mesure du possible. Pour simplifier le provisionnement des utilisateurs sur les appareils partagés avec un ensemble fixe d'utilisateurs, tels que les appareils pour les travailleurs de transition, sous Android 9.0 (niveau d'API 28) ou version ultérieure, vous pouvez mettre en cache les packages d'application (APK) nécessaires pour les sessions multi-utilisateurs.

L'installation d'un APK mis en cache (déjà installé sur l'appareil) se déroule en deux étapes:

  1. Le composant d'administration d'un appareil entièrement géré (ou d'un délégué, voir ci-dessous) définit la liste des APK à conserver sur l'appareil.
  2. Les composants administrateur des utilisateurs secondaires affiliés (ou de leurs délégués) peuvent installer l'APK mis en cache au nom de l'utilisateur. Si nécessaire, les administrateurs de l'appareil entièrement géré, l'utilisateur principal ou un profil professionnel affilié (ou leurs délégués) peuvent également installer l'application mise en cache.

Pour définir la liste des APK à conserver sur l'appareil, l'administrateur appelle DevicePolicyManager.setKeepUninstalledPackages(). Cette méthode ne vérifie pas que l'APK est installé sur l'appareil. Cette méthode est utile si vous souhaitez installer une application juste avant d'en avoir besoin pour un utilisateur. Pour obtenir la liste des packages précédemment définis, vous pouvez appeler DevicePolicyManager.getKeepUninstalledPackages(). Une fois que vous avez appelé setKeepUninstalledPackages() avec des modifications ou lorsqu'un utilisateur secondaire est supprimé, le système supprime tous les APK mis en cache qui ne sont plus nécessaires.

Pour installer un APK mis en cache, appelez DevicePolicyManager.installExistingPackage(). Cette méthode ne peut installer qu'une application que le système a déjà mise en cache. Votre solution dédiée (ou l'utilisateur d'un appareil) doit d'abord installer l'application sur l'appareil pour que vous puissiez appeler cette méthode.

L'exemple suivant montre comment utiliser ces appels d'API dans l'administration d'un appareil entièrement géré et d'un utilisateur secondaire:

Kotlin

// Set the package to keep. This method assumes that the package is already
// installed on the device by managed Google Play.
val cachedAppPackageName = "com.example.android.myapp"
dpm.setKeepUninstalledPackages(adminName, listOf(cachedAppPackageName))

// ...

// The admin of a secondary user installs the app.
val success = dpm.installExistingPackage(adminName, cachedAppPackageName)

Java

// Set the package to keep. This method assumes that the package is already
// installed on the device by managed Google Play.
String cachedAppPackageName = "com.example.android.myapp";
List<String> packages = new ArrayList<String>();
packages.add(cachedAppPackageName);
dpm.setKeepUninstalledPackages(adminName, packages);

// ...

// The admin of a secondary user installs the app.
boolean success = dpm.installExistingPackage(adminName, cachedAppPackageName);

Déléguer des applications

Vous pouvez déléguer la gestion de la mise en cache des applications à une autre application. Cela peut permettre de séparer les fonctionnalités de votre solution ou d'offrir aux administrateurs informatiques la possibilité d'utiliser leurs propres applications. L'application déléguée obtient les mêmes autorisations que le composant d'administration. Par exemple, un délégué d'application de l'administrateur d'un utilisateur secondaire peut appeler installExistingPackage(), mais pas setKeepUninstalledPackages().

Pour effectuer un appel délégué, appelez DevicePolicyManager.setDelegatedScopes() et incluez DELEGATION_KEEP_UNINSTALLED_PACKAGES dans l'argument de champs d'application. L'exemple suivant montre comment désigner une autre application comme délégué:

Kotlin

var delegatePackageName = "com.example.tools.kept_app_assist"

// Check that the package is installed before delegating.
try {
    context.packageManager.getPackageInfo(delegatePackageName, 0)
    dpm.setDelegatedScopes(
            adminName,
            delegatePackageName,
            listOf(DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES))
} catch (e: PackageManager.NameNotFoundException) {
    // The delegate app isn't installed. Send a report to the IT admin ...
}

Java

String delegatePackageName = "com.example.tools.kept_app_assist";

// Check that the package is installed before delegating.
try {
  context.getPackageManager().getPackageInfo(delegatePackageName, 0);
  dpm.setDelegatedScopes(
      adminName,
      delegatePackageName,
      Arrays.asList(DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES));
} catch (PackageManager.NameNotFoundException e) {
  // The delegate app isn't installed. Send a report to the IT admin ...
}

Si tout se passe bien, l'application déléguée reçoit la diffusion ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED et devient le délégué. L'application peut appeler les méthodes de ce guide comme s'il s'agissait du propriétaire de l'appareil ou du propriétaire du profil. Lors de l'appel des méthodes DevicePolicyManager, le délégué transmet null pour l'argument du composant administrateur.

Installer des packages d'applis

Il est parfois utile d'installer une application personnalisée mise en cache localement sur un appareil dédié. Par exemple, des appareils dédiés sont fréquemment déployés dans des environnements à bande passante limitée ou dans des zones sans connexion Internet. La solution de votre appareil dédié doit tenir compte de la bande passante de vos clients. Votre application peut lancer l'installation d'un autre package d'application (APK) à l'aide des classes PackageInstaller.

Bien que n'importe quelle application puisse installer des APK, les administrateurs d'appareils entièrement gérés peuvent installer (ou désinstaller) des packages sans intervention de l'utilisateur. L'administrateur peut gérer l'appareil, un utilisateur secondaire affilié ou un profil professionnel affilié. Une fois l'installation terminée, le système publie une notification que tous les utilisateurs de l'appareil voient. Cette notification informe les utilisateurs de l'appareil que l'application a été installée (ou mise à jour) par leur administrateur.

Tableau 2. Versions d'Android compatibles avec l'installation de packages sans intervention de l'utilisateur
Version d'Android Composant d'administration pour l'installation et la désinstallation
Android 9.0 (niveau d'API 28) ou version ultérieure Utilisateurs secondaires et profils professionnels affiliés, sur des appareils entièrement gérés
Android 6.0 (niveau d'API 23) ou version ultérieure Appareils entièrement gérés

La façon dont vous distribuez une ou plusieurs copies de l'APK sur les appareils dédiés dépendra de la distance entre les appareils et éventuellement de leur distance. Votre solution doit respecter les bonnes pratiques de sécurité avant d'installer des APK sur des appareils dédiés.

Vous pouvez utiliser PackageInstaller.Session pour créer une session qui met en file d'attente un ou plusieurs APK en vue de leur installation. Dans l'exemple suivant, nous recevons des commentaires sur l'état dans notre activité (mode singleTop), mais vous pouvez utiliser un service ou un broadcast receiver:

Kotlin

// First, create a package installer session.
val packageInstaller = context.packageManager.packageInstaller
val params = PackageInstaller.SessionParams(
        PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = packageInstaller.createSession(params)
val session = packageInstaller.openSession(sessionId)

// Add the APK binary to the session. The APK is included in our app binary
// and is read from res/raw but file storage is a more typical location.
// The I/O streams can't be open when installation begins.
session.openWrite("apk", 0, -1).use { output ->
    getContext().resources.openRawResource(R.raw.app).use { input ->
        input.copyTo(output, 2048)
    }
}

// Create a status receiver to report progress of the installation.
// We'll use the current activity.
// Here we're requesting status feedback to our Activity but this can be a
// service or broadcast receiver.
val intent = Intent(context, activity.javaClass)
intent.action = "com.android.example.APK_INSTALLATION_ACTION"
val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
val statusReceiver = pendingIntent.intentSender

// Start the installation. Because we're an admin of a fully managed device,
// there isn't any user interaction.
session.commit(statusReceiver)

Java

// First, create a package installer session.
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);

// Add the APK binary to the session. The APK is included in our app binary
// and is read from res/raw but file storage is a more typical location.
try (
    // These I/O streams can't be open when installation begins.
    OutputStream output = session.openWrite("apk", 0, -1);
    InputStream input = getContext().getResources().openRawResource(R.raw.app);
) {
  byte[] buffer = new byte[2048];
  int n;
  while ((n = input.read(buffer)) >= 0) {
    output.write(buffer, 0, n);
  }
}

// Create a status receiver to report progress of the installation.
// We'll use the current activity.
// Here we're requesting status feedback to our Activity but this can be a
// service or broadcast receiver.
Intent intent = new Intent(context, getActivity().getClass());
intent.setAction("com.android.example.APK_INSTALLATION_ACTION");
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();

// Start the installation. Because we're an admin of a fully managed device,
// there isn't any user interaction.
session.commit(statusReceiver);

La session envoie des commentaires sur l'état de l'installation à l'aide d'intents. Vérifiez le champ EXTRA_STATUS de chaque intent pour obtenir l'état. N'oubliez pas que les administrateurs ne reçoivent pas la mise à jour de l'état STATUS_PENDING_USER_ACTION, car l'utilisateur de l'appareil n'a pas besoin d'approuver l'installation.

Pour désinstaller des applications, vous pouvez appeler PackageInstaller.uninstall. Les administrateurs d'appareils entièrement gérés, d'utilisateurs et de profils professionnels peuvent désinstaller des packages sans intervention de l'utilisateur exécutant des versions d'Android compatibles (voir le tableau 2).

Figer les mises à jour du système

Les appareils Android reçoivent des mises à jour Over The Air (OTA) du système et du logiciel d'application. Pour figer la version de l'OS pendant les périodes critiques, telles que les vacances ou d'autres périodes d'affluence, les appareils dédiés peuvent suspendre les mises à jour du système OTA pendant 90 jours maximum. Pour en savoir plus, consultez Gérer les mises à jour du système.

Remote Config

Les configurations gérées d'Android permettent aux administrateurs informatiques de configurer votre application à distance. Vous pouvez exposer des paramètres tels que des listes d'autorisation, des hôtes réseau ou des URL de contenu pour rendre votre application plus utile aux administrateurs informatiques.

Si votre application expose sa configuration, n'oubliez pas d'inclure les paramètres dans votre documentation. Pour savoir comment exposer la configuration de votre application et réagir aux modifications des paramètres, consultez Configurer des configurations gérées.

Configuration du développement

Lorsque vous développez votre solution pour des appareils dédiés, il est parfois utile de définir votre application comme administrateur d'un appareil entièrement géré sans avoir à rétablir la configuration d'usine. Pour définir l'administrateur d'un appareil entièrement géré, procédez comme suit:

  1. Créez et installez votre application de contrôle des règles relatives aux appareils (DPC) sur l'appareil.
  2. Vérifiez qu'aucun compte n'est enregistré sur l'appareil.
  3. Exécutez la commande suivante dans le shell Android Debug Bridge (adb). Dans l'exemple, vous devez remplacer com.example.dpc/.MyDeviceAdminReceiver par le nom du composant d'administration de votre application:

    adb shell dpm set-device-owner com.example.dpc/.MyDeviceAdminReceiver

Pour aider les clients à déployer votre solution, vous devez examiner d'autres méthodes d'enregistrement. Nous vous recommandons d'enregistrer avec un code QR pour les appareils dédiés.

Ressources supplémentaires

Pour en savoir plus sur les appareils dédiés, consultez les documents suivants: