Administrar actualizaciones del sistema

En esta guía para desarrolladores, se explica cómo el controlador de política del dispositivo (DPC) puede administrar las actualizaciones del sistema Android en nombre del usuario del dispositivo.

Introducción

Los dispositivos Android pueden recibir e instalar actualizaciones inalámbricas del sistema y el software de aplicaciones. Android notifica al usuario del dispositivo que hay una actualización del sistema disponible y que puede instalarla de inmediato o más tarde.

Con tu DPC, un administrador de TI puede gestionar las actualizaciones del sistema para el usuario del dispositivo. Los DPC pueden tener un dispositivo completamente administrado (denominado propietario del dispositivo) o un perfil de trabajo (llamado propietario del perfil). En la Tabla 1, se muestra cómo los propietarios de dispositivos pueden administrar las actualizaciones del sistema, mientras que los propietarios de perfiles solo pueden informar información sobre las actualizaciones del sistema.

Tabla 1: Las tareas disponibles para los DPC dependen del modo de propietario

Tarea Propietario del dispositivo Perfil del propietario
Cómo comprobar si hay actualizaciones del sistema pendientes
Recibe devoluciones de llamada cuando haya nuevas actualizaciones del sistema disponibles.
Establece una política de actualización local para controlar cuándo Android instala actualizaciones del sistema
Cómo inmovilizar la versión del SO durante los períodos críticos

Comprueba si hay actualizaciones pendientes

Una actualización pendiente es una actualización del sistema de un dispositivo que todavía no se instaló. Tu DPC puede ayudar a los administradores de TI a verificar qué dispositivos tienen actualizaciones del sistema pendientes y, quizás, pedirles a los usuarios del dispositivo que instalen actualizaciones críticas de inmediato.

Los propietarios de dispositivos y perfiles que se ejecutan en Android 8.0 (nivel de API 26) o versiones posteriores pueden verificar si un dispositivo tiene una actualización del sistema pendiente. Llama a DevicePolicyManager.getPendingSystemUpdate(), que muestra null si el dispositivo está actualizado. Si hay una actualización del sistema pendiente, el método mostrará información sobre la actualización.

Más información sobre una actualización pendiente

Después de llamar a getPendingSystemUpdate(), puedes inspeccionar el valor de SystemUpdateInfo que se muestra para obtener más información sobre la actualización pendiente. En el siguiente ejemplo, se muestra cómo podrías saber cuándo estuvo disponible por primera vez una actualización pendiente para el 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));
}

Devoluciones de llamada del sistema

Cuando hay una actualización disponible, el sistema Android notifica a los propietarios del dispositivo sobre la nueva actualización. En Android 8.0 o versiones posteriores, el sistema también notifica a los propietarios del perfil.

En tu subclase DeviceAdminReceiver, anula la devolución de llamada onSystemUpdatePending(). No es necesario que te registres ni publiques tu DPC para recibir la devolución de llamada. Es posible que el sistema llame a este método más de una vez para una sola actualización, por lo que debes verificar el estado de la actualización antes de responder. Llama a getPendingSystemUpdate() para obtener más información sobre la actualización del sistema en la devolución de llamada. En el siguiente ejemplo, se muestra cómo puedes hacerlo:

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

Cuando un sistema tiene más de un DPC (por ejemplo, perfiles de trabajo en dispositivos completamente administrados), tanto el propietario del dispositivo como el propietario del perfil reciben la devolución de llamada.

Actualizar políticas

El propietario de un dispositivo puede establecer una política de actualizaciones del sistema local para un dispositivo a fin de controlar cuándo se instalan las actualizaciones. La política de actualización del sistema puede ser de uno de estos tres tipos:

Automático
Instala actualizaciones del sistema en cuanto están disponibles (sin interacción del usuario). Si estableces este tipo de política, se instalarán de inmediato todas las actualizaciones pendientes que puedan posponerse o que estén esperando un período de mantenimiento.
Pared con ventana
Instala actualizaciones del sistema durante un período de mantenimiento diario (sin interacción del usuario). Establece el inicio y el final del período de mantenimiento diario, como minutos del día, cuando crees una política nueva con ventanas.
Pospuesto
Pospón la instalación de las actualizaciones del sistema por 30 días. Una vez finalizado el período de 30 días, el sistema le solicita al usuario del dispositivo que instale la actualización.

Períodos de posposición

El sistema limita cada actualización a una posposición de 30 días. El período comienza cuando el sistema pospone por primera vez la actualización, y establecer nuevas políticas de posposición no extenderá el período.

Además de la posposición, es posible que Android no pueda instalar una actualización por otros motivos, como conectividad, espacio en el disco insuficiente o batería baja.

El sistema restablece el temporizador de posposición de 30 días si hay una actualización diferente disponible durante el período, lo que les da a los administradores de TI la oportunidad de probar las actualizaciones combinadas del sistema. Una vez que transcurran 30 días sin una nueva actualización, el sistema le solicitará al usuario que instale todas las actualizaciones pendientes. Más adelante, cuando haya una nueva actualización del sistema disponible, comenzará de nuevo el período de 30 días.

Cómo establecer una política

Puedes establecer políticas de actualización en Android 8.0 (nivel de API 26) o versiones posteriores. Para especificar cuándo el dispositivo debe instalar actualizaciones del sistema, crea una instancia de SystemUpdatePolicy usando uno de los tres tipos descritos anteriormente. Para establecer una política, el propietario de tu dispositivo llama al método setSystemUpdatePolicy() de DevicePolicyManager. En la siguiente muestra de código, se indica cómo hacerlo. Para ver un ejemplo de política con ventanas, consulta la documentación de 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);

Una vez que creas las instancias de políticas, no se pueden cambiar. Para cambiar cuándo un dispositivo instala actualizaciones, puedes crear y establecer una política nueva. Para quitar una política de un dispositivo, llama a setSystemUpdatePolicy() y pasa null como el argumento policy. Después de que tu DPC quite una política, el usuario del dispositivo verá notificaciones de actualizaciones del sistema disponibles.

Las apps pueden llamar a getSystemUpdatePolicy() para obtener la política actual del dispositivo. Si este método muestra null, significa que no hay ninguna política establecida.

Períodos de congelación

Para inmovilizar la versión del SO durante períodos críticos, como días festivos u otros horarios de mayor actividad, los propietarios de dispositivos pueden suspender las actualizaciones del sistema durante un máximo de 90 días. Cuando un dispositivo se encuentra dentro de un período de suspensión, se comporta de la siguiente manera:

  • El dispositivo no recibe notificaciones sobre actualizaciones pendientes del sistema.
  • Las actualizaciones del sistema del SO no están instaladas.
  • Los usuarios del dispositivo no pueden buscar actualizaciones del sistema de forma manual en Configuración.

El sistema aplica un período de búfer obligatorio de 60 días después de cualquier período de suspensión definido para evitar que el dispositivo se bloquee de forma indefinida. Recuerda que bloquear las actualizaciones del sistema puede impedir que los dispositivos reciban actualizaciones críticas.

Figura 1: Se establecieron dos períodos de suspensión para un dispositivo
Calendario que muestra dos períodos sin actualización en un año con búferes de 60 días.

Estableces períodos de suspensión en una política de actualización. No puedes establecer períodos de suspensión sin establecer una política. Cuando el dispositivo esté fuera de los períodos de suspensión que estableciste, se aplica el comportamiento normal de la política (automático, en ventanas o pospuesto).

Cómo configurar un período sin actualización

Puedes establecer períodos de suspensión en Android 9 (nivel de API 28) o versiones posteriores. El propietario de un dispositivo establece un período sin actualización en una política de actualización del sistema antes de establecer la política para el dispositivo. Los pasos son los siguientes:

  1. Crea una nueva política de actualización del sistema (o bien obtén la actual).
  2. Llama a setFreezePeriods() para establecer los períodos de suspensión en la política.
  3. Establece la política y los períodos de suspensión para el dispositivo llamando a setSystemUpdatePolicy().

Debido a que el período sin actualización se repite anualmente, las fechas de inicio y finalización del período se representan con valores de mes y día. El día de inicio debe comenzar al menos 60 días después del final de cualquier período sin actualización anterior. En el siguiente ejemplo, se muestra cómo establecer dos períodos sin actualización para una política de actualización del sistema existente:

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

Tanto el día de inicio como el día de finalización son inclusivos. Si el día de inicio es mayor que el día de finalización (como winterSale en el ejemplo anterior), el período sin actualización se extiende hasta el año siguiente.

Cuando se establecen períodos de suspensión en una política de actualización del sistema, Android prueba los siguientes requisitos:

  • No hay un período sin actualización que supere los 90 días.
  • El intervalo entre los períodos sin actualización es de al menos 60 días.
  • Los períodos de suspensión no se superponen.
  • No hay períodos de suspensión duplicados.

Cuando se configura la política de actualización del sistema para un dispositivo, Android repite estas pruebas y, además, incluye los períodos de suspensión actuales o anteriores del dispositivo.

Android arroja una SystemUpdatePolicy.ValidationFailedException cuando falla cualquiera de estas pruebas.

Para obtener una lista de los períodos de suspensión establecidos previamente en un objeto de política de actualización del sistema, todas las apps instaladas pueden llamar a SystemUpdatePolicy.getFreezePeriods(). En el siguiente ejemplo, se llama a este método para registrar los períodos de suspensión de 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());
  }
}

Años bisiestos

Android usa el calendario ISO 8601 (también llamado calendario gregoriano) para calcular los períodos de suspensión y omite los años bisiestos. Esto significa que el 29 de febrero no se reconoce como una fecha válida y se considera como si fuera el 28 de febrero. Por lo tanto, el 29 de febrero no se cuenta cuando se calcula la duración de un período sin actualización.

Desarrollo y pruebas

Mientras desarrollas y pruebas la función de actualización del sistema de tu DPC, es posible que debas crear muchos períodos de suspensión. Debido a que Android verifica un intervalo de 60 días entre períodos sin actualización anteriores, es posible que no puedas establecer un nuevo período sin antes borrar el registro de los períodos anteriores. Para borrar el registro del período sin actualización del dispositivo, ejecuta el siguiente comando en el shell de Android Debug Bridge (adb):

adb shell dpm clear-freeze-period-record

Para confirmar si un dispositivo se encuentra en período de suspensión, verifica que la interfaz de usuario para las actualizaciones del sistema esté inhabilitada.

Recursos adicionales

Para obtener más información sobre las actualizaciones del sistema, consulta la documentación de actualizaciones inalámbricas del Proyecto de código abierto de Android.