Istruzioni sui dispositivi dedicati

Questo libro di ricette aiuta sviluppatori e integratori di sistemi a migliorare la loro soluzione di dispositivi dedicata. Segui le nostre ricette illustrative per trovare soluzioni per il comportamento dei dispositivi dedicati. Questo libro di ricette è ideale per gli sviluppatori che hanno già un'app dedicata per i dispositivi. Se hai appena iniziato, leggi la panoramica sui dispositivi dedicati.

App Home personalizzate

Queste ricette sono utili se sviluppi un'app che sostituisce la schermata Home e Avvio app di Android.

Diventa l'app Home

Puoi impostare la tua app come app principale del dispositivo in modo che venga avviata automaticamente all'avvio del dispositivo. Puoi anche attivare il pulsante Home che porta in primo piano l'app inclusa nella lista consentita in modalità di blocco attività.

Tutte le app Home gestiscono la categoria di intent CATEGORY_HOME, in questo modo il sistema riconosce un'app Home. Per diventare l'app Home predefinita, imposta una delle attività della tua app come gestore di intent di Google Home preferito chiamando DevicePolicyManager.addPersistentPreferredActivity() come mostrato nell'esempio seguente:

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

Devi comunque dichiarare il filtro intent nel file manifest dell'app, come mostrato nel seguente snippet XML:

<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>

In genere preferisci che l'app Avvio app non venga visualizzata nella schermata Panoramica. Tuttavia, non è necessario aggiungere excludeFromRecents alla dichiarazione di attività perché Avvio app di Android nasconde l'attività avviata inizialmente quando il sistema è in esecuzione in modalità di blocco attività.

Mostra attività separate

FLAG_ACTIVITY_NEW_TASK può essere un flag utile per le app di tipo Avvio app perché ogni nuova attività viene visualizzata come voce separata nella schermata Panoramica. Per scoprire di più sulle attività nella schermata Panoramica, consulta la schermata Recenti.

Chioschi pubblici

Queste ricette sono perfette per i dispositivi inattivi negli spazi pubblici, ma possono anche aiutare molti utenti di dispositivi dedicati a concentrarsi sulle loro attività.

Bloccare il dispositivo

Per assicurarti che i dispositivi vengano utilizzati per lo scopo previsto, puoi aggiungere le limitazioni utente elencate nella tabella 1.

Tabella 1. Limitazioni per gli utenti relative ai dispositivi kiosk
Limitazione utente Descrizione
DISALLOW_FACTORY_RESET Questa azione impedisce all'utente di ripristinare le impostazioni di fabbrica del dispositivo. Gli amministratori di dispositivi completamente gestiti e l'utente principale possono impostare questa limitazione.
DISALLOW_SAFE_BOOT Impedisce a un utente del dispositivo di avviare il dispositivo in modalità provvisoria, in cui il sistema non lancerà automaticamente l'app. Gli amministratori di dispositivi completamente gestiti e l'utente principale possono impostare questa limitazione.
DISALLOW_MOUNT_PHYSICAL_MEDIA Impedisce all'utente del dispositivo di montare eventuali volumi di archiviazione che potrebbe collegare al dispositivo. Gli amministratori di dispositivi completamente gestiti e l'utente principale possono impostare questa limitazione.
DISALLOW_ADJUST_VOLUME Disattiva l'audio del dispositivo e impedisce all'utente di modificare le impostazioni del volume dell'audio e della vibrazione. Verifica che il kiosk non abbia bisogno di audio per la riproduzione di contenuti multimediali o le funzioni di accessibilità. Gli amministratori di dispositivi completamente gestiti, l'utente principale, gli utenti secondari e i profili di lavoro possono impostare questa limitazione.
DISALLOW_ADD_USER Impedisce all'utente del dispositivo di aggiungere nuovi utenti, ad esempio utenti secondari o utenti con limitazioni. Il sistema aggiunge automaticamente questa limitazione utente ai dispositivi completamente gestiti, ma potrebbe essere stata cancellata. Gli amministratori di dispositivi completamente gestiti e l'utente principale possono impostare questa limitazione.

Lo snippet seguente mostra come impostare le limitazioni:

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

Ti consigliamo di rimuovere queste limitazioni quando la tua app è in modalità amministratore, in modo che un amministratore IT possa comunque utilizzare queste funzionalità per la manutenzione del dispositivo. Per rimuovere la limitazione, chiama DevicePolicyManager.clearUserRestriction().

Elimina finestre di dialogo di errore

