管理系统更新

本开发者指南介绍了设备策略控制器 (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 系统更新。

回滚更新

在某些情况下,可以使用 Google Play 系统更新回滚 (GPSUR) 工具 来恢复由于有问题的 Google Play 系统更新安装导致的设备状态。此工具应由高级用户使用,或在支持人员指示时使用,因为它可能会导致数据丢失。以下是使用 GPSUR 工具的方法:

  1. 如果您的计算机上正在运行 Android 调试桥 (adb),请在继续之前停止 adb 服务,以免其干扰回滚过程。要停止 adb,请运行 adb kill-server
  2. 打开 GPSUR 工具.
  3. 单击**允许 ADB 访问**以允许该工具通过 adb 与您的测试设备通信。
  4. 单击**添加新设备**。
  5. 从列表中选择您的设备,然后单击**连接**。此列表可能不包含完整的设备名称。
  6. 在设备屏幕上,选择**始终允许来自此计算机**,然后单击**确定**以接受 USB 调试连接。
  7. 在浏览器中选择已连接的设备。
  8. 如果设备上有可用的回滚,则页面上的按钮文本应从无可用回滚切换到回滚最近更新。单击**回滚最近更新**。
  9. 阅读**确认回滚**模态中的警告,然后单击**确认**。
  10. 等待回滚完成。完成后,将出现**回滚成功**模态,设备将重新启动。现在可以安全地拔下设备。

其他资源

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