summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2010-11-22 15:59:56 -0800
committerDianne Hackborn <hackbod@google.com>2010-11-22 18:35:55 -0800
commit621e17de87f18003aba2dedb719a2941020a7902 (patch)
tree978b402ced5bd03d3b4f6eaa9fbaaf186427823c
parent703c5f39c58168829e8d8f7ed7b5aea3f4fb600b (diff)
downloadframeworks_base-621e17de87f18003aba2dedb719a2941020a7902.zip
frameworks_base-621e17de87f18003aba2dedb719a2941020a7902.tar.gz
frameworks_base-621e17de87f18003aba2dedb719a2941020a7902.tar.bz2
Implement issue #3221502: New APIs to support new back stack / task navigation
What this adds: - A new Intent activity flag to completely replace an existing task. - A new Intent activity flag to bring the current home task up behind a new task being started/brought to the foreground. - New versions of startActivity() that take an array of Intents to be started, allowing applications to start a task in a specific state. - A public moveTaskToFront() method on ActivityManager, with a new flag that allows the caller to have the task moved to the front with the current home task immediately behind it. Change-Id: Ie8028d09acffb5349d98043c67676daba09f75c8
-rw-r--r--api/current.xml106
-rw-r--r--core/java/android/app/Activity.java26
-rw-r--r--core/java/android/app/ActivityManager.java26
-rw-r--r--core/java/android/app/ActivityManagerNative.java91
-rw-r--r--core/java/android/app/ContextImpl.java13
-rw-r--r--core/java/android/app/IActivityManager.java15
-rw-r--r--core/java/android/app/Instrumentation.java42
-rw-r--r--core/java/android/app/PendingIntent.java68
-rw-r--r--core/java/android/content/Context.java22
-rw-r--r--core/java/android/content/ContextWrapper.java5
-rw-r--r--core/java/android/content/Intent.java24
-rw-r--r--core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java2
-rw-r--r--docs/html/guide/topics/fundamentals.jd55
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java10
-rw-r--r--policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java11
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java122
-rw-r--r--services/java/com/android/server/am/ActivityStack.java275
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java39
-rw-r--r--services/java/com/android/server/am/TaskRecord.java10
-rw-r--r--test-runner/src/android/test/mock/MockContext.java5
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java2
22 files changed, 819 insertions, 161 deletions
diff --git a/api/current.xml b/api/current.xml
index 4025f3c..4400314 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -23927,6 +23927,21 @@
<parameter name="packageName" type="java.lang.String">
</parameter>
</method>
+<method name="moveTaskToFront"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="taskId" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="restartPackage"
return="void"
abstract="false"
@@ -23940,6 +23955,17 @@
<parameter name="packageName" type="java.lang.String">
</parameter>
</method>
+<field name="MOVE_TASK_WITH_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="RECENT_WITH_EXCLUDED"
type="int"
transient="false"
@@ -33066,6 +33092,25 @@
visibility="public"
>
</method>
+<method name="getActivities"
+ return="android.app.PendingIntent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="getActivity"
return="android.app.PendingIntent"
abstract="false"
@@ -47145,6 +47190,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="startActivities"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
<method name="startActivity"
return="void"
abstract="true"
@@ -48602,6 +48660,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="startActivities"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
<method name="startActivity"
return="void"
abstract="false"
@@ -52554,6 +52625,17 @@
visibility="public"
>
</field>
+<field name="FLAG_ACTIVITY_CLEAR_TASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32768"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_ACTIVITY_CLEAR_TOP"
type="int"
transient="false"
@@ -52708,6 +52790,17 @@
visibility="public"
>
</field>
+<field name="FLAG_ACTIVITY_TASK_ON_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16384"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_DEBUG_LOG_RESOLUTION"
type="int"
transient="false"
@@ -174471,6 +174564,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="startActivities"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intents" type="android.content.Intent[]">
+</parameter>
+</method>
<method name="startActivity"
return="void"
abstract="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5174f19..d69a179 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3135,6 +3135,30 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Launch a new activity. You will not receive any information about when
+ * the activity exits. This implementation overrides the base version,
+ * providing information about
+ * the activity performing the launch. Because of this additional
+ * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not
+ * required; if not specified, the new activity will be added to the
+ * task of the caller.
+ *
+ * <p>This method throws {@link android.content.ActivityNotFoundException}
+ * if there was no Activity found to run the given Intent.
+ *
+ * @param intents The intents to start.
+ *
+ * @throws android.content.ActivityNotFoundException
+ *
+ * @see #startActivityForResult
+ */
+ @Override
+ public void startActivities(Intent[] intents) {
+ mInstrumentation.execStartActivities(this, mMainThread.getApplicationThread(),
+ mToken, this, intents);
+ }
+
+ /**
* Like {@link #startActivity(Intent)}, but taking a IntentSender
* to start; see
* {@link #startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}
@@ -3616,7 +3640,7 @@ public class Activity extends ContextThemeWrapper
ActivityManagerNative.getDefault().getIntentSender(
IActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
mParent == null ? mToken : mParent.mToken,
- mEmbeddedID, requestCode, data, null, flags);
+ mEmbeddedID, requestCode, new Intent[] { data }, null, flags);
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
// Empty
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fe1e7d7..e168034 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -334,6 +334,32 @@ public class ActivityManager {
}
/**
+ * Flag for {@link #moveTaskToFront(int, int)}: also move the "home"
+ * activity along with the task, so it is positioned immediately behind
+ * the task.
+ */
+ public static final int MOVE_TASK_WITH_HOME = 0x00000001;
+
+ /**
+ * Ask that the task associated with a given task ID be moved to the
+ * front of the stack, so it is now visible to the user. Requires that
+ * the caller hold permission {@link android.Manifest.permission#REORDER_TASKS}
+ * or a SecurityException will be thrown.
+ *
+ * @param taskId The identifier of the task to be moved, as found in
+ * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
+ * @param flags Additional operational flags, 0 or more of
+ * {@link #MOVE_TASK_WITH_HOME}.
+ */
+ public void moveTaskToFront(int taskId, int flags) {
+ try {
+ ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ }
+ }
+
+ /**
* Information you can retrieve about a particular Service that is
* currently running in the system.
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 8cc6428..273e3c6 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -492,7 +492,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case MOVE_TASK_TO_FRONT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int task = data.readInt();
- moveTaskToFront(task);
+ int fl = data.readInt();
+ moveTaskToFront(task, fl);
reply.writeNoException();
return true;
}
@@ -791,13 +792,19 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
IBinder token = data.readStrongBinder();
String resultWho = data.readString();
int requestCode = data.readInt();
- Intent requestIntent = data.readInt() != 0
- ? Intent.CREATOR.createFromParcel(data) : null;
- String requestResolvedType = data.readString();
+ Intent[] requestIntents;
+ String[] requestResolvedTypes;
+ if (data.readInt() != 0) {
+ requestIntents = data.createTypedArray(Intent.CREATOR);
+ requestResolvedTypes = data.createStringArray();
+ } else {
+ requestIntents = null;
+ requestResolvedTypes = null;
+ }
int fl = data.readInt();
IIntentSender res = getIntentSender(type, packageName, token,
- resultWho, requestCode, requestIntent,
- requestResolvedType, fl);
+ resultWho, requestCode, requestIntents,
+ requestResolvedTypes, fl);
reply.writeNoException();
reply.writeStrongBinder(res != null ? res.asBinder() : null);
return true;
@@ -1355,6 +1362,33 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case START_ACTIVITIES_IN_PACKAGE_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ int uid = data.readInt();
+ Intent[] intents = data.createTypedArray(Intent.CREATOR);
+ String[] resolvedTypes = data.createStringArray();
+ IBinder resultTo = data.readStrongBinder();
+ int result = startActivitiesInPackage(uid, intents, resolvedTypes, resultTo);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
+ case START_ACTIVITIES_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ Intent[] intents = data.createTypedArray(Intent.CREATOR);
+ String[] resolvedTypes = data.createStringArray();
+ IBinder resultTo = data.readStrongBinder();
+ int result = startActivities(app, intents, resolvedTypes, resultTo);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
}
return super.onTransact(code, data, reply, flags);
@@ -1829,12 +1863,13 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return list;
}
- public void moveTaskToFront(int task) throws RemoteException
+ public void moveTaskToFront(int task, int flags) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(task);
+ data.writeInt(flags);
mRemote.transact(MOVE_TASK_TO_FRONT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -2283,7 +2318,7 @@ class ActivityManagerProxy implements IActivityManager
}
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags)
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags)
throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2293,13 +2328,13 @@ class ActivityManagerProxy implements IActivityManager
data.writeStrongBinder(token);
data.writeString(resultWho);
data.writeInt(requestCode);
- if (intent != null) {
+ if (intents != null) {
data.writeInt(1);
- intent.writeToParcel(data, 0);
+ data.writeTypedArray(intents, 0);
+ data.writeStringArray(resolvedTypes);
} else {
data.writeInt(0);
}
- data.writeString(resolvedType);
data.writeInt(flags);
mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
reply.readException();
@@ -3026,5 +3061,39 @@ class ActivityManagerProxy implements IActivityManager
return res;
}
+ public int startActivities(IApplicationThread caller,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ data.writeTypedArray(intents, 0);
+ data.writeStringArray(resolvedTypes);
+ data.writeStrongBinder(resultTo);
+ mRemote.transact(START_ACTIVITIES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
+ public int startActivitiesInPackage(int uid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(uid);
+ data.writeTypedArray(intents, 0);
+ data.writeStringArray(resolvedTypes);
+ data.writeStrongBinder(resultTo);
+ mRemote.transact(START_ACTIVITIES_IN_PACKAGE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 82b3f75..d2de382 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -844,6 +844,19 @@ class ContextImpl extends Context {
}
@Override
+ public void startActivities(Intent[] intents) {
+ if ((intents[0].getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ throw new AndroidRuntimeException(
+ "Calling startActivities() from outside of an Activity "
+ + " context requires the FLAG_ACTIVITY_NEW_TASK flag on first Intent."
+ + " Is this really what you want?");
+ }
+ mMainThread.getInstrumentation().execStartActivities(
+ getOuterContext(), mMainThread.getApplicationThread(), null,
+ (Activity)null, intents);
+ }
+
+ @Override
public void startIntentSender(IntentSender intent,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
throws IntentSender.SendIntentException {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c9d5448..dc18083 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -134,7 +134,7 @@ public interface IActivityManager extends IInterface {
public List getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
throws RemoteException;
- public void moveTaskToFront(int task) throws RemoteException;
+ public void moveTaskToFront(int task, int flags) throws RemoteException;
public void moveTaskToBack(int task) throws RemoteException;
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
@@ -199,7 +199,8 @@ public interface IActivityManager extends IInterface {
public static final int INTENT_SENDER_SERVICE = 4;
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags) throws RemoteException;
+ int requestCode, Intent[] intents, String[] resolvedTypes,
+ int flags) throws RemoteException;
public void cancelIntentSender(IIntentSender sender) throws RemoteException;
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer) throws RemoteException;
@@ -208,7 +209,8 @@ public interface IActivityManager extends IInterface {
public void setProcessLimit(int max) throws RemoteException;
public int getProcessLimit() throws RemoteException;
- public void setProcessForeground(IBinder token, int pid, boolean isForeground) throws RemoteException;
+ public void setProcessForeground(IBinder token, int pid,
+ boolean isForeground) throws RemoteException;
public int checkPermission(String permission, int pid, int uid)
throws RemoteException;
@@ -332,6 +334,11 @@ public interface IActivityManager extends IInterface {
public boolean dumpHeap(String process, boolean managed, String path,
ParcelFileDescriptor fd) throws RemoteException;
+ public int startActivities(IApplicationThread caller,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
+ public int startActivitiesInPackage(int uid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -544,4 +551,6 @@ public interface IActivityManager extends IInterface {
int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
int CHECK_GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+119;
+ int START_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+120;
+ int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 1deb9fb..ad811d8 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1354,8 +1354,8 @@ public class Instrumentation {
* {@hide}
*/
public ActivityResult execStartActivity(
- Context who, IBinder contextThread, IBinder token, Activity target,
- Intent intent, int requestCode) {
+ Context who, IBinder contextThread, IBinder token, Activity target,
+ Intent intent, int requestCode) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
synchronized (mSync) {
@@ -1386,6 +1386,44 @@ public class Instrumentation {
/**
* Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
+ * but accepts an array of activities to be started. Note that active
+ * {@link ActivityMonitor} objects only match against the first activity in
+ * the array.
+ *
+ * {@hide}
+ */
+ public void execStartActivities(Context who, IBinder contextThread,
+ IBinder token, Activity target, Intent[] intents) {
+ IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (mActivityMonitors != null) {
+ synchronized (mSync) {
+ final int N = mActivityMonitors.size();
+ for (int i=0; i<N; i++) {
+ final ActivityMonitor am = mActivityMonitors.get(i);
+ if (am.match(who, null, intents[0])) {
+ am.mHits++;
+ if (am.isBlocking()) {
+ return;
+ }
+ break;
+ }
+ }
+ }
+ }
+ try {
+ String[] resolvedTypes = new String[intents.length];
+ for (int i=0; i<intents.length; i++) {
+ resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
+ }
+ int result = ActivityManagerNative.getDefault()
+ .startActivities(whoThread, intents, resolvedTypes, token);
+ checkStartActivityResult(result, intents[0]);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
* but for calls from a {#link Fragment}.
*
* @param who The Context from which the activity is being started.
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index be1dc4a..5b43b65 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -195,7 +195,67 @@ public final class PendingIntent implements Parcelable {
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
IActivityManager.INTENT_SENDER_ACTIVITY, packageName,
- null, null, requestCode, intent, resolvedType, flags);
+ null, null, requestCode, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null, flags);
+ return target != null ? new PendingIntent(target) : null;
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Like {@link #getActivity(Context, int, Intent, int)}, but allows an
+ * array of Intents to be supplied. The first Intent in the array is
+ * taken as the primary key for the PendingIntent, like the single Intent
+ * given to {@link #getActivity(Context, int, Intent, int)}. Upon sending
+ * the resulting PendingIntent, all of the Intents are started in the same
+ * way as they would be by passing them to {@link Context#startActivities(Intent[])}.
+ *
+ * <p class="note">
+ * The <em>first</em> intent in the array will be started outside of the context of an
+ * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK
+ * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent. (Activities after
+ * the first in the array are started in the context of the previous activity
+ * in the array, so FLAG_ACTIVITY_NEW_TASK is not needed nor desired for them.)
+ * </p>
+ *
+ * <p class="note">
+ * The <em>last</em> intent in the array represents the key for the
+ * PendingIntent. In other words, it is the significant element for matching
+ * (as done with the single intent given to {@link #getActivity(Context, int, Intent, int)},
+ * its content will be the subject of replacement by
+ * {@link #send(Context, int, Intent)} and {@link #FLAG_UPDATE_CURRENT}, etc.
+ * This is because it is the most specific of the supplied intents, and the
+ * UI the user actually sees when the intents are started.
+ * </p>
+ *
+ * @param context The Context in which this PendingIntent should start
+ * the activity.
+ * @param requestCode Private request code for the sender (currently
+ * not used).
+ * @param intents Array of Intents of the activities to be launched.
+ * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE},
+ * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT},
+ * or any of the flags as supported by
+ * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
+ * of the intent that can be supplied when the actual send happens.
+ *
+ * @return Returns an existing or new PendingIntent matching the given
+ * parameters. May return null only if {@link #FLAG_NO_CREATE} has been
+ * supplied.
+ */
+ public static PendingIntent getActivities(Context context, int requestCode,
+ Intent[] intents, int flags) {
+ String packageName = context.getPackageName();
+ String[] resolvedTypes = new String[intents.length];
+ for (int i=0; i<intents.length; i++) {
+ resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
+ }
+ try {
+ IIntentSender target =
+ ActivityManagerNative.getDefault().getIntentSender(
+ IActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ null, null, requestCode, intents, resolvedTypes, flags);
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -230,7 +290,8 @@ public final class PendingIntent implements Parcelable {
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
IActivityManager.INTENT_SENDER_BROADCAST, packageName,
- null, null, requestCode, intent, resolvedType, flags);
+ null, null, requestCode, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null, flags);
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -266,7 +327,8 @@ public final class PendingIntent implements Parcelable {
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
IActivityManager.INTENT_SENDER_SERVICE, packageName,
- null, null, requestCode, intent, resolvedType, flags);
+ null, null, requestCode, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null, flags);
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a463afc..27be51a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -725,6 +725,28 @@ public abstract class Context {
public abstract void startActivity(Intent intent);
/**
+ * Launch multiple new activities. This is generally the same as calling
+ * {@link #startActivity(Intent)} for the first Intent in the array,
+ * that activity during its creation calling {@link #startActivity(Intent)}
+ * for the second entry, etc. Note that unlike that approach, generally
+ * none of the activities except the last in the array will be created
+ * at this point, but rather will be created when the user first visits
+ * them (due to pressing back from the activity on top).
+ *
+ * <p>This method throws {@link ActivityNotFoundException}
+ * if there was no Activity found for <em>any</em> given Intent. In this
+ * case the state of the activity stack is undefined (some Intents in the
+ * list may be on it, some not), so you probably want to avoid such situations.
+ *
+ * @param intents An array of Intents to be started.
+ *
+ * @throws ActivityNotFoundException
+ *
+ * @see PackageManager#resolveActivity
+ */
+ public abstract void startActivities(Intent[] intents);
+
+ /**
* Like {@link #startActivity(Intent)}, but taking a IntentSender
* to start. If the IntentSender is for an activity, that activity will be started
* as if you had called the regular {@link #startActivity(Intent)}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3f5d215..f8928e4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -266,6 +266,11 @@ public class ContextWrapper extends Context {
}
@Override
+ public void startActivities(Intent[] intents) {
+ mBase.startActivities(intents);
+ }
+
+ @Override
public void startIntentSender(IntentSender intent,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
throws IntentSender.SendIntentException {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4b6333e..84cb2fb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2614,13 +2614,29 @@ public class Intent implements Parcelable, Cloneable {
* animation to go to the next activity state. This doesn't mean an
* animation will never run -- if another activity change happens that doesn't
* specify this flag before the activity started here is displayed, then
- * that transition will be used. This this flag can be put to good use
+ * that transition will be used. This flag can be put to good use
* when you are going to do a series of activity operations but the
* animation seen by the user shouldn't be driven by the first activity
* change but rather a later one.
*/
public static final int FLAG_ACTIVITY_NO_ANIMATION = 0X00010000;
/**
+ * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+ * this flag will cause any existing task that would be associated with the
+ * activity to be cleared before the activity is started. That is, the activity
+ * becomes the new root of an otherwise empty task, and any old activities
+ * are finished. This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
+ */
+ public static final int FLAG_ACTIVITY_CLEAR_TASK = 0X00008000;
+ /**
+ * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+ * this flag will cause a newly launching task to be placed on top of the current
+ * home activity task (if there is one). That is, pressing back from the task
+ * will always return the user to home even if that was not the last activity they
+ * saw. This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
+ */
+ public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000;
+ /**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
*/
@@ -4871,18 +4887,22 @@ public class Intent implements Parcelable, Cloneable {
* @see #FLAG_DEBUG_LOG_RESOLUTION
* @see #FLAG_FROM_BACKGROUND
* @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT
- * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
+ * @see #FLAG_ACTIVITY_CLEAR_TASK
* @see #FLAG_ACTIVITY_CLEAR_TOP
+ * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
* @see #FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
* @see #FLAG_ACTIVITY_FORWARD_RESULT
* @see #FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
* @see #FLAG_ACTIVITY_MULTIPLE_TASK
* @see #FLAG_ACTIVITY_NEW_TASK
+ * @see #FLAG_ACTIVITY_NO_ANIMATION
* @see #FLAG_ACTIVITY_NO_HISTORY
* @see #FLAG_ACTIVITY_NO_USER_ACTION
* @see #FLAG_ACTIVITY_PREVIOUS_IS_TOP
* @see #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ * @see #FLAG_ACTIVITY_REORDER_TO_FRONT
* @see #FLAG_ACTIVITY_SINGLE_TOP
+ * @see #FLAG_ACTIVITY_TASK_ON_HOME
* @see #FLAG_RECEIVER_REGISTERED_ONLY
*/
public Intent setFlags(int flags) {
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index ada7f36..1984167 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -120,7 +120,7 @@ public class HeavyWeightSwitcherActivity extends Activity {
private OnClickListener mSwitchOldListener = new OnClickListener() {
public void onClick(View v) {
try {
- ActivityManagerNative.getDefault().moveTaskToFront(mCurTask);
+ ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0);
} catch (RemoteException e) {
}
finish();
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index 84c2ed2..fffc1cd 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -19,6 +19,7 @@ page.title=Application Fundamentals
<li><a href="#lmodes">Launch modes</a></li>
<li><a href="#clearstack">Clearing the stack</a></li>
<li><a href="#starttask">Starting tasks</a></li>
+ <li><a href="#commonpatterns">Common patterns</a></li>
</ol></li>
<li><a href="#procthread">Processes and Threads</a>
<ol>
@@ -892,6 +893,60 @@ See <a href="#clearstack">Clearing the stack</a>, earlier.
</p>
+<h3 id="commonpatterns">Common patterns</h3>
+
+<p>
+In most cases an application won't use any flags or special features.
+This gives the standard interaction the user expects: launching the application
+brings any existing task to the foreground, or starts the main activity in
+a new task if there isn't one.
+</p>
+
+<p>
+If an application posts notifications, it needs to decide how the user's
+selection of a notification should impact any currently running task. The
+current suggested behavior is that any current tasks be completely removed,
+replaced with a new task containing a stack of activities representing where
+the user is jumping in to the app. This can be accomplished with a combination
+of the {@link android.app.PendingIntent#getActivities PendingIntent.getActivities()}
+method and {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK Intent.FLAG_ACTIVITY_CLEAR_TASK}.
+</p>
+
+<p>
+For example, here is sample code to build an array of Intents to launch an
+application into an activity three levels deep. The first Intent is always
+the main Intent of the application as started by the launcher. The exact
+details of the Intent data will of course depend on your application.
+</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/app/StatusBarNotifications.java
+ intent_array}
+
+<p>
+In some cases an application may not want to directly launch its application
+from a notification, but instead go to a intermediate summary activity. To
+accomplish this, the summary activity should be given a task affinity that
+is different from the main application (one will typically give it no affinity,
+that is "") so that it does not get launched into any existing application task.
+</p>
+
+{@sample development/samples/ApiDemos/AndroidManifest.xml no_task_affinity}
+
+<p>
+The PendingIntent to launch this then does not need to supply anything special:
+</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/app/IncomingMessage.java
+ pending_intent}
+
+<p>
+If an application implements an app widget, it should generally use the same
+approach as the first one for notifications: when the user taps on the app
+widget it should throw away any current task of the application and start a
+new task with potentially multiple activities representing the state the
+user is jumping in to.
+</p>
+
<h2 id="procthread">Processes and Threads</h2>
<p>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index ff2a4ed..a98ef0b 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -193,14 +193,13 @@ public class RecentApplicationsActivity extends Activity {
ActivityDescription item = mActivityDescriptions.get(n);
if (item.id >= 0) {
// This is an active task; it should just go to the foreground.
- IActivityManager am = ActivityManagerNative.getDefault();
- try {
- am.moveTaskToFront(item.id);
- } catch (RemoteException e) {
- }
+ final ActivityManager am = (ActivityManager)
+ getSystemService(Context.ACTIVITY_SERVICE);
+ am.moveTaskToFront(item.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else if (item.intent != null) {
// prepare a launch intent and send it
- item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
try {
if (DBG) Log.v(TAG, "Starting intent " + item.intent);
startActivity(item.intent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index e0b05f9..0c31304 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -252,13 +252,13 @@ public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnC
ActivityDescription ad = (ActivityDescription)v.getTag();
if (ad.id >= 0) {
// This is an active task; it should just go to the foreground.
- IActivityManager am = ActivityManagerNative.getDefault();
- try {
- am.moveTaskToFront(ad.id);
- } catch (RemoteException e) {
- }
+ final ActivityManager am = (ActivityManager)
+ getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ am.moveTaskToFront(ad.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else {
Intent intent = ad.intent;
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
getContext().startActivity(intent);
}
diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
index d9e8c2b..f53092d 100644
--- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
+++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
@@ -138,13 +138,12 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener
RecentTag tag = (RecentTag)b.getTag();
if (tag.info.id >= 0) {
// This is an active task; it should just go to the foreground.
- IActivityManager am = ActivityManagerNative.getDefault();
- try {
- am.moveTaskToFront(tag.info.id);
- } catch (RemoteException e) {
- }
+ final ActivityManager am = (ActivityManager)
+ getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ am.moveTaskToFront(tag.info.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else if (tag.intent != null) {
- tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
try {
getContext().startActivity(tag.intent);
} catch (ActivityNotFoundException e) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e815524..1773eca 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -2026,7 +2026,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,
- null, null, 0, 0, 0, false, false);
+ null, null, 0, 0, 0, false, false, null);
}
}
@@ -2082,7 +2082,7 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
- null, null, 0, 0, 0, false, false);
+ null, null, 0, 0, 0, false, false, null);
}
}
}
@@ -2121,13 +2121,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mPendingActivityLaunches.clear();
}
-
+
public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
- return mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, null);
}
@@ -2138,7 +2138,7 @@ public final class ActivityManagerService extends ActivityManagerNative
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
WaitResult res = new WaitResult();
- mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, res, null);
return res;
@@ -2149,12 +2149,12 @@ public final class ActivityManagerService extends ActivityManagerNative
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return mMainStack.startActivityMayWait(caller, intent, resolvedType,
+ return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, config);
}
- public int startActivityIntentSender(IApplicationThread caller,
+ public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues) {
@@ -2267,7 +2267,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// those are not yet exposed to user code, so there is no need.
int res = mMainStack.startActivityLocked(r.app.thread, intent,
r.resolvedType, null, 0, aInfo, resultTo, resultWho,
- requestCode, -1, r.launchedFromUid, false, false);
+ requestCode, -1, r.launchedFromUid, false, false, null);
Binder.restoreCallingIdentity(origId);
r.finishing = wasFinishing;
@@ -2289,38 +2289,28 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(
"startActivityInPackage only available to the system");
}
-
- final boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
- // Collect information about the target of the Intent.
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
+ return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+ null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false, null, null);
+ }
- if (aInfo != null) {
- // Store the found target back into the intent, because now that
- // we have it we never want to do this again. For example, if the
- // user navigates back to this point in the history, we should
- // always restart the exact same activity.
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
- }
+ public final int startActivities(IApplicationThread caller,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+ }
- synchronized(this) {
- return mMainStack.startActivityLocked(null, intent, resolvedType,
- null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid,
- onlyIfNeeded, componentSpecified);
+ public final int startActivitiesInPackage(int uid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+
+ // This is so super not safe, that only the system (or okay root)
+ // can do it.
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.myUid()) {
+ throw new SecurityException(
+ "startActivityInPackage only available to the system");
}
+
+ return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
}
final void addRecentTaskLocked(TaskRecord task) {
@@ -3890,16 +3880,30 @@ public final class ActivityManagerService extends ActivityManagerNative
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags) {
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
// Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors() == true) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- if (type == INTENT_SENDER_BROADCAST) {
- if ((intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ if (intents != null) {
+ if (intents.length < 1) {
+ throw new IllegalArgumentException("Intents array length must be >= 1");
+ }
+ for (int i=0; i<intents.length; i++) {
+ Intent intent = intents[i];
+ if (intent == null) {
+ throw new IllegalArgumentException("Null intent at index " + i);
+ }
+ if (intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ if (type == INTENT_SENDER_BROADCAST &&
+ (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ throw new IllegalArgumentException(
+ "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ }
+ intents[i] = new Intent(intent);
+ }
+ if (resolvedTypes != null && resolvedTypes.length != intents.length) {
throw new IllegalArgumentException(
- "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ "Intent array length does not match resolvedTypes length");
}
}
@@ -3922,7 +3926,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
return getIntentSenderLocked(type, packageName, callingUid,
- token, resultWho, requestCode, intent, resolvedType, flags);
+ token, resultWho, requestCode, intents, resolvedTypes, flags);
} catch (RemoteException e) {
throw new SecurityException(e);
@@ -3932,7 +3936,7 @@ public final class ActivityManagerService extends ActivityManagerNative
IIntentSender getIntentSenderLocked(int type,
String packageName, int callingUid, IBinder token, String resultWho,
- int requestCode, Intent intent, String resolvedType, int flags) {
+ int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
ActivityRecord activity = null;
if (type == INTENT_SENDER_ACTIVITY_RESULT) {
int index = mMainStack.indexOfTokenLocked(token);
@@ -3953,14 +3957,24 @@ public final class ActivityManagerService extends ActivityManagerNative
PendingIntentRecord.Key key = new PendingIntentRecord.Key(
type, packageName, activity, resultWho,
- requestCode, intent, resolvedType, flags);
+ requestCode, intents, resolvedTypes, flags);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
if (rec != null) {
if (!cancelCurrent) {
if (updateCurrent) {
- rec.key.requestIntent.replaceExtras(intent);
+ if (rec.key.requestIntent != null) {
+ rec.key.requestIntent.replaceExtras(intents != null ? intents[0] : null);
+ }
+ if (intents != null) {
+ intents[intents.length-1] = rec.key.requestIntent;
+ rec.key.allIntents = intents;
+ rec.key.allResolvedTypes = resolvedTypes;
+ } else {
+ rec.key.allIntents = null;
+ rec.key.allResolvedTypes = null;
+ }
}
return rec;
}
@@ -5006,7 +5020,7 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* TODO: Add mController hook
*/
- public void moveTaskToFront(int task) {
+ public void moveTaskToFront(int task, int flags) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToFront()");
@@ -5021,6 +5035,11 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (tr.taskId == task) {
+ if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+ // Caller wants the home activity moved with it. To accomplish this,
+ // we'll just move the home task to the top first.
+ mMainStack.moveHomeToFrontLocked();
+ }
mMainStack.moveTaskToFrontLocked(tr, null);
return;
}
@@ -5028,6 +5047,11 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
if (hr.task.taskId == task) {
+ if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+ // Caller wants the home activity moved with it. To accomplish this,
+ // we'll just move the home task to the top first.
+ mMainStack.moveHomeToFrontLocked();
+ }
mMainStack.moveTaskToFrontLocked(hr.task, null);
return;
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 51dc84e..b4ea036 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -99,8 +99,8 @@ public class ActivityStack {
static final int DESTROY_TIMEOUT = 10*1000;
// How long until we reset a task when the user returns to it. Currently
- // 30 minutes.
- static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30;
+ // disabled.
+ static final long ACTIVITY_INACTIVE_RESET_TIME = 0;
// How long between activity launches that we consider safe to not warn
// the user about an unexpected activity being launched on top.
@@ -1487,7 +1487,8 @@ public class ActivityStack {
ActivityRecord newActivity) {
boolean forceReset = (newActivity.info.flags
&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
- if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
+ if (ACTIVITY_INACTIVE_RESET_TIME > 0
+ && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
if ((newActivity.info.flags
&ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
forceReset = true;
@@ -1573,8 +1574,7 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- target.task = new TaskRecord(mService.mCurTask, target.info, null,
- (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ target.task = new TaskRecord(mService.mCurTask, target.info, null);
target.task.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
@@ -1776,11 +1776,11 @@ public class ActivityStack {
* activities on top of it and return the instance.
*
* @param newR Description of the new activity being started.
- * @return Returns the old activity that should be continue to be used,
+ * @return Returns the old activity that should be continued to be used,
* or null if none was found.
*/
private final ActivityRecord performClearTaskLocked(int taskId,
- ActivityRecord newR, int launchFlags, boolean doClear) {
+ ActivityRecord newR, int launchFlags) {
int i = mHistory.size();
// First find the requested task.
@@ -1806,17 +1806,18 @@ public class ActivityStack {
if (r.realActivity.equals(newR.realActivity)) {
// Here it is! Now finish everything in front...
ActivityRecord ret = r;
- if (doClear) {
- while (i < (mHistory.size()-1)) {
- i++;
- r = (ActivityRecord)mHistory.get(i);
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- i--;
- }
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId != taskId) {
+ break;
+ }
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i--;
}
}
@@ -1843,6 +1844,51 @@ public class ActivityStack {
}
/**
+ * Completely remove all activities associated with an existing task.
+ */
+ private final void performClearTaskLocked(int taskId) {
+ int i = mHistory.size();
+
+ // First find the requested task.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId == taskId) {
+ i++;
+ break;
+ }
+ }
+
+ // Now clear it.
+ while (i > 0) {
+ i--;
+ ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ if (r.task.taskId != taskId) {
+ // We hit the bottom. Now finish it all...
+ while (i < (mHistory.size()-1)) {
+ i++;
+ r = (ActivityRecord)mHistory.get(i);
+ if (r.task.taskId != taskId) {
+ // Whoops hit the end.
+ return;
+ }
+ if (r.finishing) {
+ continue;
+ }
+ if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i--;
+ }
+ }
+ return;
+ }
+ }
+ }
+
+ /**
* Find the activity in the history stack within the given task. Returns
* the index within the history at which it's found, or < 0 if not found.
*/
@@ -1882,7 +1928,7 @@ public class ActivityStack {
int grantedMode, ActivityInfo aInfo, IBinder resultTo,
String resultWho, int requestCode,
int callingPid, int callingUid, boolean onlyIfNeeded,
- boolean componentSpecified) {
+ boolean componentSpecified, ActivityRecord[] outActivity) {
int err = START_SUCCESS;
@@ -2004,6 +2050,9 @@ public class ActivityStack {
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
+ if (outActivity != null) {
+ outActivity[0] = r;
+ }
if (mMainStack) {
if (mResumedActivity == null
@@ -2038,6 +2087,16 @@ public class ActivityStack {
grantedUriPermissions, grantedMode, onlyIfNeeded, true);
}
+ final void moveHomeToFrontFromLaunchLocked(int launchFlags) {
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
+ // Caller wants to appear on home activity, so before starting
+ // their own activity we will bring home to the front.
+ moveHomeToFrontLocked();
+ }
+ }
+
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
int grantedMode, boolean onlyIfNeeded, boolean doResume) {
@@ -2111,6 +2170,7 @@ public class ActivityStack {
}
boolean addingToTask = false;
+ TaskRecord reuseTask = null;
if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
@@ -2148,6 +2208,7 @@ public class ActivityStack {
if (callerAtFront) {
// We really do want to push this one into the
// user's face, right now.
+ moveHomeToFrontFromLaunchLocked(launchFlags);
moveTaskToFrontLocked(taskTop.task, r);
}
}
@@ -2166,7 +2227,16 @@ public class ActivityStack {
}
return START_RETURN_INTENT_TO_CALLER;
}
- if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
+ if ((launchFlags &
+ (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
+ == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
+ // The caller has requested to completely replace any
+ // exising task with its new activity. Well that should
+ // not be too hard...
+ reuseTask = taskTop.task;
+ performClearTaskLocked(taskTop.task.taskId);
+ 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) {
// In this situation we want to remove all activities
@@ -2174,7 +2244,7 @@ public class ActivityStack {
// cases this means we are resetting the task to its
// initial state.
ActivityRecord top = performClearTaskLocked(
- taskTop.task.taskId, r, launchFlags, true);
+ taskTop.task.taskId, r, launchFlags);
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different
@@ -2235,7 +2305,7 @@ public class ActivityStack {
// for now we'll just drop it.
taskTop.task.setIntent(r.intent, r.info);
}
- if (!addingToTask) {
+ if (!addingToTask && reuseTask == null) {
// We didn't do anything... but it was needed (a.k.a., client
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
@@ -2298,19 +2368,23 @@ public class ActivityStack {
// Should this be considered a new task?
if (r.resultTo == null && !addingToTask
&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // todo: should do better management of integers.
- mService.mCurTask++;
- if (mService.mCurTask <= 0) {
- mService.mCurTask = 1;
+ if (reuseTask == null) {
+ // todo: should do better management of integers.
+ mService.mCurTask++;
+ if (mService.mCurTask <= 0) {
+ mService.mCurTask = 1;
+ }
+ r.task = new TaskRecord(mService.mCurTask, r.info, intent);
+ if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ + " in new task " + r.task);
+ } else {
+ r.task = reuseTask;
}
- r.task = new TaskRecord(mService.mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new task " + r.task);
newTask = true;
if (mMainStack) {
mService.addRecentTaskLocked(r.task);
}
+ moveHomeToFrontFromLaunchLocked(launchFlags);
} else if (sourceRecord != null) {
if (!addingToTask &&
@@ -2319,7 +2393,7 @@ public class ActivityStack {
// task, but the caller has asked to clear that task if the
// activity is already running.
ActivityRecord top = performClearTaskLocked(
- sourceRecord.task.taskId, r, launchFlags, true);
+ sourceRecord.task.taskId, r, launchFlags);
if (top != null) {
logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
top.deliverNewIntentLocked(callingUid, r.intent);
@@ -2361,9 +2435,8 @@ public class ActivityStack {
ActivityRecord prev =
N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
r.task = prev != null
- ? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent,
- (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
+ ? prev.task
+ : new TaskRecord(mService.mCurTask, r.info, intent);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new guessed " + r.task);
}
@@ -2386,21 +2459,7 @@ public class ActivityStack {
return START_SUCCESS;
}
- final int startActivityMayWait(IApplicationThread caller,
- Intent intent, String resolvedType, Uri[] grantedUriPermissions,
- int grantedMode, IBinder resultTo,
- String resultWho, int requestCode, boolean onlyIfNeeded,
- boolean debug, WaitResult outResult, Configuration config) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- boolean componentSpecified = intent.getComponent() != null;
-
- // Don't modify the client's object!
- intent = new Intent(intent);
-
+ ActivityInfo resolveActivity(Intent intent, String resolvedType, boolean debug) {
// Collect information about the target of the Intent.
ActivityInfo aInfo;
try {
@@ -2429,11 +2488,32 @@ public class ActivityStack {
}
}
}
+ return aInfo;
+ }
+
+ final int startActivityMayWait(IApplicationThread caller, int callingUid,
+ Intent intent, String resolvedType, Uri[] grantedUriPermissions,
+ int grantedMode, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded,
+ boolean debug, WaitResult outResult, Configuration config) {
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug);
synchronized (mService) {
int callingPid;
- int callingUid;
- if (caller == null) {
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
callingPid = Binder.getCallingPid();
callingUid = Binder.getCallingUid();
} else {
@@ -2472,8 +2552,8 @@ public class ActivityStack {
IIntentSender target = mService.getIntentSenderLocked(
IActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, null, null, 0, intent,
- resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+ realCallingUid, null, null, 0, new Intent[] { intent },
+ new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT);
Intent newIntent = new Intent();
@@ -2518,7 +2598,7 @@ public class ActivityStack {
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, callingPid, callingUid,
- onlyIfNeeded, componentSpecified);
+ onlyIfNeeded, componentSpecified, null);
if (mConfigWillChange && mMainStack) {
// If the caller also wants to switch to a new configuration,
@@ -2569,6 +2649,75 @@ public class ActivityStack {
}
}
+ final int startActivities(IApplicationThread caller, int callingUid,
+ Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ if (intents == null) {
+ throw new NullPointerException("intents is null");
+ }
+ if (resolvedTypes == null) {
+ throw new NullPointerException("resolvedTypes is null");
+ }
+ if (intents.length != resolvedTypes.length) {
+ throw new IllegalArgumentException("intents are length different than resolvedTypes");
+ }
+
+ ActivityRecord[] outActivity = new ActivityRecord[1];
+
+ int callingPid;
+ if (callingUid >= 0) {
+ callingPid = -1;
+ } else if (caller == null) {
+ callingPid = Binder.getCallingPid();
+ callingUid = Binder.getCallingUid();
+ } else {
+ callingPid = callingUid = -1;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+
+ for (int i=0; i<intents.length; i++) {
+ Intent intent = intents[i];
+ if (intent == null) {
+ continue;
+ }
+
+ // Refuse possible leaked file descriptors
+ if (intent != null && intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ boolean componentSpecified = intent.getComponent() != null;
+
+ // Don't modify the client's object!
+ intent = new Intent(intent);
+
+ // Collect information about the target of the Intent.
+ ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false);
+
+ if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
+ throw new IllegalArgumentException(
+ "FLAG_CANT_SAVE_STATE not supported here");
+ }
+
+ int res = startActivityLocked(caller, intent, resolvedTypes[i],
+ null, 0, aInfo, resultTo, null, -1, callingPid, callingUid,
+ false, componentSpecified, outActivity);
+ if (res < 0) {
+ return res;
+ }
+
+ resultTo = outActivity[0];
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return IActivityManager.START_SUCCESS;
+ }
+
void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r,
long thisTime, long totalTime) {
for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) {
@@ -3252,6 +3401,24 @@ public class ActivityStack {
removeHistoryRecordsForAppLocked(mFinishingActivities, app);
}
+ /**
+ * Move the current home activity's task (if one exists) to the front
+ * of the stack.
+ */
+ final void moveHomeToFrontLocked() {
+ TaskRecord homeTask = null;
+ for (int i=mHistory.size()-1; i>=0; i--) {
+ ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+ if (hr.isHomeActivity) {
+ homeTask = hr.task;
+ }
+ }
+ if (homeTask != null) {
+ moveTaskToFrontLocked(homeTask, null);
+ }
+ }
+
+
final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 7a85eb8..ee6e420 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -47,20 +47,24 @@ class PendingIntentRecord extends IIntentSender.Stub {
final int requestCode;
final Intent requestIntent;
final String requestResolvedType;
+ Intent[] allIntents;
+ String[] allResolvedTypes;
final int flags;
final int hashCode;
private static final int ODD_PRIME_NUMBER = 37;
Key(int _t, String _p, ActivityRecord _a, String _w,
- int _r, Intent _i, String _it, int _f) {
+ int _r, Intent[] _i, String[] _it, int _f) {
type = _t;
packageName = _p;
activity = _a;
who = _w;
requestCode = _r;
- requestIntent = _i;
- requestResolvedType = _it;
+ requestIntent = _i != null ? _i[_i.length-1] : null;
+ requestResolvedType = _it != null ? _it[_it.length-1] : null;
+ allIntents = _i;
+ allResolvedTypes = _it;
flags = _f;
int hash = 23;
@@ -72,11 +76,11 @@ class PendingIntentRecord extends IIntentSender.Stub {
if (_a != null) {
hash = (ODD_PRIME_NUMBER*hash) + _a.hashCode();
}
- if (_i != null) {
- hash = (ODD_PRIME_NUMBER*hash) + _i.filterHashCode();
+ if (requestIntent != null) {
+ hash = (ODD_PRIME_NUMBER*hash) + requestIntent.filterHashCode();
}
- if (_it != null) {
- hash = (ODD_PRIME_NUMBER*hash) + _it.hashCode();
+ if (requestResolvedType != null) {
+ hash = (ODD_PRIME_NUMBER*hash) + requestResolvedType.hashCode();
}
hash = (ODD_PRIME_NUMBER*hash) + _p.hashCode();
hash = (ODD_PRIME_NUMBER*hash) + _t;
@@ -209,9 +213,24 @@ class PendingIntentRecord extends IIntentSender.Stub {
switch (key.type) {
case IActivityManager.INTENT_SENDER_ACTIVITY:
try {
- owner.startActivityInPackage(uid,
- finalIntent, resolvedType,
- resultTo, resultWho, requestCode, false);
+ if (key.allIntents != null && key.allIntents.length > 1) {
+ Intent[] allIntents = new Intent[key.allIntents.length];
+ String[] allResolvedTypes = new String[key.allIntents.length];
+ System.arraycopy(key.allIntents, 0, allIntents, 0,
+ key.allIntents.length);
+ if (key.allResolvedTypes != null) {
+ System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
+ key.allResolvedTypes.length);
+ }
+ allIntents[allIntents.length-1] = finalIntent;
+ allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
+ owner.startActivitiesInPackage(uid, allIntents,
+ allResolvedTypes, resultTo);
+ } else {
+ owner.startActivityInPackage(uid,
+ finalIntent, resolvedType,
+ resultTo, resultWho, requestCode, false);
+ }
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Unable to send startActivity intent", e);
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index bcb8f54..09d9c3b6 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -26,7 +26,6 @@ import java.io.PrintWriter;
class TaskRecord {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
- final boolean clearOnBackground; // As per the original activity.
Intent intent; // The original intent that started the task.
Intent affinityIntent; // Intent of affinity-moved activity that started this task.
ComponentName origActivity; // The non-alias activity component of the intent.
@@ -38,11 +37,9 @@ class TaskRecord {
String stringName; // caching of toString() result.
- TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
- boolean _clearOnBackground) {
+ TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
taskId = _taskId;
affinity = info.taskAffinity;
- clearOnBackground = _clearOnBackground;
setIntent(_intent, info);
}
@@ -86,9 +83,8 @@ class TaskRecord {
}
void dump(PrintWriter pw, String prefix) {
- if (clearOnBackground || numActivities != 0 || rootWasReset) {
- pw.print(prefix); pw.print("clearOnBackground="); pw.print(clearOnBackground);
- pw.print(" numActivities="); pw.print(numActivities);
+ if (numActivities != 0 || rootWasReset) {
+ pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
pw.print(" rootWasReset="); pw.println(rootWasReset);
}
if (affinity != null) {
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index c31c9cc..3b52252 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -246,6 +246,11 @@ public class MockContext extends Context {
}
@Override
+ public void startActivities(Intent[] intents) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void startIntentSender(IntentSender intent,
Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
throws IntentSender.SendIntentException {
diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
index fec3671..4cacbc4 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
@@ -39,7 +39,7 @@ public class ActivityManagerPermissionTests extends TestCase {
@SmallTest
public void testREORDER_TASKS() {
try {
- mAm.moveTaskToFront(-1);
+ mAm.moveTaskToFront(0, 0);
fail("IActivityManager.moveTaskToFront did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {