Work on issue #17357238: Recents is often slow if not used in a while
Add a new activity attribute, resumeWhilePausing, that allows an
activity specifying it to immediately start running without waiting
for the previous activity to pause. The recents activity is updated
to use this.
The implementation of this is ultimately fairly simple -- if we are
in the path of resuming such an activity, and find that we first need
to pause the existing activity, then within the activity manager we
do the regular pause flow but act like it has immediately finished
pausing right then so that we can immediately go on to the resume.
To make this clean, we tell the activity when asking it to pause that
it should not come back and tell us it is done, because we aren't in
any way waiting for it.
One potentially important change I needed to make here is the pause
callback no longer provides the saved persistent state, because we
now can't count on that callback happening. I don't think there was
really any utility in this anyway -- all modern apps will have their
save state flow happen as part of stopping, not pausing, so we'll
only capture that saved state when the stop is reported back anyway.
And since we do send the saved state back when stopping, it would
always blow away whatever we had gotten at the pause.
Finally, update the documentation for AppTask.startActivity(), and
fix the implementation handling that to be cleaner -- we need to
deal with inTask first before getting in to "oh noes add NEW_TASK
if this isn't coming from a calling activity" flow.
Change-Id: Ia1da0fac90d7bdbaafdda2e34850d795ce17a39f
diff --git a/api/current.txt b/api/current.txt
index 8f9a528..72e9035 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1062,6 +1062,7 @@
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
field public static final int restrictedAccountType = 16843733; // 0x10103d5
field public static final int restrictionType = 16843923; // 0x1010493
+ field public static final int resumeWhilePausing = 16843955; // 0x10104b3
field public static final int reversible = 16843851; // 0x101044b
field public static final int right = 16843183; // 0x10101af
field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
@@ -8411,6 +8412,7 @@
field public static final int FLAG_MULTIPROCESS = 1; // 0x1
field public static final int FLAG_NO_HISTORY = 128; // 0x80
field public static final int FLAG_RELINQUISH_TASK_IDENTITY = 4096; // 0x1000
+ field public static final int FLAG_RESUME_WHILE_PAUSING = 16384; // 0x4000
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
field public static final int LAUNCH_MULTIPLE = 0; // 0x0
diff --git a/api/removed.txt b/api/removed.txt
index a272cf4..7f5f46c 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,11 +1,3 @@
-package android {
-
- public static final class R.attr {
- field public static final int __removed1 = 16843955; // 0x10104b3
- }
-
-}
-
package android.media {
public class AudioFormat {
@@ -78,11 +70,3 @@
}
-package com.android.internal {
-
- public static final class R.attr {
- field public static final int __removed1 = 16843955; // 0x10104b3
- }
-
-}
-
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3e03893..9486a72 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2656,17 +2656,24 @@
/**
* Start an activity in this task. Brings the task to the foreground. If this task
- * is not currently active (that is, its id < 0), then the activity being started
- * needs to be started as a new task and the Intent's ComponentName must match the
- * base ComponenentName of the recent task entry. Otherwise, the activity being
- * started must <b>not</b> be launched as a new task -- not through explicit intent
- * flags nor implicitly as the singleTask or singleInstance launch modes.
+ * is not currently active (that is, its id < 0), then a new activity for the given
+ * Intent will be launched as the root of the task and the task brought to the
+ * foreground. Otherwise, if this task is currently active and the Intent does not specify
+ * an activity to launch in a new task, then a new activity for the given Intent will
+ * be launched on top of the task and the task brought to the foreground. If this
+ * task is currently active and the Intent specifies {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+ * or would otherwise be launched in to a new task, then the activity not launched but
+ * this task be brought to the foreground and a new intent delivered to the top
+ * activity if appropriate.
*
- * <p>See {@link Activity#startActivity(android.content.Intent, android.os.Bundle)
- * Activity.startActivity} for more information.</p>
+ * <p>In other words, you generally want to use an Intent here that does not specify
+ * {@link Intent#FLAG_ACTIVITY_NEW_TASK} or {@link Intent#FLAG_ACTIVITY_NEW_DOCUMENT},
+ * and let the system do the right thing.</p>
*
* @param intent The Intent describing the new activity to be launched on the task.
* @param options Optional launch options.
+ *
+ * @see Activity#startActivity(android.content.Intent, android.os.Bundle)
*/
public void startActivity(Context context, Intent intent, Bundle options) {
ActivityThread thread = ActivityThread.currentActivityThread();
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1f7e450..394b183 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -509,8 +509,7 @@
case ACTIVITY_PAUSED_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- PersistableBundle persistentState = data.readPersistableBundle();
- activityPaused(token, persistentState);
+ activityPaused(token);
reply.writeNoException();
return true;
}
@@ -2829,13 +2828,12 @@
data.recycle();
reply.recycle();
}
- public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException
+ public void activityPaused(IBinder token) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
- data.writePersistableBundle(persistentState);
mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4f2a3bc..111689e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -563,11 +563,11 @@
}
public final void schedulePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges) {
+ boolean userLeaving, int configChanges, boolean dontReport) {
sendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token,
- (userLeaving ? 1 : 0),
+ (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
configChanges);
}
@@ -1283,13 +1283,15 @@
} break;
case PAUSE_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
+ handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
+ (msg.arg1&2) != 0);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case PAUSE_ACTIVITY_FINISHING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
+ handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
+ (msg.arg1&1) != 0);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case STOP_ACTIVITY_SHOW:
@@ -3142,7 +3144,7 @@
}
private void handlePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges) {
+ boolean userLeaving, int configChanges, boolean dontReport) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
//Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
@@ -3159,9 +3161,11 @@
}
// Tell the activity manager we have paused.
- try {
- ActivityManagerNative.getDefault().activityPaused(token, r.persistentState);
- } catch (RemoteException ex) {
+ if (!dontReport) {
+ try {
+ ActivityManagerNative.getDefault().activityPaused(token);
+ } catch (RemoteException ex) {
+ }
}
mSomeActivitiesChanged = true;
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 63e8707..0123e167 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -78,7 +78,8 @@
boolean finished = data.readInt() != 0;
boolean userLeaving = data.readInt() != 0;
int configChanges = data.readInt();
- schedulePauseActivity(b, finished, userLeaving, configChanges);
+ boolean dontReport = data.readInt() != 0;
+ schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport);
return true;
}
@@ -689,13 +690,14 @@
}
public final void schedulePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges) throws RemoteException {
+ boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
data.writeInt(finished ? 1 : 0);
data.writeInt(userLeaving ? 1 :0);
data.writeInt(configChanges);
+ data.writeInt(dontReport ? 1 : 0);
mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 99428e8..9483680 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -111,7 +111,7 @@
public void activityResumed(IBinder token) throws RemoteException;
public void activityIdle(IBinder token, Configuration config,
boolean stopProfiling) throws RemoteException;
- public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException;
+ public void activityPaused(IBinder token) throws RemoteException;
public void activityStopped(IBinder token, Bundle state,
PersistableBundle persistentState, CharSequence description) throws RemoteException;
public void activitySlept(IBinder token) throws RemoteException;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index a7546d9..f53075c 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -49,7 +49,7 @@
*/
public interface IApplicationThread extends IInterface {
void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
- int configChanges) throws RemoteException;
+ int configChanges, boolean dontReport) throws RemoteException;
void scheduleStopActivity(IBinder token, boolean showWindow,
int configChanges) throws RemoteException;
void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index dbf49c5..8d3126d 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -255,16 +255,22 @@
* Bit in {@link #flags}: If set, a task rooted at this activity will have its
* baseIntent replaced by the activity immediately above this. Each activity may further
* relinquish its identity to the activity above it using this flag. Set from the
- * android.R.attr#relinquishTaskIdentity attribute.
+ * {@link android.R.attr#relinquishTaskIdentity} attribute.
*/
public static final int FLAG_RELINQUISH_TASK_IDENTITY = 0x1000;
/**
* Bit in {@link #flags} indicating that tasks started with this activity are to be
* removed from the recent list of tasks when the last activity in the task is finished.
- * {@link android.R.attr#autoRemoveFromRecents}
+ * Corresponds to {@link android.R.attr#autoRemoveFromRecents}
*/
public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 0x2000;
/**
+ * Bit in {@link #flags} indicating that this activity can start is creation/resume
+ * while the previous activity is still pausing. Corresponds to
+ * {@link android.R.attr#resumeWhilePausing}
+ */
+ public static final int FLAG_RESUME_WHILE_PAUSING = 0x4000;
+ /**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the primary user. Only works with broadcast receivers. Set from the
* android.R.attr#primaryUserOnly attribute.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6d40dcf..e0fd532 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3110,6 +3110,12 @@
false)) {
a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
}
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestActivity_resumeWhilePausing,
+ false)) {
+ a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+ }
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e905a3a..10c2518e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -939,7 +939,7 @@
{@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
+ {@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
@@ -994,6 +994,15 @@
TaskDescription to change labels, colors and icons in the recent task list. -->
<attr name="relinquishTaskIdentity" format="boolean" />
+ <!-- Indicate that it is okay for this activity be resumed while the previous
+ activity is in the process of pausing, without waiting for the previous pause
+ to complete. Use this with caution: your activity can not acquire any exclusive
+ resources (such as opening the camera or recording audio) when it launches, or it
+ may conflict with the previous activity and fail.
+
+ <p>The default value of this attribute is <code>false</code>. -->
+ <attr name="resumeWhilePausing" format="boolean" />
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1678,6 +1687,7 @@
<attr name="maxRecents" />
<attr name="autoRemoveFromRecents" />
<attr name="relinquishTaskIdentity" />
+ <attr name="resumeWhilePausing" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a4c3474..5b047f7 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2266,7 +2266,7 @@
<public type="attr" name="windowReenterTransition" />
<public type="attr" name="windowSharedElementReturnTransition" />
<public type="attr" name="windowSharedElementReenterTransition" />
- <public type="attr" name="__removed1" />
+ <public type="attr" name="resumeWhilePausing" />
<public type="attr" name="datePickerMode"/>
<public type="attr" name="timePickerMode"/>
<public type="attr" name="inset" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d4ebb01..b94a258 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -176,6 +176,7 @@
android:theme="@style/RecentsStyle"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
+ android:resumeWhilePausing="true"
android:exported="true">
<intent-filter>
<action android:name="com.android.systemui.TOGGLE_RECENTS" />
@@ -196,6 +197,8 @@
android:label="@string/accessibility_desc_recent_apps"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
+ android:stateNotNeeded="true"
+ android:resumeWhilePausing="true"
android:theme="@style/RecentsTheme">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6ddad41..1397ea4 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6278,12 +6278,12 @@
}
@Override
- public final void activityPaused(IBinder token, PersistableBundle persistentState) {
+ public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- stack.activityPausedLocked(token, false, persistentState);
+ stack.activityPausedLocked(token, false);
}
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 81c379a..6168546 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -284,7 +284,7 @@
if (r.app != null) {
mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
- activityPausedLocked(r.appToken, true, r.persistentState);
+ activityPausedLocked(r.appToken, true);
}
} break;
case LAUNCH_TICK_MSG: {
@@ -712,7 +712,7 @@
// Still have something resumed; can't sleep until it is paused.
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
- startPausingLocked(false, true);
+ startPausingLocked(false, true, false, false);
return true;
}
if (mPausingActivity != null) {
@@ -790,22 +790,38 @@
return null;
}
- final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
+ /**
+ * Start pausing the currently resumed activity. It is an error to call this if there
+ * is already an activity being paused or there is no resumed activity.
+ *
+ * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+ * @param uiSleeping True if this is happening with the user interface going to sleep (the
+ * screen turning off).
+ * @param resuming True if this is being called as part of resuming the top activity, so
+ * we shouldn't try to instigate a resume here.
+ * @param dontWait True if the caller does not want to wait for the pause to complete. If
+ * set to true, we will immediately complete the pause here before returning.
+ * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+ * it to tell us when it is done.
+ */
+ final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
+ boolean dontWait) {
if (mPausingActivity != null) {
- Slog.e(TAG, "Trying to pause when pause is already pending for "
- + mPausingActivity, new RuntimeException("here").fillInStackTrace());
+ Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity);
+ completePauseLocked(false);
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
- Slog.e(TAG, "Trying to pause when nothing is resumed",
- new RuntimeException("here").fillInStackTrace());
- mStackSupervisor.resumeTopActivitiesLocked();
- return;
+ if (!resuming) {
+ Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+ mStackSupervisor.resumeTopActivitiesLocked();
+ }
+ return false;
}
if (mActivityContainer.mParentActivity == null) {
// Top level stack, not a child. Look for child stacks.
- mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping);
+ mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
}
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
@@ -834,7 +850,7 @@
prev.shortComponentName);
mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
- userLeaving, prev.configChangeFlags);
+ userLeaving, prev.configChangeFlags, dontWait);
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
@@ -865,39 +881,46 @@
if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
}
- // Schedule a pause timeout in case the app doesn't respond.
- // We don't give it much time because this directly impacts the
- // responsiveness seen by the user.
- Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
- msg.obj = prev;
- prev.pauseTime = SystemClock.uptimeMillis();
- mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
- if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ if (dontWait) {
+ // If the caller said they don't want to wait for the pause, then complete
+ // the pause now.
+ completePauseLocked(false);
+ return false;
+
+ } else {
+ // Schedule a pause timeout in case the app doesn't respond.
+ // We don't give it much time because this directly impacts the
+ // responsiveness seen by the user.
+ Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
+ msg.obj = prev;
+ prev.pauseTime = SystemClock.uptimeMillis();
+ mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+ if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+ return true;
+ }
+
} else {
// This activity failed to schedule the
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ if (!resuming) {
+ mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ }
+ return false;
}
}
- final void activityPausedLocked(IBinder token, boolean timeout,
- PersistableBundle persistentState) {
+ final void activityPausedLocked(IBinder token, boolean timeout) {
if (DEBUG_PAUSE) Slog.v(
TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- if (persistentState != null) {
- r.persistentState = persistentState;
- mService.notifyTaskPersisterLocked(r.task, false);
- }
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
- r.state = ActivityState.PAUSED;
- completePauseLocked();
+ completePauseLocked(true);
} else {
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
r.userId, System.identityHashCode(r), r.shortComponentName,
@@ -948,11 +971,12 @@
}
}
- private void completePauseLocked() {
+ private void completePauseLocked(boolean resumeNext) {
ActivityRecord prev = mPausingActivity;
if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
if (prev != null) {
+ prev.state = ActivityState.PAUSED;
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
@@ -995,19 +1019,21 @@
mPausingActivity = null;
}
- final ActivityStack topStack = mStackSupervisor.getFocusedStack();
- if (!mService.isSleepingOrShuttingDown()) {
- mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
- } else {
- mStackSupervisor.checkReadyForSleepLocked();
- ActivityRecord top = topStack.topRunningActivityLocked(null);
- if (top == null || (prev != null && top != prev)) {
- // If there are no more activities available to run,
- // do resume anyway to start something. Also if the top
- // activity on the stack is not the just paused activity,
- // we need to go ahead and resume it to ensure we complete
- // an in-flight app switch.
- mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+ if (resumeNext) {
+ final ActivityStack topStack = mStackSupervisor.getFocusedStack();
+ if (!mService.isSleepingOrShuttingDown()) {
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
+ } else {
+ mStackSupervisor.checkReadyForSleepLocked();
+ ActivityRecord top = topStack.topRunningActivityLocked(null);
+ if (top == null || (prev != null && top != prev)) {
+ // If there are no more activities available to run,
+ // do resume anyway to start something. Also if the top
+ // activity on the stack is not the just paused activity,
+ // we need to go ahead and resume it to ensure we complete
+ // an in-flight app switch.
+ mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+ }
}
}
@@ -1607,10 +1633,10 @@
// We need to start pausing the current activity so the top one
// can be resumed...
- boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving);
+ boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;
+ boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
if (mResumedActivity != null) {
- pausing = true;
- startPausingLocked(userLeaving, false);
+ pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);
}
if (pausing) {
@@ -2720,7 +2746,7 @@
if (mPausingActivity == null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
- startPausingLocked(false, false);
+ startPausingLocked(false, false, false, false);
}
if (endTask) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 780efa1..f821daf 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -592,7 +592,7 @@
* @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
* @return true if any activity was paused as a result of this call.
*/
- boolean pauseBackStacks(boolean userLeaving) {
+ boolean pauseBackStacks(boolean userLeaving, boolean resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -601,8 +601,8 @@
if (!isFrontStack(stack) && stack.mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.mResumedActivity);
- stack.startPausingLocked(userLeaving, false);
- someActivityPaused = true;
+ someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
+ dontWait);
}
}
}
@@ -631,7 +631,8 @@
return pausing;
}
- void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping) {
+ void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping,
+ boolean resuming, boolean dontWait) {
// TODO: Put all stacks in supervisor and iterate through them instead.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -639,7 +640,7 @@
final ActivityStack stack = stacks.get(stackNdx);
if (stack.mResumedActivity != null &&
stack.mActivityContainer.mParentActivity == parent) {
- stack.startPausingLocked(userLeaving, uiSleeping);
+ stack.startPausingLocked(userLeaving, uiSleeping, resuming, dontWait);
}
}
}
@@ -1640,57 +1641,8 @@
}
}
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
- "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } 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
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (launchSingleInstance || launchSingleTask) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
-
- ActivityInfo newTaskInfo = null;
- Intent newTaskIntent = null;
- ActivityStack sourceStack;
- if (sourceRecord != null) {
- if (sourceRecord.finishing) {
- // If the source is finishing, we can't further count it as our source. This
- // is because the task it is associated with may now be empty and on its way out,
- // so we don't want to blindly throw it in to that task. Instead we will take
- // the NEW_TASK flow and try to find a task for it. But save the task information
- // so it can be used when creating the new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
- Slog.w(TAG, "startActivity called from finishing " + sourceRecord
- + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- newTaskInfo = sourceRecord.info;
- newTaskIntent = sourceRecord.task.intent;
- }
- sourceRecord = null;
- sourceStack = null;
- } else {
- sourceStack = sourceRecord.task.stack;
- }
- } else {
- sourceStack = null;
- }
-
boolean addingToTask = false;
- boolean movedHome = false;
TaskRecord reuseTask = null;
- ActivityStack targetStack;
-
- intent.setFlags(launchFlags);
// If the caller is not coming from another activity, but has given us an
// explicit task into which they would like us to launch the new activity,
@@ -1746,6 +1698,58 @@
inTask = null;
}
+ if (inTask == null) {
+ if (sourceRecord == null) {
+ // This activity is not being started from another... in this
+ // case we -always- start a new task.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
+ Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
+ "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ } 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
+ // own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ } else if (launchSingleInstance || launchSingleTask) {
+ // The activity being started is a single instance... it always
+ // gets launched into its own task.
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+ }
+
+ ActivityInfo newTaskInfo = null;
+ Intent newTaskIntent = null;
+ ActivityStack sourceStack;
+ if (sourceRecord != null) {
+ if (sourceRecord.finishing) {
+ // If the source is finishing, we can't further count it as our source. This
+ // is because the task it is associated with may now be empty and on its way out,
+ // so we don't want to blindly throw it in to that task. Instead we will take
+ // the NEW_TASK flow and try to find a task for it. But save the task information
+ // so it can be used when creating the new task.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+ + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ newTaskInfo = sourceRecord.info;
+ newTaskIntent = sourceRecord.task.intent;
+ }
+ sourceRecord = null;
+ sourceStack = null;
+ } else {
+ sourceStack = sourceRecord.task.stack;
+ }
+ } else {
+ sourceStack = null;
+ }
+
+ boolean movedHome = false;
+ ActivityStack targetStack;
+
+ intent.setFlags(launchFlags);
+
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
@@ -3840,7 +3844,7 @@
mContainerState = CONTAINER_STATE_NO_SURFACE;
((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
if (mStack.mPausingActivity == null && mStack.mResumedActivity != null) {
- mStack.startPausingLocked(false, true);
+ mStack.startPausingLocked(false, true, false, false);
}
}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index e03b9c8..44787e7 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -30,7 +30,6 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.BitmapFactory;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -391,6 +390,17 @@
}
@Override
+ protected void onPause() {
+ super.onPause();
+ Log.i(TAG, "I'm such a slooow poor loser");
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ }
+ Log.i(TAG, "See?");
+ }
+
+ @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mOverrideConfig != null) {