管理系统更新

本开发者指南介绍了设备策略控制器 (DPC) 如何代表设备用户管理 Android 系统更新。

简介

Android 设备可以接收和安装系统和应用程序软件的无线 (OTA) 更新。Android 会通知设备用户系统更新可用,设备用户可以立即或稍后安装更新。

使用您的 DPC,IT 管理员可以管理设备用户的系统更新。DPC 可以拥有完全托管的设备(称为设备所有者)或可以拥有工作配置文件(称为配置文件所有者)。表 1 显示了设备所有者如何管理系统更新,而配置文件所有者只能报告有关系统更新的信息。

表 1:DPC 可用的任务取决于所有者模式

任务 设备所有者 配置文件所有者
检查是否有挂起的系统更新
当新的系统更新可用时接收回调
设置本地更新策略以控制 Android 何时安装系统更新
在关键时期冻结操作系统版本

检查是否有挂起的更新

挂起的更新是指尚未安装的设备的系统更新。您的 DPC 可以帮助 IT 管理员检查哪些设备有挂起的系统更新,并可能要求设备用户及时安装关键更新。

在 Android 8.0(API 级别 26)或更高版本上运行的设备所有者和配置文件所有者可以检查设备是否有挂起的系统更新。调用DevicePolicyManager.getPendingSystemUpdate(),如果设备是最新的,则返回null。如果系统更新挂起,则该方法会返回有关更新的信息。

了解有关待处理更新的更多信息

调用getPendingSystemUpdate()后,您可以检查返回的SystemUpdateInfo值以了解有关待处理更新的更多信息。以下示例说明了如何查找设备何时首次可以使用待处理更新

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

系统回调

当更新可用时,Android系统会通知设备所有者有关新更新的信息。在Android 8.0或更高版本中,系统还会通知配置文件所有者。

在您的DeviceAdminReceiver子类中,覆盖onSystemUpdatePending()回调。您无需注册或宣传您的DPC以接收回调。系统可能会针对单个更新多次调用此方法,因此请在响应之前检查更新的状态。调用getPendingSystemUpdate()以在回调中了解有关系统更新的更多信息。以下示例说明了如何执行此操作

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

当系统有多个DPC时,例如完全托管设备上的工作配置文件,设备所有者和配置文件所有者都会收到回调。

更新策略

设备所有者可以通过为设备设置本地系统更新策略来控制何时安装更新。系统更新策略可以是三种类型之一

自动
一旦更新可用,就会安装系统更新(无需用户交互)。设置此策略类型会立即安装任何可能被推迟或等待维护窗口的待处理更新。
窗口化
在每日维护窗口期间安装系统更新(无需用户交互)。在创建新的窗口化策略时,设置每日维护窗口的开始和结束时间(以分钟为单位)。
推迟
将系统更新的安装推迟30天。30天期限结束后,系统会提示设备用户安装更新。

推迟期限

系统将每个更新限制为一次30天的推迟。期限从系统首次推迟更新时开始,设置新的推迟策略不会延长期限。

除了推迟之外,Android可能由于其他原因无法安装更新,例如无连接、磁盘空间不足或电量不足。

如果在此期间有不同的更新可用,系统会重置30天的推迟计时器,从而使IT管理员有机会尝试组合的系统更新。在30天内没有新更新后,系统会提示用户安装所有待处理更新。稍后,当新的系统更新可用时,30天期限将重新开始。

如何设置策略

您可以在Android 8.0(API级别26)或更高版本中设置更新策略。要指定设备何时应安装系统更新,请使用上面概述的三种类型之一创建SystemUpdatePolicy的实例。要设置策略,您的设备所有者会调用DevicePolicyManager方法setSystemUpdatePolicy()。以下代码示例说明了如何执行此操作。要查看窗口化策略示例,请参阅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);

创建策略实例后,无法更改它们。要更改设备安装更新的时间,您可以创建并设置新策略。要从设备中删除策略,请调用setSystemUpdatePolicy()并将null作为policy参数传递。您的DPC删除策略后,设备用户会看到任何可用系统更新的通知。

应用可以调用getSystemUpdatePolicy()以获取设备的当前策略。如果此方法返回null,则表示当前未设置策略。

冻结期

为了在关键时期(例如假期或其他繁忙时间)冻结操作系统版本,设备所有者可以将系统更新暂停长达90天。当设备处于冻结期时,其行为如下

  • 设备不会收到有关待处理系统更新的任何通知。
  • 不会安装对操作系统的系统更新。
  • 设备用户无法在“设置”中手动检查系统更新。

系统会在任何定义的冻结期之后强制执行强制性的60天缓冲期,以防止无限期冻结设备。请记住,冻结系统更新可能会阻止设备接收关键更新。

图1. 为设备设置的两个冻结期
Calendar showing two freeze periods in a year with 60-day buffers.

您在更新策略上设置冻结期。您不能在没有设置策略的情况下设置冻结期。当设备位于您设置的任何冻结期之外时,将应用正常策略行为(自动、窗口化或推迟)。

如何设置冻结期

您可以在Android 9(API级别28)或更高版本中设置冻结期。设备所有者在为设备设置策略之前,会在系统更新策略上设置冻结期。步骤如下

  1. 创建一个新的(或获取当前的)系统更新策略。
  2. 通过调用setFreezePeriods()在策略上设置冻结期。
  3. 通过调用setSystemUpdatePolicy()设置设备的策略和冻结期。

由于冻结期每年重复一次,因此该期限的开始和结束日期由月和日值表示。开始日期必须至少在任何先前冻结期结束后的60天后开始。以下示例说明了如何为现有的系统更新策略设置两个冻结期

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

开始日期和结束日期都包含在内。如果开始日期大于结束日期(例如,前面示例中的winterSale),则冻结期将延续到下一年。

在系统更新策略上设置冻结期时,Android会测试以下要求

  • 没有冻结期超过90天。
  • 冻结期之间的间隔至少为60天。
  • 冻结期不重叠。
  • 没有重复的冻结期。

为设备设置系统更新策略时,Android会重复这些测试,并包含设备的任何当前或过去的冻结期。

当任何这些测试失败时,Android会抛出SystemUpdatePolicy.ValidationFailedException

要获取之前在系统更新策略对象上设置的冻结期列表,所有已安装的应用都可以调用SystemUpdatePolicy.getFreezePeriods()。以下示例调用此方法来记录设备的冻结期

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

闰年

Android使用ISO 8601日历(也称为公历)来计算冻结期,并且会忽略闰年。这意味着2月29日不被识别为有效日期,并且会被视为2月28日。因此,在计算冻结期的持续时间时,不会计算2月29日。

开发和测试

在开发和测试DPC的系统更新功能时,您可能需要创建许多冻结期。由于Android会检查过去冻结期之间的60天间隔,因此您可能无法设置新的冻结期,除非首先清除过去期限的记录。要清除设备的冻结期记录,请在Android调试桥(adb)shell中运行以下命令

adb shell dpm clear-freeze-period-record

您可以通过检查系统更新的用户界面是否已禁用,来确认设备是否处于冻结期。

Google Play系统更新(Mainline)

Google Play系统更新(也称为Mainline更新)会自动下载,但需要设备重新启动才能安装。这些更新不会触发自动重新启动,而是会安装在下一次用户、管理员或策略触发的重新启动时。由系统更新策略触发的重新启动将安装关联的Google/OEM系统更新以及任何先前下载的Google Play系统更新。

还可以通过导航到设置 > 关于 > Android 版本 > Google Play系统更新手动安装Google Play系统更新。

其他资源

要了解有关系统更新的更多信息,请阅读Android开源项目的OTA更新文档。