In alcuni ambienti, ad esempio dimostrazioni di vendita al dettaglio o visualizzazioni di informazioni pubbliche, potresti non voler mostrare le finestre di dialogo di errore agli utenti. In Android 9.0 (livello API 28) o versioni successive, puoi eliminare le finestre di dialogo di errore di sistema per le app che hanno arresti anomali o che non rispondono aggiungendo la limitazione utente DISALLOW_SYSTEM_ERROR_DIALOGS. Il sistema riavvia le app che non rispondono come se l'utente del dispositivo avesse chiuso l'app dalla finestra di dialogo. Nell'esempio seguente viene illustrato come eseguire questa operazione:

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

Se un amministratore dell'utente principale o secondario imposta questa limitazione, il sistema elimina le finestre di dialogo di errore solo per quell'utente. Se un amministratore di un dispositivo completamente gestito imposta questa limitazione, il sistema elimina le finestre di dialogo per tutti gli utenti.

Mantieni lo schermo acceso

Se stai creando un kiosk, puoi interrompere la modalità di sospensione di un dispositivo quando è in corso l'attività dell'app. Aggiungi il flag di layout FLAG_KEEP_SCREEN_ON alla finestra dell'app come mostrato nell'esempio seguente:

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

Puoi controllare che il dispositivo sia collegato a un caricabatterie CA, USB o wireless. Registrati per gli annunci con cambio batteria e utilizza i valori BatteryManager per rilevare lo stato di ricarica. Puoi persino inviare avvisi remoti a un amministratore IT se il dispositivo viene scollegato. Per istruzioni dettagliate, consulta Monitorare il livello della batteria e lo stato di ricarica.

Puoi anche configurare l'impostazione globale STAY_ON_WHILE_PLUGGED_IN per mantenere attivo il dispositivo mentre è collegato a una fonte di alimentazione. Gli amministratori di dispositivi completamente gestiti in Android 6.0 (livello API 23) o versioni successive possono chiamare DevicePolicyManager.setGlobalSetting() come mostrato nell'esempio seguente:

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

Pacchetti di app

Questa sezione contiene ricette per installare in modo efficiente le app sui dispositivi dedicati.

Memorizza nella cache i pacchetti dell'app

Se gli utenti di un dispositivo condiviso condividono tutti un insieme comune di app, ha senso evitare di scaricare app, se possibile. Per semplificare il provisioning degli utenti sui dispositivi condivisi con un insieme fisso di utenti, ad esempio i dispositivi per i turnisti, in Android 9.0 (livello API 28) o versioni successive, puoi memorizzare nella cache i pacchetti di app (APK) necessari per le sessioni multiutente.

L'installazione di un APK memorizzato nella cache (già installato sul dispositivo) avviene in due fasi:

  1. Il componente Amministratore di un dispositivo completamente gestito (o di un delegato (vedi di seguito)) consente di impostare l'elenco di APK da conservare sul dispositivo.
  2. I componenti amministrativi degli utenti secondari affiliati (o dei loro delegati) possono installare l'APK memorizzato nella cache per conto dell'utente. Anche gli amministratori del dispositivo completamente gestito, dell'utente principale o di un profilo di lavoro affiliato (o dei relativi delegati) possono installare l'app memorizzata nella cache, se necessario.

Per impostare l'elenco di APK da conservare sul dispositivo, l'amministratore chiama DevicePolicyManager.setKeepUninstalledPackages(). Questo metodo non controlla che l'APK sia installato sul dispositivo. È utile se vuoi installare un'app appena prima che ti serva per un utente. Per visualizzare un elenco dei pacchetti impostati in precedenza, puoi chiamare DevicePolicyManager.getKeepUninstalledPackages(). Dopo aver chiamato setKeepUninstalledPackages() con modifiche o quando viene eliminato un utente secondario, il sistema elimina gli eventuali APK memorizzati nella cache che non sono più necessari.

Per installare un APK memorizzato nella cache, chiama DevicePolicyManager.installExistingPackage(). Questo metodo consente di installare soltanto un'app che il sistema ha già memorizzato nella cache. Per poter chiamare questo metodo, la soluzione del dispositivo dedicata (o l'utente di un dispositivo) deve installare l'app sul dispositivo.

Il seguente esempio mostra come utilizzare queste chiamate API nell'amministrazione di un dispositivo completamente gestito e di un utente secondario:

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

Delegare app

Puoi delegare un'altra app a gestire la memorizzazione nella cache delle app. Puoi farlo per separare le funzionalità della tua soluzione o per offrire agli amministratori IT la possibilità di utilizzare le proprie app. L'app delegata riceve le stesse autorizzazioni del componente amministratore. Ad esempio, un delegato dell'app dell'amministratore di un utente secondario può chiamare installExistingPackage(), ma non può chiamare setKeepUninstalledPackages().

Per effettuare una chiamata del delegato DevicePolicyManager.setDelegatedScopes() e includere DELEGATION_KEEP_UNINSTALLED_PACKAGES nell'argomento ambiti. L'esempio seguente mostra come impostare un'altra app come delegato:

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 ...
}

