锁定任务模式

本开发者指南介绍了如何将专用设备锁定到单个应用或一组应用。如果您是企业移动管理 (EMM) 开发人员或解决方案集成人员,请阅读本指南,将锁定任务模式添加到您的解决方案中。

概述

Android 可以以沉浸式、类似信息亭的方式运行任务,称为*锁定任务模式*。如果您正在开发信息亭应用程序或启动器来呈现一组应用,则可以使用锁定任务模式。当系统处于锁定任务模式时,设备用户通常无法看到通知、访问非允许列表中的应用或返回主屏幕(除非主屏幕在允许列表中)。

当系统处于锁定任务模式时,只有设备策略控制器 (DPC) 允许列出的应用才能运行。应用被列入允许列表,因为使用设备的人不能总是退出锁定任务模式。

将被允许列入锁定任务模式的应用和允许列表 DPC 组合在一起的方式将取决于您要解决的问题。以下是一些示例

  • 一个单一的应用包,它结合了信息亭(用于呈现内容)和迷你 DPC(用于将自己列入锁定任务模式的允许列表)。
  • 一个 DPC,它是企业移动管理解决方案的一部分,在锁定任务模式下启动客户的移动应用。

可用性

系统可以在 Android 5.0 或更高版本中以锁定任务模式运行。表 1 显示了哪些版本的 Android 支持通过用户列入应用的允许列表。

表 1. Android 版本对 DPC 管理模式的支持
Android 版本 DPC 管理 注释
Android 5.0(API 级别 21)或更高版本 完全管理的设备
Android 8.0(API 级别 26)或更高版本 关联的辅助用户 辅助用户必须与主要用户关联。请参阅 多个用户 概述。
Android 9.0(API 级别 28)或更高版本 辅助用户

在 Android 9.0 或更高版本中,DPC 可以将任何应用的活动启动到锁定任务模式中。在早期版本中,应用必须已经支持以锁定任务模式启动自己的活动。

允许列入应用的允许列表

DPC 必须在应用可以在锁定任务模式下使用之前将它们列入允许列表。调用 DevicePolicyManager.setLockTaskPackages() 将应用列入锁定任务模式的允许列表,如下面的示例所示

Kotlin

// Allowlist two apps.
private val KIOSK_PACKAGE = "com.example.kiosk"
private val PLAYER_PACKAGE = "com.example.player"
private val APP_PACKAGES = arrayOf(KIOSK_PACKAGE, PLAYER_PACKAGE)

// ...

val context = context
val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE)
        as DevicePolicyManager
val adminName = getComponentName(context)
dpm.setLockTaskPackages(adminName, APP_PACKAGES)

Java

// Allowlist two apps.
private static final String KIOSK_PACKAGE = "com.example.kiosk";
private static final String PLAYER_PACKAGE = "com.example.player";
private static final String[] APP_PACKAGES = {KIOSK_PACKAGE, PLAYER_PACKAGE};

// ...

