Gestire gli aggiornamenti di sistema

Questa guida per gli sviluppatori spiega in che modo il controller dei criteri dei dispositivi (DPC) può gestire gli aggiornamenti di sistema Android per conto dell'utente del dispositivo.

Introduzione

I dispositivi Android possono ricevere e installare aggiornamenti over-the-air (OTA) del sistema e del software dell'applicazione. Android avvisa l'utente del dispositivo che è disponibile un aggiornamento di sistema e che l'utente può installarlo immediatamente o in un secondo momento.

Un amministratore IT può gestire gli aggiornamenti di sistema per l'utente del dispositivo tramite il DPC. I DPC possono essere proprietari di un dispositivo completamente gestito (denominato proprietario del dispositivo) o possono possedere un profilo di lavoro (chiamato proprietario del profilo). La tabella 1 mostra in che modo i proprietari dei dispositivi possono gestire gli aggiornamenti di sistema, mentre i proprietari dei profili possono solo segnalare le informazioni sugli aggiornamenti di sistema.

Tabella 1: le attività disponibili per i DPC dipendono dalla modalità proprietario

Attività Proprietario del dispositivo Proprietario del profilo
Verificare la presenza di aggiornamenti di sistema in sospeso
Ricevere callback quando sono disponibili nuovi aggiornamenti di sistema
Imposta un criterio di aggiornamento locale per controllare quando Android installa gli aggiornamenti di sistema
Bloccare la versione del sistema operativo nei periodi critici

Verifica la presenza di aggiornamenti in sospeso

Un aggiornamento in sospeso è un aggiornamento di sistema di un dispositivo non ancora installato. Il DPC può aiutare gli amministratori IT a verificare quali dispositivi hanno aggiornamenti di sistema in attesa e magari chiedere agli utenti dei dispositivi di installare tempestivamente aggiornamenti critici.

I proprietari di dispositivi e i proprietari di profili con Android 8.0 (livello API 26) o versioni successive possono controllare se per un dispositivo è in sospeso un aggiornamento di sistema. Chiama DevicePolicyManager.getPendingSystemUpdate() che restituisce null se il dispositivo è aggiornato. Se un aggiornamento di sistema è in attesa, il metodo restituisce informazioni sull'aggiornamento.

Scopri di più su un aggiornamento in sospeso

Dopo aver chiamato getPendingSystemUpdate(), puoi esaminare il valore SystemUpdateInfo restituito per scoprire di più sull'aggiornamento in attesa. L'esempio seguente mostra come potresti scoprire quando un aggiornamento in attesa era disponibile per la prima volta per il dispositivo:

Kotlin

val firstAvailable =
        dpm.getPendingSystemUpdate(adminName)?.receivedTime
firstAvailable?.let {
    Log.i(TAG, "Update first available: ${Date(firstAvailable)}")
}

Java

SystemUpdateInfo updateInfo = dpm.getPendingSystemUpdate(adminName);
if (updateInfo != null) {
  Long firstAvailable = updateInfo.getReceivedTime();
  Log.i(TAG, "Update first available: " + new Date(firstAvailable));
}

Callback di sistema

Quando un aggiornamento diventa disponibile, il sistema Android invia una notifica ai proprietari del dispositivo. In Android 8.0 o versioni successive, il sistema invia una notifica anche ai proprietari dei profili.

Nella sottoclasse DeviceAdminReceiver, esegui l'override del callback onSystemUpdatePending(). Non è necessario registrare o pubblicizzare il tuo DPC per ricevere la richiamata. Il sistema potrebbe chiamare questo metodo più di una volta per un singolo aggiornamento, quindi controlla lo stato dell'aggiornamento prima di rispondere. Chiama il numero getPendingSystemUpdate() per scoprire di più sull'aggiornamento di sistema nel callback. Nell'esempio seguente viene illustrato come eseguire questa operazione:

Kotlin

/**
 * Called when a new update is available.
 */
