Redefine FLAG_ACTIVITY_NEW_DOCUMENT without NEW_TASK

Change definition of FLAG_ACTIVITY_NEW_DOCUMENT from
FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET to
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET alone.

Also add new documentLaunchMode of "never" to allow activity
writers to keep their activity from being launched in document mode.

Fixes bug 15468528.

Change-Id: Ied11adf97e85c5d3f99f4c0bbbb4a2905dcfb24e
diff --git a/api/current.txt b/api/current.txt
index 6cbcadc..bd9f990 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7533,12 +7533,12 @@
     field public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304; // 0x400000
     field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
     field public static final int FLAG_ACTIVITY_CLEAR_TOP = 67108864; // 0x4000000
-    field public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288; // 0x80000
+    field public static final deprecated int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288; // 0x80000
     field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
     field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
     field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
     field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
-    field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 268959744; // 0x10080000
+    field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
     field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
     field public static final int FLAG_ACTIVITY_NO_ANIMATION = 65536; // 0x10000
     field public static final int FLAG_ACTIVITY_NO_HISTORY = 1073741824; // 0x40000000
@@ -7989,6 +7989,7 @@
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int DOCUMENT_LAUNCH_ALWAYS = 2; // 0x2
     field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1
+    field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
     field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
     field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
     field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bd07470..186c996 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3712,30 +3712,8 @@
      */
     public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000;
     /**
-     * If set, this marks a point in the task's activity stack that should
-     * be cleared when the task is reset.  That is, the next time the task
-     * is brought to the foreground with
-     * {@link #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED} (typically as a result of
-     * the user re-launching it from home), this activity and all on top of
-     * it will be finished so that the user does not return to them, but
-     * instead returns to whatever activity preceeded it.
-     *
-     * <p>When this flag is assigned to the root activity all activities up
-     * to, but not including the root activity, will be cleared. This prevents
-     * this flag from being used to finish all activities in a task and thereby
-     * ending the task.
-     *
-     * <p>This is useful for cases where you have a logical break in your
-     * application.  For example, an e-mail application may have a command
-     * to view an attachment, which launches an image view activity to
-     * display it.  This activity should be part of the e-mail application's
-     * task, since it is a part of the task the user is involved in.  However,
-     * if the user leaves that task, and later selects the e-mail app from
-     * home, we may like them to return to the conversation they were
-     * viewing, not the picture attachment, since that is confusing.  By
-     * setting this flag when launching the image viewer, that viewer and
-     * any activities it starts will be removed the next time the user returns
-     * to mail.
+     * @deprecated As of API 21 this performs identically to
+     * {@link #FLAG_ACTIVITY_NEW_DOCUMENT} which should be used instead of this.
      */
     public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000;
     /**
@@ -3762,8 +3740,7 @@
      * @see android.R.attr#documentLaunchMode
      * @see #FLAG_ACTIVITY_MULTIPLE_TASK
      */
