システム アップデートを管理する

このデベロッパー ガイドでは、デバイスのユーザーに代わって Device Policy Controller(DPC)が Android システム アップデートを管理する方法について説明します。

はじめに

Android デバイスは、システム ソフトウェアやアプリ ソフトウェアの無線(OTA)アップデートを受信してインストールできます。Android は、システム アップデートが利用可能であることと、デバイス ユーザーはアップデートを直ちにまたは後でインストールできることを、デバイス ユーザーに通知します。

IT 管理者は DPC を使用して、デバイス ユーザーのシステム アップデートを管理できます。DPC は、完全管理対象デバイス(デバイス所有者)または仕事用プロファイル(プロファイル所有者)を所有できます。表 1 は、デバイス所有者がシステム アップデートを管理する方法を示しています。一方、プロファイル所有者はシステム アップデートに関する情報しか報告できません。

表 1: DPC で使用できるタスクはオーナーモードによって異なる

タスク デバイス オーナー プロフィールの所有者
保留中のシステム アップデートを確認する
新しいシステム アップデートが利用可能になったときにコールバックを受け取る
ローカル アップデート ポリシーを設定して Android がシステム アップデートをインストールするタイミングを管理する
重要な期間に OS バージョンを固定する

保留中のアップデートを確認

保留中のアップデートとは、まだインストールされていないデバイスのシステム アップデートのことです。 IT 管理者は DPC を使用して、システム アップデートが保留中のデバイスを確認できます。また、デバイスのユーザーに重要なアップデートを迅速にインストールするよう求めることもできます。

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 を登録またはアドバタイズする必要はありません。1 回のアップデートでこのメソッドが複数回呼び出される場合があるため、応答する前にアップデートのステータスを確認してください。コールバックでシステム アップデートの詳細情報を確認するには、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 がある場合、デバイス所有者とプロファイル所有者の両方がコールバックを受信します。

ポリシーの更新

デバイス所有者は、デバイスにローカル システム アップデート ポリシーを設定することで、アップデートのインストールタイミングを制御できます。システム アップデート ポリシーは、次の 3 つのタイプのいずれかです。

自動
システム アップデートが利用可能になり次第、ユーザーの操作なしでインストールします。このポリシータイプを設定すると、延期またはメンテナンスの時間枠を待機する保留中の更新が直ちにインストールされます。
窓(まど)つき
毎日のメンテナンスの時間枠で(ユーザーの操作なしで)システム アップデートをインストールします。新しいウィンドウ ポリシーを作成するときに、1 日のメンテナンスの時間枠の開始時刻と終了時刻を分単位で設定します。
延期
システム アップデートのインストールを 30 日間延期します。30 日が経過すると、デバイスのユーザーにアップデートのインストールを求めるメッセージが表示されます。

延期期間

システムにより、各更新の延期は 30 日間で 1 回のみとなっています。システムは最初に更新を延期する時点から始まり、新しい延期ポリシーを設定してもその期間が延長されることはありません。

延期の他にも、接続がない、ディスク容量が不足している、バッテリー残量が少ないなど、その他の理由で Android がアップデートをインストールできないことがあります。

この期間中に別のアップデートが利用可能になると、30 日間の延期タイマーがリセットされるため、IT 管理者は組み合わせたシステム アップデートを試すことができます。新しいアップデートがないまま 30 日が経過すると、保留中のアップデートをすべてインストールするように求めるメッセージがユーザーに表示されます。その後、新しいシステム アップデートが利用可能になると、再び 30 日間の期間が開始されます。

ポリシーの設定方法

Android 8.0(API レベル 26)以降でアップデート ポリシーを設定できます。デバイスにシステム アップデートをインストールするタイミングを指定するには、上記の 3 つのタイプのいずれかを使用して 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() を呼び出して、policy 引数として null を渡します。DPC がポリシーを削除すると、利用可能なシステム アップデートの通知がデバイス ユーザーに表示されます。

アプリは getSystemUpdatePolicy() を呼び出すことで、デバイスの現在のポリシーを取得できます。このメソッドが null を返した場合は、現在ポリシーが設定されていないことを意味します。

凍結期間

デバイス所有者は、休日やその他の繁忙期などの重要な期間に OS バージョンを凍結するために、システム アップデートを最大 90 日間停止できます。デバイスが凍結期間中の場合、デバイスは次のように動作します。

  • デバイスは、保留中のシステム アップデートに関する通知を一切受信しません。
  • OS のシステム アップデートはインストールされていません。
  • デバイスのユーザーが [設定] でシステム アップデートを手動で確認できない。

デバイスが無期限にフリーズするのを防ぐため、定義済みの凍結期間の後に 60 日間のバッファ期間が必須となります。システム アップデートを凍結すると、デバイスが重要なアップデートを受信できなくなる可能性があることにご注意ください。

図 1.デバイスに 2 回の凍結期間を設定
1 年に 2 回の凍結期間と 60 日間の猶予期間を示すカレンダー。

更新ポリシーで凍結期間を設定します。ポリシーを設定せずに凍結期間を設定することはできません。設定した凍結期間を過ぎた場合、通常のポリシーの動作(自動、ウィンドウ処理、延期)が適用されます。

凍結期間を設定する方法

Android 9(API レベル 28)以降では、凍結期間を設定できます。デバイス所有者は、デバイスにポリシーを設定する前に、システム アップデート ポリシーで凍結期間を設定します。手順は次のとおりです。

  1. 新しいシステム アップデート ポリシーを作成します(または現在のシステム アップデート ポリシーを取得します)。
  2. setFreezePeriods() を呼び出して、ポリシーに凍結期間を設定します。
  3. setSystemUpdatePolicy() を呼び出して、デバイスのポリシーと凍結期間を設定します。

凍結期間は毎年繰り返されるため、期間の開始日と終了日は月と日の値で表されます。開始日は、前回の凍結期間の終了から 60 日以上経過している必要があります。次の例は、既存のシステム アップデート ポリシーに 2 つの凍結期間を設定する方法を示しています。

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 Debug Bridge(adb)シェルで次のコマンドを実行します。

adb shell dpm clear-freeze-period-record

デバイスが凍結期間中かどうかは、システム アップデートのユーザー インターフェースが無効になっていることで確認できます。

参考情報

システム アップデートについて詳しくは、Android オープンソース プロジェクトの OTA アップデートのドキュメントをご覧ください。