override fun onSystemUpdatePending(context: Context?, intent: Intent?,
                                   receivedTime: Long) {

    // System update information is supported in API level 26 or higher.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        return
    }

    val updateInfo = getManager(context)
            .getPendingSystemUpdate(getWho(context))
            ?: return
    if (updateInfo.securityPatchState ==
            SystemUpdateInfo.SECURITY_PATCH_STATE_TRUE) {
        // Perhaps install because this is a security patch.
        // ...
    }
}

Java

/**
 * Called when a new update is available.
 */
public void onSystemUpdatePending (Context context, Intent intent,
                                   long receivedTime) {

  // System update information is supported in API level 26 or higher.
  if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
    return;
  }
  SystemUpdateInfo updateInfo = getManager(context)
      .getPendingSystemUpdate(getWho(context));
  if (updateInfo == null) {
    return;
  }
  if (updateInfo.getSecurityPatchState() ==
      SystemUpdateInfo.SECURITY_PATCH_STATE_TRUE) {
    // Perhaps install because this is a security patch.
    // ...
  }
}

Quando un sistema ha più di un DPC, ad esempio profili di lavoro su dispositivi completamente gestiti, il proprietario del dispositivo e il proprietario del profilo ricevono il callback.

Aggiorna criteri

Un proprietario del dispositivo può controllare quando vengono installati gli aggiornamenti impostando un criterio di aggiornamento di sistema locale per il dispositivo. Il criterio di aggiornamento del sistema può essere di tre tipi:

