管理系统更新

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

简介

Android 设备可以接收和安装系统和应用软件的无线下载 (OTA) 更新。Android 会通知设备用户有可用的系统更新,并且设备用户可以立即安装更新,也可以稍后安装。

IT 管理员可以使用您的 DPC 为设备用户管理系统更新。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.为设备设置的两个冻结期
日历显示一年中有两个冻结期,每个冻结期为 60 天。

您可以为更新政策设置冻结期。如果不设置政策,就无法设置冻结期。当设备处于您设置的任何冻结期之外时,系统会应用正常的政策行为(自动、窗口化或推迟)。

如何设置冻结期

您可以在 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

如需确认设备是否处于冻结期,您可以检查系统更新界面是否处于停用状态。

其他资源

如需详细了解系统更新,请参阅 Android 开源项目的 OTA 更新文档。