Se tutto va bene, l'app delegata riceve la trasmissione ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED e diventa il delegato. L'app può chiamare i metodi in questa guida come se fosse il proprietario del dispositivo o del profilo. Quando chiama i metodi DevicePolicyManager, il delegato passa null per l'argomento del componente amministratore.

Installa pacchetti di app

A volte è utile installare un'app personalizzata memorizzata nella cache locale su un dispositivo dedicato. Ad esempio, i dispositivi dedicati vengono spesso distribuiti in ambienti con larghezza di banda limitata o in aree senza connettività a internet. La soluzione dedicata al tuo dispositivo deve tenere conto della larghezza di banda dei tuoi clienti. La tua app può avviare l'installazione di un altro pacchetto dell'app (APK) utilizzando le classi PackageInstaller.

Gli APK possono essere installati da qualsiasi app, ma gli amministratori dei dispositivi completamente gestiti possono installare (o disinstallare) pacchetti senza interazione da parte dell'utente. L'amministratore può gestire il dispositivo, un utente secondario affiliato o un profilo di lavoro affiliato. Al termine dell'installazione, il sistema pubblica una notifica visibile a tutti gli utenti del dispositivo. La notifica informa gli utenti del dispositivo che l'app è stata installata (o aggiornata) dal loro amministratore.

Tabella 2. Versioni di Android che supportano l'installazione di pacchetti senza interazione da parte dell'utente
Versione di Android Componente amministratore per installazione e disinstallazione
Android 9.0 (livello API 28) o versioni successive Utenti secondari e profili di lavoro affiliati, entrambi su dispositivi completamente gestiti
Android 6.0 (livello API 23) o versioni successive Dispositivi completamente gestiti

Le modalità di distribuzione di una o più copie dell'APK ai dispositivi dedicati dipendono dalla distanza dei dispositivi ed eventualmente dalla loro distanza tra loro. La tua soluzione deve seguire le best practice per la sicurezza prima di installare APK su dispositivi dedicati.

Puoi utilizzare PackageInstaller.Session per creare una sessione che accoda uno o più APK per l'installazione. Nel seguente esempio riceviamo feedback sullo stato nella nostra attività (modalità singleTop), ma puoi utilizzare un servizio o un ricevitore di trasmissione:

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 sessione invia un feedback sullo stato dell'installazione utilizzando gli intent. Controlla il campo EXTRA_STATUS di ogni intent per ottenere lo status. Ricorda che gli amministratori non ricevono l'aggiornamento dello stato di STATUS_PENDING_USER_ACTION perché l'utente del dispositivo non deve approvare l'installazione.

Per disinstallare le app, puoi chiamare PackageInstaller.uninstall. Gli amministratori di dispositivi completamente gestiti, utenti e profili di lavoro possono disinstallare i pacchetti senza interazione da parte dell'utente che eseguono versioni di Android supportate (vedi la tabella 2).

Blocca gli aggiornamenti di sistema

I dispositivi Android ricevono aggiornamenti over-the-air (OTA) del sistema e del software dell'applicazione. Per bloccare la versione del sistema operativo in periodi critici, ad esempio durante le festività o in altri orari di punta, i dispositivi dedicati possono sospendere gli aggiornamenti di sistema OTA per un massimo di 90 giorni. Per saperne di più, consulta Gestire gli aggiornamenti di sistema.

Configurazione remota

Le configurazioni gestite di Android consentono agli amministratori IT di configurare da remoto la tua app. Potresti voler esporre impostazioni come liste consentite, host di rete o URL di contenuti per rendere l'app più utile per gli amministratori IT.

Se l'applicazione espone la propria configurazione, ricordati di includere le impostazioni nella documentazione. Per scoprire di più sull'esposizione della configurazione dell'app e su come reagire alle modifiche alle impostazioni, leggi l'articolo Configurare le configurazioni gestite.

Configurazione dello sviluppo

Durante lo sviluppo della tua soluzione per dispositivi dedicati, a volte è utile impostare la tua app come amministratore di un dispositivo completamente gestito senza dover ripristinare i dati di fabbrica. Per impostare l'amministratore di un dispositivo completamente gestito:

  1. Crea e installa l'app controller dei criteri dei dispositivi (DPC) sul dispositivo.
  2. Verifica che non ci siano account sul dispositivo.
  3. Esegui il comando seguente nella shell di Android Debug Bridge (adb). Devi sostituire com.example.dpc/.MyDeviceAdminReceiver nell'esempio con il nome del componente di amministrazione dell'app:

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

Per aiutare i clienti a eseguire il deployment della tua soluzione, devi valutare altri metodi di registrazione. Consigliamo la registrazione con codice QR per i dispositivi dedicati.

Risorse aggiuntive

Per scoprire di più sui dispositivi dedicati, leggi i seguenti documenti: