blob: 5b31d5fc593fab54e92ba41614d12b87fed1ddde [file] [log] [blame]
Benjamin Franza83859f2017-07-03 16:34:14 +01001/*
2 * Copyright 2017, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.am;
18
19import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
20import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
21import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
Wale Ogunwale0568aed2017-09-08 13:29:37 -070022import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
Benjamin Franza83859f2017-07-03 16:34:14 +010023import static android.content.Context.DEVICE_POLICY_SERVICE;
24import static android.content.Context.STATUS_BAR_SERVICE;
Charles He858f1322017-11-27 17:11:04 -080025import static android.content.Intent.ACTION_CALL_EMERGENCY;
Benjamin Franza83859f2017-07-03 16:34:14 +010026import static android.os.UserHandle.USER_ALL;
Amith Yamasani7cbbf2222017-08-30 14:22:37 -070027import static android.os.UserHandle.USER_CURRENT;
Charles He858f1322017-11-27 17:11:04 -080028import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
Benjamin Franza83859f2017-07-03 16:34:14 +010029import static android.view.Display.DEFAULT_DISPLAY;
Charles He520b2832017-09-02 15:27:16 +010030
Wale Ogunwale98875612018-10-12 07:53:02 -070031import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
32import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
33import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
34import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
Benjamin Franza83859f2017-07-03 16:34:14 +010035import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
36import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
37import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
38import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
39import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
40
41import android.annotation.NonNull;
42import android.annotation.Nullable;
43import android.app.Activity;
44import android.app.ActivityManager;
Charles Hee078db72017-10-19 18:03:20 +010045import android.app.StatusBarManager;
46import android.app.admin.DevicePolicyManager;
Benjamin Franza83859f2017-07-03 16:34:14 +010047import android.app.admin.IDevicePolicyManager;
Charles Hee078db72017-10-19 18:03:20 +010048import android.content.ComponentName;
Benjamin Franza83859f2017-07-03 16:34:14 +010049import android.content.Context;
Charles He858f1322017-11-27 17:11:04 -080050import android.content.Intent;
Benjamin Franza83859f2017-07-03 16:34:14 +010051import android.os.Binder;
52import android.os.Debug;
53import android.os.Handler;
54import android.os.IBinder;
55import android.os.RemoteException;
56import android.os.ServiceManager;
57import android.provider.Settings;
Charles He858f1322017-11-27 17:11:04 -080058import android.telecom.TelecomManager;
Charles Hee078db72017-10-19 18:03:20 +010059import android.util.Pair;
Benjamin Franza83859f2017-07-03 16:34:14 +010060import android.util.Slog;
Charles He520b2832017-09-02 15:27:16 +010061import android.util.SparseArray;
Charles He858f1322017-11-27 17:11:04 -080062import android.util.SparseIntArray;
Benjamin Franza83859f2017-07-03 16:34:14 +010063
64import com.android.internal.annotations.VisibleForTesting;
Tony Mak2bf36912018-03-21 15:45:52 +000065import com.android.internal.policy.IKeyguardDismissCallback;
Benjamin Franza83859f2017-07-03 16:34:14 +010066import com.android.internal.statusbar.IStatusBarService;
67import com.android.internal.widget.LockPatternUtils;
68import com.android.server.LocalServices;
69import com.android.server.statusbar.StatusBarManagerInternal;
70import com.android.server.wm.WindowManagerService;
71
72import java.io.PrintWriter;
73import java.util.ArrayList;
Charles He520b2832017-09-02 15:27:16 +010074import java.util.Arrays;
Benjamin Franza83859f2017-07-03 16:34:14 +010075
76/**
77 * Helper class that deals with all things related to task locking. This includes the screen pinning
78 * mode that can be launched via System UI as well as the fully locked mode that can be achieved
79 * on fully managed devices.
80 *
81 * Note: All methods in this class should only be called with the ActivityManagerService lock held.
82 *
83 * @see Activity#startLockTask()
84 * @see Activity#stopLockTask()
85 */
86public class LockTaskController {
Wale Ogunwale98875612018-10-12 07:53:02 -070087 private static final String TAG = TAG_WITH_CLASS_NAME ? "LockTaskController" : TAG_ATM;
Benjamin Franza83859f2017-07-03 16:34:14 +010088 private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
89
90 @VisibleForTesting
Charles Hee078db72017-10-19 18:03:20 +010091 static final int STATUS_BAR_MASK_LOCKED = StatusBarManager.DISABLE_MASK
92 & (~StatusBarManager.DISABLE_EXPAND)
93 & (~StatusBarManager.DISABLE_NOTIFICATION_TICKER)
94 & (~StatusBarManager.DISABLE_SYSTEM_INFO)
95 & (~StatusBarManager.DISABLE_BACK);
Benjamin Franza83859f2017-07-03 16:34:14 +010096 @VisibleForTesting
Charles Hee078db72017-10-19 18:03:20 +010097 static final int STATUS_BAR_MASK_PINNED = StatusBarManager.DISABLE_MASK
98 & (~StatusBarManager.DISABLE_BACK)
99 & (~StatusBarManager.DISABLE_HOME)
100 & (~StatusBarManager.DISABLE_RECENT);
101
102 private static final SparseArray<Pair<Integer, Integer>> STATUS_BAR_FLAG_MAP_LOCKED;
103 static {
104 STATUS_BAR_FLAG_MAP_LOCKED = new SparseArray<>();
105
106 STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO,
107 new Pair<>(StatusBarManager.DISABLE_CLOCK, StatusBarManager.DISABLE2_SYSTEM_ICONS));
108
109 STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS,
110 new Pair<>(StatusBarManager.DISABLE_NOTIFICATION_ICONS
111 | StatusBarManager.DISABLE_NOTIFICATION_ALERTS,
112 StatusBarManager.DISABLE2_NOTIFICATION_SHADE));
113
114 STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_HOME,
115 new Pair<>(StatusBarManager.DISABLE_HOME, StatusBarManager.DISABLE2_NONE));
116
Benjamin Franzcaffa772018-02-05 16:36:10 +0000117 STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW,
Charles Hee078db72017-10-19 18:03:20 +0100118 new Pair<>(StatusBarManager.DISABLE_RECENT, StatusBarManager.DISABLE2_NONE));
119
120 STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
121 new Pair<>(StatusBarManager.DISABLE_NONE,
122 StatusBarManager.DISABLE2_GLOBAL_ACTIONS));
123 }
Benjamin Franza83859f2017-07-03 16:34:14 +0100124
125 /** Tag used for disabling of keyguard */
126 private static final String LOCK_TASK_TAG = "Lock-to-App";
127
128 private final IBinder mToken = new Binder();
129 private final ActivityStackSupervisor mSupervisor;
130 private final Context mContext;
131
132 // The following system services cannot be final, because they do not exist when this class
133 // is instantiated during device boot
134 @VisibleForTesting
135 IStatusBarService mStatusBarService;
136 @VisibleForTesting
137 IDevicePolicyManager mDevicePolicyManager;
138 @VisibleForTesting
139 WindowManagerService mWindowManager;
140 @VisibleForTesting
141 LockPatternUtils mLockPatternUtils;
Charles He858f1322017-11-27 17:11:04 -0800142 @VisibleForTesting
143 TelecomManager mTelecomManager;
Benjamin Franza83859f2017-07-03 16:34:14 +0100144
145 /**
Charles Heff9b4dff2017-09-22 10:18:37 +0100146 * The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode.
147 *
148 * The first task in the list, which started the current LockTask session, is called the root
149 * task. It coincides with the Home task in a typical multi-app kiosk deployment. When there are
150 * more than one locked tasks, the root task can't be finished. Nor can it be moved to the back
151 * of the stack by {@link ActivityStack#moveTaskToBackLocked(int)};
152 *
153 * Calling {@link Activity#stopLockTask()} on the root task will finish all tasks but itself in
154 * this list, and the device will exit LockTask mode.
155 *
156 * The list is empty if LockTask is inactive.
Benjamin Franza83859f2017-07-03 16:34:14 +0100157 */
158 private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
159
160 /**
Charles He520b2832017-09-02 15:27:16 +0100161 * Packages that are allowed to be launched into the lock task mode for each user.
162 */
163 private final SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
164
165 /**
Charles Hee078db72017-10-19 18:03:20 +0100166 * Features that are allowed by DPC to show during LockTask mode.
167 */
Charles He858f1322017-11-27 17:11:04 -0800168 private final SparseIntArray mLockTaskFeatures = new SparseIntArray();
Charles Hee078db72017-10-19 18:03:20 +0100169
170 /**
Benjamin Franza83859f2017-07-03 16:34:14 +0100171 * Store the current lock task mode. Possible values:
172 * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
173 * {@link ActivityManager#LOCK_TASK_MODE_PINNED}
174 */
Charles Heff9b4dff2017-09-22 10:18:37 +0100175 private int mLockTaskModeState = LOCK_TASK_MODE_NONE;
Benjamin Franza83859f2017-07-03 16:34:14 +0100176
177 /**
178 * This is ActivityStackSupervisor's Handler.
179 */
180 private final Handler mHandler;
181
182 LockTaskController(Context context, ActivityStackSupervisor supervisor,
183 Handler handler) {
184 mContext = context;
185 mSupervisor = supervisor;
186 mHandler = handler;
187 }
188
189 /**
190 * Set the window manager instance used in this class. This is necessary, because the window
191 * manager does not exist during instantiation of this class.
192 */
193 void setWindowManager(WindowManagerService windowManager) {
194 mWindowManager = windowManager;
195 }
196
197 /**
198 * @return the current lock task state. This can be any of
199 * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
200 * {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
201 */
202 int getLockTaskModeState() {
203 return mLockTaskModeState;
204 }
205
206 /**
Charles He520b2832017-09-02 15:27:16 +0100207 * @return whether the given task is locked at the moment. Locked tasks cannot be moved to the
208 * back of the stack.
Benjamin Franza83859f2017-07-03 16:34:14 +0100209 */
Charles Heff9b4dff2017-09-22 10:18:37 +0100210 @VisibleForTesting
211 boolean isTaskLocked(TaskRecord task) {
212 return mLockTaskModeTasks.contains(task);
213 }
214
215 /**
216 * @return {@code true} whether this task first started the current LockTask session.
217 */
218 private boolean isRootTask(TaskRecord task) {
219 return mLockTaskModeTasks.indexOf(task) == 0;
220 }
221
222 /**
223 * @return whether the given activity is blocked from finishing, because it is the only activity
224 * of the last locked task and finishing it would mean that lock task mode is ended illegally.
225 */
226 boolean activityBlockedFromFinish(ActivityRecord activity) {
227 final TaskRecord task = activity.getTask();
228 if (activity == task.getRootActivity()
229 && activity == task.getTopActivity()
230 && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
231 && isRootTask(task)) {
232 Slog.i(TAG, "Not finishing task in lock task mode");
Benjamin Franza83859f2017-07-03 16:34:14 +0100233 showLockTaskToast();
234 return true;
235 }
236 return false;
237 }
238
239 /**
Charles Heff9b4dff2017-09-22 10:18:37 +0100240 * @return whether the given task can be moved to the back of the stack with
241 * {@link ActivityStack#moveTaskToBackLocked(int)}
242 * @see #mLockTaskModeTasks
Benjamin Franza83859f2017-07-03 16:34:14 +0100243 */
Charles Heff9b4dff2017-09-22 10:18:37 +0100244 boolean canMoveTaskToBack(TaskRecord task) {
245 if (isRootTask(task)) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100246 showLockTaskToast();
Charles Heff9b4dff2017-09-22 10:18:37 +0100247 return false;
Benjamin Franza83859f2017-07-03 16:34:14 +0100248 }
Charles Heff9b4dff2017-09-22 10:18:37 +0100249 return true;
Benjamin Franza83859f2017-07-03 16:34:14 +0100250 }
251
252 /**
Charles Hed62f9652017-11-01 10:05:51 +0000253 * @return whether the requested task is allowed to be locked (either whitelisted, or declares
254 * lockTaskMode="always" in the manifest).
255 */
256 boolean isTaskWhitelisted(TaskRecord task) {
257 switch(task.mLockTaskAuth) {
258 case LOCK_TASK_AUTH_WHITELISTED:
259 case LOCK_TASK_AUTH_LAUNCHABLE:
260 case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
261 return true;
262 case LOCK_TASK_AUTH_PINNABLE:
263 case LOCK_TASK_AUTH_DONT_LOCK:
264 default:
265 return false;
266 }
267 }
268
269 /**
270 * @return whether the requested task is disallowed to be launched.
Benjamin Franza83859f2017-07-03 16:34:14 +0100271 */
272 boolean isLockTaskModeViolation(TaskRecord task) {
273 return isLockTaskModeViolation(task, false);
274 }
275
276 /**
277 * @param isNewClearTask whether the task would be cleared as part of the operation.
Charles Hed62f9652017-11-01 10:05:51 +0000278 * @return whether the requested task is disallowed to be launched.
Benjamin Franza83859f2017-07-03 16:34:14 +0100279 */
280 boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
281 if (isLockTaskModeViolationInternal(task, isNewClearTask)) {
282 showLockTaskToast();
283 return true;
284 }
285 return false;
286 }
287
Benjamin Franz7dcbfb02018-01-16 15:16:16 +0000288 /**
289 * @return the root task of the lock task.
290 */
291 TaskRecord getRootTask() {
292 if (mLockTaskModeTasks.isEmpty()) {
293 return null;
294 }
295 return mLockTaskModeTasks.get(0);
296 }
297
Benjamin Franza83859f2017-07-03 16:34:14 +0100298 private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) {
299 // TODO: Double check what's going on here. If the task is already in lock task mode, it's
300 // likely whitelisted, so will return false below.
Charles Heff9b4dff2017-09-22 10:18:37 +0100301 if (isTaskLocked(task) && !isNewClearTask) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100302 // If the task is already at the top and won't be cleared, then allow the operation
303 return false;
304 }
Charles Hed62f9652017-11-01 10:05:51 +0000305
306 // Allow recents activity if enabled by policy
307 if (task.isActivityTypeRecents() && isRecentsAllowed(task.userId)) {
308 return false;
Benjamin Franza83859f2017-07-03 16:34:14 +0100309 }
Charles Hed62f9652017-11-01 10:05:51 +0000310
Charles He858f1322017-11-27 17:11:04 -0800311 // Allow emergency calling when the device is protected by a locked keyguard
312 if (isKeyguardAllowed(task.userId) && isEmergencyCallTask(task)) {
313 return false;
314 }
315
Charles Hed62f9652017-11-01 10:05:51 +0000316 return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
317 }
318
319 private boolean isRecentsAllowed(int userId) {
320 return (getLockTaskFeaturesForUser(userId)
Benjamin Franzcaffa772018-02-05 16:36:10 +0000321 & DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW) != 0;
Benjamin Franza83859f2017-07-03 16:34:14 +0100322 }
323
Charles He858f1322017-11-27 17:11:04 -0800324 private boolean isKeyguardAllowed(int userId) {
325 return (getLockTaskFeaturesForUser(userId)
326 & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
327 }
328
329 private boolean isEmergencyCallTask(TaskRecord task) {
330 final Intent intent = task.intent;
331 if (intent == null) {
332 return false;
333 }
334
335 // 1. The emergency keypad activity launched on top of the keyguard
336 if (EMERGENCY_DIALER_COMPONENT.equals(intent.getComponent())) {
337 return true;
338 }
339
340 // 2. The intent sent by the keypad, which is handled by Telephony
341 if (ACTION_CALL_EMERGENCY.equals(intent.getAction())) {
342 return true;
343 }
344
345 // 3. Telephony then starts the default package for making the call
346 final TelecomManager tm = getTelecomManager();
347 final String dialerPackage = tm != null ? tm.getSystemDialerPackage() : null;
348 if (dialerPackage != null && dialerPackage.equals(intent.getComponent().getPackageName())) {
349 return true;
350 }
351
352 return false;
353 }
354
Benjamin Franza83859f2017-07-03 16:34:14 +0100355 /**
356 * Stop the current lock task mode.
357 *
Charles Heff9b4dff2017-09-22 10:18:37 +0100358 * This is called by {@link ActivityManagerService} and performs various checks before actually
359 * finishing the locked task.
360 *
361 * @param task the task that requested the end of lock task mode ({@code null} for quitting app
362 * pinning mode)
363 * @param isSystemCaller indicates whether this request comes from the system via
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700364 * {@link ActivityTaskManagerService#stopSystemLockTaskMode()}. If
Charles Heff9b4dff2017-09-22 10:18:37 +0100365 * {@code true}, it means the user intends to stop pinned mode through UI;
366 * otherwise, it's called by an app and we need to stop locked or pinned
367 * mode, subject to checks.
Benjamin Franza83859f2017-07-03 16:34:14 +0100368 * @param callingUid the caller that requested the end of lock task mode.
Charles Heff9b4dff2017-09-22 10:18:37 +0100369 * @throws IllegalArgumentException if the calling task is invalid (e.g., {@code null} or not in
370 * foreground)
Benjamin Franza83859f2017-07-03 16:34:14 +0100371 * @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if
372 * they differ from the one that launched lock task mode.
373 */
Charles Heff9b4dff2017-09-22 10:18:37 +0100374 void stopLockTaskMode(@Nullable TaskRecord task, boolean isSystemCaller, int callingUid) {
375 if (mLockTaskModeState == LOCK_TASK_MODE_NONE) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100376 return;
377 }
378
Charles Heff9b4dff2017-09-22 10:18:37 +0100379 if (isSystemCaller) {
380 if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
381 clearLockedTasks("stopAppPinning");
382 } else {
383 Slog.e(TAG_LOCKTASK, "Attempted to stop LockTask with isSystemCaller=true");
384 showLockTaskToast();
385 }
386
387 } else {
388 // Ensure calling activity is not null
389 if (task == null) {
390 throw new IllegalArgumentException("can't stop LockTask for null task");
391 }
392
393 // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
394 // It is possible lockTaskMode was started by the system process because
395 // android:lockTaskMode is set to a locking value in the application manifest
396 // instead of the app calling startLockTaskMode. In this case
397 // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the
398 // {@link TaskRecord.effectiveUid} instead. Also caller with
399 // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task.
400 if (callingUid != task.mLockTaskUid
401 && (task.mLockTaskUid != 0 || callingUid != task.effectiveUid)) {
402 throw new SecurityException("Invalid uid, expected " + task.mLockTaskUid
403 + " callingUid=" + callingUid + " effectiveUid=" + task.effectiveUid);
404 }
405
406 // We don't care if it's pinned or locked mode; this will stop it anyways.
407 clearLockedTask(task);
408 }
409 }
410
411 /**
412 * Clear all locked tasks and request the end of LockTask mode.
413 *
414 * This method is called by {@link UserController} when starting a new foreground user, and,
415 * unlike {@link #stopLockTaskMode(TaskRecord, boolean, int)}, it doesn't perform the checks.
416 */
417 void clearLockedTasks(String reason) {
418 if (DEBUG_LOCKTASK) Slog.i(TAG_LOCKTASK, "clearLockedTasks: " + reason);
419 if (!mLockTaskModeTasks.isEmpty()) {
420 clearLockedTask(mLockTaskModeTasks.get(0));
421 }
422 }
423
424 /**
425 * Clear one locked task from LockTask mode.
426 *
427 * If the requested task is the root task (see {@link #mLockTaskModeTasks}), then all locked
428 * tasks are cleared. Otherwise, only the requested task is cleared. LockTask mode is stopped
429 * when the last locked task is cleared.
430 *
431 * @param task the task to be cleared from LockTask mode.
432 */
433 void clearLockedTask(final TaskRecord task) {
434 if (task == null || mLockTaskModeTasks.isEmpty()) return;
435
436 if (task == mLockTaskModeTasks.get(0)) {
437 // We're removing the root task while there are other locked tasks. Therefore we should
438 // clear all locked tasks in reverse order.
439 for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx > 0; --taskNdx) {
440 clearLockedTask(mLockTaskModeTasks.get(taskNdx));
441 }
442 }
443
444 removeLockedTask(task);
445 if (mLockTaskModeTasks.isEmpty()) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100446 return;
447 }
Charles Heff9b4dff2017-09-22 10:18:37 +0100448 task.performClearTaskLocked();
Andrii Kulianab132ee2018-07-24 22:10:21 +0800449 mSupervisor.resumeFocusedStacksTopActivitiesLocked();
Benjamin Franza83859f2017-07-03 16:34:14 +0100450 }
451
452 /**
453 * Remove the given task from the locked task list. If this was the last task in the list,
454 * lock task mode is stopped.
455 */
Charles Heff9b4dff2017-09-22 10:18:37 +0100456 private void removeLockedTask(final TaskRecord task) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100457 if (!mLockTaskModeTasks.remove(task)) {
458 return;
459 }
Charles Heff9b4dff2017-09-22 10:18:37 +0100460 if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: removed " + task);
Benjamin Franza83859f2017-07-03 16:34:14 +0100461 if (mLockTaskModeTasks.isEmpty()) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100462 if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
463 " last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
464 mHandler.post(() -> performStopLockTask(task.userId));
465 }
466 }
467
Benjamin Franza83859f2017-07-03 16:34:14 +0100468 // This method should only be called on the handler thread
469 private void performStopLockTask(int userId) {
470 // When lock task ends, we enable the status bars.
471 try {
Charles Hee078db72017-10-19 18:03:20 +0100472 setStatusBarState(LOCK_TASK_MODE_NONE, userId);
473 setKeyguardState(LOCK_TASK_MODE_NONE, userId);
474 if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
475 lockKeyguardIfNeeded();
Benjamin Franza83859f2017-07-03 16:34:14 +0100476 }
Benjamin Franza83859f2017-07-03 16:34:14 +0100477 if (getDevicePolicyManager() != null) {
478 getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
479 }
Charles Hebfe82d12017-10-20 11:59:44 +0100480 if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000481 getStatusBarService().showPinningEnterExitToast(false /* entering */);
Charles Hebfe82d12017-10-20 11:59:44 +0100482 }
Benjamin Franz3662b152018-01-16 17:23:44 +0000483 mWindowManager.onLockTaskStateChanged(LOCK_TASK_MODE_NONE);
Benjamin Franza83859f2017-07-03 16:34:14 +0100484 } catch (RemoteException ex) {
485 throw new RuntimeException(ex);
486 } finally {
487 mLockTaskModeState = LOCK_TASK_MODE_NONE;
488 }
489 }
490
491 /**
Charles Hebfe82d12017-10-20 11:59:44 +0100492 * Show the lock task violation toast. Currently we only show toast for screen pinning mode, and
493 * no-op if the device is in locked mode.
Benjamin Franza83859f2017-07-03 16:34:14 +0100494 */
495 void showLockTaskToast() {
Charles Hebfe82d12017-10-20 11:59:44 +0100496 if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000497 try {
498 getStatusBarService().showPinningEscapeToast();
499 } catch (RemoteException e) {
500 Slog.e(TAG, "Failed to send pinning escape toast", e);
501 }
Charles Hebfe82d12017-10-20 11:59:44 +0100502 }
Benjamin Franza83859f2017-07-03 16:34:14 +0100503 }
504
505 // Starting lock task
506
507 /**
508 * Method to start lock task mode on a given task.
509 *
510 * @param task the task that should be locked.
Charles Heff9b4dff2017-09-22 10:18:37 +0100511 * @param isSystemCaller indicates whether this request was initiated by the system via
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700512 * {@link ActivityTaskManagerService#startSystemLockTaskMode(int)}. If
Charles Heff9b4dff2017-09-22 10:18:37 +0100513 * {@code true}, this intends to start pinned mode; otherwise, we look
514 * at the calling task's mLockTaskAuth to decide which mode to start.
Benjamin Franza83859f2017-07-03 16:34:14 +0100515 * @param callingUid the caller that requested the launch of lock task mode.
516 */
Charles Heff9b4dff2017-09-22 10:18:37 +0100517 void startLockTaskMode(@NonNull TaskRecord task, boolean isSystemCaller, int callingUid) {
518 if (!isSystemCaller) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100519 task.mLockTaskUid = callingUid;
520 if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
521 // startLockTask() called by app, but app is not part of lock task whitelist. Show
Charles Heff9b4dff2017-09-22 10:18:37 +0100522 // app pinning request. We will come back here with isSystemCaller true.
Benjamin Franza83859f2017-07-03 16:34:14 +0100523 if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
524 StatusBarManagerInternal statusBarManager = LocalServices.getService(
525 StatusBarManagerInternal.class);
526 if (statusBarManager != null) {
527 statusBarManager.showScreenPinningRequest(task.taskId);
528 }
529 return;
530 }
531 }
532
533 // System can only initiate screen pinning, not full lock task mode
Charles Heff9b4dff2017-09-22 10:18:37 +0100534 if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
535 isSystemCaller ? "Locking pinned" : "Locking fully");
536 setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
Benjamin Franza83859f2017-07-03 16:34:14 +0100537 "startLockTask", true);
538 }
539
540 /**
541 * Start lock task mode on the given task.
542 * @param lockTaskModeState whether fully locked or pinned mode.
543 * @param andResume whether the task should be brought to foreground as part of the operation.
544 */
545 private void setLockTaskMode(@NonNull TaskRecord task, int lockTaskModeState,
546 String reason, boolean andResume) {
547 // Should have already been checked, but do it again.
548 if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
549 if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
550 "setLockTaskMode: Can't lock due to auth");
551 return;
552 }
553 if (isLockTaskModeViolation(task)) {
554 Slog.e(TAG_LOCKTASK, "setLockTaskMode: Attempt to start an unauthorized lock task.");
555 return;
556 }
557
Bryce Lee1a990e52018-04-23 10:54:11 -0700558 final Intent taskIntent = task.intent;
559 if (mLockTaskModeTasks.isEmpty() && taskIntent != null) {
Charles Hed62f9652017-11-01 10:05:51 +0000560 mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.userId);
Benjamin Franza83859f2017-07-03 16:34:14 +0100561 // Start lock task on the handler thread
562 mHandler.post(() -> performStartLockTask(
Bryce Lee1a990e52018-04-23 10:54:11 -0700563 taskIntent.getComponent().getPackageName(),
Benjamin Franza83859f2017-07-03 16:34:14 +0100564 task.userId,
565 lockTaskModeState));
566 }
Benjamin Franza83859f2017-07-03 16:34:14 +0100567 if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task +
568 " Callers=" + Debug.getCallers(4));
Charles Heff9b4dff2017-09-22 10:18:37 +0100569
570 if (!mLockTaskModeTasks.contains(task)) {
571 mLockTaskModeTasks.add(task);
572 }
Benjamin Franza83859f2017-07-03 16:34:14 +0100573
574 if (task.mLockTaskUid == -1) {
575 task.mLockTaskUid = task.effectiveUid;
576 }
577
578 if (andResume) {
Wale Ogunwale66e16852017-10-19 13:35:52 -0700579 mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
Benjamin Franza83859f2017-07-03 16:34:14 +0100580 lockTaskModeState != LOCK_TASK_MODE_NONE);
Andrii Kulianab132ee2018-07-24 22:10:21 +0800581 mSupervisor.resumeFocusedStacksTopActivitiesLocked();
Benjamin Franza83859f2017-07-03 16:34:14 +0100582 mWindowManager.executeAppTransition();
583 } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
Wale Ogunwale0568aed2017-09-08 13:29:37 -0700584 mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
Wale Ogunwalea0f5b5e2017-10-11 09:37:23 -0700585 DEFAULT_DISPLAY, task.getStack(), true /* forceNonResizable */);
Benjamin Franza83859f2017-07-03 16:34:14 +0100586 }
587 }
588
589 // This method should only be called on the handler thread
590 private void performStartLockTask(String packageName, int userId, int lockTaskModeState) {
591 // When lock task starts, we disable the status bars.
592 try {
Charles Hebfe82d12017-10-20 11:59:44 +0100593 if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000594 getStatusBarService().showPinningEnterExitToast(true /* entering */);
Charles Hebfe82d12017-10-20 11:59:44 +0100595 }
Benjamin Franz3662b152018-01-16 17:23:44 +0000596 mWindowManager.onLockTaskStateChanged(lockTaskModeState);
Benjamin Franza83859f2017-07-03 16:34:14 +0100597 mLockTaskModeState = lockTaskModeState;
Charles Hee078db72017-10-19 18:03:20 +0100598 setStatusBarState(lockTaskModeState, userId);
599 setKeyguardState(lockTaskModeState, userId);
Benjamin Franza83859f2017-07-03 16:34:14 +0100600 if (getDevicePolicyManager() != null) {
601 getDevicePolicyManager().notifyLockTaskModeChanged(true, packageName, userId);
602 }
603 } catch (RemoteException ex) {
604 throw new RuntimeException(ex);
605 }
606 }
607
608 /**
Charles He520b2832017-09-02 15:27:16 +0100609 * Update packages that are allowed to be launched in lock task mode.
610 * @param userId Which user this whitelist is associated with
611 * @param packages The whitelist of packages allowed in lock task mode
612 * @see #mLockTaskPackages
Benjamin Franza83859f2017-07-03 16:34:14 +0100613 */
Charles He520b2832017-09-02 15:27:16 +0100614 void updateLockTaskPackages(int userId, String[] packages) {
615 mLockTaskPackages.put(userId, packages);
616
617 boolean taskChanged = false;
Benjamin Franza83859f2017-07-03 16:34:14 +0100618 for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
619 final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
Charles He520b2832017-09-02 15:27:16 +0100620 final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
621 || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
Benjamin Franza83859f2017-07-03 16:34:14 +0100622 lockedTask.setLockTaskAuth();
Charles He520b2832017-09-02 15:27:16 +0100623 final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
624 || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
625
626 if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED
627 || lockedTask.userId != userId
628 || !wasWhitelisted || isWhitelisted) {
629 continue;
Benjamin Franza83859f2017-07-03 16:34:14 +0100630 }
Charles He520b2832017-09-02 15:27:16 +0100631
632 // Terminate locked tasks that have recently lost whitelist authorization.
633 if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
634 lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
635 removeLockedTask(lockedTask);
636 lockedTask.performClearTaskLocked();
637 taskChanged = true;
Benjamin Franza83859f2017-07-03 16:34:14 +0100638 }
639
640 for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
Wale Ogunwalea0f5b5e2017-10-11 09:37:23 -0700641 mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated();
Benjamin Franza83859f2017-07-03 16:34:14 +0100642 }
Charles He520b2832017-09-02 15:27:16 +0100643
Benjamin Franza83859f2017-07-03 16:34:14 +0100644 final ActivityRecord r = mSupervisor.topRunningActivityLocked();
Charles He520b2832017-09-02 15:27:16 +0100645 final TaskRecord task = (r != null) ? r.getTask() : null;
646 if (mLockTaskModeTasks.isEmpty() && task!= null
Benjamin Franza83859f2017-07-03 16:34:14 +0100647 && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
648 // This task must have just been authorized.
649 if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
650 "onLockTaskPackagesUpdated: starting new locktask task=" + task);
Charles He520b2832017-09-02 15:27:16 +0100651 setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false);
652 taskChanged = true;
Benjamin Franza83859f2017-07-03 16:34:14 +0100653 }
Charles He520b2832017-09-02 15:27:16 +0100654
655 if (taskChanged) {
Andrii Kulianab132ee2018-07-24 22:10:21 +0800656 mSupervisor.resumeFocusedStacksTopActivitiesLocked();
Benjamin Franza83859f2017-07-03 16:34:14 +0100657 }
658 }
659
Charles He520b2832017-09-02 15:27:16 +0100660 boolean isPackageWhitelisted(int userId, String pkg) {
661 if (pkg == null) {
662 return false;
663 }
664 String[] whitelist;
665 whitelist = mLockTaskPackages.get(userId);
666 if (whitelist == null) {
667 return false;
668 }
669 for (String whitelistedPkg : whitelist) {
670 if (pkg.equals(whitelistedPkg)) {
671 return true;
672 }
673 }
674 return false;
675 }
676
Benjamin Franza83859f2017-07-03 16:34:14 +0100677 /**
Charles Hee078db72017-10-19 18:03:20 +0100678 * Update the UI features that are enabled for LockTask mode.
679 * @param userId Which user these feature flags are associated with
680 * @param flags Bitfield of feature flags
681 * @see DevicePolicyManager#setLockTaskFeatures(ComponentName, int)
682 */
683 void updateLockTaskFeatures(int userId, int flags) {
684 int oldFlags = getLockTaskFeaturesForUser(userId);
685 if (flags == oldFlags) {
686 return;
687 }
688
689 mLockTaskFeatures.put(userId, flags);
Charles Heff9b4dff2017-09-22 10:18:37 +0100690 if (!mLockTaskModeTasks.isEmpty() && userId == mLockTaskModeTasks.get(0).userId) {
Charles Hee078db72017-10-19 18:03:20 +0100691 mHandler.post(() -> {
692 if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
693 setStatusBarState(mLockTaskModeState, userId);
694 setKeyguardState(mLockTaskModeState, userId);
695 }
696 });
697 }
698 }
699
700 /**
701 * Helper method for configuring the status bar disabled state.
702 * Should only be called on the handler thread to avoid race.
703 */
704 private void setStatusBarState(int lockTaskModeState, int userId) {
705 IStatusBarService statusBar = getStatusBarService();
706 if (statusBar == null) {
707 Slog.e(TAG, "Can't find StatusBarService");
708 return;
709 }
710
711 // Default state, when lockTaskModeState == LOCK_TASK_MODE_NONE
712 int flags1 = StatusBarManager.DISABLE_NONE;
713 int flags2 = StatusBarManager.DISABLE2_NONE;
714
715 if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
716 flags1 = STATUS_BAR_MASK_PINNED;
717
718 } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
719 int lockTaskFeatures = getLockTaskFeaturesForUser(userId);
720 Pair<Integer, Integer> statusBarFlags = getStatusBarDisableFlags(lockTaskFeatures);
721 flags1 = statusBarFlags.first;
722 flags2 = statusBarFlags.second;
723 }
724
725 try {
726 statusBar.disable(flags1, mToken, mContext.getPackageName());
727 statusBar.disable2(flags2, mToken, mContext.getPackageName());
728 } catch (RemoteException e) {
729 Slog.e(TAG, "Failed to set status bar flags", e);
730 }
731 }
732
733 /**
734 * Helper method for configuring the keyguard disabled state.
735 * Should only be called on the handler thread to avoid race.
736 */
737 private void setKeyguardState(int lockTaskModeState, int userId) {
738 if (lockTaskModeState == LOCK_TASK_MODE_NONE) {
739 mWindowManager.reenableKeyguard(mToken);
740
741 } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
Charles He858f1322017-11-27 17:11:04 -0800742 if (isKeyguardAllowed(userId)) {
Charles Hee078db72017-10-19 18:03:20 +0100743 mWindowManager.reenableKeyguard(mToken);
Charles He858f1322017-11-27 17:11:04 -0800744 } else {
Tony Mak2bf36912018-03-21 15:45:52 +0000745 // If keyguard is not secure and it is locked, dismiss the keyguard before
746 // disabling it, which avoids the platform to think the keyguard is still on.
747 if (mWindowManager.isKeyguardLocked() && !mWindowManager.isKeyguardSecure()) {
748 mWindowManager.dismissKeyguard(new IKeyguardDismissCallback.Stub() {
749 @Override
750 public void onDismissError() throws RemoteException {
751 Slog.i(TAG, "setKeyguardState: failed to dismiss keyguard");
752 }
753
754 @Override
755 public void onDismissSucceeded() throws RemoteException {
756 mHandler.post(
757 () -> mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG));
758 }
759
760 @Override
761 public void onDismissCancelled() throws RemoteException {
762 Slog.i(TAG, "setKeyguardState: dismiss cancelled");
763 }
764 }, null);
765 } else {
766 mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
767 }
Charles Hee078db72017-10-19 18:03:20 +0100768 }
769
770 } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
771 mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
772 }
773 }
774
775 /**
776 * Helper method for locking the device immediately. This may be necessary when the device
777 * leaves the pinned mode.
778 */
779 private void lockKeyguardIfNeeded() {
780 try {
781 boolean shouldLockKeyguard = Settings.Secure.getIntForUser(
782 mContext.getContentResolver(),
783 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
784 USER_CURRENT) != 0;
785 if (shouldLockKeyguard) {
786 mWindowManager.lockNow(null);
Lucas Dupinc80c67e2017-12-04 14:29:10 -0800787 mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
Charles Hee078db72017-10-19 18:03:20 +0100788 getLockPatternUtils().requireCredentialEntry(USER_ALL);
789 }
790 } catch (Settings.SettingNotFoundException e) {
791 // No setting, don't lock.
792 }
793 }
794
795 /**
796 * Translates from LockTask feature flags to StatusBarManager disable and disable2 flags.
797 * @param lockTaskFlags Bitfield of flags as per
798 * {@link DevicePolicyManager#setLockTaskFeatures(ComponentName, int)}
799 * @return A {@link Pair} of {@link StatusBarManager#disable(int)} and
800 * {@link StatusBarManager#disable2(int)} flags
801 */
802 @VisibleForTesting
803 Pair<Integer, Integer> getStatusBarDisableFlags(int lockTaskFlags) {
804 // Everything is disabled by default
805 int flags1 = StatusBarManager.DISABLE_MASK;
806 int flags2 = StatusBarManager.DISABLE2_MASK;
807 for (int i = STATUS_BAR_FLAG_MAP_LOCKED.size() - 1; i >= 0; i--) {
808 Pair<Integer, Integer> statusBarFlags = STATUS_BAR_FLAG_MAP_LOCKED.valueAt(i);
809 if ((STATUS_BAR_FLAG_MAP_LOCKED.keyAt(i) & lockTaskFlags) != 0) {
810 flags1 &= ~statusBarFlags.first;
811 flags2 &= ~statusBarFlags.second;
812 }
813 }
814 // Some flags are not used for LockTask purposes, so we mask them
815 flags1 &= STATUS_BAR_MASK_LOCKED;
816 return new Pair<>(flags1, flags2);
817 }
818
819 /**
820 * Gets the cached value of LockTask feature flags for a specific user.
821 */
822 private int getLockTaskFeaturesForUser(int userId) {
823 return mLockTaskFeatures.get(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
824 }
825
Benjamin Franza83859f2017-07-03 16:34:14 +0100826 // Should only be called on the handler thread
827 @Nullable
828 private IStatusBarService getStatusBarService() {
829 if (mStatusBarService == null) {
830 mStatusBarService = IStatusBarService.Stub.asInterface(
831 ServiceManager.checkService(STATUS_BAR_SERVICE));
832 if (mStatusBarService == null) {
833 Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
834 }
835 }
836 return mStatusBarService;
837 }
838
839 // Should only be called on the handler thread
840 @Nullable
841 private IDevicePolicyManager getDevicePolicyManager() {
842 if (mDevicePolicyManager == null) {
843 mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
844 ServiceManager.checkService(DEVICE_POLICY_SERVICE));
845 if (mDevicePolicyManager == null) {
846 Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE");
847 }
848 }
849 return mDevicePolicyManager;
850 }
851
852 @NonNull
853 private LockPatternUtils getLockPatternUtils() {
854 if (mLockPatternUtils == null) {
855 // We don't preserve the LPU object to save memory
856 return new LockPatternUtils(mContext);
857 }
858 return mLockPatternUtils;
859 }
860
Charles He858f1322017-11-27 17:11:04 -0800861 @Nullable
862 private TelecomManager getTelecomManager() {
863 if (mTelecomManager == null) {
864 // We don't preserve the TelecomManager object to save memory
865 return mContext.getSystemService(TelecomManager.class);
866 }
867 return mTelecomManager;
868 }
869
Benjamin Franza83859f2017-07-03 16:34:14 +0100870 public void dump(PrintWriter pw, String prefix) {
Charles He520b2832017-09-02 15:27:16 +0100871 pw.println(prefix + "LockTaskController");
872 prefix = prefix + " ";
873 pw.println(prefix + "mLockTaskModeState=" + lockTaskModeToString());
874 pw.println(prefix + "mLockTaskModeTasks=");
875 for (int i = 0; i < mLockTaskModeTasks.size(); ++i) {
876 pw.println(prefix + " #" + i + " " + mLockTaskModeTasks.get(i));
877 }
878 pw.println(prefix + "mLockTaskPackages (userId:packages)=");
879 for (int i = 0; i < mLockTaskPackages.size(); ++i) {
880 pw.println(prefix + " u" + mLockTaskPackages.keyAt(i)
881 + ":" + Arrays.toString(mLockTaskPackages.valueAt(i)));
882 }
Benjamin Franza83859f2017-07-03 16:34:14 +0100883 }
884
885 private String lockTaskModeToString() {
886 switch (mLockTaskModeState) {
887 case LOCK_TASK_MODE_LOCKED:
888 return "LOCKED";
889 case LOCK_TASK_MODE_PINNED:
890 return "PINNED";
891 case LOCK_TASK_MODE_NONE:
892 return "NONE";
893 default: return "unknown=" + mLockTaskModeState;
894 }
895 }
896}