-    public static final int FLAG_ACTIVITY_NEW_DOCUMENT =
-            FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | FLAG_ACTIVITY_NEW_TASK;
+    public static final int FLAG_ACTIVITY_NEW_DOCUMENT = FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
     /**
      * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint}
      * callback from occurring on the current frontmost activity before it is
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index cfe4712..791e5aa 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -84,6 +84,11 @@
      */
     public static final int DOCUMENT_LAUNCH_ALWAYS = 2;
     /**
+     * Constant corresponding to <code>never</code> in
+     * the {@link android.R.attr#documentLaunchMode} attribute.
+     */
+    public static final int DOCUMENT_LAUNCH_NEVER = 3;
+    /**
      * The document launch mode style requested by the activity. From the
      * {@link android.R.attr#documentLaunchMode} attribute, one of
      * {@link #DOCUMENT_LAUNCH_NONE}, {@link #DOCUMENT_LAUNCH_INTO_EXISTING},
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3a0f767..814d8fc0 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -898,16 +898,22 @@
          android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} to every Intent used to launch
          the activity.
 
-         <p>The documentLaunchMode attribute may be assigned one of three values, "none",
-         "intoExisting" and "always", described in detail below. For values other than
-         <code>none</code> the activity must be defined with
-         {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>.
+         <p>The documentLaunchMode attribute may be assigned one of four values, "none",
+         "intoExisting", "always" and "never", described in detail below. For values other than
+         <code>none</code> and <code>never</code> the activity must be defined with
+         {@link android.R.attr#launchMode} <code>standard</code>.
          If this attribute is not specified, <code>none</code> will be used.
          Note that <code>none</code> can be overridden at run time if the Intent used
-         to launch it contains the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+         to launch it contains the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+         Intent.FLAG_ACTIVITY_NEW_DOCUMENT}.
          Similarly <code>intoExisting</code> will be overridden by the flag
-         {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} combined with
-         {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+         {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+         Intent.FLAG_ACTIVITY_NEW_DOCUMENT} combined with
+         {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+         Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. If the value of
+         documentLaunchModes is <code>never</code> then any use of
+.........{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+         Intent.FLAG_ACTIVITY_NEW_DOCUMENT} to launch this activity will be ignored. -->
     <attr name="documentLaunchMode">
         <!-- The default mode, which will create a new task only when
              {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
@@ -931,6 +937,13 @@
              and {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
              Intent.FLAG_ACTIVITY_MULTIPLE_TASK} both set. -->
         <enum name="always" value="2" />
+        <!-- This activity will not be launched into a new document even if the Intent contains
+             {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+             Intent.FLAG_ACTIVITY_NEW_DOCUMENT}. This gives the activity writer ultimate
+             control over how their activity is used. Note that applications prior to api
+             21 will default to documentLaunchMode="none" so only activities that explicitly
+             opt out with <code>"never"</code> may do so. -->
+        <enum name="never" value="3" />
     </attr>
 
     <!-- The maximum number of entries of tasks rooted at this activity in the recent task list.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0cc53d1..35f8f31 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1243,7 +1243,7 @@
             // to ensure that it is safe to do so.  If the upcoming activity will also
             // be part of the voice session, we can only launch it if it has explicitly
             // said it supports the VOICE category, or it is a part of the calling app.
-            if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
+            if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                     && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
                 try {
                     if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
@@ -1460,6 +1460,47 @@
 
         int launchFlags = intent.getFlags();
 
+        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
+                (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+                        r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK)) {
+            // 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\"");
+            launchFlags &=
+                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        } else {
+            switch (r.info.documentLaunchMode) {
+                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
+                    launchFlags &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+                    break;
+            }
+        }
+
+        if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+            // For whatever reason this activity is being launched into a new
+            // task...  yet the caller has requested a result back.  Well, that
+            // is pretty messed up, so instead immediately send back a cancel
+            // and let the new task continue launched as normal without a
+            // dependency on its originator.
+            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+            r.resultTo.task.stack.sendActivityResultLocked(-1,
+                    r.resultTo, r.resultWho, r.requestCode,
+                    Activity.RESULT_CANCELED, null);
+            r.resultTo = null;
+        }
+
+        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
+            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+        }
+
         // We'll invoke onUserLeaving before onPause only if the launching
         // activity did not explicitly state that this is an automated launch.
         mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
@@ -1490,20 +1531,6 @@
             }
         }
 
-        switch (r.info.documentLaunchMode) {
-            case ActivityInfo.DOCUMENT_LAUNCH_NONE:
-                break;
-            case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
-                intent.addFlags(
-                        Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-                launchFlags = intent.getFlags();
-                break;
-            case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
-                launchFlags = intent.getFlags();
-                break;
-        }
-        final boolean newDocument = intent.isDocument();
         if (sourceRecord == null) {
             // This activity is not being started from another...  in this
             // case we -always- start a new task.
@@ -1512,11 +1539,6 @@
                         "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
                 launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
             }
-        } else if (newDocument) {
-            if (r.launchMode != ActivityInfo.LAUNCH_MULTIPLE) {
-                Slog.w(TAG, "FLAG_ACTIVITY_NEW_DOCUMENT and launchMode != \"standard\"");
-                r.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-            }
         } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
             // The original activity who is starting us is running as a single
             // instance...  this new activity it is starting must go on its
@@ -1555,18 +1577,7 @@
             sourceStack = null;
         }
 
-        if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
-            // For whatever reason this activity is being launched into a new
-            // task...  yet the caller has requested a result back.  Well, that
-            // is pretty messed up, so instead immediately send back a cancel
-            // and let the new task continue launched as normal without a
-            // dependency on its originator.
-            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
-            r.resultTo.task.stack.sendActivityResultLocked(-1,
-                    r.resultTo, r.resultWho, r.requestCode,
-                Activity.RESULT_CANCELED, null);
-            r.resultTo = null;
-        }
+        intent.setFlags(launchFlags);
 
         boolean addingToTask = false;
         boolean movedHome = false;
@@ -1801,7 +1812,7 @@
 
         // Should this be considered a new task?
         if (r.resultTo == null && !addingToTask
-                && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+                && (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;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 1df230e..79e2d9d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -647,8 +647,9 @@
         final int numActivities = activities.size();
         for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
-            if (!r.isPersistable() || (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
-                    Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) {
+            if (!r.isPersistable() || (activityNdx > 0 &&
+                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0)) {
+                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
                 break;
             }
             out.startTag(null, TAG_ACTIVITY);