Context context = getContext();
DevicePolicyManager dpm =
    (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName adminName = getComponentName(context);
dpm.setLockTaskPackages(adminName, APP_PACKAGES);

要查找先前已列入锁定任务模式白名单的应用,DPC 可以调用 DevicePolicyManager.getLockTaskPackages()。其他应用可以调用 DevicePolicyManager.isLockTaskPermitted() 来确认应用包是否支持锁定任务模式。

启动锁定任务模式

在 Android 9.0(API 级别 28)或更高版本中,您可以以锁定任务模式启动另一个应用的活动。如果活动已在前景或后台运行,则需要重新启动活动。调用 ActivityOptions.setLockTaskEnabled(),并在启动活动时提供这些选项。以下代码片段展示了一种执行此操作的方法

Kotlin

// Set an option to turn on lock task mode when starting the activity.
val options = ActivityOptions.makeBasic()
options.setLockTaskEnabled(true)

// Start our kiosk app's main activity with our lock task mode option.
val packageManager = context.packageManager
val launchIntent = packageManager.getLaunchIntentForPackage(KIOSK_PACKAGE)
if (launchIntent != null) {
    context.startActivity(launchIntent, options.toBundle())
}

Java

// Set an option to turn on lock task mode when starting the activity.
ActivityOptions options = ActivityOptions.makeBasic();
options.setLockTaskEnabled(true);

// Start our kiosk app's main activity with our lock task mode option.
PackageManager packageManager = context.getPackageManager();
Intent launchIntent = packageManager.getLaunchIntentForPackage(KIOSK_PACKAGE);
if (launchIntent != null) {
  context.startActivity(launchIntent, options.toBundle());
}

在 Android 9.0 之前的版本中,应用通过调用 Activity.startLockTask() 来以锁定任务模式启动自己的活动。要调用此方法,活动必须在前景运行(请参阅 活动生命周期概念),因此我们建议在 onResume() 方法(ActivityFragment 的方法)中调用。以下是如何调用 startLockTask()

Kotlin

// In our Fragment subclass.
override fun onResume() {
    super.onResume()
    // First, confirm that this package is allowlisted to run in lock task mode.
    if (dpm.isLockTaskPermitted(context.packageName)) {
        activity.startLockTask()
    } else {
        // Because the package isn't allowlisted, calling startLockTask() here
        // would put the activity into screen pinning mode.
    }
}

Java

// In our Fragment subclass.
@Override
public void onResume() {
  super.onResume();

  // First, confirm that this package is allowlisted to run in lock task mode.
  if (dpm.isLockTaskPermitted(context.getPackageName())) {
    getActivity().startLockTask();
  } else {
    // Because the package isn't allowlisted, calling startLockTask() here
    // would put the activity into screen pinning mode.
  }
}

不要在设备被锁定时启动锁定任务模式,因为用户可能无法解锁设备。您可以调用 KeyguardManager 方法来确定设备是否被锁定,并使用 Activity 生命周期回调(例如 onResume(),它在解锁后被调用)来启动锁定任务模式。

处于锁定任务模式下的应用可以启动新的活动,只要该活动不启动新的任务——除了启动已列入白名单的应用的任务。要了解任务与活动之间的关系,请阅读指南 了解任务和后退堆栈

或者,您可以在 应用清单文件 中声明活动在系统运行于锁定任务模式时应如何运行。要使系统自动以锁定任务模式运行您的活动,请将 android:lockTaskMode 属性设置为 if_whitelisted,如下例所示

<activity
    android:name=".MainActivity"
    android:lockTaskMode="if_whitelisted">
    <!-- ... -->
</activity>

您可以通过阅读 lockTaskMode 参考来了解更多关于在应用清单文件中声明选项的信息。

停止锁定任务模式

DPC 可以通过从白名单中删除应用包来远程停止锁定任务模式。在 Android 6.0(API 级别 23)或更高版本中,调用 DevicePolicyManager.setLockTaskPackages(),并将包名称从白名单数组中省略。更新白名单后,应用将返回到堆栈中的前一个任务。

如果活动先前调用了 startLockTask(),则该活动可以调用 Activity.stopLockTask() 来停止锁定任务模式。此方法仅适用于启动锁定任务模式的活动。

生命周期回调

您的 DPC 可能需要了解应用(在同一个用户下运行)何时进入和退出锁定任务模式。要接收回调,请在 DPC 的 DeviceAdminReceiver 子类中覆盖以下回调方法

onLockTaskModeEntering()
应用进入锁定任务模式后调用。您可以从 pkg 参数中获取应用的包名称。
onLockTaskModeExiting()
应用退出锁定任务模式后调用。此回调不会接收有关应用的信息。

如果将另一个应用启动到锁定任务模式,则需要在自己的应用中跟踪运行状态。要检查当前应用是否在锁定任务模式下运行,请使用 ActivityManager 上的方法,如下例所示

Kotlin

// Check if this app is in lock task mode. Screen pinning doesn't count.
var isLockTaskModeRunning = false

val activityManager = context
        .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    isLockTaskModeRunning =
            activityManager.lockTaskModeState ==
            ActivityManager.LOCK_TASK_MODE_LOCKED
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Deprecated in API level 23.
    isLockTaskModeRunning = activityManager.isInLockTaskMode
}

if (isLockTaskModeRunning) {
    // Show the exit button ...
}

Java

// Check if this app is in lock task mode. Screen pinning doesn't count.
boolean isLockTaskModeRunning = false;

ActivityManager activityManager = (ActivityManager)
    getContext().getSystemService(Context.ACTIVITY_SERVICE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  isLockTaskModeRunning = activityManager.getLockTaskModeState()
      == ActivityManager.LOCK_TASK_MODE_LOCKED;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  // Deprecated in API level 23.
  isLockTaskModeRunning = activityManager.isInLockTaskMode();
}

