Android 15 Lock Task 模式深度分析(第一部分)
目录
- [Lock Task 模式概述](#Lock Task 模式概述)
- 核心架构与实现
- 代码流程分析
- 关键类与方法
- 应用层使用方法
- 企业管理配置
- 使用场景
- AOSP其他窗口模式对比
- 版本演进历史
- 最佳实践与注意事项
1. Lock Task 模式概述
1.1 什么是Lock Task模式
Lock Task模式是Android从5.0(Lollipop)开始引入的一种特殊任务锁定机制,主要用于企业环境和专用设备场景。它允许设备管理员或应用将设备锁定到特定的应用或任务,限制用户访问其他应用或系统功能。
核心特性:
- 任务锁定: 将用户限制在特定应用或任务集合中
- 系统UI限制: 可配置地禁用状态栏、通知、导航按钮等
- 安全隔离: 防止用户切换到其他应用或访问敏感功能
- 可配置性: 支持细粒度的功能控制
1.2 三种Lock Task模式
根据源码 /frameworks/base/core/java/android/app/ActivityManager.java,系统定义了三种Lock Task状态:
java
// ActivityManager.java: Line 1231-1241
public static final int LOCK_TASK_MODE_NONE = 0; // 未启用锁定任务模式
public static final int LOCK_TASK_MODE_LOCKED = 1; // 完全锁定模式(企业模式)
public static final int LOCK_TASK_MODE_PINNED = 2; // 屏幕固定模式(Screen Pinning)
1.2.1 LOCK_TASK_MODE_NONE (0)
- 正常模式,没有任务锁定
- 用户可以自由切换应用和访问所有系统功能
1.2.2 LOCK_TASK_MODE_LOCKED (1)
- 完全锁定模式(企业/Kiosk模式)
- 需要DeviceOwner或ProfileOwner权限
- 设备完全锁定在白名单应用中
- 系统功能根据DevicePolicyManager配置进行限制
- 通常用于企业专用设备、自助终端等场景
特点:
- 无法通过物理按键退出(除非配置允许)
- 可配置允许的系统功能(Home、Recents、通知等)
- 需要管理员权限才能退出
- 支持多任务白名单
1.2.3 LOCK_TASK_MODE_PINNED (2)
- 屏幕固定模式(Screen Pinning)
- 普通应用可以调用,用户需要确认
- 锁定单个任务到屏幕
- 用户可以通过特定手势退出(同时按Back+Recents或根据设置)
- 主要用于临时演示、考试模式等
特点:
- 需要用户明确同意
- 可以通过系统设置的手势退出
- 退出后可选择是否需要解锁设备
- 仅锁定单个任务
1.3 与其他模式的区别
| 特性 | Lock Task (Locked) | Screen Pinning | 正常模式 |
|---|---|---|---|
| 权限要求 | DeviceOwner/ProfileOwner | 无(用户确认) | 无 |
| 退出方式 | 代码或管理员 | 手势组合 | 无限制 |
| 多任务支持 | 支持白名单多任务 | 仅单任务 | 无限制 |
| 系统UI控制 | 完全可配置 | 固定限制 | 无限制 |
| 典型场景 | Kiosk、MDM | 演示、考试 | 日常使用 |
2. 核心架构与实现
2.1 架构图
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Activity │ │DevicePolicy │ │Settings App │ │
│ │startLockTask │ │ Manager │ │Screen Pinning│ │
│ │stopLockTask │ │ │ │ │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼──────────────────┼──────────────────┼─────────────┘
│ │ │
│ │ │
┌─────────▼──────────────────▼──────────────────▼─────────────┐
│ Framework Layer │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ActivityTaskManagerService (ATMS) │ │
│ │ startLockTaskMode() / stopLockTaskMode() │ │
│ └─────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼────────────────────────────────┐ │
│ │ LockTaskController (核心控制器) │ │
│ │ • 模式状态管理 (mLockTaskModeState) │ │
│ │ • 任务列表维护 (mLockTaskModeTasks) │ │
│ │ • 白名单管理 (mLockTaskPackages) │ │
│ │ • 权限检查 (getLockTaskAuth) │ │
│ │ • UI状态控制 (setStatusBarState/setKeyguardState) │ │
│ └──────┬──────────┬──────────┬──────────┬───────────────┘ │
│ │ │ │ │ │
└─────────┼──────────┼──────────┼──────────┼───────────────────┘
│ │ │ │
┌─────────▼──────────▼──────────▼──────────▼───────────────────┐
│ System Services │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ StatusBar │ │WindowManager │ │DevicePolicy │ │
│ │ Service │ │ Service │ │ Service │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└───────────────────────────────────────────────────────────────┘
2.2 核心类关系
ActivityTaskManagerService
├── LockTaskController (主控制器)
│ ├── mLockTaskModeTasks: ArrayList<Task>
│ ├── mLockTaskPackages: SparseArray<String[]>
│ ├── mLockTaskFeatures: SparseIntArray
│ └── mLockTaskModeState: int
│
├── Task
│ ├── mLockTaskAuth: int (权限级别)
│ └── mLockTaskUid: int (启动者UID)
│
└── ActivityRecord
└── lockTaskLaunchMode: int (Manifest配置)
2.3 关键文件路径
| 文件 | 路径 | 作用 |
|---|---|---|
| LockTaskController.java | /frameworks/base/services/core/java/com/android/server/wm/ |
核心控制器,管理Lock Task状态和逻辑 |
| ActivityTaskManagerService.java | /frameworks/base/services/core/java/com/android/server/wm/ |
ATMS,提供公开接口 |
| Activity.java | /frameworks/base/core/java/android/app/ |
应用层API(startLockTask/stopLockTask) |
| ActivityManager.java | /frameworks/base/core/java/android/app/ |
定义常量和状态 |
| ActivityInfo.java | /frameworks/base/core/java/android/content/pm/ |
定义lockTaskMode属性 |
| DevicePolicyManager.java | /frameworks/base/core/java/android/app/admin/ |
企业策略管理 |
| Task.java | /frameworks/base/services/core/java/com/android/server/wm/ |
任务类,包含锁定授权信息 |
| ScreenPinningRequest.java | /frameworks/base/packages/SystemUI/src/com/android/systemui/recents/ |
SystemUI屏幕固定请求UI |
3. 代码流程分析
3.1 启动Lock Task流程
3.1.1 应用层调用流程
Application
│
├─ Activity.startLockTask()
│ └─ frameworks/base/core/java/android/app/Activity.java: Line 9456
│
├─ ActivityClient.getInstance().startLockTaskModeByToken(mToken)
│ └─ 通过Binder IPC调用服务端
│
├─ ActivityTaskManagerService.startLockTaskModeByToken()
│ └─ frameworks/base/services/core/java/com/android/server/wm/
│ ActivityTaskManagerService.java
│
└─ ATMS.startLockTaskMode(task, isSystemCaller=false)
└─ Line 2591
3.1.2 系统层调用流程(Screen Pinning)
SystemUI (ScreenPinningRequest)
│
├─ User clicks "Start" button
│ └─ ScreenPinningRequest.onClick()
│ └─ Line 188-193
│
├─ ActivityTaskManager.getService().startSystemLockTaskMode(taskId)
│ └─ Line 190
│
├─ ATMS.startSystemLockTaskMode(taskId)
│ └─ Line 2574
│
└─ ATMS.startLockTaskMode(task, isSystemCaller=true)
└─ Line 2591
3.1.3 详细启动流程
源码路径 : frameworks/base/services/core/java/com/android/server/wm/LockTaskController.java
java
// Line 648-684
void startLockTaskMode(@NonNull Task task, boolean isSystemCaller, int callingUid) {
// 步骤1: 检查任务授权
if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
return; // 任务不允许锁定
}
// 步骤2: 处理非系统调用
if (!isSystemCaller) {
task.mLockTaskUid = callingUid; // 记录调用者UID
// 步骤3: 检查授权类型
if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
// 需要用户确认 - 显示Screen Pinning对话框
StatusBarManagerInternal statusBarManager =
LocalServices.getService(StatusBarManagerInternal.class);
if (statusBarManager != null) {
statusBarManager.showScreenPinningRequest(
task.mTaskId, task.mUserId);
}
return; // 等待用户确认后再次调用
} else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
// 如果当前是Screen Pinning模式,先退出
stopLockTaskMode(null, true, callingUid);
}
}
// 步骤4: 移除PIP任务(Picture-in-Picture)
mSupervisor.mRootWindowContainer.removeRootTasksInWindowingModes(
WINDOWING_MODE_PINNED);
// 步骤5: 设置Lock Task模式
int lockTaskModeState = isSystemCaller ?
LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED;
setLockTaskMode(task, lockTaskModeState, "startLockTask", true);
}
关键步骤说明:
- 授权检查 : 验证任务的
mLockTaskAuth属性 - 用户确认: PINNABLE类型需要显示确认对话框
- 模式冲突处理: 处理与Screen Pinning的冲突
- PIP清理: 移除画中画任务
- 模式设置 : 调用
setLockTaskMode()执行实际锁定
3.1.4 setLockTaskMode核心逻辑
java
// Line 691-737
private void setLockTaskMode(@NonNull Task task, int lockTaskModeState,
String reason, boolean andResume) {
// 验证授权
if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
return;
}
// 检查是否违反Lock Task规则
if (isLockTaskModeViolation(task)) {
Slog.e(TAG_LOCKTASK, "Attempt to start unauthorized lock task");
return;
}
// 首次进入Lock Task - 初始化
if (mLockTaskModeTasks.isEmpty() && task.intent != null) {
mSupervisor.mRecentTasks.onLockTaskModeStateChanged(
lockTaskModeState, task.mUserId);
// 在Handler线程启动Lock Task
mHandler.post(() -> performStartLockTask(
task.intent.getComponent().getPackageName(),
task.mUserId,
lockTaskModeState));
}
// 添加任务到锁定列表
if (!mLockTaskModeTasks.contains(task)) {
mLockTaskModeTasks.add(task);
}
// 设置Lock Task UID
if (task.mLockTaskUid == -1) {
task.mLockTaskUid = task.effectiveUid;
}
// 将任务移到前台
if (andResume) {
mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
lockTaskModeState != LOCK_TASK_MODE_NONE);
mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities();
}
}
3.1.5 performStartLockTask - 系统状态设置
java
// Line 740-760
private void performStartLockTask(String packageName, int userId,
int lockTaskModeState) {
try {
// 1. 显示进入Toast(仅Screen Pinning)
if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
IStatusBarService statusBar = getStatusBarService();
if (statusBar != null) {
statusBar.showPinningEnterExitToast(true);
}
}
// 2. 通知WindowManager
mWindowManager.onLockTaskStateChanged(lockTaskModeState);
// 3. 更新内部状态
mLockTaskModeState = lockTaskModeState;
// 4. 通知任务变更监听器
mTaskChangeNotificationController.notifyLockTaskModeChanged(
mLockTaskModeState);
// 5. 设置StatusBar状态(禁用相关功能)
setStatusBarState(lockTaskModeState, userId);
// 6. 设置Keyguard状态(禁用锁屏)
setKeyguardState(lockTaskModeState, userId);
// 7. 通知DevicePolicyManager
if (getDevicePolicyManager() != null) {
getDevicePolicyManager().notifyLockTaskModeChanged(
true, packageName, userId);
}
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
}
3.2 退出Lock Task流程
3.2.1 退出流程调用链
Application/System
│
├─ Activity.stopLockTask()
│ └─ ActivityClient.getInstance().stopLockTaskModeByToken(mToken)
│
├─ ATMS.stopLockTaskModeInternal(token, isSystemCaller)
│ └─ Line 2617
│
├─ LockTaskController.stopLockTaskMode(task, stopAppPinning, uid)
│ └─ Line 493
│
├─ LockTaskController.clearLockedTask(task)
│ └─ Line 552
│
└─ LockTaskController.performStopLockTask(userId)
└─ Line 588 (在Handler线程执行)
3.2.2 stopLockTaskMode详细逻辑
java
// Line 493-528
void stopLockTaskMode(@Nullable Task task, boolean stopAppPinning,
int callingUid) {
// 步骤1: 检查当前状态
if (mLockTaskModeState == LOCK_TASK_MODE_NONE) {
return; // 未处于Lock Task模式
}
// 步骤2: 处理Screen Pinning退出
if (stopAppPinning) {
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
clearLockedTasks("stopAppPinning");
} else {
// 完全锁定模式不能通过此方式退出
Slog.e(TAG_LOCKTASK, "Attempted to stop app pinning while fully locked");
showLockTaskToast(); // 显示提示
}
return;
}
// 步骤3: 验证调用者权限
if (task == null) {
throw new IllegalArgumentException("can't stop LockTask for null task");
}
// 步骤4: 检查UID匹配
if (callingUid != task.mLockTaskUid &&
(task.mLockTaskUid != 0 || callingUid != task.effectiveUid)) {
throw new SecurityException("Invalid uid, expected " +
task.mLockTaskUid + " callingUid=" + callingUid);
}
// 步骤5: 清除任务
clearLockedTask(task);
}
3.2.3 clearLockedTask - 任务清理
java
// Line 552-569
void clearLockedTask(final Task task) {
if (task == null || mLockTaskModeTasks.isEmpty()) {
return;
}
// 如果是根任务,清除所有锁定任务
if (task == mLockTaskModeTasks.get(0)) {
// 反向清除所有子任务
for (int i = mLockTaskModeTasks.size() - 1; i > 0; --i) {
clearLockedTask(mLockTaskModeTasks.get(i));
}
}
// 移除任务
removeLockedTask(task);
// 如果还有剩余任务,继续
if (mLockTaskModeTasks.isEmpty()) {
return;
}
// 清理任务内容
task.performClearTaskForReuse(false);
mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities();
}
3.2.4 performStopLockTask - 系统状态恢复
java
// Line 588-617
private void performStopLockTask(int userId) {
// 步骤1: 更新状态(提前更新避免竞态)
final int oldLockTaskModeState = mLockTaskModeState;
mLockTaskModeState = LOCK_TASK_MODE_NONE;
// 步骤2: 通知状态变更
mTaskChangeNotificationController.notifyLockTaskModeChanged(
mLockTaskModeState);
try {
// 步骤3: 恢复StatusBar
setStatusBarState(mLockTaskModeState, userId);
// 步骤4: 恢复Keyguard
setKeyguardState(mLockTaskModeState, userId);
// 步骤5: 退出Screen Pinning时锁定设备
if (oldLockTaskModeState == LOCK_TASK_MODE_PINNED) {
lockKeyguardIfNeeded(userId);
}
// 步骤6: 通知DevicePolicyManager
if (getDevicePolicyManager() != null) {
getDevicePolicyManager().notifyLockTaskModeChanged(
false, null, userId);
}
// 步骤7: 显示退出Toast
if (oldLockTaskModeState == LOCK_TASK_MODE_PINNED) {
IStatusBarService statusBar = getStatusBarService();
if (statusBar != null) {
statusBar.showPinningEnterExitToast(false);
}
}
// 步骤8: 通知WindowManager
mWindowManager.onLockTaskStateChanged(mLockTaskModeState);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
}
3.3 权限检查流程
3.3.1 getLockTaskAuth - 计算任务授权级别
源码 : LockTaskController.java: Line 812-844
java
int getLockTaskAuth(@Nullable ActivityRecord rootActivity, @Nullable Task task) {
// 步骤1: 基本检查
if (rootActivity == null && task == null) {
return LOCK_TASK_AUTH_DONT_LOCK;
}
if (rootActivity == null) {
return LOCK_TASK_AUTH_PINNABLE;
}
// 步骤2: 获取包名和用户ID
final String pkg = (task == null || task.realActivity == null) ?
rootActivity.packageName : task.realActivity.getPackageName();
final int userId = task != null ? task.mUserId : rootActivity.mUserId;
int lockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
// 步骤3: 根据Manifest配置计算权限
switch (rootActivity.lockTaskLaunchMode) {
case LOCK_TASK_LAUNCH_MODE_DEFAULT:
// 如果在白名单中 -> ALLOWLISTED,否则 -> PINNABLE
lockTaskAuth = isPackageAllowlisted(userId, pkg)
? LOCK_TASK_AUTH_ALLOWLISTED
: LOCK_TASK_AUTH_PINNABLE;
break;
case LOCK_TASK_LAUNCH_MODE_NEVER:
// 永不锁定
lockTaskAuth = LOCK_TASK_AUTH_DONT_LOCK;
break;
case LOCK_TASK_LAUNCH_MODE_ALWAYS:
// 特权应用,自动锁定
lockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
break;
case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED:
// 仅白名单中可锁定
lockTaskAuth = isPackageAllowlisted(userId, pkg)
? LOCK_TASK_AUTH_LAUNCHABLE
: LOCK_TASK_AUTH_PINNABLE;
break;
}
return lockTaskAuth;
}
3.3.2 权限级别说明
源码 : LockTaskController.java: Line 132-141
java
/** 不能进入Lock Task模式 */
static final int LOCK_TASK_AUTH_DONT_LOCK = 0;
/** 可以进入Screen Pinning,需要用户批准。不能覆盖现有Lock Task */
static final int LOCK_TASK_AUTH_PINNABLE = 1;
/** 自动以LOCKED模式启动。可以覆盖现有Lock Task */
static final int LOCK_TASK_AUTH_LAUNCHABLE = 2;
/** 无需批准进入Lock Task。可以覆盖现有Lock Task */
static final int LOCK_TASK_AUTH_ALLOWLISTED = 3;
/** 特权应用,自动以LOCKED模式启动。可以覆盖现有Lock Task */
static final int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4;
4. 关键类与方法
4.1 LockTaskController核心成员
源码路径 : /frameworks/base/services/core/java/com/android/server/wm/LockTaskController.java
java
public class LockTaskController {
// 状态管理
private volatile int mLockTaskModeState = LOCK_TASK_MODE_NONE;
// 任务列表(按进入Lock Task顺序)
private final ArrayList<Task> mLockTaskModeTasks = new ArrayList<>();
// 白名单管理(userId -> 包名数组)
private final SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
// 功能标志(userId -> feature flags)
private final SparseIntArray mLockTaskFeatures = new SparseIntArray();
// 系统服务引用
IStatusBarService mStatusBarService;
IDevicePolicyManager mDevicePolicyManager;
WindowManagerService mWindowManager;
LockPatternUtils mLockPatternUtils;
// Handler用于异步操作
private final Handler mHandler;
}
4.2 关键方法列表
| 方法名 | 作用 | 调用时机 |
|---|---|---|
startLockTaskMode(Task, boolean, int) |
启动Lock Task模式 | Activity.startLockTask()或系统调用 |
stopLockTaskMode(Task, boolean, int) |
停止Lock Task模式 | Activity.stopLockTask()或系统调用 |
getLockTaskAuth(ActivityRecord, Task) |
计算任务授权级别 | 任务创建或状态检查时 |
isLockTaskModeViolation(Task, boolean) |
检查是否违反Lock Task规则 | 启动新任务时 |
updateLockTaskPackages(int, String[]) |
更新白名单 | DPM调用 |
updateLockTaskFeatures(int, int) |
更新功能标志 | DPM调用 |
setStatusBarState(int, int) |
配置状态栏状态 | 进入/退出Lock Task |
setKeyguardState(int, int) |
配置锁屏状态 | 进入/退出Lock Task |
isTaskLocked(Task) |
检查任务是否锁定 | 任务操作前 |
activityBlockedFromFinish(ActivityRecord) |
检查Activity是否允许结束 | Activity.finish()时 |
4.3 StatusBar禁用标志
源码 : LockTaskController.java: Line 94-126
java
// PINNED模式的StatusBar禁用标志
static final int STATUS_BAR_MASK_PINNED = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK) // 允许返回
& (~StatusBarManager.DISABLE_HOME) // 允许Home
& (~StatusBarManager.DISABLE_RECENT); // 允许最近任务
// LOCKED模式的StatusBar禁用标志(更严格)
static final int STATUS_BAR_MASK_LOCKED = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_EXPAND) // 禁止下拉
& (~StatusBarManager.DISABLE_NOTIFICATION_TICKER)
& (~StatusBarManager.DISABLE_SYSTEM_INFO)
& (~StatusBarManager.DISABLE_BACK);
4.4 Lock Task Feature标志映射
DevicePolicyManager中定义的功能标志:
java
// DevicePolicyManager.java: Line 3018-3091
public static final int LOCK_TASK_FEATURE_NONE = 0;
public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 系统信息(时钟等)
public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 1 << 1; // 通知
public static final int LOCK_TASK_FEATURE_HOME = 1 << 2; // Home按钮
public static final int LOCK_TASK_FEATURE_OVERVIEW = 1 << 3; // 最近任务
public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 1 << 4; // 电源菜单
public static final int LOCK_TASK_FEATURE_KEYGUARD = 1 << 5; // 锁屏
public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = 1 << 6; // 阻止启动新Activity
映射到StatusBar标志:
java
// LockTaskController.java: Line 105-126
static {
STATUS_BAR_FLAG_MAP_LOCKED = new SparseArray<>();
// SYSTEM_INFO -> 允许显示时钟和系统图标
STATUS_BAR_FLAG_MAP_LOCKED.append(
DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO,
new Pair<>(StatusBarManager.DISABLE_CLOCK,
StatusBarManager.DISABLE2_SYSTEM_ICONS));
// NOTIFICATIONS -> 允许通知图标和通知面板
STATUS_BAR_FLAG_MAP_LOCKED.append(
DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS,
new Pair<>(StatusBarManager.DISABLE_NOTIFICATION_ICONS |
StatusBarManager.DISABLE_NOTIFICATION_ALERTS,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE));
// HOME -> 允许Home按钮
STATUS_BAR_FLAG_MAP_LOCKED.append(
DevicePolicyManager.LOCK_TASK_FEATURE_HOME,
new Pair<>(StatusBarManager.DISABLE_HOME,
StatusBarManager.DISABLE2_NONE));
// OVERVIEW -> 允许最近任务按钮
STATUS_BAR_FLAG_MAP_LOCKED.append(
DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW,
new Pair<>(StatusBarManager.DISABLE_RECENT,
StatusBarManager.DISABLE2_NONE));
// GLOBAL_ACTIONS -> 允许电源菜单
STATUS_BAR_FLAG_MAP_LOCKED.append(
DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
new Pair<>(StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_GLOBAL_ACTIONS));
}
5. 应用层使用方法
5.1 Manifest配置
源码路径 : /frameworks/base/core/res/res/values/attrs_manifest.xml
在AndroidManifest.xml的<activity>标签中配置android:lockTaskMode属性:
xml
<activity
android:name=".MainActivity"
android:lockTaskMode="normal|never|always|if_whitelisted"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
lockTaskMode属性值说明:
| 值 | 常量 | 说明 |
|---|---|---|
normal (0) |
LOCK_TASK_LAUNCH_MODE_DEFAULT |
默认模式。可调用startLockTask(),需白名单或用户确认 |
never (1) |
LOCK_TASK_LAUNCH_MODE_NEVER |
禁止锁定。无法进入Lock Task模式 |
always (2) |
LOCK_TASK_LAUNCH_MODE_ALWAYS |
自动锁定。Activity启动时自动进入LOCKED模式(需系统权限) |
if_whitelisted (3) |
LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED |
白名单模式。在白名单中自动锁定,否则需用户确认 |
示例 : /frameworks/base/tests/LockTaskTests/AndroidManifest.xml
xml
<!-- 默认模式 -->
<activity
android:name=".LockDefaultActivity"
android:lockTaskMode="normal" />
<!-- 禁止锁定 -->
<activity
android:name=".LockTaskNeverActivity"
android:lockTaskMode="never" />
<!-- 白名单模式 -->
<activity
android:name=".LockWhitelistedActivity"
android:lockTaskMode="if_whitelisted" />
<!-- 自动锁定(特权应用) -->
<activity
android:name=".LockAtLaunchActivity"
android:lockTaskMode="always" />
5.2 代码API使用
5.2.1 启动Lock Task
源码 : /frameworks/base/core/java/android/app/Activity.java: Line 9456-9458
java
public class MainActivity extends Activity {
@Override
protected void onResume() {
super.onResume();
// 方式1: 直接启动Lock Task
try {
startLockTask();
} catch (SecurityException e) {
// 处理权限异常
Log.e(TAG, "Failed to start lock task", e);
}
}
// 方式2: 检查后启动
private void startLockTaskIfNeeded() {
ActivityManager am = (ActivityManager)
getSystemService(Context.ACTIVITY_SERVICE);
// 检查当前状态
int lockTaskMode = am.getLockTaskModeState();
if (lockTaskMode == ActivityManager.LOCK_TASK_MODE_NONE) {
startLockTask();
}
}
}
注意事项:
- 必须在Activity的
onResume()之后调用 - 如果应用不在白名单且lockTaskMode为normal,会显示用户确认对话框
- 特权应用(系统应用)或白名单应用可以直接进入LOCKED模式
- 普通应用只能进入PINNED模式(Screen Pinning)
5.2.2 停止Lock Task
源码 : /frameworks/base/core/java/android/app/Activity.java: Line 9476-9478
java
public class MainActivity extends Activity {
private void exitLockTask() {
try {
stopLockTask();
} catch (SecurityException e) {
// 只有启动Lock Task的Activity才能停止
Log.e(TAG, "Failed to stop lock task", e);
}
}
@Override
public void onBackPressed() {
// 在Lock Task模式中,可以选择性地处理返回键
ActivityManager am = (ActivityManager)
getSystemService(Context.ACTIVITY_SERVICE);
if (am.getLockTaskModeState() != ActivityManager.LOCK_TASK_MODE_NONE) {
// 显示确认对话框或直接退出
showExitConfirmDialog();
} else {
super.onBackPressed();
}
}
private void showExitConfirmDialog() {
new AlertDialog.Builder(this)
.setTitle("退出锁定模式")
.setMessage("确定要退出锁定模式吗?")
.setPositiveButton("确定", (dialog, which) -> stopLockTask())
.setNegativeButton("取消", null)
.show();
}
}
5.2.3 检查Lock Task状态
java
public class LockTaskHelper {
private final Context mContext;
public boolean isInLockTaskMode() {
ActivityManager am = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
return am.isInLockTaskMode();
}
public int getLockTaskModeState() {
ActivityManager am = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
return am.getLockTaskModeState();
}
public String getLockTaskModeString() {
int mode = getLockTaskModeState();
switch (mode) {
case ActivityManager.LOCK_TASK_MODE_NONE:
return "正常模式";
case ActivityManager.LOCK_TASK_MODE_LOCKED:
return "完全锁定模式";
case ActivityManager.LOCK_TASK_MODE_PINNED:
return "屏幕固定模式";
default:
return "未知模式";
}
}
}
5.2.4 显示退出提示
源码 : /frameworks/base/core/java/android/app/Activity.java: Line 9485-9487
java
public class MainActivity extends Activity {
private void showExitMessage() {
// 显示系统定义的退出Lock Task模式提示消息
showLockTaskEscapeMessage();
}
}
5.3 完整示例应用
java
package com.example.locktaskdemo;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class LockTaskDemoActivity extends Activity {
private static final String TAG = "LockTaskDemo";
private TextView mStatusText;
private Button mStartButton;
private Button mStopButton;
private ActivityManager mActivityManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mActivityManager = (ActivityManager)
getSystemService(Context.ACTIVITY_SERVICE);
mStatusText = findViewById(R.id.status_text);
mStartButton = findViewById(R.id.start_button);
mStopButton = findViewById(R.id.stop_button);
mStartButton.setOnClickListener(v -> startLockTaskMode());
mStopButton.setOnClickListener(v -> stopLockTaskMode());
updateUI();
}
@Override
protected void onResume() {
super.onResume();
updateUI();
}
private void startLockTaskMode() {
try {
startLockTask();
Toast.makeText(this, "Lock Task模式已启动", Toast.LENGTH_SHORT).show();
Log.i(TAG, "Lock Task mode started");
} catch (SecurityException e) {
Toast.makeText(this, "无法启动Lock Task: " + e.getMessage(),
Toast.LENGTH_LONG).show();
Log.e(TAG, "Failed to start lock task", e);
}
}
private void stopLockTaskMode() {
try {
stopLockTask();
Toast.makeText(this, "Lock Task模式已停止", Toast.LENGTH_SHORT).show();
Log.i(TAG, "Lock Task mode stopped");
} catch (SecurityException e) {
Toast.makeText(this, "无法停止Lock Task: " + e.getMessage(),
Toast.LENGTH_LONG).show();
Log.e(TAG, "Failed to stop lock task", e);
}
}
private void updateUI() {
int lockTaskMode = mActivityManager.getLockTaskModeState();
String statusText;
switch (lockTaskMode) {
case ActivityManager.LOCK_TASK_MODE_LOCKED:
statusText = "状态: 完全锁定模式 (LOCKED)";
mStartButton.setEnabled(false);
mStopButton.setEnabled(true);
break;
case ActivityManager.LOCK_TASK_MODE_PINNED:
statusText = "状态: 屏幕固定模式 (PINNED)";
mStartButton.setEnabled(false);
mStopButton.setEnabled(true);
break;
case ActivityManager.LOCK_TASK_MODE_NONE:
default:
statusText = "状态: 正常模式 (NONE)";
mStartButton.setEnabled(true);
mStopButton.setEnabled(false);
break;
}
mStatusText.setText(statusText);
}
@Override
public void onBackPressed() {
if (mActivityManager.isInLockTaskMode()) {
// 在Lock Task模式中,显示提示而不是直接退出
showLockTaskEscapeMessage();
} else {
super.onBackPressed();
}
}
}
布局文件 (res/layout/activity_main.xml):
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">
<TextView
android:id="@+id/status_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="状态: 正常模式"
android:textSize="18sp"
android:layout_marginBottom="32dp"/>
<Button
android:id="@+id/start_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启动 Lock Task"
android:layout_marginBottom="16dp"/>
<Button
android:id="@+id/stop_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="停止 Lock Task"
android:enabled="false"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="提示:\n• 需要设备管理员权限或用户确认\n• 在锁定模式中,系统功能会受限\n• 可以通过代码或手势退出"
android:textSize="14sp"
android:layout_marginTop="32dp"/>
</LinearLayout>
Manifest配置:
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.locktaskdemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Lock Task Demo"
android:theme="@style/Theme.AppCompat">
<activity
android:name=".LockTaskDemoActivity"
android:lockTaskMode="normal"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
致敬前辈,砥砺前行!