Add Lock Task Mode.

When in lock task mode only the specified task may run. All
attempts to switch to another task are ignored until the task
finishes or a call to stopLockTaskMode() is made.

Change-Id: I6cfe92fe1bcf22cd47b5398c08e23c52a4092dda
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b2cf846..9315648 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -231,6 +231,10 @@
 
     InputManagerInternal mInputManagerInternal;
 
+    /** If non-null then the task specified remains in front and no other tasks may be started
+     * until the task exits or #stopLockTaskMode() is called. */
+    private TaskRecord mLockTaskModeTask;
+
     public ActivityStackSupervisor(ActivityManagerService service) {
         mService = service;
         PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
@@ -1505,6 +1509,10 @@
                         ? findTaskLocked(r)
                         : findActivityLocked(intent, r.info);
                 if (intentActivity != null) {
+                    if (isLockTaskModeViolation(intentActivity.task)) {
+                        Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
+                        return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+                    }
                     if (r.task == null) {
                         r.task = intentActivity.task;
                     }
@@ -1715,6 +1723,10 @@
         // Should this be considered a new task?
         if (r.resultTo == null && !addingToTask
                 && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+            if (isLockTaskModeViolation(reuseTask)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
             targetStack = adjustStackFocus(r);
             targetStack.moveToFront();
             if (reuseTask == null) {
@@ -1739,6 +1751,10 @@
             }
         } else if (sourceRecord != null) {
             TaskRecord sourceTask = sourceRecord.task;
+            if (isLockTaskModeViolation(sourceTask)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
             targetStack = sourceTask.stack;
             targetStack.moveToFront();
             if (!addingToTask &&
@@ -1782,6 +1798,10 @@
             // An existing activity is starting this new activity, so we want
             // to keep the new one in the same task as the one that is starting
             // it.
+            if (isLockTaskModeViolation(sourceTask)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
             r.setTask(sourceTask, sourceRecord.thumbHolder, false);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in existing task " + r.task + " from source " + sourceRecord);
@@ -2098,17 +2118,18 @@
         }
     }
 
-    void findTaskToMoveToFrontLocked(int taskId, int flags, Bundle options) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
-            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                if (stacks.get(stackNdx).findTaskToMoveToFrontLocked(taskId, flags, options)) {
-                    if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
-                            + stacks.get(stackNdx));
-                    return;
-                }
-            }
+    void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options) {
+        if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+            mUserLeaving = true;
         }
+        if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+            // Caller wants the home activity moved with it.  To accomplish this,
+            // we'll just indicate that this task returns to the home task.
+            task.mOnTopOfHome = true;
+        }
+        task.stack.moveTaskToFrontLocked(task, null, options);
+        if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
+                + task.stack);
     }
 
     ActivityStack getStack(int stackId) {
@@ -2288,6 +2309,7 @@
             }
         }
         checkReadyForSleepLocked();
+        setLockTaskModeLocked(null);
     }
 
     boolean shutdownLocked(int timeout) {
@@ -2872,6 +2894,35 @@
         return list;
     }
 
+    void setLockTaskModeLocked(TaskRecord task) {
+        if (task == null) {
+            // Take out of lock task mode.
+            mLockTaskModeTask = null;
+            return;
+        }
+        if (isLockTaskModeViolation(task)) {
+            Slog.e(TAG, "setLockTaskMode: Attempt to start a second Lock Task Mode task.");
+            return;
+        }
+        mLockTaskModeTask = task;
+        findTaskToMoveToFrontLocked(task, 0, null);
+        resumeTopActivitiesLocked();
+    }
+
+    boolean isLockTaskModeViolation(TaskRecord task) {
+        return mLockTaskModeTask != null && mLockTaskModeTask != task;
+    }
+
+    void endLockTaskModeIfTaskEnding(TaskRecord task) {
+        if (mLockTaskModeTask != null && mLockTaskModeTask == task) {
+            mLockTaskModeTask = null;
+        }
+    }
+
+    boolean isInLockTaskMode() {
+        return mLockTaskModeTask != null;
+    }
+
     private final class ActivityStackSupervisorHandler extends Handler {
 
         public ActivityStackSupervisorHandler(Looper looper) {