ダイレクト ブート モードをサポートする

Android 7.0 は、デバイスのロックを解除していない状態でユーザーがデバイスの電源を入れた場合、セキュアなダイレクト ブート モードで動作します。この機能をサポートするため、システムは次の 2 つのデータ保存先を備えています。

  • 認証情報暗号化ストレージ: デフォルトの保存先であり、ユーザーがデバイスのロックを解除した後にのみ使用できます。
  • デバイス暗号化ストレージ: この保存先はダイレクト ブート モード中とユーザーがデバイスのロックを解除した後のどちらでも使用できます。

デフォルトでは、ダイレクト ブート モード中にアプリは実行されません。ダイレクト ブート モード中にアプリの稼働が必要な場合は、このモード中でも実行するアプリ コンポーネントを登録します。ダイレクト ブート モード中に実行する必要があるアプリの主なユースケースは次のとおりです。

  • 目覚まし時計アプリなど、通知がスケジュールされているアプリ。
  • SMS アプリなど、重要なユーザー通知を表示するアプリ。
  • Talkback など、ユーザー補助機能サービスを提供するアプリ。

ダイレクト ブート モードで実行中にアプリがデータにアクセスする必要がある場合は、デバイス暗号化ストレージを使用します。デバイス暗号化ストレージには鍵で暗号化されたデータが保存され、この鍵はデバイスが確認付きブートに成功した後でのみ利用可能になります。

ユーザー認証情報(PIN やパスワードなど)に関連付けられた鍵による暗号化が必要なデータには、認証情報暗号化ストレージを使用します。認証情報暗号化ストレージには、ユーザーがデバイスのロック解除に成功した後で、デバイスを再起動するまでアクセスできます。ユーザーがデバイスをロック解除した後にロック画面を有効にしても、認証情報暗号化ストレージは引き続き利用できます。

ダイレクト ブート中に実行するためのアクセスをリクエストする

ダイレクト ブート モード中にアプリを実行したり、デバイス暗号化ストレージにアクセスしたりするには、アプリ コンポーネントをシステムに登録する必要があります。アプリをシステムに登録するには、コンポーネントを「暗号化対応」としてマークします。コンポーネントを暗号化対応としてマークするには、マニフェスト内で android:directBootAware 属性を true に設定します。

暗号化対応コンポーネントを登録しておくと、デバイスを再起動したときにシステムから ACTION_LOCKED_BOOT_COMPLETED ブロードキャスト メッセージを受信できます。この時点でデバイス暗号化ストレージを利用できるようになり、ダイレクト ブート モード中に実行する必要があるタスク(スケジュール設定したアラームのトリガーなど)をコンポーネントが実行できるようになります。

以下のコード スニペットで、アプリ マニフェスト内で BroadcastReceiver を暗号化対応として登録し、ACTION_LOCKED_BOOT_COMPLETED 用のインテント フィルタを追加する方法の例を示します。

<receiver
  android:directBootAware="true" >
  ...
  <intent-filter>
    <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
  </intent-filter>
</receiver>

ユーザーがデバイスのロックを解除すると、すべてのコンポーネントがデバイス暗号化ストレージと認証情報暗号化ストレージの両方にアクセスできるようになります。

デバイス暗号化ストレージにアクセスする

デバイス暗号化ストレージにアクセスするには、Context.createDeviceProtectedStorageContext() を呼び出して、2 つ目の Context インスタンスを作成します。このコンテキストを使用して実行されたストレージ API 呼び出しはすべて、デバイス暗号化ストレージにアクセスします。次の例では、デバイス暗号化ストレージにアクセスして既存のアプリのデータファイルを開きます。

Kotlin

val directBootContext: Context = appContext.createDeviceProtectedStorageContext()
// Access appDataFilename that lives in device encrypted storage
val inStream: InputStream = directBootContext.openFileInput(appDataFilename)
// Use inStream to read content...

Java

Context directBootContext = appContext.createDeviceProtectedStorageContext();
// Access appDataFilename that lives in device encrypted storage
FileInputStream inStream = directBootContext.openFileInput(appDataFilename);
// Use inStream to read content...

デバイス暗号化ストレージを使用するのは、ダイレクト ブート モード中にアクセスが必要な情報だけに限るようにしてください。デバイス暗号化ストレージを汎用的な暗号化された保存場所として使用しないでください。ユーザーの個人情報や、ダイレクト ブート モード中に必要ではない暗号化データに対しては、認証情報暗号化ストレージを使用してください。

ユーザーによるデバイスのロック解除の通知を受信する

再起動後にユーザーがデバイスのロックを解除すると、アプリは認証情報暗号化ストレージにアクセスするよう切り替えて、ユーザー認証情報が必要な通常のシステム サービスを使用するようになります。