if (isLockTaskModeRunning) {
  // Show the exit button ...
}

自定义 UI

当应用在锁定任务模式下运行时,系统用户界面 (UI) 会发生以下变化

  • 状态栏为空,隐藏了通知和系统信息。
  • 隐藏了主页和概览按钮。
  • 其他应用无法启动新活动。
  • 锁定屏幕(如果已设置)已禁用。

在 Android 9.0 或更高版本中,启用锁定任务模式后,您的 DPC 可以启用设备上的某些系统 UI 功能——这对创建自定义启动器的开发者很有用。调用 DevicePolicyManager.setLockTaskFeatures(),如下面的代码片段所示

Kotlin

// Enable the Home and Overview buttons so that our custom launcher can respond
// using our custom activities. Implicitly disables all other features.
dpm.setLockTaskFeatures(
        adminName,
        DevicePolicyManager.LOCK_TASK_FEATURE_HOME or
              DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW)

Java

// Enable the Home and Overview buttons so that our custom launcher can respond
// using our custom activities. Implicitly disables all other features.
dpm.setLockTaskFeatures(adminName,
    DevicePolicyManager.LOCK_TASK_FEATURE_HOME |
          DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW);

系统会禁用您未包含在 flags 参数中的任何功能。已启用的 UI 功能在启动锁定任务模式之间保持不变。如果设备已处于锁定任务模式,您对锁定任务功能所做的任何更改都会立即显示。表 2 描述了您可以自定义的 UI 功能。

表 2. 可自定义的锁定任务模式下的系统 UI 功能
系统 UI 功能 描述
LOCK_TASK_FEATURE_HOME 显示主页按钮。为自定义启动器启用——点击已启用的主页按钮没有操作,除非您将默认的 Android 启动器列入白名单。
LOCK_TASK_FEATURE_OVERVIEW 显示概览按钮(点击此按钮会打开 最近打开的应用 屏幕)。如果启用此按钮,还必须启用主页按钮。
LOCK_TASK_FEATURE_GLOBAL_ACTIONS 启用全局操作对话框,该对话框在长按电源按钮时显示。当 setLockTaskFeatures() 未被调用时启用的唯一功能。如果禁用此对话框,用户通常无法关闭设备电源。
LOCK_TASK_FEATURE_NOTIFICATIONS 启用所有应用的通知。这会在状态栏中显示通知图标、弹出式通知和可扩展的通知栏。如果启用此按钮,还必须启用主页按钮。点击通知操作和打开新面板的按钮在锁定任务模式下不起作用。
LOCK_TASK_FEATURE_SYSTEM_INFO 启用状态栏的系统信息区域,该区域包含连接性、电池、声音和振动选项等指示器。
LOCK_TASK_FEATURE_KEYGUARD 启用设备上可能设置的任何锁定屏幕。通常不适用于使用公共用户的设备,例如信息亭或数字标牌。
LOCK_TASK_FEATURE_NONE 禁用上面列出的所有系统 UI 功能。

DPC 可以调用 DevicePolicyManager.getLockTaskFeatures() 来获取设备在启用锁定任务模式时可用的功能列表。当设备退出锁定任务模式时,用户界面将恢复到现有设备策略规定的状态。

阻止窗口和叠加层

当应用在锁定任务模式下运行时,其他应用和后台服务可以创建新的窗口,Android 会将这些窗口显示在处于锁定任务模式下的应用前面。应用和服务创建这些窗口是为了向使用设备的人员显示吐司、对话框和叠加层。您的 DPC 可以通过添加 DISALLOW_CREATE_WINDOWS 用户限制来阻止这种情况。以下示例展示了如何在 onLockTaskModeEntering() 回调中执行此操作

Kotlin

// Called just after entering lock task mode.
override fun onLockTaskModeEntering(context: Context, intent: Intent) {
    val dpm = getManager(context)
    val admin = getWho(context)

    dpm.addUserRestriction(admin, UserManager.DISALLOW_CREATE_WINDOWS)
}

Java

// Called just after entering lock task mode.
public void onLockTaskModeEntering(Context context, Intent intent) {
  DevicePolicyManager dpm = getManager(context);
  ComponentName admin = getWho(context);

  dpm.addUserRestriction(admin, UserManager.DISALLOW_CREATE_WINDOWS);
}

您的 DPC 可以在设备退出锁定任务模式时删除用户限制。

其他资源

要了解有关专用设备的更多信息,请阅读以下文档