diff options
-rw-r--r-- | api/current.txt | 2 | ||||
-rw-r--r-- | api/removed.txt | 16 | ||||
-rw-r--r-- | core/java/android/app/ActivityManager.java | 21 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 6 | ||||
-rw-r--r-- | core/java/android/app/ActivityThread.java | 20 | ||||
-rw-r--r-- | core/java/android/app/ApplicationThreadNative.java | 6 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 2 | ||||
-rw-r--r-- | core/java/android/app/IApplicationThread.java | 2 | ||||
-rw-r--r-- | core/java/android/content/pm/ActivityInfo.java | 10 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 6 | ||||
-rw-r--r-- | core/res/res/values/attrs_manifest.xml | 12 | ||||
-rw-r--r-- | core/res/res/values/public.xml | 2 | ||||
-rw-r--r-- | packages/SystemUI/AndroidManifest.xml | 3 | ||||
-rwxr-xr-x | services/core/java/com/android/server/am/ActivityManagerService.java | 4 | ||||
-rwxr-xr-x | services/core/java/com/android/server/am/ActivityStack.java | 118 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/ActivityStackSupervisor.java | 114 | ||||
-rw-r--r-- | tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java | 12 |
17 files changed, 209 insertions, 147 deletions
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 @@ package android { 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 @@ package android.content.pm { 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 android.view.inputmethod { } -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 @@ public class ActivityManager { /** * 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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ class ActivityManagerProxy implements IActivityManager 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 class ActivityThread { } 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 @@ public final class ActivityThread { } 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 @@ public final class ActivityThread { } 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 @@ public final class ActivityThread { } // 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..0123e16 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -78,7 +78,8 @@ public abstract class ApplicationThreadNative extends Binder 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 @@ class ApplicationThreadProxy implements IApplicationThread { } 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 interface IActivityManager extends IInterface { 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 @@ import java.util.Map; */ 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 @@ public class ActivityInfo extends ComponentInfo * 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 @@ public class PackageParser { 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..10c2518 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 @@ public final class ActivityManagerService extends ActivityManagerNative } @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 @@ final class ActivityStack { 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { } } - 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { * @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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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.ContentProviderClient; 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 @@ public class ActivityTestMain extends Activity { } @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) { |