再起動後、ユーザーがデバイスのロックを解除したときに通知を受信するには、実行中のコンポーネントを対象とする BroadcastReceiver を登録して、ロック解除の通知メッセージをリッスンするようにします。デバイスの起動後、ユーザーがデバイスのロックを解除したときの動作は次のとおりです。

  • 即時通知を必要とするフォアグラウンド プロセスをアプリが使用している場合は、ACTION_USER_UNLOCKED メッセージをリッスンします。
  • アプリが遅延通知で対応できるバックグラウンド プロセスのみを使用している場合は、ACTION_BOOT_COMPLETED メッセージをリッスンします。

ユーザーがデバイスのロックを解除しているかどうかを確認するには、UserManager.isUserUnlocked() を呼び出します。

既存のデータを移行する

ユーザーがデバイスをアップデートしてダイレクト ブート モードを使用できるようにした場合、既存のデータをデバイス暗号化ストレージに移行することが必要になる場合があります。移行先コンテキストをメソッド呼び出し元、移行元コンテキストを引数として Context.moveSharedPreferencesFrom()Context.moveDatabaseFrom() を使用し、認証情報暗号化ストレージとデバイス暗号化ストレージの間で、設定データとデータベース データを移動させます。

パスワードや承認トークンなどのユーザーの個人情報は、認証情報暗号化ストレージからデバイス暗号化ストレージに移行しないでください。他にどのデータをデバイス暗号化ストレージに移行するかは、慎重に判断してください。場合によっては、この 2 つの暗号化ストレージにデータセットを振り分けて管理する必要もあります。

暗号化対応アプリをテストする

ダイレクト ブート モードを有効にして、暗号化対応アプリをテストします。

最新バージョンの Android を搭載したほとんどのデバイスでは、ロック画面認証情報(PIN、パターン、パスワード)が設定されていれば、ダイレクト ブート モードが有効です。厳密には、ファイルベースの暗号化を使用するすべてのデバイスが該当します。デバイスでファイルベースの暗号化が使用されているかを確認するには、次のシェルコマンドを実行します。

adb shell getprop ro.crypto.type

出力が file の場合、デバイスでファイルベースの暗号化が有効になっています。

デフォルトでファイルベースの暗号化を使用していないデバイスでは、ダイレクト ブート モードをテストする他のオプションを利用できる場合があります。

  • フルディスク暗号化(ro.crypto.type=block)を使用しており、Android 7.0 から Android 12 までを搭載している一部のデバイスでは、ファイルベースの暗号化に変換できます。変換には次の 2 つの方法があります。

      警告: ファイルベースの暗号化に変換するどちらの方法も、デバイス上のすべてのユーザーデータがワイプされます。

    • デバイスで [開発者向けオプション] がまだ有効になっていない場合は、[設定] > [デバイス情報] に進み、[ビルド番号] を 7 回タップして有効にします。次に、[設定] > [開発者向けオプション] に移動して、[ファイル暗号化に変換する] を選択します。
    • または、次のシェルコマンドを実行します。
      adb reboot-bootloader
      fastboot --wipe-and-use-fbe
      
  • Android 13 以前を搭載しているデバイスは、ダイレクト ブート モードの「エミュレーション」をサポートしています。この場合、ファイル権限を使用して、暗号化されたファイルに対するロックとロック解除の影響をシミュレートします。エミュレートされたモードはデータの損失を引き起こす可能性があるため、開発時にのみ使用してください。エミュレートされたダイレクト ブート モードを有効にするには、デバイスでロックパターンを設定します。ロックパターンの設定時に [安全な起動] 画面が表示された場合は、[スキップ] を選択します。次に、以下のシェルコマンドを実行します。

    adb shell sm set-emulate-fbe true
    

    エミュレートされたダイレクト ブート モードを無効にするには、次のシェルコマンドを実行します。

    adb shell sm set-emulate-fbe false
    

    これらのコマンドのいずれかを実行すると、デバイスが再起動します。

デバイス ポリシーの暗号化ステータスをチェックする

デバイス管理アプリは DevicePolicyManager.getStorageEncryptionStatus() を使用して、デバイスの現在の暗号化ステータスをチェックできます。

アプリが Android 7.0(API 24)より前の API レベルをターゲットとしている場合、デバイスがダイレクト ブートでフルディスク暗号化またはファイルベースの暗号化を使用しているのであれば、getStorageEncryptionStatus()ENCRYPTION_STATUS_ACTIVE を返します。どちらの場合においても、データは常に暗号化されて安全に保存されます。

アプリが Android 7.0(API 24)以降をターゲットとしている場合、デバイスがフルディスク暗号化を使用しているのであれば、getStorageEncryptionStatus()ENCRYPTION_STATUS_ACTIVE を返します。デバイスがダイレクト ブートでファイルベースの暗号化を使用しているのであれば、ENCRYPTION_STATUS_ACTIVE_PER_USER を返します。

Android 7.0 をターゲットとするデバイス管理アプリを作成する場合は、ENCRYPTION_STATUS_ACTIVEENCRYPTION_STATUS_ACTIVE_PER_USER の両方をチェックして、デバイスが暗号化されているかを判断するようにしてください。

他のサンプルコード

このページで説明した API の使用方法の他の例については、DirectBoot サンプルをご覧ください。