summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Benjamin Franz <bfranz@google.com> 2017-07-03 16:34:14 +0100
committer Benjamin Franz <bfranz@google.com> 2017-08-25 14:54:42 +0100
commita83859ffaa4d339729cbc1a8c114b73c6afedb00 (patch)
tree4ad7b36a0f35ffe34b1f2d2eedcde78b956c7009
parentb1671e0e1affc085e0e196e79ee1185d03a5ae91 (diff)
Refactor all lock task related methods into a new class
This simplifies the way lock task mode is called by creating a class that is fully responsible for managing locked tasks. This enables unit testing and makes it easier to see where individual calls are coming from. Feeling is that there are some subtle bugs around synchronization and expectations on the locked task APIs. Also added unit tests for the new manager class. Test: bit FrameworksServicesTests:com.android.server.am.LockTaskManagerTest Test: go/wm-smoke Test: cts-tradefed run cts -m DevicePolicyManager --test com.android.cts.devicepolicy.DeviceOwnerTest#testLockTask_deviceOwnerUser Bug: 63909481 Change-Id: I3fa8606778fea5a801c21aebeb231db5a6bee47f
-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 {