summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityManager.java22
-rw-r--r--core/java/android/app/IActivityManager.aidl1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java157
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java343
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java9
-rw-r--r--services/core/java/com/android/server/am/LockTaskController.java572
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java8
-rw-r--r--services/core/java/com/android/server/am/UserController.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java355
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java22
14 files changed, 1012 insertions, 501 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a0082e4e57bb..86f07a7b72fe 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4294,28 +4294,6 @@ public class ActivityManager {
}
/**
- * @hide
- */
- public void startLockTaskMode(int taskId) {
- try {
- getService().startLockTaskModeById(taskId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * @hide
- */
- public void stopLockTaskMode() {
- try {
- getService().stopLockTaskMode();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Return whether currently in lock task mode. When in this mode
* no new tasks can be created or switched to.
*
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index fa9d7ca8ebc5..e0e4aeb33668 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -391,7 +391,6 @@ interface IActivityManager {
// Start of L transactions
String getTagForIntentSender(in IIntentSender sender, in String prefix);
boolean startUserInBackground(int userid);
- void startLockTaskModeById(int taskId);
void startLockTaskModeByToken(in IBinder token);
void stopLockTaskMode();
boolean isInLockTaskMode();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index c44e1db367b8..cfe0a4a6005b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -504,7 +504,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
// If we recently long-pressed the other button then they were
// long-pressed 'together'
if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
- activityManager.stopLockTaskMode();
+ activityManager.stopSystemLockTaskMode();
// When exiting refresh disabled flags.
mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
return true;
@@ -522,7 +522,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
} else if (touchExplorationEnabled && inLockTaskMode) {
// When in accessibility mode a long press that is recents (not back)
// should stop lock task.
- activityManager.stopLockTaskMode();
+ activityManager.stopSystemLockTaskMode();
// When exiting refresh disabled flags.
mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
return true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 47f3ec69fd03..5b2b76a936e3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
@@ -173,8 +174,6 @@ import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
@@ -355,10 +354,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
-import com.android.server.job.JobSchedulerInternal;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -404,14 +399,15 @@ import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.firewall.IntentFirewall;
+import com.android.server.job.JobSchedulerInternal;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.WindowManagerService;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
-import java.text.SimpleDateFormat;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -429,6 +425,7 @@ import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -696,6 +693,11 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
String mDeviceOwnerName;
+ /**
+ * The controller for all operations related to locktask.
+ */
+ final LockTaskController mLockTaskController;
+
final UserController mUserController;
final AppErrors mAppErrors;
@@ -2562,6 +2564,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mWindowManager = wm;
mStackSupervisor.setWindowManager(wm);
mActivityStarter.setWindowManager(wm);
+ mLockTaskController.setWindowManager(wm);
}
public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
@@ -2689,6 +2692,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mUiHandler = injector.getUiHandler(null);
mUserController = null;
mVrController = null;
+ mLockTaskController = null;
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2786,6 +2790,7 @@ public class ActivityManagerService extends IActivityManager.Stub
new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
mActivityStarter = new ActivityStarter(this, mStackSupervisor);
mRecentTasks = new RecentTasks(this, mStackSupervisor);
+ mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
mProcessCpuThread = new Thread("CpuTracker") {
@Override
@@ -4979,12 +4984,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
// finish.
- if (tr.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV && rootR == r &&
- mStackSupervisor.isLastLockedTask(tr)) {
- Slog.i(TAG, "Not finishing task in lock task mode");
- mStackSupervisor.showLockTaskToast();
+ if (mLockTaskController.activityBlockedFromFinish(r)) {
return false;
}
+
if (mController != null) {
// Find the first activity that is not finishing.
ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
@@ -5110,9 +5113,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
// can finish.
final TaskRecord task = r.getTask();
- if (task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV &&
- mStackSupervisor.isLastLockedTask(task) && task.getRootActivity() == r) {
- mStackSupervisor.showLockTaskToast();
+ if (mLockTaskController.activityBlockedFromFinish(r)) {
return false;
}
return task.getStack().finishActivityAffinityLocked(r);
@@ -10372,8 +10373,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.d(TAG, "Could not find task for id: "+ taskId);
return;
}
- if (mStackSupervisor.isLockTaskModeViolation(task)) {
- mStackSupervisor.showLockTaskToast();
+ if (mLockTaskController.isLockTaskModeViolation(task)) {
Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
return;
}
@@ -10820,6 +10820,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void updateLockTaskPackages(int userId, String[] packages) {
+ // TODO: move this into LockTaskController
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != SYSTEM_UID) {
enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
@@ -10829,17 +10830,21 @@ public class ActivityManagerService extends IActivityManager.Stub
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
Arrays.toString(packages));
mLockTaskPackages.put(userId, packages);
- mStackSupervisor.onLockTaskPackagesUpdatedLocked();
+ mLockTaskController.onLockTaskPackagesUpdated();
}
}
-
- void startLockTaskModeLocked(TaskRecord task) {
+ private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isAppPinning) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
- if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+ if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
return;
}
+ final ActivityStack stack = mStackSupervisor.getFocusedStack();
+ if (stack == null || task != stack.topTask()) {
+ throw new IllegalArgumentException("Invalid task, not in foreground");
+ }
+
// When a task is locked, dismiss the pinned stack if it exists
final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
PINNED_STACK_ID);
@@ -10847,63 +10852,26 @@ public class ActivityManagerService extends IActivityManager.Stub
mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
}
- // isSystemInitiated is used to distinguish between locked and pinned mode, as pinned mode
+ // isAppPinning is used to distinguish between locked and pinned mode, as pinned mode
// is initiated by system after the pinning request was shown and locked mode is initiated
// by an authorized app directly
final int callingUid = Binder.getCallingUid();
- boolean isSystemInitiated = callingUid == SYSTEM_UID;
long ident = Binder.clearCallingIdentity();
try {
- if (!isSystemInitiated) {
- task.mLockTaskUid = callingUid;
- if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
- // startLockTask() called by app and task mode is lockTaskModeDefault.
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
- StatusBarManagerInternal statusBarManager =
- LocalServices.getService(StatusBarManagerInternal.class);
- if (statusBarManager != null) {
- statusBarManager.showScreenPinningRequest(task.taskId);
- }
- return;
- }
-
- final ActivityStack stack = mStackSupervisor.getFocusedStack();
- if (stack == null || task != stack.topTask()) {
- throw new IllegalArgumentException("Invalid task, not in foreground");
- }
- }
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, isSystemInitiated ? "Locking pinned" :
- "Locking fully");
- mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
- ActivityManager.LOCK_TASK_MODE_PINNED :
- ActivityManager.LOCK_TASK_MODE_LOCKED,
- "startLockTask", true);
+ mLockTaskController.startLockTaskMode(task, isAppPinning, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
- public void startLockTaskModeById(int taskId) {
- synchronized (this) {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
- if (task != null) {
- startLockTaskModeLocked(task);
- }
- }
- }
-
- @Override
public void startLockTaskModeByToken(IBinder token) {
synchronized (this) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
return;
}
- final TaskRecord task = r.getTask();
- if (task != null) {
- startLockTaskModeLocked(task);
- }
+ startLockTaskModeLocked(r.getTask(), false /* not system initiated */);
}
}
@@ -10914,7 +10882,8 @@ public class ActivityManagerService extends IActivityManager.Stub
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- startLockTaskModeById(taskId);
+ startLockTaskModeLocked(mStackSupervisor.anyTaskForIdLocked(taskId),
+ true /* system initiated */);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -10923,41 +10892,28 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void stopLockTaskMode() {
- final TaskRecord lockTask = mStackSupervisor.getLockedTaskLocked();
- if (lockTask == null) {
- // Our work here is done.
- return;
- }
+ stopLockTaskModeInternal(false /* not system initiated */);
+ }
+
+ /**
+ * This API should be called by SystemUI only when user perform certain action to dismiss
+ * lock task mode. We should only dismiss pinned lock task mode in this case.
+ */
+ @Override
+ public void stopSystemLockTaskMode() throws RemoteException {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
+ stopLockTaskModeInternal(true /* system initiated */);
+ }
+ private void stopLockTaskModeInternal(boolean isSystemRequest) {
final int callingUid = Binder.getCallingUid();
- final int lockTaskUid = lockTask.mLockTaskUid;
- final int lockTaskModeState = mStackSupervisor.getLockTaskModeState();
- if (lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE) {
- // Done.
- return;
- } else {
- // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
- // It is possible lockTaskMode was started by the system process because
- // android:lockTaskMode is set to a locking value in the application manifest
- // instead of the app calling startLockTaskMode. In this case
- // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the
- // {@link TaskRecord.effectiveUid} instead. Also caller with
- // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task.
- if (checkCallingPermission(MANAGE_ACTIVITY_STACKS) != PERMISSION_GRANTED
- && callingUid != lockTaskUid
- && (lockTaskUid != 0 || callingUid != lockTask.effectiveUid)) {
- throw new SecurityException("Invalid uid, expected " + lockTaskUid
- + " callingUid=" + callingUid + " effectiveUid=" + lockTask.effectiveUid);
- }
- }
long ident = Binder.clearCallingIdentity();
try {
- Log.d(TAG, "stopLockTaskMode");
- // Stop lock task
synchronized (this) {
- mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
- "stopLockTask", true);
+ mLockTaskController.stopLockTaskMode(isSystemRequest, callingUid);
}
+ // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
+ // task and jumping straight into a call in the case of emergency call back.
TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
if (tm != null) {
tm.showInCallScreen(false);
@@ -10967,28 +10923,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- /**
- * This API should be called by SystemUI only when user perform certain action to dismiss
- * lock task mode. We should only dismiss pinned lock task mode in this case.
- */
- @Override
- public void stopSystemLockTaskMode() throws RemoteException {
- if (mStackSupervisor.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED) {
- stopLockTaskMode();
- } else {
- mStackSupervisor.showLockTaskToast();
- }
- }
-
@Override
public boolean isInLockTaskMode() {
- return getLockTaskModeState() != ActivityManager.LOCK_TASK_MODE_NONE;
+ return getLockTaskModeState() != LOCK_TASK_MODE_NONE;
}
@Override
public int getLockTaskModeState() {
synchronized (this) {
- return mStackSupervisor.getLockTaskModeState();
+ return mLockTaskController.getLockTaskModeState();
}
}
@@ -10999,7 +10942,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (r == null) {
return;
}
- mStackSupervisor.showLockTaskEscapeMessageLocked(r.getTask());
+ mLockTaskController.showLockTaskToast();
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 45357cb67fd1..403e01d5ae44 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2224,7 +2224,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
int runTaskLock(PrintWriter pw) throws RemoteException {
String taskIdStr = getNextArgRequired();
if (taskIdStr.equals("stop")) {
- mInterface.stopLockTaskMode();
+ mInterface.stopSystemLockTaskMode();
} else {
int taskId = Integer.parseInt(taskIdStr);
mInterface.startSystemLockTaskMode(taskId);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4a9b98d7be00..d30d3a579848 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1205,7 +1205,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
boolean isKeyguardLocked = service.isKeyguardLocked();
- boolean isCurrentAppLocked = mStackSupervisor.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ boolean isCurrentAppLocked = service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null;
// Don't return early if !isNotLocked, since we want to throw an exception if the activity
// is in an incorrect state
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 896f846feec0..ec313958af73 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3682,7 +3682,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
if (endTask) {
- mStackSupervisor.removeLockedTaskLocked(task);
+ mService.mLockTaskController.removeLockedTask(task);
}
} else if (r.state != ActivityState.PAUSING) {
// If the activity is PAUSING, we will complete the finish once
@@ -4536,8 +4536,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
Slog.i(TAG, "moveTaskToBack: " + tr);
// If the task is locked, then show the lock task toast
- if (mStackSupervisor.isLockedTask(tr)) {
- mStackSupervisor.showLockTaskToast();
+ if (!mService.mLockTaskController.checkLockedTask(tr)) {
return false;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e8bc68f21981..476b017dd462 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -20,8 +20,6 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
-import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
@@ -36,20 +34,18 @@ import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.Process.SYSTEM_UID;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
import static android.view.Display.TYPE_VIRTUAL;
-
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
@@ -57,10 +53,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
@@ -85,11 +79,8 @@ import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.ActivityStack.STACK_VISIBLE;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
@@ -109,9 +100,7 @@ import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
-import android.app.StatusBarManager;
import android.app.WaitResult;
-import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -134,19 +123,15 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.MediaStore;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -162,9 +147,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.TransferPipe;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.PinnedStackWindowController;
@@ -184,7 +167,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
- private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
@@ -210,17 +192,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
static final int HANDLE_DISPLAY_ADDED = FIRST_SUPERVISOR_STACK_MSG + 5;
static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
- static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9;
- static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
- static final int SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
- private static final String LOCK_TASK_TAG = "Lock-to-App";
-
// Used to indicate if an object (e.g. stack) that we are trying to get
// should be created if it doesn't exist already.
static final boolean CREATE_IF_NEEDED = true;
@@ -288,11 +265,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
/** Action restriction: launching the activity is restricted by an app op. */
private static final int ACTIVITY_RESTRICTION_APPOP = 2;
- /** Status Bar Service **/
- private IBinder mToken = new Binder();
- private IStatusBarService mStatusBarService;
- private IDevicePolicyManager mDevicePolicyManager;
-
// For debugging to make sure the caller when acquiring/releasing our
// wake lock is the system process.
static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
@@ -409,20 +381,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
private DisplayManagerInternal mDisplayManagerInternal;
private InputManagerInternal mInputManagerInternal;
- /** The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
- * may be finished until there is only one entry left. If this is empty the system is not
- * in lockTask mode. */
- ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
- /** Store the current lock task mode. Possible values:
- * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
- * {@link ActivityManager#LOCK_TASK_MODE_PINNED}
- */
- private int mLockTaskModeState;
- /**
- * Notifies the user when entering/exiting lock-task.
- */
- private LockTaskNotify mLockTaskNotify;
-
/** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
boolean inResumeTopActivity;
@@ -619,34 +577,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mLaunchingActivity.setReferenceCounted(false);
}
- // This function returns a IStatusBarService. The value is from ServiceManager.
- // getService and is cached.
- private IStatusBarService getStatusBarService() {
- synchronized (mService) {
- if (mStatusBarService == null) {
- mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
- if (mStatusBarService == null) {
- Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
- }
- }
- return mStatusBarService;
- }
- }
-
- private IDevicePolicyManager getDevicePolicyManager() {
- synchronized (mService) {
- if (mDevicePolicyManager == null) {
- mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
- ServiceManager.checkService(Context.DEVICE_POLICY_SERVICE));
- if (mDevicePolicyManager == null) {
- Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE");
- }
- }
- return mDevicePolicyManager;
- }
- }
-
void setWindowManager(WindowManagerService wm) {
synchronized (mService) {
mWindowManager = wm;
@@ -1383,8 +1313,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE ||
task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) {
- setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE",
- false);
+ mService.mLockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
}
try {
@@ -3568,18 +3497,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- private String lockTaskModeToString() {
- switch (mLockTaskModeState) {
- case LOCK_TASK_MODE_LOCKED:
- return "LOCKED";
- case LOCK_TASK_MODE_PINNED:
- return "PINNED";
- case LOCK_TASK_MODE_NONE:
- return "NONE";
- default: return "unknown=" + mLockTaskModeState;
- }
- }
-
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
@@ -3588,7 +3505,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
pw.print(prefix); pw.println("mStacks=" + mStacks);
- pw.print(prefix); pw.print("mLockTaskModeState=" + lockTaskModeToString());
+ // TODO: move this to LockTaskController
final SparseArray<String[]> packages = mService.mLockTaskPackages;
if (packages.size() > 0) {
pw.print(prefix); pw.println("mLockTaskPackages (userId:packages)=");
@@ -3604,8 +3521,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- pw.println(" mLockTaskModeTasks" + mLockTaskModeTasks);
mKeyguardController.dump(pw, prefix);
+ mService.mLockTaskController.dump(pw, prefix);
}
/**
@@ -4021,45 +3938,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return list;
}
- TaskRecord getLockedTaskLocked() {
- final int top = mLockTaskModeTasks.size() - 1;
- if (top >= 0) {
- return mLockTaskModeTasks.get(top);
- }
- return null;
- }
-
- boolean isLockedTask(TaskRecord task) {
- return mLockTaskModeTasks.contains(task);
- }
-
- boolean isLastLockedTask(TaskRecord task) {
- return mLockTaskModeTasks.size() == 1 && mLockTaskModeTasks.contains(task);
- }
-
- void removeLockedTaskLocked(final TaskRecord task) {
- if (!mLockTaskModeTasks.remove(task)) {
- return;
- }
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "removeLockedTaskLocked: removed " + task);
- if (mLockTaskModeTasks.isEmpty()) {
- // Last one.
- if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
- " last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
- final Message lockTaskMsg = Message.obtain();
- lockTaskMsg.arg1 = task.userId;
- lockTaskMsg.what = LOCK_TASK_END_MSG;
- mHandler.sendMessage(lockTaskMsg);
- }
- }
-
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
int preferredDisplayId, int actualStackId) {
handleNonResizableTaskIfNeeded(task, preferredStackId, preferredDisplayId, actualStackId,
false /* forceNonResizable */);
}
- private void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
+ void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
int preferredDisplayId, int actualStackId, boolean forceNonResizable) {
final boolean isSecondaryDisplayPreferred =
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY)
@@ -4118,152 +4003,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- void showLockTaskToast() {
- if (mLockTaskNotify != null) {
- mLockTaskNotify.showToast(mLockTaskModeState);
- }
- }
-
- void showLockTaskEscapeMessageLocked(TaskRecord task) {
- if (mLockTaskModeTasks.contains(task)) {
- mHandler.sendEmptyMessage(SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG);
- }
- }
-
- void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason,
- boolean andResume) {
- if (task == null) {
- // Take out of lock task mode if necessary
- final TaskRecord lockedTask = getLockedTaskLocked();
- if (lockedTask != null) {
- removeLockedTaskLocked(lockedTask);
- if (!mLockTaskModeTasks.isEmpty()) {
- // There are locked tasks remaining, can only finish this task, not unlock it.
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
- "setLockTaskModeLocked: Tasks remaining, can't unlock");
- lockedTask.performClearTaskLocked();
- resumeFocusedStackTopActivityLocked();
- return;
- }
- }
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
- "setLockTaskModeLocked: No tasks to unlock. Callers=" + Debug.getCallers(4));
- return;
- }
-
- // Should have already been checked, but do it again.
- if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
- "setLockTaskModeLocked: Can't lock due to auth");
- return;
- }
- if (isLockTaskModeViolation(task)) {
- Slog.e(TAG_LOCKTASK, "setLockTaskMode: Attempt to start an unauthorized lock task.");
- return;
- }
-
- if (mLockTaskModeTasks.isEmpty()) {
- // First locktask.
- final Message lockTaskMsg = Message.obtain();
- lockTaskMsg.obj = task.intent.getComponent().getPackageName();
- lockTaskMsg.arg1 = task.userId;
- lockTaskMsg.what = LOCK_TASK_START_MSG;
- lockTaskMsg.arg2 = lockTaskModeState;
- mHandler.sendMessage(lockTaskMsg);
- }
- // Add it or move it to the top.
- if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskModeLocked: Locking to " + task +
- " Callers=" + Debug.getCallers(4));
- mLockTaskModeTasks.remove(task);
- mLockTaskModeTasks.add(task);
-
- if (task.mLockTaskUid == -1) {
- task.mLockTaskUid = task.effectiveUid;
- }
-
- if (andResume) {
- findTaskToMoveToFrontLocked(task, 0, null, reason,
- lockTaskModeState != LOCK_TASK_MODE_NONE);
- resumeFocusedStackTopActivityLocked();
- mWindowManager.executeAppTransition();
- } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
- handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY,
- task.getStackId(), true /* forceNonResizable */);
- }
- }
-
- boolean isLockTaskModeViolation(TaskRecord task) {
- return isLockTaskModeViolation(task, false);
- }
-
- boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
- if (getLockedTaskLocked() == task && !isNewClearTask) {
- return false;
- }
- final int lockTaskAuth = task.mLockTaskAuth;
- switch (lockTaskAuth) {
- case LOCK_TASK_AUTH_DONT_LOCK:
- return !mLockTaskModeTasks.isEmpty();
- case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
- case LOCK_TASK_AUTH_LAUNCHABLE:
- case LOCK_TASK_AUTH_WHITELISTED:
- return false;
- case LOCK_TASK_AUTH_PINNABLE:
- // Pinnable tasks can't be launched on top of locktask tasks.
- return !mLockTaskModeTasks.isEmpty();
- default:
- Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
- return true;
- }
- }
-
- void onLockTaskPackagesUpdatedLocked() {
- boolean didSomething = false;
- for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
- final boolean wasWhitelisted =
- (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) ||
- (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED);
- lockedTask.setLockTaskAuth();
- final boolean isWhitelisted =
- (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) ||
- (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED);
- if (wasWhitelisted && !isWhitelisted) {
- // Lost whitelisting authorization. End it now.
- if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
- lockedTask + " mLockTaskAuth=" + lockedTask.lockTaskAuthToString());
- removeLockedTaskLocked(lockedTask);
- lockedTask.performClearTaskLocked();
- didSomething = true;
- }
- }
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
- for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = stacks.get(stackNdx);
- stack.onLockTaskPackagesUpdatedLocked();
- }
- }
- final ActivityRecord r = topRunningActivityLocked();
- final TaskRecord task = r != null ? r.getTask() : null;
- if (mLockTaskModeTasks.isEmpty() && task != null
- && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
- // This task must have just been authorized.
- if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
- "onLockTaskPackagesUpdated: starting new locktask task=" + task);
- setLockTaskModeLocked(task, ActivityManager.LOCK_TASK_MODE_LOCKED, "package updated",
- false);
- didSomething = true;
- }
- if (didSomething) {
- resumeFocusedStackTopActivityLocked();
- }
- }
-
- int getLockTaskModeState() {
- return mLockTaskModeState;
- }
-
void activityRelaunchedLocked(IBinder token) {
mWindowManager.notifyAppRelaunchingFinished(token);
if (mService.isSleepingOrShuttingDownLocked()) {
@@ -4446,78 +4185,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
case HANDLE_DISPLAY_REMOVED: {
handleDisplayRemoved(msg.arg1);
} break;
- case LOCK_TASK_START_MSG: {
- // When lock task starts, we disable the status bars.
- try {
- if (mLockTaskNotify == null) {
- mLockTaskNotify = new LockTaskNotify(mService.mContext);
- }
- mLockTaskNotify.show(true);
- mLockTaskModeState = msg.arg2;
- if (getStatusBarService() != null) {
- int flags = 0;
- if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
- flags = StatusBarManager.DISABLE_MASK
- & (~StatusBarManager.DISABLE_BACK);
- } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
- flags = StatusBarManager.DISABLE_MASK
- & (~StatusBarManager.DISABLE_BACK)
- & (~StatusBarManager.DISABLE_HOME)
- & (~StatusBarManager.DISABLE_RECENT);
- }
- getStatusBarService().disable(flags, mToken,
- mService.mContext.getPackageName());
- }
- mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
- if (getDevicePolicyManager() != null) {
- getDevicePolicyManager().notifyLockTaskModeChanged(true,
- (String)msg.obj, msg.arg1);
- }
- } catch (RemoteException ex) {
- throw new RuntimeException(ex);
- }
- } break;
- case LOCK_TASK_END_MSG: {
- // When lock task ends, we enable the status bars.
- try {
- if (getStatusBarService() != null) {
- getStatusBarService().disable(StatusBarManager.DISABLE_NONE, mToken,
- mService.mContext.getPackageName());
- }
- mWindowManager.reenableKeyguard(mToken);
- if (getDevicePolicyManager() != null) {
- getDevicePolicyManager().notifyLockTaskModeChanged(false, null,
- msg.arg1);
- }
- if (mLockTaskNotify == null) {
- mLockTaskNotify = new LockTaskNotify(mService.mContext);
- }
- mLockTaskNotify.show(false);
- try {
- boolean shouldLockKeyguard = Settings.Secure.getInt(
- mService.mContext.getContentResolver(),
- Settings.Secure.LOCK_TO_APP_EXIT_LOCKED) != 0;
- if (mLockTaskModeState == LOCK_TASK_MODE_PINNED && shouldLockKeyguard) {
- mWindowManager.lockNow(null);
- mWindowManager.dismissKeyguard(null /* callback */);
- new LockPatternUtils(mService.mContext)
- .requireCredentialEntry(UserHandle.USER_ALL);
- }
- } catch (SettingNotFoundException e) {
- // No setting, don't lock.
- }
- } catch (RemoteException ex) {
- throw new RuntimeException(ex);
- } finally {
- mLockTaskModeState = LOCK_TASK_MODE_NONE;
- }
- } break;
- case SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG: {
- if (mLockTaskNotify == null) {
- mLockTaskNotify = new LockTaskNotify(mService.mContext);
- }
- mLockTaskNotify.showToast(LOCK_TASK_MODE_PINNED);
- } break;
case LAUNCH_TASK_BEHIND_COMPLETE: {
synchronized (mService) {
ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 761f9eef65fd..235477e24a27 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1033,10 +1033,9 @@ class ActivityStarter {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
// the device would otherwise leave the locked task.
- if (mSupervisor.isLockTaskModeViolation(reusedActivity.getTask(),
+ if (mService.mLockTaskController.isLockTaskModeViolation(reusedActivity.getTask(),
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
- mSupervisor.showLockTaskToast();
Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -1813,7 +1812,7 @@ class ActivityStarter {
mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
}
- if (mSupervisor.isLockTaskModeViolation(mStartActivity.getTask())) {
+ if (mService.mLockTaskController.isLockTaskModeViolation(mStartActivity.getTask())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -1832,7 +1831,7 @@ class ActivityStarter {
}
private int setTaskFromSourceRecord() {
- if (mSupervisor.isLockTaskModeViolation(mSourceRecord.getTask())) {
+ if (mService.mLockTaskController.isLockTaskModeViolation(mSourceRecord.getTask())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
@@ -1926,7 +1925,7 @@ class ActivityStarter {
private int setTaskFromInTask() {
// The caller is asking that the new activity be started in an explicit
// task it has provided to us.
- if (mSupervisor.isLockTaskModeViolation(mInTask)) {
+ if (mService.mLockTaskController.isLockTaskModeViolation(mInTask)) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
new file mode 100644
index 000000000000..c03ac86a2bf9
--- /dev/null
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.StatusBarManager.DISABLE_BACK;
+import static android.app.StatusBarManager.DISABLE_HOME;
+import static android.app.StatusBarManager.DISABLE_MASK;
+import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.app.StatusBarManager.DISABLE_RECENT;
+import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.content.Context.STATUS_BAR_SERVICE;
+import static android.os.UserHandle.USER_ALL;
+import static android.provider.Settings.Secure.LOCK_TO_APP_EXIT_LOCKED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.admin.IDevicePolicyManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.WindowManagerService;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Helper class that deals with all things related to task locking. This includes the screen pinning
+ * mode that can be launched via System UI as well as the fully locked mode that can be achieved
+ * on fully managed devices.
+ *
+ * Note: All methods in this class should only be called with the ActivityManagerService lock held.
+ *
+ * @see Activity#startLockTask()
+ * @see Activity#stopLockTask()
+ */
+public class LockTaskController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "LockTaskController" : TAG_AM;
+ private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
+
+ @VisibleForTesting
+ static final int STATUS_BAR_MASK_LOCKED = DISABLE_MASK
+ & (~DISABLE_BACK);
+ @VisibleForTesting
+ static final int STATUS_BAR_MASK_PINNED = DISABLE_MASK
+ & (~DISABLE_BACK)
+ & (~DISABLE_HOME)
+ & (~DISABLE_RECENT);
+
+ /** Tag used for disabling of keyguard */
+ private static final String LOCK_TASK_TAG = "Lock-to-App";
+
+ private final IBinder mToken = new Binder();
+ private final ActivityStackSupervisor mSupervisor;
+ private final Context mContext;
+
+ // The following system services cannot be final, because they do not exist when this class
+ // is instantiated during device boot
+ @VisibleForTesting
+ IStatusBarService mStatusBarService;
+ @VisibleForTesting
+ IDevicePolicyManager mDevicePolicyManager;
+ @VisibleForTesting
+ WindowManagerService mWindowManager;
+ @VisibleForTesting
+ LockPatternUtils mLockPatternUtils;
+
+ /**
+ * Helper that is responsible for showing the right toast when a disallowed activity operation
+ * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
+ * fully locked mode we only show that unlocking is blocked.
+ */
+ @VisibleForTesting
+ LockTaskNotify mLockTaskNotify;
+
+ /**
+ * The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
+ * may be finished until there is only one entry left. If this is empty the system is not
+ * in lockTask mode.
+ */
+ private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
+
+ /**
+ * Store the current lock task mode. Possible values:
+ * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
+ * {@link ActivityManager#LOCK_TASK_MODE_PINNED}
+ */
+ private int mLockTaskModeState;
+
+ /**
+ * This is ActivityStackSupervisor's Handler.
+ */
+ private final Handler mHandler;
+
+ LockTaskController(Context context, ActivityStackSupervisor supervisor,
+ Handler handler) {
+ mContext = context;
+ mSupervisor = supervisor;
+ mHandler = handler;
+ }
+
+ /**
+ * Set the window manager instance used in this class. This is necessary, because the window
+ * manager does not exist during instantiation of this class.
+ */
+ void setWindowManager(WindowManagerService windowManager) {
+ mWindowManager = windowManager;
+ }
+
+ /**
+ * @return the current lock task state. This can be any of
+ * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
+ * {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
+ */
+ int getLockTaskModeState() {
+ return mLockTaskModeState;
+ }
+
+ /**
+ * @return whether the given task can be moved to the back of the stack. Locked tasks cannot be
+ * moved to the back of the stack.
+ */
+ boolean checkLockedTask(TaskRecord task) {
+ if (mLockTaskModeTasks.contains(task)) {
+ showLockTaskToast();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return whether the given activity is blocked from finishing, because it is the root activity
+ * of the last locked task and finishing it would mean that lock task mode is ended illegally.
+ */
+ boolean activityBlockedFromFinish(ActivityRecord activity) {
+ TaskRecord task = activity.getTask();
+ if (activity == task.getRootActivity()
+ && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
+ && mLockTaskModeTasks.size() == 1
+ && mLockTaskModeTasks.contains(task)) {
+ Slog.i(TAG, "Not finishing task in lock task mode");
+ showLockTaskToast();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return whether the requested task is allowed to be launched.
+ */
+ boolean isLockTaskModeViolation(TaskRecord task) {
+ return isLockTaskModeViolation(task, false);
+ }
+
+ /**
+ * @param isNewClearTask whether the task would be cleared as part of the operation.
+ * @return whether the requested task is allowed to be launched.
+ */
+ boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
+ if (isLockTaskModeViolationInternal(task, isNewClearTask)) {
+ showLockTaskToast();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) {
+ // TODO: Double check what's going on here. If the task is already in lock task mode, it's
+ // likely whitelisted, so will return false below.
+ if (getLockedTask() == task && !isNewClearTask) {
+ // If the task is already at the top and won't be cleared, then allow the operation
+ return false;
+ }
+ final int lockTaskAuth = task.mLockTaskAuth;
+ switch (lockTaskAuth) {
+ case LOCK_TASK_AUTH_DONT_LOCK:
+ return !mLockTaskModeTasks.isEmpty();
+ case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
+ case LOCK_TASK_AUTH_LAUNCHABLE:
+ case LOCK_TASK_AUTH_WHITELISTED:
+ return false;
+ case LOCK_TASK_AUTH_PINNABLE:
+ // Pinnable tasks can't be launched on top of locktask tasks.
+ return !mLockTaskModeTasks.isEmpty();
+ default:
+ Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
+ return true;
+ }
+ }
+
+ /**
+ * Stop the current lock task mode.
+ *
+ * @param isSystemInitiated indicates whether this request was initiated by the system via
+ * {@link ActivityManagerService#stopSystemLockTaskMode()}.
+ * @param callingUid the caller that requested the end of lock task mode.
+ * @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if
+ * they differ from the one that launched lock task mode.
+ */
+ void stopLockTaskMode(boolean isSystemInitiated, int callingUid) {
+ final TaskRecord lockTask = getLockedTask();
+ if (lockTask == null || mLockTaskModeState == LOCK_TASK_MODE_NONE) {
+ // Our work here is done.
+ return;
+ }
+
+ if (isSystemInitiated && mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
+ // As system can only start app pinning, we also only let it unlock in this mode.
+ showLockTaskToast();
+ return;
+ }
+
+ // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
+ // It is possible lockTaskMode was started by the system process because
+ // android:lockTaskMode is set to a locking value in the application manifest
+ // instead of the app calling startLockTaskMode. In this case
+ // {@link TaskRecord.mLockTaskUid} will be 0, so we compare the callingUid to the
+ // {@link TaskRecord.effectiveUid} instead. Also caller with
+ // {@link MANAGE_ACTIVITY_STACKS} can stop any lock task.
+ if (!isSystemInitiated && callingUid != lockTask.mLockTaskUid
+ && (lockTask.mLockTaskUid != 0 || callingUid != lockTask.effectiveUid)) {
+ throw new SecurityException("Invalid uid, expected " + lockTask.mLockTaskUid
+ + " callingUid=" + callingUid + " effectiveUid=" + lockTask.effectiveUid);
+ }
+
+ clearLockTaskMode("stopLockTask");
+ }
+
+ /**
+ * Remove the given task from the locked task list. If this was the last task in the list,
+ * lock task mode is stopped.
+ */
+ void removeLockedTask(final TaskRecord task) {
+ if (!mLockTaskModeTasks.remove(task)) {
+ return;
+ }
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "removeLockedTask: removed " + task);
+ if (mLockTaskModeTasks.isEmpty()) {
+ // Last one.
+ if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
+ " last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
+ mHandler.post(() -> performStopLockTask(task.userId));
+ }
+ }
+
+ /**
+ * Remove the topmost task from the locked task list. If this is the last task in the list, it
+ * will result in the end of locked task mode.
+ */
+ void clearLockTaskMode(String reason) {
+ // Take out of lock task mode if necessary
+ final TaskRecord lockedTask = getLockedTask();
+ if (lockedTask != null) {
+ removeLockedTask(lockedTask);
+ if (!mLockTaskModeTasks.isEmpty()) {
+ // There are locked tasks remaining, can only finish this task, not unlock it.
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
+ "setLockTaskMode: Tasks remaining, can't unlock");
+ lockedTask.performClearTaskLocked();
+ mSupervisor.resumeFocusedStackTopActivityLocked();
+ return;
+ }
+ }
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
+ "setLockTaskMode: No tasks to unlock. Callers=" + Debug.getCallers(4));
+ }
+
+ // This method should only be called on the handler thread
+ private void performStopLockTask(int userId) {
+ // When lock task ends, we enable the status bars.
+ try {
+ if (getStatusBarService() != null) {
+ getStatusBarService().disable(DISABLE_NONE, mToken,
+ mContext.getPackageName());
+ }
+ mWindowManager.reenableKeyguard(mToken);
+ if (getDevicePolicyManager() != null) {
+ getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
+ }
+ getLockTaskNotify().show(false);
+ try {
+ boolean shouldLockKeyguard = Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ LOCK_TO_APP_EXIT_LOCKED) != 0;
+ if (mLockTaskModeState == LOCK_TASK_MODE_PINNED && shouldLockKeyguard) {
+ mWindowManager.lockNow(null);
+ mWindowManager.dismissKeyguard(null /* callback */);
+ getLockPatternUtils().requireCredentialEntry(USER_ALL);
+ }
+ } catch (Settings.SettingNotFoundException e) {
+ // No setting, don't lock.
+ }
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ } finally {
+ mLockTaskModeState = LOCK_TASK_MODE_NONE;
+ }
+ }
+
+ /**
+ * Show the lock task violation toast.
+ */
+ void showLockTaskToast() {
+ mHandler.post(() -> getLockTaskNotify().showToast(mLockTaskModeState));
+ }
+
+ // Starting lock task
+
+ /**
+ * Method to start lock task mode on a given task.
+ *
+ * @param task the task that should be locked.
+ * @param isSystemInitiated indicates whether this request was initiated by the system via
+ * {@link ActivityManagerService#startSystemLockTaskMode(int)}.
+ * @param callingUid the caller that requested the launch of lock task mode.
+ */
+ void startLockTaskMode(@NonNull TaskRecord task, boolean isSystemInitiated,
+ int callingUid) {
+ if (!isSystemInitiated) {
+ task.mLockTaskUid = callingUid;
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
+ // startLockTask() called by app, but app is not part of lock task whitelist. Show
+ // app pinning request. We will come back here with isSystemInitiated true.
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
+ StatusBarManagerInternal statusBarManager = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ if (statusBarManager != null) {
+ statusBarManager.showScreenPinningRequest(task.taskId);
+ }
+ return;
+ }
+ }
+
+ // System can only initiate screen pinning, not full lock task mode
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, isSystemInitiated ? "Locking pinned" : "Locking fully");
+ setLockTaskMode(task, isSystemInitiated ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
+ "startLockTask", true);
+ }
+
+ /**
+ * Start lock task mode on the given task.
+ * @param lockTaskModeState whether fully locked or pinned mode.
+ * @param andResume whether the task should be brought to foreground as part of the operation.
+ */
+ private void setLockTaskMode(@NonNull TaskRecord task, int lockTaskModeState,
+ String reason, boolean andResume) {
+ // Should have already been checked, but do it again.
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
+ "setLockTaskMode: Can't lock due to auth");
+ return;
+ }
+ if (isLockTaskModeViolation(task)) {
+ Slog.e(TAG_LOCKTASK, "setLockTaskMode: Attempt to start an unauthorized lock task.");
+ return;
+ }
+
+ if (mLockTaskModeTasks.isEmpty()) {
+ // Start lock task on the handler thread
+ mHandler.post(() -> performStartLockTask(
+ task.intent.getComponent().getPackageName(),
+ task.userId,
+ lockTaskModeState));
+ }
+
+ // Add it or move it to the top.
+ if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task +
+ " Callers=" + Debug.getCallers(4));
+ mLockTaskModeTasks.remove(task);
+ mLockTaskModeTasks.add(task);
+
+ if (task.mLockTaskUid == -1) {
+ task.mLockTaskUid = task.effectiveUid;
+ }
+
+ if (andResume) {
+ mSupervisor.findTaskToMoveToFrontLocked(task, 0, null, reason,
+ lockTaskModeState != LOCK_TASK_MODE_NONE);
+ mSupervisor.resumeFocusedStackTopActivityLocked();
+ mWindowManager.executeAppTransition();
+ } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
+ mSupervisor.handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY,
+ task.getStackId(), true /* forceNonResizable */);
+ }
+ }
+
+ // This method should only be called on the handler thread
+ private void performStartLockTask(String packageName, int userId, int lockTaskModeState) {
+ // When lock task starts, we disable the status bars.
+ try {
+ getLockTaskNotify().show(true);
+ mLockTaskModeState = lockTaskModeState;
+ if (getStatusBarService() != null) {
+ int flags = 0;
+ if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
+ flags = STATUS_BAR_MASK_LOCKED;
+ } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+ flags = STATUS_BAR_MASK_PINNED;
+ }
+ getStatusBarService().disable(flags, mToken, mContext.getPackageName());
+ }
+ mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+ if (getDevicePolicyManager() != null) {
+ getDevicePolicyManager().notifyLockTaskModeChanged(true, packageName, userId);
+ }
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Called when the list of packages whitelisted for lock task mode is changed. Any currently
+ * locked tasks that got removed from the whitelist will be finished.
+ */
+ // TODO: Missing unit tests
+ void onLockTaskPackagesUpdated() {
+ boolean didSomething = false;
+ for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
+ final boolean wasWhitelisted =
+ (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) ||
+ (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED);
+ lockedTask.setLockTaskAuth();
+ final boolean isWhitelisted =
+ (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) ||
+ (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED);
+ if (wasWhitelisted && !isWhitelisted) {
+ // Lost whitelisting authorization. End it now.
+ if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
+ lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
+ removeLockedTask(lockedTask);
+ lockedTask.performClearTaskLocked();
+ didSomething = true;
+ }
+ }
+
+ for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
+ ArrayList<ActivityStack> stacks = mSupervisor.getChildAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
+ stack.onLockTaskPackagesUpdatedLocked();
+ }
+ }
+ final ActivityRecord r = mSupervisor.topRunningActivityLocked();
+ final TaskRecord task = r != null ? r.getTask() : null;
+ if (mLockTaskModeTasks.isEmpty() && task != null
+ && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
+ // This task must have just been authorized.
+ if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
+ "onLockTaskPackagesUpdated: starting new locktask task=" + task);
+ setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated",
+ false);
+ didSomething = true;
+ }
+ if (didSomething) {
+ mSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+ }
+
+ /**
+ * @return the topmost locked task
+ */
+ private TaskRecord getLockedTask() {
+ final int top = mLockTaskModeTasks.size() - 1;
+ if (top >= 0) {
+ return mLockTaskModeTasks.get(top);
+ }
+ return null;
+ }
+
+ // Should only be called on the handler thread
+ @Nullable
+ private IStatusBarService getStatusBarService() {
+ if (mStatusBarService == null) {
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.checkService(STATUS_BAR_SERVICE));
+ if (mStatusBarService == null) {
+ Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
+ }
+ }
+ return mStatusBarService;
+ }
+
+ // Should only be called on the handler thread
+ @Nullable
+ private IDevicePolicyManager getDevicePolicyManager() {
+ if (mDevicePolicyManager == null) {
+ mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.checkService(DEVICE_POLICY_SERVICE));
+ if (mDevicePolicyManager == null) {
+ Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE");
+ }
+ }
+ return mDevicePolicyManager;
+ }
+
+ @NonNull
+ private LockPatternUtils getLockPatternUtils() {
+ if (mLockPatternUtils == null) {
+ // We don't preserve the LPU object to save memory
+ return new LockPatternUtils(mContext);
+ }
+ return mLockPatternUtils;
+ }
+
+ // Should only be called on the handler thread
+ @NonNull
+ private LockTaskNotify getLockTaskNotify() {
+ if (mLockTaskNotify == null) {
+ mLockTaskNotify = new LockTaskNotify(mContext);
+ }
+ return mLockTaskNotify;
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mLockTaskModeState=" + lockTaskModeToString());
+ pw.println(" mLockTaskModeTasks" + mLockTaskModeTasks);
+ }
+
+ private String lockTaskModeToString() {
+ switch (mLockTaskModeState) {
+ case LOCK_TASK_MODE_LOCKED:
+ return "LOCKED";
+ case LOCK_TASK_MODE_PINNED:
+ return "PINNED";
+ case LOCK_TASK_MODE_NONE:
+ return "NONE";
+ default: return "unknown=" + mLockTaskModeState;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 88d894449185..dd29b9549acd 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -121,7 +121,7 @@ import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static java.lang.Integer.MAX_VALUE;
-final class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
+class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -469,7 +469,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
}
void removeWindowContainer() {
- mService.mStackSupervisor.removeLockedTaskLocked(this);
+ mService.mLockTaskController.removeLockedTask(this);
mWindowContainerController.removeContainer();
if (!StackId.persistTaskBounds(getStackId())) {
// Reset current bounds for task whose bounds shouldn't be persisted so it uses
@@ -1394,7 +1394,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
/**
* Completely remove all activities associated with an existing task.
*/
- final void performClearTaskLocked() {
+ void performClearTaskLocked() {
mReuseTask = true;
performClearTaskAtIndexLocked(0, !PAUSE_IMMEDIATELY);
mReuseTask = false;
@@ -1526,7 +1526,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
" mLockTaskAuth=" + lockTaskAuthToString());
}
- boolean isLockTaskWhitelistedLocked() {
+ private boolean isLockTaskWhitelistedLocked() {
String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
if (pkg == null) {
return false;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 7e9a2ac91127..29445673dfff 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -171,8 +171,6 @@ class UserController {
@GuardedBy("mLock")
private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
- private volatile UserManagerService mUserManager;
-
private final LockPatternUtils mLockPatternUtils;
UserController(ActivityManagerService service) {
@@ -831,8 +829,9 @@ class UserController {
}
if (foreground) {
- mInjector.getActivityStackSupervisor().setLockTaskModeLocked(
- null, ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false);
+ // TODO: I don't think this does what the caller think it does. Seems to only
+ // remove one locked task and won't work if multiple locked tasks are present.
+ mInjector.getLockTaskController().clearLockTaskMode("startUser");
}
final UserInfo userInfo = getUserInfo(userId);
@@ -1777,5 +1776,9 @@ class UserController {
ActivityStackSupervisor getActivityStackSupervisor() {
return mService.mStackSupervisor;
}
+
+ LockTaskController getLockTaskController() {
+ return mService.mLockTaskController;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
new file mode 100644
index 000000000000..e98e5bfb110e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.os.Process.SYSTEM_UID;
+import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED;
+import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.StatusBarManager;
+import android.app.admin.IDevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.WindowManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.verification.VerificationMode;
+
+/**
+ * Unit tests for {@link LockTaskController}.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.am.LockTaskControllerTest
+ */
+@Presubmit
+@SmallTest
+public class LockTaskControllerTest {
+ private static final String TEST_PACKAGE_NAME = "com.test.package";
+ private static final String TEST_CLASS_NAME = TEST_PACKAGE_NAME + ".TestClass";
+ private static final int TEST_USER_ID = 123;
+ private static final int TEST_UID = 10467;
+
+ @Mock private ActivityStackSupervisor mSupervisor;
+ @Mock private IDevicePolicyManager mDevicePolicyManager;
+ @Mock private IStatusBarService mStatusBarService;
+ @Mock private WindowManagerService mWindowManager;
+ @Mock private LockPatternUtils mLockPatternUtils;
+ @Mock private LockTaskNotify mLockTaskNotify;
+ @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+
+ private LockTaskController mLockTaskController;
+ private Context mContext;
+ private String mLockToAppSetting;
+
+ @Before
+ public void setUp() throws Exception {
+ // This property is used to allow mocking of package private classes with mockito
+ System.setProperty("dexmaker.share_classloader", "true");
+
+ MockitoAnnotations.initMocks(this);
+
+ mContext = InstrumentationRegistry.getTargetContext();
+ mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mLockTaskController = new LockTaskController(mContext, mSupervisor,
+ new ImmediatelyExecuteHandler());
+
+ mLockTaskController.setWindowManager(mWindowManager);
+ mLockTaskController.mStatusBarService = mStatusBarService;
+ mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
+ mLockTaskController.mLockPatternUtils = mLockPatternUtils;
+ mLockTaskController.mLockTaskNotify = mLockTaskNotify;
+
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
+ }
+
+ @Test
+ public void testPreconditions() {
+ // GIVEN nothing has happened
+
+ // THEN current lock task mode should be NONE
+ assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+ }
+
+ @Test
+ public void testStartLockTaskMode_once() throws Exception {
+ // GIVEN a task record with whitelisted auth
+ TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+
+ // WHEN calling setLockTaskMode for LOCKED mode without resuming
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+ // THEN the lock task mode state should be LOCKED
+ assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+ // THEN the task should be locked
+ assertTrue(mLockTaskController.checkLockedTask(tr));
+
+ // THEN lock task mode should be started
+ verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED);
+ }
+
+ @Test
+ public void testStartLockTaskMode_twice() throws Exception {
+ // GIVEN two task records with whitelisted auth
+ TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+
+ // WHEN calling setLockTaskMode for LOCKED mode on both tasks
+ mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+ mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+
+ // THEN the lock task mode state should be LOCKED
+ assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+ // THEN neither of the tasks should be able to move to back of stack
+ assertTrue(mLockTaskController.checkLockedTask(tr1));
+ assertTrue(mLockTaskController.checkLockedTask(tr2));
+
+ // THEN lock task mode should be started
+ verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED);
+ }
+
+ @Test
+ public void testStartLockTaskMode_pinningRequest() throws Exception {
+ // GIVEN a task record that is not whitelisted, i.e. with pinned auth
+ TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+
+ // WHEN calling startLockTaskMode
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+ // THEN a pinning request should be shown
+ verify(mStatusBarManagerInternal).showScreenPinningRequest(anyInt());
+ }
+
+ @Test
+ public void testStartLockTaskMode_pinnedBySystem() throws Exception {
+ // GIVEN a task record with pinned auth
+ TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+
+ // WHEN the system calls startLockTaskMode
+ mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
+
+ // THEN the lock task mode state should be PINNED
+ assertEquals(LOCK_TASK_MODE_PINNED, mLockTaskController.getLockTaskModeState());
+ // THEN the task should be locked
+ assertTrue(mLockTaskController.checkLockedTask(tr));
+
+ // THEN lock task mode should be started
+ verifyLockTaskStarted(STATUS_BAR_MASK_PINNED);
+ }
+
+ @Test
+ public void testLockTaskViolation() throws Exception {
+ // GIVEN one task records with whitelisted auth that is in lock task mode
+ TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+ // THEN it's not a lock task violation to try and launch this task without clearing
+ assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false));
+
+ // THEN it's a lock task violation to launch another task that is not whitelisted
+ assertTrue(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+ TaskRecord.LOCK_TASK_AUTH_PINNABLE)));
+ // THEN it's a lock task violation to launch another task that is disallowed from lock task
+ assertTrue(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+ TaskRecord.LOCK_TASK_AUTH_DONT_LOCK)));
+
+ // THEN it's no a lock task violation to launch another task that is whitelisted
+ assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+ TaskRecord.LOCK_TASK_AUTH_WHITELISTED)));
+ assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+ TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE)));
+ // THEN it's not a lock task violation to launch another task that is priv launchable
+ assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
+ TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV)));
+ }
+
+ @Test
+ public void testStopLockTaskMode() throws Exception {
+ // GIVEN one task record with whitelisted auth that is in lock task mode
+ TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+ // WHEN the same caller calls stopLockTaskMode
+ mLockTaskController.stopLockTaskMode(false, TEST_UID);
+
+ // THEN the lock task mode should be NONE
+ assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+ // THEN the task should no longer be locked
+ assertFalse(mLockTaskController.checkLockedTask(tr));
+ // THEN lock task mode should have been finished
+ verifyLockTaskStopped(times(1));
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testStopLockTaskMode_DifferentCaller() throws Exception {
+ // GIVEN one task record with whitelisted auth that is in lock task mode
+ TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+ // WHEN a different caller calls stopLockTaskMode
+ mLockTaskController.stopLockTaskMode(false, TEST_UID + 1);
+
+ // THEN security exception should be thrown, because different caller tried to unlock
+ }
+
+ @Test
+ public void testStopLockTaskMode_SystemCaller() throws Exception {
+ // GIVEN one task record with whitelisted auth that is in lock task mode
+ TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+ // WHEN system calls stopLockTaskMode
+ mLockTaskController.stopLockTaskMode(true, SYSTEM_UID);
+
+ // THEN a lock tash toast should be shown
+ verify(mLockTaskNotify).showToast(LOCK_TASK_MODE_LOCKED);
+ // THEN lock task mode should still be active
+ assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+ }
+
+ @Test
+ public void testStopLockTaskMode_twoTasks() throws Exception {
+ // GIVEN two task records with whitelisted auth that is in lock task mode
+ TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+ mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
+ mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
+
+ // WHEN calling stopLockTaskMode
+ mLockTaskController.stopLockTaskMode(false, TEST_UID);
+
+ // THEN the lock task mode should still be active
+ assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
+ // THEN the first task should still be locked
+ assertTrue(mLockTaskController.checkLockedTask(tr1));
+ // THEN the top task should no longer be locked
+ assertFalse(mLockTaskController.checkLockedTask(tr2));
+ // THEN lock task mode should not have been finished
+ verifyLockTaskStopped(never());
+ }
+
+ @Test
+ public void testStopLockTaskMode_pinned() throws Exception {
+ // GIVEN one task records that is in pinned mode
+ TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+ mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
+ // GIVEN that the keyguard is required to show after unlocking
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1);
+
+ // WHEN calling stopLockTask
+ mLockTaskController.stopLockTaskMode(true, SYSTEM_UID);
+
+ // THEN the lock task mode should no longer be active
+ assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
+ // THEN the task should no longer be locked
+ assertFalse(mLockTaskController.checkLockedTask(tr));
+ // THEN lock task mode should have been finished
+ verifyLockTaskStopped(times(1));
+ // THEN the keyguard should be shown
+ verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
+ }
+
+ private TaskRecord getTaskRecord(int lockTaskAuth) {
+ TaskRecord tr = mock(TaskRecord.class);
+ tr.mLockTaskAuth = lockTaskAuth;
+ tr.intent = new Intent()
+ .setComponent(new ComponentName(TEST_PACKAGE_NAME, TEST_CLASS_NAME));
+ tr.userId = TEST_USER_ID;
+ return tr;
+ }
+
+ private void verifyLockTaskStarted(int statusBarMask) throws Exception {
+ // THEN the keyguard should have been disabled
+ verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
+ // THEN the status bar should have been disabled
+ verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
+ eq(mContext.getPackageName()));
+ // THEN the DO/PO should be informed about the operation
+ verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
+ TEST_USER_ID);
+ }
+
+ private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
+ // THEN the keyguard should have been disabled
+ verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class));
+ // THEN the status bar should have been disabled
+ verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
+ any(IBinder.class), eq(mContext.getPackageName()));
+ // THEN the DO/PO should be informed about the operation
+ verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
+ }
+
+ /**
+ * Special handler implementation that executes any message / runnable posted immediately on the
+ * thread that it's posted on rather than enqueuing them on its looper.
+ */
+ private static class ImmediatelyExecuteHandler extends Handler {
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ if (msg.getCallback() != null) {
+ msg.getCallback().run();
+ }
+ return true;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 7a4746a7f54b..4475de5aa14e 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,7 +16,6 @@
package com.android.server.am;
-import android.app.ActivityManager;
import android.app.IUserSwitchObserver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -57,16 +56,15 @@ import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_M
import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
public class UserControllerTest extends AndroidTestCase {
@@ -117,11 +115,7 @@ public class UserControllerTest extends AndroidTestCase {
Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
- Mockito.verify(mInjector.getActivityStackSupervisor()).setLockTaskModeLocked(
- nullable(TaskRecord.class),
- eq(ActivityManager.LOCK_TASK_MODE_NONE),
- anyString(),
- anyBoolean());
+ Mockito.verify(mInjector.getLockTaskController()).clearLockTaskMode(anyString());
startForegroundUserAssertions();
}
@@ -131,11 +125,7 @@ public class UserControllerTest extends AndroidTestCase {
Mockito.verify(
mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
- Mockito.verify(mInjector.getActivityStackSupervisor(), never()).setLockTaskModeLocked(
- nullable(TaskRecord.class),
- eq(ActivityManager.LOCK_TASK_MODE_NONE),
- anyString(),
- anyBoolean());
+ verifyZeroInteractions(mInjector.getLockTaskController());
startBackgroundUserAssertions();
}
@@ -324,6 +314,7 @@ public class UserControllerTest extends AndroidTestCase {
UserManagerInternal userManagerInternalMock;
WindowManagerService windowManagerMock;
ActivityStackSupervisor activityStackSupervisor;
+ LockTaskController lockTaskController;
private Context mCtx;
List<Intent> sentIntents = new ArrayList<>();
@@ -337,6 +328,7 @@ public class UserControllerTest extends AndroidTestCase {
userManagerInternalMock = mock(UserManagerInternal.class);
windowManagerMock = mock(WindowManagerService.class);
activityStackSupervisor = mock(ActivityStackSupervisor.class);
+ lockTaskController = mock(LockTaskController.class);
}
@Override
@@ -399,6 +391,10 @@ public class UserControllerTest extends AndroidTestCase {
ActivityStackSupervisor getActivityStackSupervisor() {
return activityStackSupervisor;
}
+
+ LockTaskController getLockTaskController() {
+ return lockTaskController;
+ }
}
private static class TestHandler extends Handler {