锁定任务模式

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

概览

Android 可以类似于自助服务终端的沉浸式方式(称为锁定任务模式)运行任务。如果您要开发自助服务终端应用或启动器来呈现一系列应用,可以使用锁定任务模式。当系统在锁定任务模式下运行时,设备用户通常无法查看通知、访问非许可名单内应用或返回主屏幕(除非主屏幕已列入许可名单)。

只有经设备政策控制器 (DPC) 列入许可名单后,应用才能在系统处于锁定任务模式时运行。应用被列入许可名单,因为使用设备的用户无法始终退出锁定任务模式。

如何将列入锁定任务模式许可名单的应用与列入许可名单 DPC 结合使用,取决于您要解决的问题。以下是一些示例:

  • 单个应用软件包,可将自助服务终端(用于显示内容)和迷你 DPC(用于将自身列入许可名单,以用于锁定任务模式)。
  • 属于企业移动管理解决方案一部分的 DPC,在锁定任务模式下启动客户的移动应用。

提供情况

在 Android 5.0 或更高版本中,系统可以在锁定任务模式下运行。表 1 显示了哪些 Android 版本支持按用户将应用列入许可名单。

表 1. DPC 管理模式的 Android 版本支持
Android 版本 设备政策控制器 (DPC) 负责管理 备注
Android 5.0(API 级别 21)或更高版本 全代管式设备
Android 8.0(API 级别 26)或更高版本 关联的次要用户 次要用户必须与主要用户相关联。请参阅多用户概览。
Android 9.0(API 级别 28)或更高版本 次要用户

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

将应用列入许可名单

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)或更高版本中,您可以在锁定任务模式下启动另一个应用的 activity。如果某个 activity 已在前台或后台运行,您需要重新启动该 activity。调用 ActivityOptions.setLockTaskEnabled() 并在启动 activity 时提供这些选项。以下代码段展示了实现此目的的一种方法:

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() 在锁定任务模式下启动自己的 activity。如需调用此方法,activity 必须在前台运行(请参阅 activity 生命周期概念),因此我们建议在 ActivityFragmentonResume() 方法中调用。调用 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())来启动锁定任务模式。

处于锁定任务模式的应用可以启动新 activity,前提是该 activity 不会启动新任务(启动已列入许可名单的应用的任务除外)。如需了解任务与 activity 之间的关系,请阅读了解任务和返回堆栈指南。

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

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

如需详细了解如何在应用清单文件中声明选项,请参阅 lockTaskMode 参考文档。

停止锁定任务模式

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

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

生命周期回调

您的 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) 会发生以下变化:

  • 状态栏空白,并隐藏了通知和系统信息。
  • “首页”按钮和“概览”按钮处于隐藏状态。
  • 其他应用无法启动新 activity。
  • 锁定屏幕(如果已设置)处于停用状态。

在 Android 9.0 或更高版本中,启用锁定任务模式后,DPC 可在设备上启用某些系统界面功能,这对开发者创建自定义启动器很有用。调用 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 参数中未包含的任何功能。启用的界面功能会在两次启动之间持续到锁定任务模式中。如果设备已处于锁定任务模式,您对锁定任务功能所做的任何更改会立即显示。表 2 介绍了您可以自定义的界面功能。

表 2. 锁定任务模式下的自定义系统界面功能
系统界面功能 说明
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 停用上面列出的所有系统界面功能。

启用锁定任务模式后,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 可以在设备退出锁定任务模式时移除用户限制。

其他资源

如需详细了解专用设备,请参阅以下文档: