管理系统更新

本开发者指南说明您的设备政策控制器 (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 系统更新。

Google Play 系统更新也可以通过导航到 设置 > 关于 > Android 版本 > 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 开放源代码项目的 OTA 更新 文档。