Automatico
Installa gli aggiornamenti di sistema non appena diventano disponibili (senza interazione da parte dell'utente). La configurazione di questo tipo di criterio installa immediatamente eventuali aggiornamenti in attesa che potrebbero essere posticipati o in attesa di un periodo di manutenzione.
Con finestra
Installa gli aggiornamenti di sistema durante un periodo di manutenzione giornaliera (senza interazione da parte dell'utente). Imposta l'inizio e la fine del periodo di manutenzione giornaliera, sotto forma di minuti del giorno, quando crei un nuovo criterio con finestra.
Rinviata
Posticipa di 30 giorni l'installazione degli aggiornamenti di sistema. Al termine del periodo di 30 giorni, il sistema chiede all'utente del dispositivo di installare l'aggiornamento.

Periodi di posticipazione

Il sistema limita ogni aggiornamento a un rinvio di 30 giorni. Il periodo inizia nel momento in cui il sistema posticipa per la prima volta l'aggiornamento e l'impostazione di nuovi criteri di posticipazione non prolungherà il periodo.

Oltre al posticipo, Android potrebbe non essere in grado di installare un aggiornamento per altri motivi, ad esempio mancata connettività, spazio su disco insufficiente o batteria in esaurimento.

Il sistema reimposta il timer di posticipazione di 30 giorni se durante il periodo diventa disponibile un aggiornamento diverso, offrendo agli amministratori IT la possibilità di provare gli aggiornamenti di sistema combinati. Una volta trascorsi 30 giorni senza un nuovo aggiornamento, il sistema chiede all'utente di installare tutti gli aggiornamenti in attesa. In seguito, quando diventa disponibile un nuovo aggiornamento di sistema, il periodo di 30 giorni ricomincia.

Come impostare un criterio

Puoi impostare criteri di aggiornamento in Android 8.0 (livello API 26) o versioni successive. Per specificare quando il dispositivo deve installare gli aggiornamenti di sistema, crea un'istanza di SystemUpdatePolicy utilizzando uno dei tre tipi descritti sopra. Per impostare un criterio, il proprietario del dispositivo chiama il metodo DevicePolicyManager setSystemUpdatePolicy(). Il seguente esempio di codice mostra come eseguire questa operazione. Per vedere un esempio di criterio con finestre, consulta la documentazione di SystemUpdatePolicy.

Kotlin

// Create the system update policy to postpone installation for 30 days.
val policy = SystemUpdatePolicy.createPostponeInstallPolicy()

// Get a DevicePolicyManager instance to set the policy on the device.
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE)
        as DevicePolicyManager
val adminName = getComponentName(context)

// Set the policy.
dpm.setSystemUpdatePolicy(adminName, policy)

Java

// Create the system update policy to postpone installation for 30 days.
SystemUpdatePolicy policy = SystemUpdatePolicy.createPostponeInstallPolicy();

// Get a DevicePolicyManager instance to set the policy on the device.
DevicePolicyManager dpm = (DevicePolicyManager) context
    .getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName adminName = getComponentName(context);

// Set the policy.
dpm.setSystemUpdatePolicy(adminName, policy);

Le istanze dei criteri non possono essere modificate dopo averle create. Per modificare quando un dispositivo installa gli aggiornamenti, puoi creare e impostare un nuovo criterio. Per rimuovere un criterio da un dispositivo, chiama setSystemUpdatePolicy() che passa null come argomento policy. Dopo che il DPC rimuove un criterio, l'utente del dispositivo vede le notifiche relative a eventuali aggiornamenti di sistema disponibili.

Le app possono chiamare getSystemUpdatePolicy() per ottenere il criterio corrente per il dispositivo. Se questo metodo restituisce null, significa che al momento non è impostato alcun criterio.

Periodi di blocco

Per bloccare la versione del sistema operativo in periodi critici, ad esempio durante le festività o in altri orari di punta, i proprietari dei dispositivi possono sospendere gli aggiornamenti di sistema per un massimo di 90 giorni. Quando un dispositivo si trova in un periodo di blocco, si comporta come segue:

  • Il dispositivo non riceve alcuna notifica sugli aggiornamenti di sistema in sospeso.
  • Gli aggiornamenti di sistema del sistema operativo non sono installati.
  • Gli utenti dei dispositivi non possono controllare manualmente la disponibilità di aggiornamenti di sistema nelle Impostazioni.

Il sistema applica un periodo di buffer obbligatorio di 60 giorni dopo eventuali periodi di blocco definiti per evitare il blocco del dispositivo a tempo indeterminato. Ricorda che il blocco degli aggiornamenti di sistema può impedire ai dispositivi di ricevere aggiornamenti critici.

Figura 1. Due periodi di blocco impostati per un dispositivo
Calendario che mostra due periodi di blocco in un anno con buffer di 60 giorni.

I periodi di blocco vengono impostati su un criterio di aggiornamento. Non puoi impostare periodi di blocco senza impostare un criterio. Quando il dispositivo non rientra in eventuali periodi di blocco impostati, viene applicato il normale comportamento del criterio (automatico, con finestra o posticipato).

Come impostare un periodo di blocco

Puoi impostare periodi di blocco in Android 9 (livello API 28) o versioni successive. Il proprietario del dispositivo imposta un periodo di blocco su un criterio di aggiornamento del sistema prima di impostare il criterio per il dispositivo. I passaggi sono:

  1. Crea un nuovo criterio di aggiornamento di sistema (o ottieni quello attuale).
  2. Imposta i periodi di blocco sul criterio chiamando setFreezePeriods().
  3. Imposta il criterio e blocca i periodi per il dispositivo chiamando setSystemUpdatePolicy().

Poiché il periodo di blocco si ripete ogni anno, le date di inizio e fine del periodo sono rappresentate dai valori di mese e giorno. Il giorno di inizio deve iniziare almeno 60 giorni dopo la fine di qualsiasi periodo di blocco precedente. L'esempio seguente mostra come impostare due periodi di blocco per un criterio di aggiornamento di sistema esistente:

Kotlin

// Get the existing policy from the DevicePolicyController instance.
val policy = dpm.systemUpdatePolicy ?: return

try {
    // Set the two annual freeze periods on the policy for our retail
    // point-of-sale devices.
    val summerSale = FreezePeriod(
            MonthDay.of(6, 1),
            MonthDay.of(7, 31)) // Jun 1 - Jul 31 inclusive
    val winterSale = FreezePeriod(
            MonthDay.of(11, 20),
            MonthDay.of(1, 12)) // Nov 20 - Jan 12 inclusive
    policy.freezePeriods = Arrays.asList(summerSale, winterSale)

    // Set the policy again to activate the freeze periods.
    dpm.setSystemUpdatePolicy(adminName, policy)

} catch (e: SystemUpdatePolicy.ValidationFailedException) {
    // There must be previous periods recorded on the device because
    // summerSale and winterSale don’t overlap and are separated by more
    // than 60 days. Report the overlap ...
}

Java

// Get the existing policy from the DevicePolicyController instance.
SystemUpdatePolicy policy = dpm.getSystemUpdatePolicy();

try {
  // Set the two annual freeze periods on the policy for our
  // retail point-of-sale devices.
  FreezePeriod summerSale = new FreezePeriod(
      MonthDay.of(6, 1),
      MonthDay.of(7, 31)); // Jun 1 - Jul 31 inclusive
  FreezePeriod winterSale = new FreezePeriod(
      MonthDay.of(11, 20),
      MonthDay.of(1, 12)); // Nov 20 - Jan 12 inclusive
  policy.setFreezePeriods(Arrays.asList(summerSale, winterSale));

  // Don’t forget to set the policy again to activate the freeze periods.
  dpm.setSystemUpdatePolicy(adminName, policy);

} catch (SystemUpdatePolicy.ValidationFailedException e) {
  // There must be previous periods recorded on the device because summerSale
  // and winterSale don’t overlap and are separated by more than 60 days.
  // Report the overlap ...
}

Il giorno di inizio e quello di fine sono comprensivi. Se il giorno di inizio è superiore al giorno di fine (ad esempio winterSale nell'esempio precedente), il periodo di blocco si estende all'anno successivo.

Quando imposti dei periodi di blocco in un criterio di aggiornamento di sistema, Android verifica i seguenti requisiti:

  • Nessun periodo di blocco è più lungo di 90 giorni.
  • L'intervallo tra i periodi di blocco è di almeno 60 giorni.
  • I periodi di blocco non si sovrappongono.
  • Non sono presenti periodi di blocco duplicati.

Quando imposti il criterio di aggiornamento di sistema per un dispositivo, Android ripete i test e include eventuali periodi di blocco attuali o passati per il dispositivo.

Android genera un SystemUpdatePolicy.ValidationFailedException quando uno qualsiasi di questi test non va a buon fine.

Per ottenere un elenco dei periodi di blocco impostati in precedenza su un oggetto dei criteri di aggiornamento di sistema, tutte le app installate possono chiamare SystemUpdatePolicy.getFreezePeriods(). L'esempio seguente chiama questo metodo per registrare i periodi di blocco di un dispositivo:

Kotlin

// Log any freeze periods that might be set on a system update policy.
dpm.systemUpdatePolicy?.freezePeriods?.forEach {
    Log.i(TAG, "Freeze period: $it")
}

Java

// Log any freeze periods that might be set on a system update policy.
SystemUpdatePolicy currentPolicy = dpm.getSystemUpdatePolicy();
if (currentPolicy != null) { // A policy might not be set.
  for (FreezePeriod freezePeriod : currentPolicy.getFreezePeriods()) {
    Log.i(TAG, "Freeze period: " + freezePeriod.toString());
  }
}

Anni bisestili

Android utilizza il calendario ISO 8601 (chiamato anche calendario gregoriano) per calcolare i periodi di blocco e ignora gli anni bisestili. Ciò significa che il 29 febbraio non viene riconosciuto come data valida e viene considerato come se fosse il 28 febbraio. Di conseguenza, il 29 febbraio non viene conteggiato durante il calcolo della durata di un periodo di blocco.

Sviluppo e test

Durante lo sviluppo e il test della funzionalità di aggiornamento di sistema del DPC, potrebbe essere necessario creare molti periodi di blocco. Poiché Android controlla un intervallo di 60 giorni tra i periodi di blocco precedenti, potresti non essere in grado di impostare un nuovo periodo di blocco senza prima cancellare il record dei periodi di blocco precedenti. Per cancellare il record del periodo di blocco del dispositivo, esegui il comando seguente nella shell di Android Debug Bridge (adb):

adb shell dpm clear-freeze-period-record

Puoi confermare che un dispositivo si trova in un periodo di blocco controllando che l'interfaccia utente per gli aggiornamenti di sistema sia disattivata.

Risorse aggiuntive

Per ulteriori informazioni sugli aggiornamenti di sistema, leggi la documentazione sugli aggiornamenti OTA di Android Open Source Project.