am f6a7e1f7: Merge "Don\'t wait for current activity to pause before resuming next."
* commit 'f6a7e1f7cd12fbbb2e35391850aec7d7d57b8f66':
Don't wait for current activity to pause before resuming next.
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e6a1e68..5c53902 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -138,6 +138,7 @@
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -3520,13 +3521,7 @@
}
// Just in case...
- if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) {
- if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity);
- mMainStack.mPausingActivity = null;
- }
- if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) {
- mMainStack.mLastPausedActivity = null;
- }
+ mMainStack.appDiedLocked(app);
// Remove this application's activities from active lists.
mMainStack.removeHistoryRecordsForAppLocked(app);
@@ -7231,7 +7226,7 @@
mMainStack.stopIfSleepingLocked();
final long endTime = System.currentTimeMillis() + timeout;
while (mMainStack.mResumedActivity != null
- || mMainStack.mPausingActivity != null) {
+ || mMainStack.mPausingActivities.size() > 0) {
long delay = endTime - System.currentTimeMillis();
if (delay <= 0) {
Slog.w(TAG, "Activity manager shutdown timed out");
@@ -9025,8 +9020,13 @@
}
pw.println(" ");
- if (mMainStack.mPausingActivity != null) {
- pw.println(" mPausingActivity: " + mMainStack.mPausingActivity);
+ if (mMainStack.mPausingActivities.size() > 0) {
+ pw.println(" mPausingActivities: " + Arrays.toString(
+ mMainStack.mPausingActivities.toArray()));
+ }
+ if (mMainStack.mInputPausedActivities.size() > 0) {
+ pw.println(" mInputPausedActivities: " + Arrays.toString(
+ mMainStack.mInputPausedActivities.toArray()));
}
pw.println(" mResumedActivity: " + mMainStack.mResumedActivity);
pw.println(" mFocusedActivity: " + mFocusedActivity);
@@ -14759,7 +14759,13 @@
private final ActivityRecord resumedAppLocked() {
ActivityRecord resumedActivity = mMainStack.mResumedActivity;
if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = mMainStack.mPausingActivity;
+ for (int i=mMainStack.mPausingActivities.size()-1; i>=0; i--) {
+ ActivityRecord r = mMainStack.mPausingActivities.get(i);
+ if (r.app != null) {
+ resumedActivity = r;
+ break;
+ }
+ }
if (resumedActivity == null || resumedActivity.app == null) {
resumedActivity = mMainStack.topRunningActivityLocked(null);
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index cdab6c6..977ee84 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -602,6 +602,7 @@
public void windowsDrawn() {
synchronized(service) {
+ stack.reportActivityDrawnLocked(this);
if (launchTime != 0) {
final long curTime = SystemClock.uptimeMillis();
final long thisTime = curTime - launchTime;
@@ -690,7 +691,9 @@
// Hmmm, who might we be waiting for?
r = stack.mResumedActivity;
if (r == null) {
- r = stack.mPausingActivity;
+ if (stack.mPausingActivities.size() > 0) {
+ r = stack.mPausingActivities.get(stack.mPausingActivities.size()-1);
+ }
}
// Both of those null? Fall back to 'this' again
if (r == null) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c3ae6a1..0d21760 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -227,7 +227,13 @@
* When we are in the process of pausing an activity, before starting the
* next one, this variable holds the activity that is currently being paused.
*/
- ActivityRecord mPausingActivity = null;
+ final ArrayList<ActivityRecord> mPausingActivities = new ArrayList<ActivityRecord>();
+
+ /**
+ * These activities currently have their input paused, as they want for
+ * the next top activity to have its windows visible.
+ */
+ final ArrayList<ActivityRecord> mInputPausedActivities = new ArrayList<ActivityRecord>();
/**
* This is the last activity that we put into the paused state. This is
@@ -807,9 +813,9 @@
startPausingLocked(false, true);
return;
}
- if (mPausingActivity != null) {
+ if (mPausingActivities.size() > 0) {
// Still waiting for something to pause; can't sleep yet.
- if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity);
+ if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivities);
return;
}
@@ -872,11 +878,6 @@
}
private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {
- if (mPausingActivity != null) {
- RuntimeException e = new RuntimeException();
- Slog.e(TAG, "Trying to pause when pause is already pending for "
- + mPausingActivity, e);
- }
ActivityRecord prev = mResumedActivity;
if (prev == null) {
RuntimeException e = new RuntimeException();
@@ -884,19 +885,25 @@
resumeTopActivityLocked(null);
return;
}
+ if (mPausingActivities.contains(prev)) {
+ RuntimeException e = new RuntimeException();
+ Slog.e(TAG, "Trying to pause when pause when already pausing " + prev, e);
+ }
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
else if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
mResumedActivity = null;
- mPausingActivity = prev;
+ mPausingActivities.add(prev);
mLastPausedActivity = prev;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
prev.updateThumbnail(screenshotActivities(prev), null);
mService.updateCpuStats();
+ ActivityRecord pausing;
if (prev.app != null && prev.app.thread != null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
+ pausing = prev;
try {
EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
System.identityHashCode(prev),
@@ -909,12 +916,14 @@
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
+ mPausingActivities.remove(prev);
mLastPausedActivity = null;
+ pausing = null;
}
} else {
- mPausingActivity = null;
+ mPausingActivities.remove(prev);
mLastPausedActivity = null;
+ pausing = null;
}
// If we are not going to sleep, we want to ensure the device is
@@ -928,18 +937,28 @@
}
}
-
- if (mPausingActivity != null) {
+ if (pausing != null) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we're pausing the activity just because
// the screen is being turned off and the UI is sleeping, don't interrupt
// key dispatch; the same activity will pick it up again on wakeup.
if (!uiSleeping) {
- prev.pauseKeyDispatchingLocked();
+ pausing.pauseKeyDispatchingLocked();
+ mInputPausedActivities.add(prev);
} else {
if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
}
+ if (pausing.configDestroy) {
+ // The previous is being paused because the configuration
+ // is changing, which means it is actually stopping...
+ // To juggle the fact that we are also starting a new
+ // instance right now, we need to first completely stop
+ // the current instance before starting the new one.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Destroying at pause: " + prev);
+ destroyActivityLocked(pausing, true, false, "pause-config");
+ }
+
// 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.
@@ -951,7 +970,10 @@
// 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.");
- resumeTopActivityLocked(null);
+ }
+
+ if (!mService.isSleeping()) {
+ resumeTopActivityLocked(pausing);
}
}
@@ -966,16 +988,17 @@
if (index >= 0) {
r = mHistory.get(index);
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
- if (mPausingActivity == r) {
+ if (mPausingActivities.contains(r)) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
r.state = ActivityState.PAUSED;
- completePauseLocked();
+ completePauseLocked(r);
} else {
+ ActivityRecord old = mPausingActivities.size() > 0
+ ? mPausingActivities.get(0) : null;
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
System.identityHashCode(r), r.shortComponentName,
- mPausingActivity != null
- ? mPausingActivity.shortComponentName : "(none)");
+ old != null ? old.shortComponentName : "(none)");
}
}
}
@@ -1001,8 +1024,14 @@
ProcessRecord fgApp = null;
if (mResumedActivity != null) {
fgApp = mResumedActivity.app;
- } else if (mPausingActivity != null) {
- fgApp = mPausingActivity.app;
+ } else {
+ for (int i=mPausingActivities.size()-1; i>=0; i--) {
+ ActivityRecord pausing = mPausingActivities.get(i);
+ if (pausing.app == r.app) {
+ fgApp = pausing.app;
+ break;
+ }
+ }
}
if (r.app != null && fgApp != null && r.app != fgApp
&& r.lastVisibleTime > mService.mPreviousProcessVisibleTime
@@ -1014,57 +1043,48 @@
}
}
- private final void completePauseLocked() {
- ActivityRecord prev = mPausingActivity;
+ private final void completePauseLocked(ActivityRecord prev) {
if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
- prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
- } else if (prev.app != null) {
- if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
- if (prev.waitingVisible) {
- prev.waitingVisible = false;
- mWaitingVisibleActivities.remove(prev);
- if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
- TAG, "Complete pause, no longer waiting: " + prev);
- }
- if (prev.configDestroy) {
- // The previous is being paused because the configuration
- // is changing, which means it is actually stopping...
- // To juggle the fact that we are also starting a new
- // instance right now, we need to first completely stop
- // the current instance before starting the new one.
- if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
- destroyActivityLocked(prev, true, false, "pause-config");
- } else {
- mStoppingActivities.add(prev);
- if (mStoppingActivities.size() > 3) {
- // If we already have a few activities waiting to stop,
- // then give up on things going idle and start clearing
- // them out.
- if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
- scheduleIdleLocked();
- } else {
- checkReadyForSleepLocked();
- }
- }
- } else {
- if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
- prev = null;
+ if (prev.finishing) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
+ prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);
+ } else if (prev.app != null) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
+ if (prev.waitingVisible) {
+ prev.waitingVisible = false;
+ mWaitingVisibleActivities.remove(prev);
+ if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
+ TAG, "Complete pause, no longer waiting: " + prev);
}
- mPausingActivity = null;
- }
-
- if (!mService.isSleeping()) {
- resumeTopActivityLocked(prev);
+ if (prev.configDestroy) {
+ // The previous is being paused because the configuration
+ // is changing, which means it is actually stopping...
+ // To juggle the fact that we are also starting a new
+ // instance right now, we need to first completely stop
+ // the current instance before starting the new one.
+ if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
+ destroyActivityLocked(prev, true, false, "pause-config");
+ } else {
+ mStoppingActivities.add(prev);
+ if (mStoppingActivities.size() > 3) {
+ // If we already have a few activities waiting to stop,
+ // then give up on things going idle and start clearing
+ // them out.
+ if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
+ scheduleIdleLocked();
+ } else {
+ checkReadyForSleepLocked();
+ }
+ }
} else {
- checkReadyForSleepLocked();
+ if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
+ prev = null;
}
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
+ mPausingActivities.remove(prev);
+
+ if (mService.isSleeping()) {
+ checkReadyForSleepLocked();
}
if (prev.app != null && prev.cpuTimeAtResume > 0
@@ -1122,7 +1142,9 @@
if (mMainStack) {
mService.setFocusedActivityLocked(next);
}
- next.resumeKeyDispatchingLocked();
+ if (mInputPausedActivities.remove(next)) {
+ next.resumeKeyDispatchingLocked();
+ }
ensureActivitiesVisibleLocked(null, 0);
mService.mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
@@ -1352,13 +1374,6 @@
if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
- // If we are currently pausing an activity, then don't do anything
- // until that is done.
- if (mPausingActivity != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity);
- return false;
- }
-
// Okay we are now going to start a switch, to 'next'. We may first
// have to pause the current activity, but this is an important point
// where we have decided to go to 'next' so keep track of that.
@@ -2440,7 +2455,7 @@
err = startActivityUncheckedLocked(r, sourceRecord,
grantedUriPermissions, grantedMode, onlyIfNeeded, true);
- if (mDismissKeyguardOnNextActivity && mPausingActivity == null) {
+ if (mDismissKeyguardOnNextActivity && mPausingActivities.size() == 0) {
// Someone asked to have the keyguard dismissed on the next
// activity start, but we are not actually doing an activity
// switch... just dismiss the keyguard now, because we
@@ -3111,7 +3126,18 @@
}
mService.notifyAll();
}
-
+
+ void reportActivityDrawnLocked(ActivityRecord r) {
+ if (mResumedActivity == r) {
+ // Once the resumed activity has been drawn, we can stop
+ // pausing input on all other activities.
+ for (int i=mInputPausedActivities.size()-1; i>=0; i--) {
+ mInputPausedActivities.get(i).resumeKeyDispatchingLocked();
+ }
+ mInputPausedActivities.clear();
+ }
+ }
+
void reportActivityVisibleLocked(ActivityRecord r) {
for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) {
WaitResult w = mWaitingActivityVisible.get(i);
@@ -3170,7 +3196,9 @@
mService.setFocusedActivityLocked(topRunningActivityLocked(null));
}
}
- r.resumeKeyDispatchingLocked();
+ if (mInputPausedActivities.remove(r)) {
+ r.resumeKeyDispatchingLocked();
+ }
try {
r.stopped = false;
if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r
@@ -3496,7 +3524,7 @@
// Tell window manager to prepare for this one to be removed.
mService.mWindowManager.setAppVisibility(r.appToken, false);
- if (mPausingActivity == null) {
+ if (!mPausingActivities.contains(r)) {
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);
@@ -3580,6 +3608,20 @@
return r;
}
+ final void appDiedLocked(ProcessRecord app) {
+ for (int i=mPausingActivities.size()-1; i>=0; i--) {
+ ActivityRecord r = mPausingActivities.get(i);
+ if (r.app == app) {
+ if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + r);
+ mPausingActivities.remove(i);
+ mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ }
+ }
+ if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+ mLastPausedActivity = null;
+ }
+ }
+
/**
* Perform the common clean-up of an activity record. This is called both
* as part of destroyActivityLocked() (when destroying the client-side