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);