Merge "Revert "Don't wait for current activity to pause before resuming next.""
This commit is contained in:
committed by
Android (Google) Code Review
commit
b9ee4f4425
@ -138,7 +138,6 @@ import java.io.StringWriter;
|
||||
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;
|
||||
@ -2752,7 +2751,13 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
|
||||
// Just in case...
|
||||
mMainStack.appDiedLocked(app);
|
||||
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;
|
||||
}
|
||||
|
||||
// Remove this application's activities from active lists.
|
||||
mMainStack.removeHistoryRecordsForAppLocked(app);
|
||||
@ -6488,7 +6493,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
mMainStack.stopIfSleepingLocked();
|
||||
final long endTime = System.currentTimeMillis() + timeout;
|
||||
while (mMainStack.mResumedActivity != null
|
||||
|| mMainStack.mPausingActivities.size() > 0) {
|
||||
|| mMainStack.mPausingActivity != null) {
|
||||
long delay = endTime - System.currentTimeMillis();
|
||||
if (delay <= 0) {
|
||||
Slog.w(TAG, "Activity manager shutdown timed out");
|
||||
@ -8295,13 +8300,8 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
|
||||
pw.println(" ");
|
||||
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()));
|
||||
if (mMainStack.mPausingActivity != null) {
|
||||
pw.println(" mPausingActivity: " + mMainStack.mPausingActivity);
|
||||
}
|
||||
pw.println(" mResumedActivity: " + mMainStack.mResumedActivity);
|
||||
pw.println(" mFocusedActivity: " + mFocusedActivity);
|
||||
@ -13876,13 +13876,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
private final ActivityRecord resumedAppLocked() {
|
||||
ActivityRecord resumedActivity = mMainStack.mResumedActivity;
|
||||
if (resumedActivity == null || resumedActivity.app == null) {
|
||||
for (int i=mMainStack.mPausingActivities.size()-1; i>=0; i--) {
|
||||
ActivityRecord r = mMainStack.mPausingActivities.get(i);
|
||||
if (r.app != null) {
|
||||
resumedActivity = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
resumedActivity = mMainStack.mPausingActivity;
|
||||
if (resumedActivity == null || resumedActivity.app == null) {
|
||||
resumedActivity = mMainStack.topRunningActivityLocked(null);
|
||||
}
|
||||
|
@ -602,7 +602,6 @@ final class ActivityRecord {
|
||||
|
||||
public void windowsDrawn() {
|
||||
synchronized(service) {
|
||||
stack.reportActivityDrawnLocked(this);
|
||||
if (launchTime != 0) {
|
||||
final long curTime = SystemClock.uptimeMillis();
|
||||
final long thisTime = curTime - launchTime;
|
||||
@ -691,9 +690,7 @@ final class ActivityRecord {
|
||||
// Hmmm, who might we be waiting for?
|
||||
r = stack.mResumedActivity;
|
||||
if (r == null) {
|
||||
if (stack.mPausingActivities.size() > 0) {
|
||||
r = stack.mPausingActivities.get(stack.mPausingActivities.size()-1);
|
||||
}
|
||||
r = stack.mPausingActivity;
|
||||
}
|
||||
// Both of those null? Fall back to 'this' again
|
||||
if (r == null) {
|
||||
|
@ -227,13 +227,7 @@ final class ActivityStack {
|
||||
* 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.
|
||||
*/
|
||||
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>();
|
||||
ActivityRecord mPausingActivity = null;
|
||||
|
||||
/**
|
||||
* This is the last activity that we put into the paused state. This is
|
||||
@ -813,9 +807,9 @@ final class ActivityStack {
|
||||
startPausingLocked(false, true);
|
||||
return;
|
||||
}
|
||||
if (mPausingActivities.size() > 0) {
|
||||
if (mPausingActivity != null) {
|
||||
// Still waiting for something to pause; can't sleep yet.
|
||||
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivities);
|
||||
if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -878,6 +872,11 @@ final class ActivityStack {
|
||||
}
|
||||
|
||||
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();
|
||||
@ -885,25 +884,19 @@ final class ActivityStack {
|
||||
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;
|
||||
mPausingActivities.add(prev);
|
||||
mPausingActivity = 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),
|
||||
@ -916,14 +909,12 @@ final class ActivityStack {
|
||||
} catch (Exception e) {
|
||||
// Ignore exception, if process died other code will cleanup.
|
||||
Slog.w(TAG, "Exception thrown during pause", e);
|
||||
mPausingActivities.remove(prev);
|
||||
mPausingActivity = null;
|
||||
mLastPausedActivity = null;
|
||||
pausing = null;
|
||||
}
|
||||
} else {
|
||||
mPausingActivities.remove(prev);
|
||||
mPausingActivity = null;
|
||||
mLastPausedActivity = null;
|
||||
pausing = null;
|
||||
}
|
||||
|
||||
// If we are not going to sleep, we want to ensure the device is
|
||||
@ -937,28 +928,18 @@ final class ActivityStack {
|
||||
}
|
||||
}
|
||||
|
||||
if (pausing != null) {
|
||||
|
||||
if (mPausingActivity != 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) {
|
||||
pausing.pauseKeyDispatchingLocked();
|
||||
mInputPausedActivities.add(prev);
|
||||
prev.pauseKeyDispatchingLocked();
|
||||
} 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.
|
||||
@ -970,10 +951,7 @@ final class ActivityStack {
|
||||
// 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.");
|
||||
}
|
||||
|
||||
if (!mService.isSleeping()) {
|
||||
resumeTopActivityLocked(pausing);
|
||||
resumeTopActivityLocked(null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -988,17 +966,16 @@ final class ActivityStack {
|
||||
if (index >= 0) {
|
||||
r = mHistory.get(index);
|
||||
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
|
||||
if (mPausingActivities.contains(r)) {
|
||||
if (mPausingActivity == r) {
|
||||
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
|
||||
+ (timeout ? " (due to timeout)" : " (pause complete)"));
|
||||
r.state = ActivityState.PAUSED;
|
||||
completePauseLocked(r);
|
||||
completePauseLocked();
|
||||
} else {
|
||||
ActivityRecord old = mPausingActivities.size() > 0
|
||||
? mPausingActivities.get(0) : null;
|
||||
EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
|
||||
System.identityHashCode(r), r.shortComponentName,
|
||||
old != null ? old.shortComponentName : "(none)");
|
||||
mPausingActivity != null
|
||||
? mPausingActivity.shortComponentName : "(none)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1024,14 +1001,8 @@ final class ActivityStack {
|
||||
ProcessRecord fgApp = null;
|
||||
if (mResumedActivity != null) {
|
||||
fgApp = mResumedActivity.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;
|
||||
}
|
||||
}
|
||||
} else if (mPausingActivity != null) {
|
||||
fgApp = mPausingActivity.app;
|
||||
}
|
||||
if (r.app != null && fgApp != null && r.app != fgApp
|
||||
&& r.lastVisibleTime > mService.mPreviousProcessVisibleTime
|
||||
@ -1043,49 +1014,58 @@ final class ActivityStack {
|
||||
}
|
||||
}
|
||||
|
||||
private final void completePauseLocked(ActivityRecord prev) {
|
||||
private final void completePauseLocked() {
|
||||
ActivityRecord prev = mPausingActivity;
|
||||
if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
|
||||
|
||||
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();
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
|
||||
prev = null;
|
||||
mPausingActivity = null;
|
||||
}
|
||||
mPausingActivities.remove(prev);
|
||||
|
||||
if (mService.isSleeping()) {
|
||||
if (!mService.isSleeping()) {
|
||||
resumeTopActivityLocked(prev);
|
||||
} else {
|
||||
checkReadyForSleepLocked();
|
||||
}
|
||||
|
||||
if (prev != null) {
|
||||
prev.resumeKeyDispatchingLocked();
|
||||
}
|
||||
|
||||
if (prev.app != null && prev.cpuTimeAtResume > 0
|
||||
&& mService.mBatteryStatsService.isOnBattery()) {
|
||||
@ -1142,9 +1122,7 @@ final class ActivityStack {
|
||||
if (mMainStack) {
|
||||
mService.setFocusedActivityLocked(next);
|
||||
}
|
||||
if (mInputPausedActivities.remove(next)) {
|
||||
next.resumeKeyDispatchingLocked();
|
||||
}
|
||||
next.resumeKeyDispatchingLocked();
|
||||
ensureActivitiesVisibleLocked(null, 0);
|
||||
mService.mWindowManager.executeAppTransition();
|
||||
mNoAnimActivities.clear();
|
||||
@ -1374,6 +1352,13 @@ final class ActivityStack {
|
||||
|
||||
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.
|
||||
@ -2455,7 +2440,7 @@ final class ActivityStack {
|
||||
|
||||
err = startActivityUncheckedLocked(r, sourceRecord,
|
||||
grantedUriPermissions, grantedMode, onlyIfNeeded, true);
|
||||
if (mDismissKeyguardOnNextActivity && mPausingActivities.size() == 0) {
|
||||
if (mDismissKeyguardOnNextActivity && mPausingActivity == null) {
|
||||
// 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
|
||||
@ -3126,18 +3111,7 @@ final class ActivityStack {
|
||||
}
|
||||
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);
|
||||
@ -3196,9 +3170,7 @@ final class ActivityStack {
|
||||
mService.setFocusedActivityLocked(topRunningActivityLocked(null));
|
||||
}
|
||||
}
|
||||
if (mInputPausedActivities.remove(r)) {
|
||||
r.resumeKeyDispatchingLocked();
|
||||
}
|
||||
r.resumeKeyDispatchingLocked();
|
||||
try {
|
||||
r.stopped = false;
|
||||
if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r
|
||||
@ -3524,7 +3496,7 @@ final class ActivityStack {
|
||||
// Tell window manager to prepare for this one to be removed.
|
||||
mService.mWindowManager.setAppVisibility(r.appToken, false);
|
||||
|
||||
if (!mPausingActivities.contains(r)) {
|
||||
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);
|
||||
@ -3608,20 +3580,6 @@ final class ActivityStack {
|
||||
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
|
||||
|
Reference in New Issue
Block a user