Add task affiliation

Introduces new flag Intent.FLAG_ACTIVITY_LAUNCH_BEHIND which
causes the newly launched task to affiliate with the launching task.
(Later this flag will also launch the task behind the current task).
This shows up in a new member of the RecentTaskInfo class. This also
causes the recents list returned by getRecentsInfo to be rearranged
so that affiliated tasks are together.

Fixes bug 16157517.

Change-Id: Ia1386af50da2f01809278b62d249f05c6a0de951
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f095c68..bc184c6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1531,11 +1531,12 @@
         final Intent intent = r.intent;
         final int callingUid = r.launchedFromUid;
 
-        int launchFlags = intent.getFlags();
+        final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+        final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
 
+        int launchFlags = intent.getFlags();
         if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
-                (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
-                        r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK)) {
+                (launchSingleInstance || launchSingleTask)) {
             // We have a conflict between the Intent and the Activity manifest, manifest wins.
             Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
                     "\"singleInstance\" or \"singleTask\"");
@@ -1556,6 +1557,9 @@
                     break;
             }
         }
+        final int launchBehindFlags = Intent.FLAG_ACTIVITY_LAUNCH_BEHIND |
+                Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+        final boolean affiliateTask = (launchFlags & launchBehindFlags) == launchBehindFlags;
 
         if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
             // For whatever reason this activity is being launched into a new
@@ -1617,8 +1621,7 @@
             // instance...  this new activity it is starting must go on its
             // own task.
             launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
-        } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
-                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+        } else if (launchSingleInstance || launchSingleTask) {
             // The activity being started is a single instance...  it always
             // gets launched into its own task.
             launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -1658,8 +1661,7 @@
         ActivityStack targetStack;
         if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
                 (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
-                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
-                || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                || launchSingleInstance || launchSingleTask) {
             // If bring to front is requested, and no result is requested, and
             // we can find a task that was started with this same
             // component, then instead of launching bring that one to the front.
@@ -1668,9 +1670,8 @@
                 // a SINGLE_INSTANCE activity, there can be one and only one
                 // instance of it in the history, and it is always in its own
                 // unique task, so we do a special search.
-                ActivityRecord intentActivity = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
-                        ? findTaskLocked(r)
-                        : findActivityLocked(intent, r.info);
+                ActivityRecord intentActivity = !launchSingleInstance ?
+                        findTaskLocked(r) : findActivityLocked(intent, r.info);
                 if (intentActivity != null) {
                     if (isLockTaskModeViolation(intentActivity.task)) {
                         showLockTaskToast();
@@ -1708,6 +1709,9 @@
                                 sourceStack.topActivity().task == sourceRecord.task)) {
                             // We really do want to push this one into the
                             // user's face, right now.
+                            if (affiliateTask && sourceRecord != null) {
+                                intentActivity.setTaskToAffiliateWith(sourceRecord.task);
+                            }
                             movedHome = true;
                             targetStack.moveTaskToFrontLocked(intentActivity.task, r, options);
                             if ((launchFlags &
@@ -1746,8 +1750,7 @@
                         reuseTask.performClearTaskLocked();
                         reuseTask.setIntent(r.intent, r.info);
                     } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
-                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
-                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+                            || launchSingleInstance || launchSingleTask) {
                         // In this situation we want to remove all activities
                         // from the task up to the one being started.  In most
                         // cases this means we are resetting the task to its
@@ -1848,8 +1851,7 @@
                 if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                     if (top.app != null && top.app.thread != null) {
                         if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
-                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
-                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
+                            || launchSingleInstance || launchSingleTask) {
                             ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top,
                                     top.task);
                             // For paranoia, make sure we have correctly
@@ -1884,6 +1886,9 @@
         boolean newTask = false;
         boolean keepCurTransition = false;
 
+        TaskRecord taskToAffiliate = affiliateTask && sourceRecord != null ?
+                sourceRecord.task : null;
+
         // Should this be considered a new task?
         if (r.resultTo == null && !addingToTask
                 && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
@@ -1898,11 +1903,11 @@
                 r.setTask(targetStack.createTaskRecord(getNextTaskId(),
                         newTaskInfo != null ? newTaskInfo : r.info,
                         newTaskIntent != null ? newTaskIntent : intent,
-                        voiceSession, voiceInteractor, true), true);
+                        voiceSession, voiceInteractor, true), taskToAffiliate);
                 if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
                         r.task);
             } else {
-                r.setTask(reuseTask, true);
+                r.setTask(reuseTask, taskToAffiliate);
             }
             if (!movedHome) {
                 if ((launchFlags &
@@ -1914,7 +1919,7 @@
                 }
             }
         } else if (sourceRecord != null) {
-            TaskRecord sourceTask = sourceRecord.task;
+            final 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;
@@ -1922,8 +1927,7 @@
             targetStack = sourceTask.stack;
             targetStack.moveToFront();
             mWindowManager.moveTaskToTop(targetStack.topTask().taskId);
-            if (!addingToTask &&
-                    (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+            if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
                 // In this case, we are adding the activity to an existing
                 // task, but the caller has asked to clear that task if the
                 // activity is already running.
@@ -1963,7 +1967,7 @@
             // 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.
-            r.setTask(sourceTask, false);
+            r.setTask(sourceTask, null);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in existing task " + r.task + " from source " + sourceRecord);
 
@@ -1975,7 +1979,7 @@
             targetStack.moveToFront();
             ActivityRecord prev = targetStack.topActivity();
             r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(),
-                            r.info, intent, null, null, true), true);
+                            r.info, intent, null, null, true), null);
             mWindowManager.moveTaskToTop(r.task.taskId);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in new guessed " + r.task);