支持直接启动模式

Android 7.0 在设备已开机但用户尚未解锁设备的情况下,以安全、直接启动模式运行。为了支持这一点,系统为数据提供了两个存储位置

  • 凭据加密存储,这是默认存储位置,仅在用户解锁设备后可用。
  • 设备加密存储,这是在直接启动模式期间和用户解锁设备后都可用的存储位置。

默认情况下,应用程序不会在直接启动模式期间运行。如果您的应用程序需要在直接启动模式期间采取措施,您可以注册应用程序组件以在该模式期间运行。需要在直接启动模式期间运行的应用程序的一些常见用例包括

  • 具有计划通知的应用程序,例如闹钟应用程序。
  • 提供重要用户通知的应用程序,例如短信应用程序。
  • 提供辅助功能服务的应用程序,例如 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 实例调用 Context.createDeviceProtectedStorageContext() 创建第二个 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()(以目标上下文作为方法调用者,以源上下文作为参数)在凭据加密存储和设备加密存储之间迁移首选项和数据库数据。

不要将私人物品信息(例如密码或授权令牌)从凭据加密存储迁移到设备加密存储。在决定将其他数据迁移到设备加密存储时,请谨慎判断。在某些情况下,您可能需要在两个加密存储中管理单独的数据集。

测试您的加密感知应用程序

使用启用直接启动模式的设备测试您的加密感知应用程序。

大多数运行最新版 Android 的设备只要设置了锁屏凭据(PIN、图案或密码),就会启用直接启动模式。具体来说,在所有使用基于文件的加密的设备上都是如此。要检查设备是否使用基于文件的加密,请运行以下 shell 命令

adb shell getprop ro.crypto.type

如果输出为 file,则设备启用了基于文件的加密。

在默认情况下不使用基于文件的加密的设备上,可能存在其他用于测试直接启动模式的选项

  • 一些使用全盘加密(ro.crypto.type=block)并运行 Android 7.0 到 Android 12 的设备可以转换为基于文件的加密。有两种方法可以做到这一点

      警告:任何一种转换为基于文件的加密的方法都会擦除设备上的所有用户数据。

    • 在设备上,如果您尚未启用开发者选项,请转到设置 > 关于手机,然后点按版本号七次。然后转到设置 > 开发者选项,并选择转换为文件加密
    • 或者,运行以下 shell 命令
      adb reboot-bootloader
      fastboot --wipe-and-use-fbe
      
  • 运行 Android 13 或更低版本的设备支持“模拟”直接启动模式,该模式使用文件权限来模拟已锁定和解锁的加密文件的效应。仅在开发期间使用模拟模式;它可能会导致数据丢失。要启用模拟直接启动模式,请在设备上设置锁定图案,在设置锁定图案时选择“不,谢谢”以获取安全启动屏幕的提示,然后运行以下 shell 命令

    adb shell sm set-emulate-fbe true
    

    要关闭模拟直接启动模式,请运行以下 shell 命令

    adb shell sm set-emulate-fbe false
    

    运行这两个命令中的任何一个都会导致设备重启。

检查设备策略加密状态

设备管理应用程序可以使用 DevicePolicyManager.getStorageEncryptionStatus() 来检查设备的当前加密状态。

如果您的应用程序的目标 API 级别低于 Android 7.0 (API 24),则如果设备使用全盘加密或具有直接启动功能的基于文件的加密,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,以确定设备是否已加密。

其他代码示例

DirectBoot 示例进一步演示了本页介绍的 API 的使用。