diff options
author | Craig Mautner <cmautner@google.com> | 2013-04-13 00:05:50 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-04-13 00:05:50 +0000 |
commit | fd7e534b1f783c6bdd0e27196071d33fb14a7d57 (patch) | |
tree | 6f6fd4039cdb3df4d91d33e2fbf4b9088a1898f5 /services | |
parent | 0f6ba077321ef275649fdcc29c0b323e9740d739 (diff) | |
parent | de4ef020ec5c3acdc90c4ba43011dda20d98d4dd (diff) | |
download | frameworks_base-fd7e534b1f783c6bdd0e27196071d33fb14a7d57.zip frameworks_base-fd7e534b1f783c6bdd0e27196071d33fb14a7d57.tar.gz frameworks_base-fd7e534b1f783c6bdd0e27196071d33fb14a7d57.tar.bz2 |
Merge "Implement separate stacks."
Diffstat (limited to 'services')
11 files changed, 628 insertions, 472 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index e86340d..74b838f 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -2610,7 +2610,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { // If this is coming from the currently resumed activity, it is // effectively saying that app switches are allowed at this point. - final ActivityStack stack = getMainStack(); + final ActivityStack stack = getTopStack(); if (stack.mResumedActivity != null && stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) { mAppSwitchesAllowedTime = 0; @@ -5898,6 +5898,8 @@ public final class ActivityManagerService extends ActivityManagerNative } private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) { + mRecentTasks.remove(tr); + mStackSupervisor.removeTask(tr); final boolean killProcesses = (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0; Intent baseIntent = new Intent( tr.intent != null ? tr.intent : tr.affinityIntent); @@ -5955,14 +5957,12 @@ public final class ActivityManagerService extends ActivityManagerNative if (tr != null) { ActivityRecord r = tr.stack.removeTaskActivitiesLocked(taskId, -1, false); if (r != null) { - mRecentTasks.remove(tr); cleanUpRemovedTaskLocked(tr, flags); return true; } if (tr.mActivities.size() == 0) { // Caller is just removing a recent task that is // not actively running. That is easy! - mRecentTasks.remove(tr); cleanUpRemovedTaskLocked(tr, flags); return true; } @@ -6003,12 +6003,12 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public void moveTaskToBack(int task) { + public void moveTaskToBack(int taskId) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToBack()"); synchronized(this) { - TaskRecord tr = recentTaskForIdLocked(task); + TaskRecord tr = recentTaskForIdLocked(taskId); if (tr != null) { ActivityStack stack = tr.stack; if (stack.mResumedActivity != null && stack.mResumedActivity.task == tr) { @@ -6018,7 +6018,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } final long origId = Binder.clearCallingIdentity(); - stack.moveTaskToBackLocked(task, null); + stack.moveTaskToBackLocked(taskId, null); Binder.restoreCallingIdentity(origId); } } @@ -6071,7 +6071,7 @@ public final class ActivityManagerService extends ActivityManagerNative public int createStack(int relativeStackId, int position, float weight) { synchronized (this) { int stackId = mStackSupervisor.createStack(relativeStackId, position, weight); - mWindowManager.createStack(stackId, position, relativeStackId, weight); + mWindowManager.createStack(stackId, relativeStackId, position, weight); return stackId; } } @@ -7003,7 +7003,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized(this) { final long origId = Binder.clearCallingIdentity(); try { - getMainStack().unhandledBackLocked(); + getTopStack().unhandledBackLocked(); } finally { Binder.restoreCallingIdentity(origId); } @@ -7393,7 +7393,7 @@ public final class ActivityManagerService extends ActivityManagerNative PendingActivityExtras pae; Bundle extras = new Bundle(); synchronized (this) { - ActivityRecord activity = getMainStack().mResumedActivity; + ActivityRecord activity = getTopStack().mResumedActivity; if (activity == null) { Slog.w(TAG, "getTopActivityExtras failed: no resumed activity"); return null; @@ -7489,7 +7489,7 @@ public final class ActivityManagerService extends ActivityManagerNative public boolean isTopActivityImmersive() { enforceNotIsolatedCaller("startActivity"); synchronized (this) { - ActivityRecord r = getMainStack().topRunningActivityLocked(null); + ActivityRecord r = getTopStack().topRunningActivityLocked(null); return (r != null) ? r.immersive : false; } } @@ -9391,7 +9391,7 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(" mConfiguration: " + mConfiguration); if (dumpAll) { - pw.println(" mConfigWillChange: " + getMainStack().mConfigWillChange); + pw.println(" mConfigWillChange: " + getTopStack().mConfigWillChange); if (mCompatModePackages.getPackages().size() > 0) { boolean printed = false; for (Map.Entry<String, Integer> entry @@ -9451,8 +9451,8 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" mLastPowerCheckUptime="); TimeUtils.formatDuration(mLastPowerCheckUptime, pw); pw.println(""); - pw.println(" mGoingToSleep=" + getMainStack().mGoingToSleep); - pw.println(" mLaunchingActivity=" + getMainStack().mLaunchingActivity); + pw.println(" mGoingToSleep=" + getTopStack().mGoingToSleep); + pw.println(" mLaunchingActivity=" + getTopStack().mLaunchingActivity); pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq); pw.println(" mNumNonHiddenProcs=" + mNumNonHiddenProcs + " mNumHiddenProcs=" + mNumHiddenProcs @@ -12185,8 +12185,8 @@ public final class ActivityManagerService extends ActivityManagerNative return config; } - ActivityStack getMainStack() { - return mStackSupervisor.getMainStack(); + ActivityStack getTopStack() { + return mStackSupervisor.getTopStack(); } public Configuration getConfiguration() { @@ -12250,9 +12250,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (mHeadless) return true; int changes = 0; - - boolean kept = true; - + if (values != null) { Configuration newConfig = new Configuration(mConfiguration); changes = newConfig.updateFrom(values); @@ -12331,7 +12329,21 @@ public final class ActivityManagerService extends ActivityManagerNative } } - kept = mStackSupervisor.updateConfigurationLocked(changes, starting); + boolean kept = true; + final ActivityStack mainStack = mStackSupervisor.getTopStack(); + if (changes != 0 && starting == null) { + // If the configuration changed, and the caller is not already + // in the process of starting an activity, then find the top + // activity to check if its configuration needs to change. + starting = mainStack.topRunningActivityLocked(null); + } + + if (starting != null) { + kept = mainStack.ensureActivityConfigurationLocked(starting, changes); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes); + } if (values != null && mWindowManager != null) { mWindowManager.setNewConfiguration(mConfiguration); @@ -13409,15 +13421,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private final ActivityRecord resumedAppLocked() { - final ActivityStack stack = mStackSupervisor.getMainStack(); - ActivityRecord resumedActivity = stack.mResumedActivity; - if (resumedActivity == null || resumedActivity.app == null) { - resumedActivity = stack.mPausingActivity; - if (resumedActivity == null || resumedActivity.app == null) { - resumedActivity = stack.topRunningActivityLocked(null); - } - } - return resumedActivity; + return mStackSupervisor.resumedAppLocked(); } final boolean updateOomAdjLocked(ProcessRecord app) { diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 3384c20..351d329 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -127,8 +127,12 @@ final class ActivityRecord { long lastLaunchTime; // time of last lauch of this activity String stringName; // for caching of toString(). - + private boolean inHistory; // are we in the history stack? + final ActivityStackSupervisor mStackSupervisor; + + /** Launch the home activity rather than the activity at the top of stack */ + boolean mLaunchHomeTaskNext; void dump(PrintWriter pw, String prefix) { final long now = SystemClock.uptimeMillis(); @@ -268,28 +272,28 @@ final class ActivityRecord { weakActivity = new WeakReference<ActivityRecord>(activity); } - @Override public void windowsDrawn() throws RemoteException { + @Override public void windowsDrawn() { ActivityRecord activity = weakActivity.get(); if (activity != null) { activity.windowsDrawn(); } } - @Override public void windowsVisible() throws RemoteException { + @Override public void windowsVisible() { ActivityRecord activity = weakActivity.get(); if (activity != null) { activity.windowsVisible(); } } - @Override public void windowsGone() throws RemoteException { + @Override public void windowsGone() { ActivityRecord activity = weakActivity.get(); if (activity != null) { activity.windowsGone(); } } - @Override public boolean keyDispatchingTimedOut() throws RemoteException { + @Override public boolean keyDispatchingTimedOut() { ActivityRecord activity = weakActivity.get(); if (activity != null) { return activity.keyDispatchingTimedOut(); @@ -297,7 +301,7 @@ final class ActivityRecord { return false; } - @Override public long getKeyDispatchingTimeout() throws RemoteException { + @Override public long getKeyDispatchingTimeout() { ActivityRecord activity = weakActivity.get(); if (activity != null) { return activity.getKeyDispatchingTimeout(); @@ -305,6 +309,7 @@ final class ActivityRecord { return 0; } + @Override public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("Token{"); @@ -329,7 +334,7 @@ final class ActivityRecord { int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, - boolean _componentSpecified) { + boolean _componentSpecified, ActivityStackSupervisor supervisor) { service = _service; appToken = new Token(this); info = aInfo; @@ -359,6 +364,7 @@ final class ActivityRecord { thumbnailNeeded = false; idle = false; hasBeenLaunched = false; + mStackSupervisor = supervisor; // This starts out true, since the initial state of an activity // is that we have everything, and we shouldn't never consider it @@ -466,6 +472,9 @@ final class ActivityRecord { } void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) { + if (task != null && task.removeActivity(this)) { + mStackSupervisor.removeTask(task); + } if (inHistory && !finishing) { if (task != null) { task.numActivities--; @@ -524,6 +533,11 @@ final class ActivityRecord { } } + boolean isRootActivity() { + ArrayList<ActivityRecord> activities = task.mActivities; + return activities.size() == 0 || this == task.mActivities.get(0); + } + UriPermissionOwner getUriPermissionsLocked() { if (uriPermissions == null) { uriPermissions = new UriPermissionOwner(service, this); @@ -814,23 +828,23 @@ final class ActivityRecord { // Instead of doing the full stop routine here, let's just // hide any activities we now can, and let them stop when // the normal idle happens. - stack.processStoppingActivitiesLocked(false); + mStackSupervisor.processStoppingActivitiesLocked(false); } else { // If this activity was already idle, then we now need to // make sure we perform the full stop of any activities // that are waiting to do so. This is because we won't // do that while they are still waiting for this one to // become visible. - final int N = stack.mWaitingVisibleActivities.size(); + final int N = mStackSupervisor.mWaitingVisibleActivities.size(); if (N > 0) { for (int i=0; i<N; i++) { - ActivityRecord r = stack.mWaitingVisibleActivities.get(i); + ActivityRecord r = mStackSupervisor.mWaitingVisibleActivities.get(i); r.waitingVisible = false; if (ActivityManagerService.DEBUG_SWITCH) Log.v( ActivityManagerService.TAG, "Was waiting for visible: " + r); } - stack.mWaitingVisibleActivities.clear(); + mStackSupervisor.mWaitingVisibleActivities.clear(); Message msg = Message.obtain(); msg.what = ActivityStack.IDLE_NOW_MSG; stack.mHandler.sendMessage(msg); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index ca3da0b..568689f 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -16,6 +16,8 @@ package com.android.server.am; +import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; + import com.android.internal.os.BatteryStatsImpl; import com.android.internal.util.Objects; import com.android.server.am.ActivityManagerService.ItemMatcher; @@ -73,7 +75,7 @@ final class ActivityStack { static final boolean localLOGV = ActivityManagerService.localLOGV; static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH; static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE; - static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY; + static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY || true; static final boolean DEBUG_USER_LEAVING = ActivityManagerService.DEBUG_USER_LEAVING; static final boolean DEBUG_TRANSITION = ActivityManagerService.DEBUG_TRANSITION; static final boolean DEBUG_RESULTS = ActivityManagerService.DEBUG_RESULTS; @@ -162,22 +164,6 @@ final class ActivityStack { final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<ActivityRecord>(); /** - * List of activities that are waiting for a new activity - * to become visible before completing whatever operation they are - * supposed to do. - */ - final ArrayList<ActivityRecord> mWaitingVisibleActivities - = new ArrayList<ActivityRecord>(); - - /** - * List of activities that are ready to be stopped, but waiting - * for the next activity to settle down before doing so. It contains - * HistoryRecord objects. - */ - final ArrayList<ActivityRecord> mStoppingActivities - = new ArrayList<ActivityRecord>(); - - /** * List of activities that are in the process of going to sleep. */ final ArrayList<ActivityRecord> mGoingToSleepActivities @@ -259,12 +245,6 @@ final class ActivityStack { */ boolean mConfigWillChange; - /** - * Set to indicate whether to issue an onUserLeaving callback when a - * newly launched activity is being brought in front of us. - */ - boolean mUserLeaving = false; - long mInitialStartTime = 0; /** @@ -409,7 +389,7 @@ final class ActivityStack { } break; case RESUME_TOP_ACTIVITY_MSG: { synchronized (mService) { - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); } } break; case STOP_TIMEOUT_MSG: { @@ -518,16 +498,14 @@ final class ActivityStack { final ActivityRecord topActivity() { // Iterate to find the first non-empty task stack. Note that this code can - // go away once we stop storing tasks with empty mActivities lists. - ActivityRecord prev = null; + // be simplified once we stop storing tasks with empty mActivities lists. for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - prev = activities.get(activityNdx); - break; + return activities.get(activityNdx); } } - return prev; + return null; } TaskRecord taskForIdLocked(int id) { @@ -559,6 +537,10 @@ final class ActivityStack { return hadit; } + final boolean isHomeStack() { + return mStackId == HOME_STACK_ID; + } + /** * Returns the top activity in any existing task matching the given * Intent. Returns null if no such task is found. @@ -747,10 +729,10 @@ final class ActivityStack { return; } - if (mStoppingActivities.size() > 0) { + if (mStackSupervisor.mStoppingActivities.size() > 0) { // Still need to tell some activities to stop; can't sleep yet. if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop " - + mStoppingActivities.size() + " activities"); + + mStackSupervisor.mStoppingActivities.size() + " activities"); scheduleIdleLocked(); return; } @@ -826,7 +808,7 @@ final class ActivityStack { if (prev == null) { Slog.e(TAG, "Trying to pause when nothing is resumed", new RuntimeException("here").fillInStackTrace()); - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); return; } if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev); @@ -848,7 +830,7 @@ final class ActivityStack { prev.shortComponentName); prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing, userLeaving, prev.configChangeFlags); - if (mStackSupervisor.isMainStack(this)) { + if (mStackSupervisor.isFrontStack(this)) { mService.updateUsageStats(prev, false); } } catch (Exception e) { @@ -896,7 +878,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."); - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); } } @@ -954,7 +936,7 @@ final class ActivityStack { } else { if (r.configDestroy) { destroyActivityLocked(r, true, false, "stop-config"); - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); } else { // Now that this process has stopped, we may want to consider // it to be the previous app to try to keep around in case @@ -988,7 +970,7 @@ final class ActivityStack { if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev); if (prev.waitingVisible) { prev.waitingVisible = false; - mWaitingVisibleActivities.remove(prev); + mStackSupervisor.mWaitingVisibleActivities.remove(prev); if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v( TAG, "Complete pause, no longer waiting: " + prev); } @@ -1001,8 +983,8 @@ final class ActivityStack { if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev); destroyActivityLocked(prev, true, false, "pause-config"); } else { - mStoppingActivities.add(prev); - if (mStoppingActivities.size() > 3) { + mStackSupervisor.mStoppingActivities.add(prev); + if (mStackSupervisor.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. @@ -1019,18 +1001,19 @@ final class ActivityStack { mPausingActivity = null; } + final ActivityStack topStack = mStackSupervisor.getTopStack(); if (!mService.isSleepingOrShuttingDown()) { - resumeTopActivityLocked(prev); + topStack.resumeTopActivityLocked(prev); } else { checkReadyForSleepLocked(); - ActivityRecord top = topRunningActivityLocked(null); + 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. - resumeTopActivityLocked(null); + topStack.resumeTopActivityLocked(null); } } @@ -1086,18 +1069,17 @@ final class ActivityStack { mHandler.sendMessage(msg); } - if (mStackSupervisor.isMainStack(this)) { - // Should this be done for all stacks, not just mMainStack? + if (mStackSupervisor.isFrontStack(this)) { + // TODO: Should this be done for all stacks, not just mMainStack? mService.reportResumedActivityLocked(next); - } - - if (mStackSupervisor.isMainStack(this)) { mService.setFocusedActivityLocked(next); } - next.resumeKeyDispatchingLocked(); - ensureActivitiesVisibleLocked(null, 0); - mService.mWindowManager.executeAppTransition(); - mNoAnimActivities.clear(); + if (mStackSupervisor.allResumedActivitiesComplete()) { + next.resumeKeyDispatchingLocked(); + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); + mService.mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); + } // Mark the point when the activity is resuming // TODO: To be more accurate, the mark should be before the onCreate, @@ -1125,8 +1107,9 @@ final class ActivityStack { // If the top activity is not fullscreen, then we need to // make sure any activities under it are now visible. boolean aboveTop = true; - boolean behindFullscreen = false; - for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + boolean behindFullscreen = !mStackSupervisor.isFrontStack(this); + int taskNdx; + for (taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); @@ -1152,13 +1135,11 @@ final class ActivityStack { } if (r.app == null || r.app.thread == null) { - if (onlyThisProcess == null - || onlyThisProcess.equals(r.processName)) { + if (onlyThisProcess == null || onlyThisProcess.equals(r.processName)) { // This activity needs to be visible, but isn't even // running... get it started, but don't resume it // at this point. - if (DEBUG_VISBILITY) Slog.v( - TAG, "Start and freeze screen for " + r); + if (DEBUG_VISBILITY) Slog.v(TAG, "Start and freeze screen for " + r); if (r != starting) { r.startFreezingScreenLocked(r.app, configChanges); } @@ -1175,8 +1156,7 @@ final class ActivityStack { } else if (r.visible) { // If this activity is already visible, then there is nothing // else to do here. - if (DEBUG_VISBILITY) Slog.v( - TAG, "Skipping: already visible at " + r); + if (DEBUG_VISBILITY) Slog.v(TAG, "Skipping: already visible at " + r); r.stopFreezingScreenLocked(false); } else if (onlyThisProcess == null) { @@ -1213,17 +1193,21 @@ final class ActivityStack { behindFullscreen = true; } } else { + if (DEBUG_VISBILITY) Slog.v( + TAG, "Make invisible? " + r + " finishing=" + r.finishing + + " state=" + r.state + + " behindFullscreen=" + behindFullscreen); + // Now for any activities that aren't visible to the user, make + // sure they no longer are keeping the screen frozen. if (r.visible) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Making invisible: " + r); + if (DEBUG_VISBILITY) Slog.v(TAG, "Making invisible: " + r); r.visible = false; try { mService.mWindowManager.setAppVisibility(r.appToken, false); if ((r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) && r.app != null && r.app.thread != null) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Scheduling invisibility: " + r); + if (DEBUG_VISBILITY) Slog.v(TAG, "Scheduling invisibility: " + r); r.app.thread.scheduleWindowVisibility(r.appToken, false); } } catch (Exception e) { @@ -1233,8 +1217,7 @@ final class ActivityStack { + r.intent.getComponent(), e); } } else { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Already invisible: " + r); + if (DEBUG_VISBILITY) Slog.v(TAG, "Already invisible: " + r); } } } @@ -1273,22 +1256,21 @@ final class ActivityStack { // Remember how we'll process this pause/resume situation, and ensure // that the state is reset however we wind up proceeding. - final boolean userLeaving = mUserLeaving; - mUserLeaving = false; + final boolean userLeaving = mStackSupervisor.mUserLeaving; + mStackSupervisor.mUserLeaving = false; if (next == null) { // There are no more activities! Let's just start up the // Launcher... - if (mStackSupervisor.isMainStack(this)) { - ActivityOptions.abort(options); - return mService.startHomeActivityLocked(mCurrentUser); - } + ActivityOptions.abort(options); + return mService.startHomeActivityLocked(mCurrentUser); } next.delayedResume = false; // If the top activity is the resumed one, nothing to do. - if (mResumedActivity == next && next.state == ActivityState.RESUMED) { + if (mResumedActivity == next && next.state == ActivityState.RESUMED && + mStackSupervisor.allResumedActivitiesComplete()) { // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. mService.mWindowManager.executeAppTransition(); @@ -1297,13 +1279,17 @@ final class ActivityStack { return false; } + if (prev != null && prev.mLaunchHomeTaskNext && prev.finishing && + prev.task.getTopActivity() == null) { + prev.mLaunchHomeTaskNext = false; + return mService.startHomeActivityLocked(mCurrentUser); + } + // If we are sleeping, and there is no resumed activity, and the top // activity is paused, well that is the state we want. - if ((mService.mSleeping || mService.mShuttingDown) + if ((mService.isSleepingOrShuttingDown()) && mLastPausedActivity == next - && (next.state == ActivityState.PAUSED - || next.state == ActivityState.STOPPED - || next.state == ActivityState.STOPPING)) { + && mStackSupervisor.allPausedActivitiesComplete()) { // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. mService.mWindowManager.executeAppTransition(); @@ -1323,10 +1309,10 @@ final class ActivityStack { // The activity may be waiting for stop, but that is no longer // appropriate for it. - mStoppingActivities.remove(next); + mStackSupervisor.mStoppingActivities.remove(next); mGoingToSleepActivities.remove(next); next.sleeping = false; - mWaitingVisibleActivities.remove(next); + mStackSupervisor.mWaitingVisibleActivities.remove(next); next.updateOptionsLocked(options); @@ -1371,7 +1357,8 @@ final class ActivityStack { // We need to start pausing the current activity so the top one // can be resumed... - if (mResumedActivity != null) { + final ActivityStack lastStack = mStackSupervisor.getLastStack(); + if (lastStack.mResumedActivity != null) { if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing"); // At this point we want to put the upcoming activity's process // at the top of the LRU list, since we know we will be needing it @@ -1382,7 +1369,7 @@ final class ActivityStack { // happen whenever it needs to later. mService.updateLruProcessLocked(next.app, false); } - startPausingLocked(userLeaving, false); + lastStack.startPausingLocked(userLeaving, false); return true; } @@ -1404,7 +1391,7 @@ final class ActivityStack { if (prev != null && prev != next) { if (!prev.waitingVisible && next != null && !next.nowVisible) { prev.waitingVisible = true; - mWaitingVisibleActivities.add(prev); + mStackSupervisor.mWaitingVisibleActivities.add(prev); if (DEBUG_SWITCH) Slog.v( TAG, "Resuming top, waiting visible to hide: " + prev); } else { @@ -1504,7 +1491,7 @@ final class ActivityStack { // schedule launch ticks to collect information about slow apps. next.startLaunchTickingLocked(); - ActivityRecord lastResumedActivity = mResumedActivity; + ActivityRecord lastResumedActivity = lastStack.mResumedActivity; ActivityState lastState = next.state; mService.updateCpuStats(); @@ -1520,7 +1507,7 @@ final class ActivityStack { // Have the window manager re-evaluate the orientation of // the screen based on the new activity order. boolean updated = false; - if (mStackSupervisor.isMainStack(this)) { + if (mStackSupervisor.isFrontStack(this)) { Configuration config = mService.mWindowManager.updateOrientationFromAppTokens( mService.mConfiguration, next.mayFreezeScreenLocked(next.app) ? next.appToken : null); @@ -1544,13 +1531,16 @@ final class ActivityStack { // Do over! mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG); } - if (mStackSupervisor.isMainStack(this)) { + if (mStackSupervisor.isFrontStack(this)) { mService.setFocusedActivityLocked(next); } - ensureActivitiesVisibleLocked(null, 0); - mService.mWindowManager.executeAppTransition(); - mNoAnimActivities.clear(); - return true; + if (mStackSupervisor.allResumedActivitiesComplete()) { + ensureActivitiesVisibleLocked(null, 0); + mService.mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); + return true; + } + return false; } try { @@ -1587,12 +1577,12 @@ final class ActivityStack { if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to " + lastState + ": " + next); next.state = lastState; - mResumedActivity = lastResumedActivity; + lastStack.mResumedActivity = lastResumedActivity; Slog.i(TAG, "Restarting because process died: " + next); if (!next.hasBeenLaunched) { next.hasBeenLaunched = true; } else { - if (SHOW_APP_STARTING_PREVIEW && mStackSupervisor.isMainStack(this)) { + if (SHOW_APP_STARTING_PREVIEW && mStackSupervisor.isFrontStack(lastStack)) { mService.mWindowManager.setAppStartingWindow( next.appToken, next.packageName, next.theme, mService.compatibilityInfoForPackageLocked( @@ -1696,7 +1686,7 @@ final class ActivityStack { // to deliver the onUserLeaving callback to the actual frontmost // activity if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { - mUserLeaving = false; + mStackSupervisor.mUserLeaving = false; if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false"); } @@ -1710,7 +1700,7 @@ final class ActivityStack { r.putInHistory(); r.frontOfTask = newTask; - if (numActivities() > 1) { + if (!isHomeStack() || numActivities() > 0) { // We want to show the starting preview window if we are // switching to a new task, or the next activity's process is // not currently running. @@ -1784,7 +1774,7 @@ final class ActivityStack { } if (doResume) { - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); } } @@ -1874,11 +1864,11 @@ final class ActivityStack { // If the activity currently at the bottom has the // same task affinity as the one we are moving, // then merge it into the same task. - setTask(target, p.task, p.thumbHolder, false); + target.setTask(p.task, p.thumbHolder, false); if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + " out to bottom task " + p.task); } else { - setTask(target, createTaskRecord(mStackSupervisor.getNextTaskId(), target.info, + target.setTask(createTaskRecord(mStackSupervisor.getNextTaskId(), target.info, null, false), null, false); target.task.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target @@ -1912,7 +1902,7 @@ final class ActivityStack { new RuntimeException("here").fillInStackTrace()); if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p + " out to target's task " + target.task); - setTask(p, targetTask, curThumbHolder, false); + p.setTask(targetTask, curThumbHolder, false); targetTask.addActivityAtBottom(p); mService.mWindowManager.setAppGroupId(p.appToken, targetTaskId); @@ -2042,7 +2032,7 @@ final class ActivityStack { + start + "-" + i + " to task=" + task + ":" + taskInsertionPoint); for (int srcPos = start; srcPos >= i; --srcPos) { final ActivityRecord p = activities.get(srcPos); - setTask(p, task, null, false); + p.setTask(task, null, false); task.addActivityAtIndex(taskInsertionPoint, p); if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + p @@ -2153,31 +2143,6 @@ final class ActivityStack { return null; } - /** - * Reorder the history stack so that the activity at the given index is - * brought to the front. - */ - final void moveActivityToFrontLocked(ActivityRecord newTop) { - if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop - + " to stack at top", new RuntimeException("here").fillInStackTrace()); - - final TaskRecord task = newTop.task; - task.getTopActivity().frontOfTask = false; - task.mActivities.remove(newTop); - task.mActivities.add(newTop); - newTop.frontOfTask = true; - } - - final void moveHomeToFrontFromLaunchLocked(int launchFlags) { - if ((launchFlags & - (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) - == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) { - // Caller wants to appear on home activity, so before starting - // their own activity we will bring home to the front. - moveHomeToFrontLocked(); - } - } - void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long thisTime, long totalTime) { for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) { @@ -2251,7 +2216,7 @@ final class ActivityStack { } if (r.app != null && r.app.thread != null) { - if (mStackSupervisor.isMainStack(this)) { + if (mStackSupervisor.isFrontStack(this)) { if (mService.mFocusedActivity == r) { mService.setFocusedActivityLocked(topRunningActivityLocked(null)); } @@ -2290,49 +2255,6 @@ final class ActivityStack { } } - final ArrayList<ActivityRecord> processStoppingActivitiesLocked( - boolean remove) { - int N = mStoppingActivities.size(); - if (N <= 0) return null; - - ArrayList<ActivityRecord> stops = null; - - final boolean nowVisible = mResumedActivity != null - && mResumedActivity.nowVisible - && !mResumedActivity.waitingVisible; - for (int i=0; i<N; i++) { - ActivityRecord s = mStoppingActivities.get(i); - if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible=" - + nowVisible + " waitingVisible=" + s.waitingVisible - + " finishing=" + s.finishing); - if (s.waitingVisible && nowVisible) { - mWaitingVisibleActivities.remove(s); - s.waitingVisible = false; - if (s.finishing) { - // If this activity is finishing, it is sitting on top of - // everyone else but we now know it is no longer needed... - // so get rid of it. Otherwise, we need to go through the - // normal flow and hide it once we determine that it is - // hidden by the activities in front of it. - if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s); - mService.mWindowManager.setAppVisibility(s.appToken, false); - } - } - if ((!s.waitingVisible || mService.isSleepingOrShuttingDown()) && remove) { - if (localLOGV) Slog.v(TAG, "Ready to stop: " + s); - if (stops == null) { - stops = new ArrayList<ActivityRecord>(); - } - stops.add(s); - mStoppingActivities.remove(i); - N--; - i--; - } - } - - return stops; - } - final void scheduleIdleLocked() { Message msg = Message.obtain(); msg.what = IDLE_NOW_MSG; @@ -2402,7 +2324,7 @@ final class ActivityStack { ensureActivitiesVisibleLocked(null, 0); //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout); - if (!mService.mBooted && mStackSupervisor.isMainStack(this)) { + if (!mService.mBooted && mStackSupervisor.isFrontStack(this)) { mService.mBooted = true; enableScreen = true; } @@ -2411,7 +2333,7 @@ final class ActivityStack { } // Atomically retrieve all of the other things to do. - stops = processStoppingActivitiesLocked(true); + stops = mStackSupervisor.processStoppingActivitiesLocked(true); NS = stops != null ? stops.size() : 0; if ((NF=mFinishingActivities.size()) > 0) { finishes = new ArrayList<ActivityRecord>(mFinishingActivities); @@ -2427,7 +2349,7 @@ final class ActivityStack { thumbnails = null; } - if (mStackSupervisor.isMainStack(this)) { + if (mStackSupervisor.isFrontStack(this)) { booting = mService.mBooting; mService.mBooting = false; } @@ -2494,7 +2416,7 @@ final class ActivityStack { } if (activityRemoved) { - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); } return res; @@ -2581,8 +2503,7 @@ final class ActivityStack { if (!Objects.equal(cur.taskAffinity, r.taskAffinity)) { break; } - finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", - true); + finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", true); } return true; } @@ -2620,15 +2541,6 @@ final class ActivityStack { */ final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, String reason, boolean oomAdj) { - return finishActivityLocked(r, resultCode, resultData, reason, false, oomAdj); - } - - /** - * @return Returns true if this activity has been removed from the history - * list, or false if it is still in the list and will be removed later. - */ - final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, - String reason, boolean immediate, boolean oomAdj) { if (r.finishing) { Slog.w(TAG, "Duplicate finish request for " + r); return false; @@ -2655,7 +2567,7 @@ final class ActivityStack { } r.pauseKeyDispatchingLocked(); - if (mStackSupervisor.isMainStack(this)) { + if (mStackSupervisor.isFrontStack(this)) { if (mService.mFocusedActivity == r) { mService.setFocusedActivityLocked(topRunningActivityLocked(null)); } @@ -2670,9 +2582,7 @@ final class ActivityStack { mCancelledThumbnails.add(r); } - if (immediate) { - return finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, oomAdj) == null; - } else if (mResumedActivity == r) { + if (mResumedActivity == r) { boolean endTask = index <= 0; if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare close transition: finishing " + r); @@ -2711,9 +2621,9 @@ final class ActivityStack { // and the resumed activity is not yet visible, then hold off on // finishing until the resumed one becomes visible. if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) { - if (!mStoppingActivities.contains(r)) { - mStoppingActivities.add(r); - if (mStoppingActivities.size() > 3) { + if (!mStackSupervisor.mStoppingActivities.contains(r)) { + mStackSupervisor.mStoppingActivities.add(r); + if (mStackSupervisor.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. @@ -2732,9 +2642,9 @@ final class ActivityStack { } // make sure the record is cleaned out of other places. - mStoppingActivities.remove(r); + mStackSupervisor.mStoppingActivities.remove(r); mGoingToSleepActivities.remove(r); - mWaitingVisibleActivities.remove(r); + mStackSupervisor.mWaitingVisibleActivities.remove(r); if (mResumedActivity == r) { mResumedActivity = null; } @@ -2750,7 +2660,7 @@ final class ActivityStack { boolean activityRemoved = destroyActivityLocked(r, true, oomAdj, "finish-imm"); if (activityRemoved) { - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); } return activityRemoved ? null : r; } @@ -2759,7 +2669,7 @@ final class ActivityStack { // activity into the stopped state and then finish it. if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r); mFinishingActivities.add(r); - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); return r; } @@ -2872,7 +2782,7 @@ final class ActivityStack { // This could happen, for example, if we are trimming activities // down to the max limit while they are still waiting to finish. mFinishingActivities.remove(r); - mWaitingVisibleActivities.remove(r); + mStackSupervisor.mWaitingVisibleActivities.remove(r); // Remove any pending results. if (r.finishing && r.pendingResults != null) { @@ -2916,8 +2826,9 @@ final class ActivityStack { here.fillInStackTrace(); Slog.i(TAG, "Removing activity " + r + " from stack"); } - if (r.task != null) { - removeActivity(r); + final TaskRecord task = r.task; + if (task != null && task.removeActivity(r)) { + mStackSupervisor.removeTask(task); } r.takeFromHistory(); removeTimeoutsForActivityLocked(r); @@ -2989,7 +2900,7 @@ final class ActivityStack { } } if (activityRemoved) { - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); } } @@ -3106,12 +3017,12 @@ final class ActivityStack { removeActivityFromHistoryLocked(r); } } - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); } finally { Binder.restoreCallingIdentity(origId); } } - + private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list, ProcessRecord app, String listName) { int i = list.size(); @@ -3132,9 +3043,10 @@ final class ActivityStack { boolean removeHistoryRecordsForAppLocked(ProcessRecord app) { removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities"); - removeHistoryRecordsForAppLocked(mStoppingActivities, app, "mStoppingActivities"); + removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app, + "mStoppingActivities"); removeHistoryRecordsForAppLocked(mGoingToSleepActivities, app, "mGoingToSleepActivities"); - removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app, + removeHistoryRecordsForAppLocked(mStackSupervisor.mWaitingVisibleActivities, app, "mWaitingVisibleActivities"); removeHistoryRecordsForAppLocked(mFinishingActivities, app, "mFinishingActivities"); @@ -3212,24 +3124,6 @@ final class ActivityStack { return hasVisibleActivities; } - /** - * Move the current home activity's task (if one exists) to the front - * of the stack. - */ - final void moveHomeToFrontLocked() { - for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { - final TaskRecord task = mTaskHistory.get(taskNdx); - final ArrayList<ActivityRecord> activities = task.mActivities; - for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - if (r.isHomeActivity) { - moveTaskToFrontLocked(task, null, null); - return; - } - } - } - } - final void updateTransitLocked(int transit, Bundle options) { if (options != null) { ActivityRecord r = topRunningActivityLocked(null); @@ -3246,12 +3140,12 @@ final class ActivityStack { final TaskRecord task = taskForIdLocked(taskId); if (task != null) { if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { - mUserLeaving = true; + mStackSupervisor.mUserLeaving = true; } if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) { // Caller wants the home activity moved with it. To accomplish this, // we'll just move the home task to the top first. - moveHomeToFrontLocked(); + task.mActivities.get(0).mLaunchHomeTaskNext = true; } moveTaskToFrontLocked(task, null, options); return true; @@ -3295,7 +3189,7 @@ final class ActivityStack { mService.mWindowManager.moveTaskToTop(tr.taskId); - resumeTopActivityLocked(null); + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId); if (VALIDATE_TOKENS) { @@ -3320,7 +3214,7 @@ final class ActivityStack { // If we have a watcher, preflight the move before committing to it. First check // for *other* available tasks, but if none are available, then try again allowing the // current task to be selected. - if (mStackSupervisor.isMainStack(this) && mService.mController != null) { + if (mStackSupervisor.isFrontStack(this) && mService.mController != null) { ActivityRecord next = topRunningActivityLocked(null, task); if (next == null) { next = topRunningActivityLocked(null, 0); @@ -3366,15 +3260,20 @@ final class ActivityStack { validateAppTokensLocked(); } - resumeTopActivityLocked(null); + if (mResumedActivity != null && mResumedActivity.task == tr && + mResumedActivity.mLaunchHomeTaskNext) { + mResumedActivity.mLaunchHomeTaskNext = false; + return mService.startHomeActivityLocked(mCurrentUser); + } + + mStackSupervisor.getTopStack().resumeTopActivityLocked(null); return true; } public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) { TaskAccessInfo info = getTaskAccessInfoLocked(tr, true); - ActivityRecord resumed = mResumedActivity; - if (resumed != null && resumed.thumbHolder == tr) { - info.mainThumbnail = screenshotActivities(resumed); + if (mResumedActivity != null && mResumedActivity.thumbHolder == tr) { + info.mainThumbnail = screenshotActivities(mResumedActivity); } if (info.mainThumbnail == null) { info.mainThumbnail = tr.lastThumbnail; @@ -3383,11 +3282,10 @@ final class ActivityStack { } public Bitmap getTaskTopThumbnailLocked(TaskRecord tr) { - ActivityRecord resumed = mResumedActivity; - if (resumed != null && resumed.task == tr) { + if (mResumedActivity != null && mResumedActivity.task == tr) { // This task is the current resumed task, we just need to take // a screenshot of it and return that. - return screenshotActivities(resumed); + return screenshotActivities(mResumedActivity); } // Return the information about the task, to figure out the top // thumbnail to return. @@ -3483,9 +3381,8 @@ final class ActivityStack { return null; } TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index); - ActivityRecord resumed = mResumedActivity; - if (resumed != null && resumed.thumbHolder == sub.holder) { - return screenshotActivities(resumed); + if (mResumedActivity != null && mResumedActivity.thumbHolder == sub.holder) { + return screenshotActivities(mResumedActivity); } return sub.holder.lastThumbnail; } @@ -3663,7 +3560,7 @@ final class ActivityStack { if (andResume) { r.results = null; r.newIntents = null; - if (mStackSupervisor.isMainStack(this)) { + if (mStackSupervisor.isFrontStack(this)) { mService.reportResumedActivityLocked(r); } r.state = ActivityState.RESUMED; @@ -3829,7 +3726,7 @@ final class ActivityStack { boolean hasVisibleActivities = removeHistoryRecordsForAppLocked(app); if (!restarting) { - if (!resumeTopActivityLocked(null)) { + if (!mStackSupervisor.getTopStack().resumeTopActivityLocked(null)) { // If there was nothing to resume, and we are not already // restarting this process, but there is a visible activity that // is hosted by the process... then make sure all visible @@ -3920,26 +3817,12 @@ final class ActivityStack { return starting; } - private void removeActivity(ActivityRecord r) { - final TaskRecord task = r.task; - // TODO: use ActivityManagerService.removeTask to do this. - if (task.removeActivity(r)) { - if (DEBUG_ADD_REMOVE) Slog.i(TAG, "removeActivity: Removing from history, task=" - + task); - mTaskHistory.remove(task); - } - } - - void setTask(ActivityRecord r, TaskRecord newTask, ThumbnailHolder newThumbHolder, - boolean isRoot) { - if (r.task != null) { - removeActivity(r); - } - r.setTask(newTask, newThumbHolder, isRoot); + boolean removeTask(TaskRecord task) { + mTaskHistory.remove(task); + return mTaskHistory.size() == 0; } - TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, - boolean toTop) { + TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, boolean toTop) { TaskRecord task = new TaskRecord(taskId, info, intent, this); if (toTop) { mTaskHistory.add(task); @@ -3970,4 +3853,9 @@ final class ActivityStack { public int getStackId() { return mStackId; } + + @Override + public String toString() { + return "stackId=" + mStackId + " tasks=" + mTaskHistory; + } } diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 4c08f85..d3e5b48 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -61,6 +61,7 @@ import android.util.Slog; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.server.am.ActivityManagerService.PendingActivityLaunch; import com.android.server.am.ActivityStack.ActivityState; +import com.android.server.wm.StackBox; import java.io.FileDescriptor; import java.io.IOException; @@ -69,10 +70,11 @@ import java.util.ArrayList; import java.util.List; public class ActivityStackSupervisor { - static final boolean DEBUG_ADD_REMOVE = false; - static final boolean DEBUG_APP = false; - static final boolean DEBUG_SAVED_STATE = false; - static final boolean DEBUG_STATES = false; + static final boolean DEBUG = ActivityManagerService.DEBUG || false; + static final boolean DEBUG_ADD_REMOVE = DEBUG || false; + static final boolean DEBUG_APP = DEBUG || false; + static final boolean DEBUG_SAVED_STATE = DEBUG || false; + static final boolean DEBUG_STATES = DEBUG || false; public static final int HOME_STACK_ID = 0; @@ -96,12 +98,31 @@ public class ActivityStackSupervisor { /** The stack containing the launcher app */ private ActivityStack mHomeStack; - /** The stack currently receiving input or launching the next activity */ + /** The non-home stack currently receiving input or launching the next activity. If home is + * in front then mHomeStack overrides mMainStack. */ private ActivityStack mMainStack; /** All the non-launcher stacks */ private ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>(); + private static final int STACK_STATE_HOME_IN_FRONT = 0; + private static final int STACK_STATE_HOME_TO_BACK = 1; + private static final int STACK_STATE_HOME_IN_BACK = 2; + private static final int STACK_STATE_HOME_TO_FRONT = 3; + private int mStackState = STACK_STATE_HOME_IN_FRONT; + + /** List of activities that are waiting for a new activity to become visible before completing + * whatever operation they are supposed to do. */ + final ArrayList<ActivityRecord> mWaitingVisibleActivities = new ArrayList<ActivityRecord>(); + + /** List of activities that are ready to be stopped, but waiting for the next activity to + * settle down before doing so. */ + final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<ActivityRecord>(); + + /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity + * is being brought in front of us. */ + boolean mUserLeaving = false; + public ActivityStackSupervisor(ActivityManagerService service, Context context, Looper looper) { mService = service; @@ -111,7 +132,6 @@ public class ActivityStackSupervisor { void init(int userId) { mHomeStack = new ActivityStack(mService, mContext, mLooper, HOME_STACK_ID, this, userId); - setMainStack(mHomeStack); mStacks.add(mHomeStack); } @@ -122,20 +142,59 @@ public class ActivityStackSupervisor { } } - boolean isHomeStackMain() { - return mHomeStack == mMainStack; + ActivityStack getTopStack() { + switch (mStackState) { + case STACK_STATE_HOME_IN_FRONT: + case STACK_STATE_HOME_TO_FRONT: + return mHomeStack; + case STACK_STATE_HOME_IN_BACK: + case STACK_STATE_HOME_TO_BACK: + default: + return mMainStack; + } } - boolean isMainStack(ActivityStack stack) { - return stack == mMainStack; + ActivityStack getLastStack() { + switch (mStackState) { + case STACK_STATE_HOME_IN_FRONT: + case STACK_STATE_HOME_TO_BACK: + return mHomeStack; + case STACK_STATE_HOME_TO_FRONT: + case STACK_STATE_HOME_IN_BACK: + default: + return mMainStack; + } } - ActivityStack getMainStack() { - return mMainStack; + boolean isFrontStack(ActivityStack stack) { + return stack == getTopStack(); } - void setMainStack(ActivityStack stack) { - mMainStack = stack; + boolean homeIsInFront() { + return isFrontStack(mHomeStack); + } + + void moveHomeStack(boolean toFront) { + final boolean homeInFront = isFrontStack(mHomeStack); + if (homeInFront ^ toFront) { + mStackState = homeInFront ? STACK_STATE_HOME_TO_BACK : STACK_STATE_HOME_TO_FRONT; + } + } + + final void setLaunchHomeTaskNextFlag(ActivityRecord sourceRecord, ActivityRecord r, + ActivityStack stack) { + if (stack == mHomeStack) { + return; + } + if ((sourceRecord == null && getLastStack() == mHomeStack) || + (sourceRecord != null && sourceRecord.isHomeActivity)) { + if (r == null) { + r = stack.topRunningActivityLocked(null); + } + if (r != null && !r.isHomeActivity && r.isRootActivity()) { + r.mLaunchHomeTaskNext = true; + } + } } void setDismissKeyguard(boolean dismiss) { @@ -173,6 +232,33 @@ public class ActivityStackSupervisor { return mCurTaskId; } + void removeTask(TaskRecord task) { + final ActivityStack stack = task.stack; + if (stack.removeTask(task) && !stack.isHomeStack()) { + mStacks.remove(stack); + final int oldStackId = stack.mStackId; + final int newMainStackId = mService.mWindowManager.removeStack(oldStackId); + if (newMainStackId == HOME_STACK_ID) { + return; + } + if (mMainStack.mStackId == oldStackId) { + mMainStack = getStack(newMainStackId); + } + } + } + + ActivityRecord resumedAppLocked() { + ActivityStack stack = getTopStack(); + ActivityRecord resumedActivity = stack.mResumedActivity; + if (resumedActivity == null || resumedActivity.app == null) { + resumedActivity = stack.mPausingActivity; + if (resumedActivity == null || resumedActivity.app == null) { + resumedActivity = stack.topRunningActivityLocked(null); + } + } + return resumedActivity; + } + boolean attachApplicationLocked(ProcessRecord app, boolean headless) throws Exception { boolean didSomething = false; final String processName = app.processName; @@ -212,6 +298,65 @@ public class ActivityStackSupervisor { return true; } + boolean allResumedActivitiesComplete() { + final boolean homeInBack = !homeIsInFront(); + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + if (stack.isHomeStack() ^ homeInBack) { + final ActivityRecord r = stack.mResumedActivity; + if (r != null && r.state != ActivityState.RESUMED) { + return false; + } + } + } + // TODO: Not sure if this should check if all Paused are complete too. + switch (mStackState) { + case STACK_STATE_HOME_TO_BACK: + mStackState = STACK_STATE_HOME_IN_BACK; + break; + case STACK_STATE_HOME_TO_FRONT: + mStackState = STACK_STATE_HOME_IN_FRONT; + break; + } + return true; + } + + boolean allResumedActivitiesVisible() { + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + final ActivityRecord r = stack.mResumedActivity; + if (r != null && (!r.nowVisible || r.waitingVisible)) { + return false; + } + } + return true; + } + + boolean allPausedActivitiesComplete() { + final boolean homeInBack = !homeIsInFront(); + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + if (stack.isHomeStack() ^ homeInBack) { + final ActivityRecord r = stack.mLastPausedActivity; + if (r != null && r.state != ActivityState.PAUSED + && r.state != ActivityState.STOPPED + && r.state != ActivityState.STOPPING) { + return false; + } + } + } + // TODO: Not sure if this should check if all Resumed are complete too. + switch (mStackState) { + case STACK_STATE_HOME_TO_BACK: + mStackState = STACK_STATE_HOME_IN_BACK; + break; + case STACK_STATE_HOME_TO_FRONT: + mStackState = STACK_STATE_HOME_IN_FRONT; + break; + } + return true; + } + ActivityRecord getTasksLocked(int maxNum, IThumbnailReceiver receiver, PendingThumbnailsRecord pending, List<RunningTaskInfo> list) { ActivityRecord r = null; @@ -220,7 +365,7 @@ public class ActivityStackSupervisor { final ActivityStack stack = mStacks.get(stackNdx); final ActivityRecord ar = stack.getTasksLocked(maxNum - list.size(), receiver, pending, list); - if (isMainStack(stack)) { + if (isFrontStack(stack)) { r = ar; } } @@ -275,6 +420,7 @@ public class ActivityStackSupervisor { } void startHomeActivity(Intent intent, ActivityInfo aInfo) { + moveHomeStack(true); startActivityLocked(null, intent, null, aInfo, null, null, 0, 0, 0, null, 0, null, false, null); } @@ -308,10 +454,11 @@ public class ActivityStackSupervisor { callingPid = callingUid = -1; } - mMainStack.mConfigWillChange = config != null + final ActivityStack stack = getTopStack(); + stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0; if (DEBUG_CONFIGURATION) Slog.v(TAG, - "Starting activity when config will change = " + mMainStack.mConfigWillChange); + "Starting activity when config will change = " + stack.mConfigWillChange); final long origId = Binder.clearCallingIdentity(); @@ -389,14 +536,14 @@ public class ActivityStackSupervisor { aInfo, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, startFlags, options, componentSpecified, null); - if (mMainStack.mConfigWillChange) { + if (stack.mConfigWillChange) { // If the caller also wants to switch to a new configuration, // do so now. This allows a clean switch, as we are waiting // for the current activity to pause (so we will not destroy // it), and have not yet started the next activity. mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); - mMainStack.mConfigWillChange = false; + stack.mConfigWillChange = false; if (DEBUG_CONFIGURATION) Slog.v(TAG, "Updating to new configuration after starting activity."); mService.updateConfigurationLocked(config, null, false, false); @@ -407,7 +554,7 @@ public class ActivityStackSupervisor { if (outResult != null) { outResult.result = res; if (res == ActivityManager.START_SUCCESS) { - mMainStack.mWaitingActivityLaunched.add(outResult); + stack.mWaitingActivityLaunched.add(outResult); do { try { mService.wait(); @@ -415,7 +562,7 @@ public class ActivityStackSupervisor { } } while (!outResult.timeout && outResult.who == null); } else if (res == ActivityManager.START_TASK_TO_FRONT) { - ActivityRecord r = mMainStack.topRunningActivityLocked(null); + ActivityRecord r = stack.topRunningActivityLocked(null); if (r.nowVisible) { outResult.timeout = false; outResult.who = new ComponentName(r.info.packageName, r.info.name); @@ -423,7 +570,7 @@ public class ActivityStackSupervisor { outResult.thisTime = 0; } else { outResult.thisTime = SystemClock.uptimeMillis(); - mMainStack.mWaitingActivityVisible.add(outResult); + stack.mWaitingActivityVisible.add(outResult); do { try { mService.wait(); @@ -679,7 +826,7 @@ public class ActivityStackSupervisor { // launching the initial activity (that is, home), so that it can have // a chance to initialize itself while in the background, making the // switch back to it faster and look better. - if (isMainStack(stack)) { + if (isFrontStack(stack)) { mService.startSetupActivityLocked(); } @@ -852,16 +999,17 @@ public class ActivityStackSupervisor { ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, - resultRecord, resultWho, requestCode, componentSpecified); + resultRecord, resultWho, requestCode, componentSpecified, this); if (outActivity != null) { outActivity[0] = r; } - if (mMainStack.mResumedActivity == null - || mMainStack.mResumedActivity.info.applicationInfo.uid != callingUid) { + final ActivityStack stack = getTopStack(); + if (stack.mResumedActivity == null + || stack.mResumedActivity.info.applicationInfo.uid != callingUid) { if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) { PendingActivityLaunch pal = - new PendingActivityLaunch(r, sourceRecord, startFlags, mMainStack); + new PendingActivityLaunch(r, sourceRecord, startFlags, stack); mService.mPendingActivityLaunches.add(pal); setDismissKeyguard(false); ActivityOptions.abort(options); @@ -883,7 +1031,7 @@ public class ActivityStackSupervisor { mService.doPendingActivityLaunchesLocked(false); err = startActivityUncheckedLocked(r, sourceRecord, startFlags, true, options); - if (mMainStack.mPausingActivity == null) { + if (stack.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 @@ -893,6 +1041,19 @@ public class ActivityStackSupervisor { return err; } + ActivityStack getCorrectStack(ActivityRecord r) { + if (!r.isHomeActivity) { + if (mStacks.size() == 1) { + // Time to create the first app stack. + int stackId = + mService.createStack(HOME_STACK_ID, StackBox.TASK_STACK_GOES_OVER, 1.0f); + mMainStack = getStack(stackId); + } + return mMainStack; + } + return mHomeStack; + } + final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord, int startFlags, boolean doResume, Bundle options) { @@ -901,14 +1062,10 @@ public class ActivityStackSupervisor { int launchFlags = intent.getFlags(); - final ActivityStack stack = mMainStack; - ActivityStack targetStack = mMainStack; - // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. - targetStack.mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; - if (DEBUG_USER_LEAVING) Slog.v(TAG, - "startActivity() => mUserLeaving=" + targetStack.mUserLeaving); + mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; + if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving); // If the caller has asked not to resume at this point, we make note // of this in the record so that we can skip it when trying to find @@ -926,7 +1083,7 @@ public class ActivityStackSupervisor { if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) { ActivityRecord checkedCaller = sourceRecord; if (checkedCaller == null) { - checkedCaller = targetStack.topRunningNonDelayedActivityLocked(notTop); + checkedCaller = getTopStack().topRunningNonDelayedActivityLocked(notTop); } if (!checkedCaller.realActivity.equals(r.realActivity)) { // Caller is not the same as launcher, so always needed. @@ -954,6 +1111,16 @@ public class ActivityStackSupervisor { launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; } + final ActivityStack sourceStack; + final TaskRecord sourceTask; + if (sourceRecord != null) { + sourceTask = sourceRecord.task; + sourceStack = sourceTask.stack; + } else { + sourceTask = null; + sourceStack = null; + } + if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { // For whatever reason this activity is being launched into a new // task... yet the caller has requested a result back. Well, that @@ -970,6 +1137,7 @@ public class ActivityStackSupervisor { boolean addingToTask = false; boolean movedHome = false; TaskRecord reuseTask = null; + ActivityStack targetStack; if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0) || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK @@ -987,6 +1155,7 @@ public class ActivityStackSupervisor { : findActivityLocked(intent, r.info); if (intentActivity != null) { targetStack = intentActivity.task.stack; + moveHomeStack(targetStack.isHomeStack()); if (intentActivity.task.intent == null) { // This task was started because of movement of // the activity based on affinity... now that we @@ -1000,16 +1169,21 @@ public class ActivityStackSupervisor { // to have the same behavior as if a new instance was // being started, which means not bringing it to the front // if the caller is not itself in the front. - ActivityRecord curTop = targetStack.topRunningNonDelayedActivityLocked(notTop); + ActivityRecord curTop = + targetStack.topRunningNonDelayedActivityLocked(notTop); if (curTop != null && curTop.task != intentActivity.task) { r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); - boolean callerAtFront = sourceRecord == null - || curTop.task == sourceRecord.task; - if (callerAtFront) { + if (sourceRecord == null || sourceStack.topActivity() == sourceRecord) { // We really do want to push this one into the // user's face, right now. movedHome = true; - targetStack.moveHomeToFrontFromLaunchLocked(launchFlags); + if ((launchFlags & + (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) + == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) { + // Caller wants to appear on home activity, so before starting + // their own activity we will bring home to the front. + r.mLaunchHomeTaskNext = true; + } targetStack.moveTaskToFrontLocked(intentActivity.task, r, options); options = null; } @@ -1025,6 +1199,7 @@ public class ActivityStackSupervisor { // is the case, so this is it! And for paranoia, make // sure we have correctly resumed the top activity. if (doResume) { + setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack); targetStack.resumeTopActivityLocked(null, options); } else { ActivityOptions.abort(options); @@ -1117,7 +1292,8 @@ public class ActivityStackSupervisor { // don't use that intent!) And for paranoia, make // sure we have correctly resumed the top activity. if (doResume) { - stack.resumeTopActivityLocked(null, options); + setLaunchHomeTaskNextFlag(sourceRecord, intentActivity, targetStack); + targetStack.resumeTopActivityLocked(null, options); } else { ActivityOptions.abort(options); } @@ -1137,7 +1313,8 @@ public class ActivityStackSupervisor { // If the activity being launched is the same as the one currently // at the top, then we need to check if it should only be launched // once. - ActivityRecord top = targetStack.topRunningNonDelayedActivityLocked(notTop); + ActivityStack topStack = getTopStack(); + ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop); if (top != null && r.resultTo == null) { if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) { if (top.app != null && top.app.thread != null) { @@ -1149,7 +1326,8 @@ public class ActivityStackSupervisor { // For paranoia, make sure we have correctly // resumed the top activity. if (doResume) { - targetStack.resumeTopActivityLocked(null); + setLaunchHomeTaskNextFlag(sourceRecord, null, topStack); + topStack.resumeTopActivityLocked(null); } ActivityOptions.abort(options); if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) { @@ -1167,9 +1345,8 @@ public class ActivityStackSupervisor { } else { if (r.resultTo != null) { - r.resultTo.task.stack.sendActivityResultLocked(-1, - r.resultTo, r.resultWho, r.requestCode, - Activity.RESULT_CANCELED, null); + r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho, + r.requestCode, Activity.RESULT_CANCELED, null); } ActivityOptions.abort(options); return ActivityManager.START_CLASS_NOT_FOUND; @@ -1181,20 +1358,29 @@ public class ActivityStackSupervisor { // Should this be considered a new task? if (r.resultTo == null && !addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + targetStack = getCorrectStack(r); + moveHomeStack(targetStack.isHomeStack()); if (reuseTask == null) { - stack.setTask(r, targetStack.createTaskRecord(getNextTaskId(), r.info, intent, - true), null, true); - if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r - + " in new task " + r.task); + r.setTask(targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true), + null, true); + if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " + + r.task); } else { - stack.setTask(r, reuseTask, reuseTask, true); + r.setTask(reuseTask, reuseTask, true); } newTask = true; if (!movedHome) { - stack.moveHomeToFrontFromLaunchLocked(launchFlags); + if ((launchFlags & + (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) + == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) { + // Caller wants to appear on home activity, so before starting + // their own activity we will bring home to the front. + r.mLaunchHomeTaskNext = true; + } } - } else if (sourceRecord != null) { + targetStack = sourceRecord.task.stack; + moveHomeStack(targetStack.isHomeStack()); if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { // In this case, we are adding the activity to an existing @@ -1208,6 +1394,7 @@ public class ActivityStackSupervisor { // For paranoia, make sure we have correctly // resumed the top activity. if (doResume) { + setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack); targetStack.resumeTopActivityLocked(null); } ActivityOptions.abort(options); @@ -1221,11 +1408,13 @@ public class ActivityStackSupervisor { final ActivityRecord top = targetStack.findActivityInHistoryLocked(r, sourceRecord.task); if (top != null) { - targetStack.moveActivityToFrontLocked(top); - ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); + final TaskRecord task = top.task; + task.moveActivityToFrontLocked(top); + ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task); top.updateOptionsLocked(options); top.deliverNewIntentLocked(callingUid, r.intent); if (doResume) { + setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack); targetStack.resumeTopActivityLocked(null); } return ActivityManager.START_DELIVERED_TO_TOP; @@ -1234,7 +1423,7 @@ public class ActivityStackSupervisor { // An existing activity is starting this new activity, so we want // to keep the new one in the same task as the one that is starting // it. - stack.setTask(r, sourceRecord.task, sourceRecord.thumbHolder, false); + r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in existing task " + r.task); @@ -1242,10 +1431,12 @@ public class ActivityStackSupervisor { // This not being started from an existing activity, and not part // of a new task... just put it in the top task, though these days // this case should never happen. - ActivityRecord prev = stack.topActivity(); - stack.setTask(r, prev != null - ? prev.task - : stack.createTaskRecord(getNextTaskId(), r.info, intent, true), null, true); + targetStack = getLastStack(); + moveHomeStack(targetStack.isHomeStack()); + ActivityRecord prev = targetStack.topActivity(); + r.setTask(prev != null ? prev.task + : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true), + null, true); if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new guessed " + r.task); } @@ -1257,6 +1448,7 @@ public class ActivityStackSupervisor { EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId); } ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task); + setLaunchHomeTaskNextFlag(sourceRecord, r, targetStack); targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options); return ActivityManager.START_SUCCESS; } @@ -1407,11 +1599,14 @@ public class ActivityStackSupervisor { } void comeOutOfSleepIfNeededLocked() { + final boolean homeIsBack = !homeIsInFront(); final int numStacks = mStacks.size(); for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { final ActivityStack stack = mStacks.get(stackNdx); - stack.awakeFromSleepingLocked(); - stack.resumeTopActivityLocked(null); + if (stack.isHomeStack() ^ homeIsBack) { + stack.awakeFromSleepingLocked(); + stack.resumeTopActivityLocked(null); + } } } @@ -1423,28 +1618,10 @@ public class ActivityStackSupervisor { } } - boolean updateConfigurationLocked(int changes, ActivityRecord starting) { - boolean kept = true; - final int numStacks = mStacks.size(); - for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { - final ActivityStack stack = mStacks.get(stackNdx); - if (changes != 0 && starting == null) { - // If the configuration changed, and the caller is not already - // in the process of starting an activity, then find the top - // activity to check if its configuration needs to change. - starting = stack.topRunningActivityLocked(null); - } - - if (starting != null) { - if (!stack.ensureActivityConfigurationLocked(starting, changes)) { - kept = false; - } - // And we need to make sure at this point that all other activities - // are made visible with the correct configuration. - stack.ensureActivitiesVisibleLocked(starting, changes); - } + void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) { + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { + mStacks.get(stackNdx).ensureActivitiesVisibleLocked(starting, configChanges); } - return kept; } void scheduleDestroyAllActivities(ProcessRecord app, String reason) { @@ -1457,22 +1634,65 @@ public class ActivityStackSupervisor { boolean switchUserLocked(int userId, UserStartedState uss) { mCurrentUser = userId; + boolean homeInBack = !homeIsInFront(); boolean haveActivities = false; final int numStacks = mStacks.size(); for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { final ActivityStack stack = mStacks.get(stackNdx); - haveActivities |= stack.switchUserLocked(userId, uss); + if (stack.isHomeStack() ^ homeInBack) { + haveActivities |= stack.switchUserLocked(userId, uss); + } } return haveActivities; } + final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) { + int N = mStoppingActivities.size(); + if (N <= 0) return null; + + ArrayList<ActivityRecord> stops = null; + + final boolean nowVisible = allResumedActivitiesVisible(); + for (int i=0; i<N; i++) { + ActivityRecord s = mStoppingActivities.get(i); + if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + + nowVisible + " waitingVisible=" + s.waitingVisible + + " finishing=" + s.finishing); + if (s.waitingVisible && nowVisible) { + mWaitingVisibleActivities.remove(s); + s.waitingVisible = false; + if (s.finishing) { + // If this activity is finishing, it is sitting on top of + // everyone else but we now know it is no longer needed... + // so get rid of it. Otherwise, we need to go through the + // normal flow and hide it once we determine that it is + // hidden by the activities in front of it. + if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s); + mService.mWindowManager.setAppVisibility(s.appToken, false); + } + } + if ((!s.waitingVisible || mService.isSleepingOrShuttingDown()) && remove) { + if (localLOGV) Slog.v(TAG, "Ready to stop: " + s); + if (stops == null) { + stops = new ArrayList<ActivityRecord>(); + } + stops.add(s); + mStoppingActivities.remove(i); + N--; + i--; + } + } + + return stops; + } + public void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mDismissKeyguardOnNextActivity:"); pw.println(mDismissKeyguardOnNextActivity); } ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) { - return mMainStack.getDumpActivitiesLocked(name); + return getTopStack().getDumpActivitiesLocked(name); } boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll, @@ -1486,18 +1706,6 @@ public class ActivityStackSupervisor { pw.println(" Running activities (most recent first):"); dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, !dumpAll, false, dumpPackage); - if (stack.mWaitingVisibleActivities.size() > 0) { - pw.println(" "); - pw.println(" Activities waiting for another to become visible:"); - dumpHistoryList(fd, pw, stack.mWaitingVisibleActivities, " ", "Wait", false, - !dumpAll, false, dumpPackage); - } - if (stack.mStoppingActivities.size() > 0) { - pw.println(" "); - pw.println(" Activities waiting to stop:"); - dumpHistoryList(fd, pw, stack.mStoppingActivities, " ", "Stop", false, - !dumpAll, false, dumpPackage); - } if (stack.mGoingToSleepActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to sleep:"); @@ -1510,10 +1718,7 @@ public class ActivityStackSupervisor { dumpHistoryList(fd, pw, stack.mFinishingActivities, " ", "Fin", false, !dumpAll, false, dumpPackage); } - } - for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { - final ActivityStack stack = mStacks.get(stackNdx); pw.print(" Stack #"); pw.println(mStacks.indexOf(stack)); if (stack.mPausingActivity != null) { pw.println(" mPausingActivity: " + stack.mPausingActivity); @@ -1525,6 +1730,20 @@ public class ActivityStackSupervisor { } } + if (mStoppingActivities.size() > 0) { + pw.println(" "); + pw.println(" Activities waiting to stop:"); + dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll, false, + dumpPackage); + } + + if (mWaitingVisibleActivities.size() > 0) { + pw.println(" "); + pw.println(" Activities waiting for another to become visible:"); + dumpHistoryList(fd, pw, mWaitingVisibleActivities, " ", "Wait", false, !dumpAll, + false, dumpPackage); + } + if (dumpAll) { pw.println(" "); pw.println(" mCurTaskId: " + mCurTaskId); diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java index 8638140..64b9572 100644 --- a/services/java/com/android/server/am/CompatModePackages.java +++ b/services/java/com/android/server/am/CompatModePackages.java @@ -165,7 +165,7 @@ public class CompatModePackages { } public boolean getFrontActivityAskCompatModeLocked() { - ActivityRecord r = mService.getMainStack().topRunningActivityLocked(null); + ActivityRecord r = mService.getTopStack().topRunningActivityLocked(null); if (r == null) { return false; } @@ -177,7 +177,7 @@ public class CompatModePackages { } public void setFrontActivityAskCompatModeLocked(boolean ask) { - ActivityRecord r = mService.getMainStack().topRunningActivityLocked(null); + ActivityRecord r = mService.getTopStack().topRunningActivityLocked(null); if (r != null) { setPackageAskCompatModeLocked(r.packageName, ask); } @@ -199,7 +199,7 @@ public class CompatModePackages { } public int getFrontActivityScreenCompatModeLocked() { - ActivityRecord r = mService.getMainStack().topRunningActivityLocked(null); + ActivityRecord r = mService.getTopStack().topRunningActivityLocked(null); if (r == null) { return ActivityManager.COMPAT_MODE_UNKNOWN; } @@ -207,7 +207,7 @@ public class CompatModePackages { } public void setFrontActivityScreenCompatModeLocked(int mode) { - ActivityRecord r = mService.getMainStack().topRunningActivityLocked(null); + ActivityRecord r = mService.getTopStack().topRunningActivityLocked(null); if (r == null) { Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity"); return; @@ -294,7 +294,7 @@ public class CompatModePackages { Message msg = mHandler.obtainMessage(MSG_WRITE); mHandler.sendMessageDelayed(msg, 10000); - final ActivityStack stack = mService.getMainStack(); + final ActivityStack stack = mService.getTopStack(); ActivityRecord starting = stack.restartPackage(packageName); // Tell all processes that loaded this package about the change. diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index 94da7e5..45bd6d5 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -16,6 +16,9 @@ package com.android.server.am; +import static com.android.server.am.ActivityManagerService.TAG; +import static com.android.server.am.ActivityStack.DEBUG_ADD_REMOVE; + import android.app.Activity; import android.app.ActivityOptions; import android.content.ComponentName; @@ -128,6 +131,20 @@ class TaskRecord extends ThumbnailHolder { return null; } + /** + * Reorder the history stack so that the activity at the given index is + * brought to the front. + */ + final void moveActivityToFrontLocked(ActivityRecord newTop) { + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + newTop + + " to stack at top", new RuntimeException("here").fillInStackTrace()); + + getTopActivity().frontOfTask = false; + mActivities.remove(newTop); + mActivities.add(newTop); + newTop.frontOfTask = true; + } + void addActivityAtBottom(ActivityRecord r) { addActivityAtIndex(0, r); } @@ -289,10 +306,16 @@ class TaskRecord extends ThumbnailHolder { @Override public String toString() { + StringBuilder sb = new StringBuilder(128); if (stringName != null) { - return stringName; + sb.append(stringName); + sb.append(" U="); + sb.append(userId); + sb.append(" sz="); + sb.append(mActivities.size()); + sb.append('}'); + return sb.toString(); } - StringBuilder sb = new StringBuilder(128); sb.append("TaskRecord{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" #"); @@ -309,11 +332,7 @@ class TaskRecord extends ThumbnailHolder { } else { sb.append(" ??"); } - sb.append(" U="); - sb.append(userId); - sb.append(" sz="); - sb.append(mActivities.size()); - sb.append('}'); - return stringName = sb.toString(); + stringName = sb.toString(); + return toString(); } } diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java index 75df1ec..5b2cf50 100644 --- a/services/java/com/android/server/wm/DisplayContent.java +++ b/services/java/com/android/server/wm/DisplayContent.java @@ -17,8 +17,11 @@ package com.android.server.wm; import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; +import static com.android.server.wm.WindowManagerService.DEBUG_STACK; +import static com.android.server.wm.WindowManagerService.TAG; import android.graphics.Rect; +import android.util.Slog; import android.view.Display; import android.view.DisplayInfo; @@ -36,7 +39,6 @@ class DisplayContentList extends ArrayList<DisplayContent> { * WindowManagerService.mWindowMap. */ class DisplayContent { -// private final static String TAG = "DisplayContent"; /** Unique identifier of this stack. */ private final int mDisplayId; @@ -86,7 +88,7 @@ class DisplayContent { private ArrayList<StackBox> mStackBoxes = new ArrayList<StackBox>(); /** True when the home StackBox is at the top of mStackBoxes, false otherwise */ - private boolean mHomeOnTop = true; + private TaskStack mHomeStack = null; /** * Sorted most recent at top, oldest at [0]. @@ -120,7 +122,7 @@ class DisplayContent { } boolean homeOnTop() { - return mHomeOnTop; + return mStackBoxes.get(0).mStack != mHomeStack; } /** @@ -138,7 +140,7 @@ class DisplayContent { } TaskStack getHomeStack() { - return mStackBoxes.get(mHomeOnTop ? mStackBoxes.size() - 1 : 0).mStack; + return mHomeStack; } public void updateDisplayInfo() { @@ -158,12 +160,19 @@ class DisplayContent { /** Refer to {@link WindowManagerService#createStack(int, int, int, float)} */ TaskStack createStack(int stackId, int relativeStackId, int position, float weight) { TaskStack newStack = null; + if (DEBUG_STACK) Slog.d(TAG, "createStack: stackId=" + stackId + " relativeStackId=" + + relativeStackId + " position=" + position + " weight=" + weight); if (mStackBoxes.isEmpty()) { + if (stackId != HOME_STACK_ID) { + throw new IllegalArgumentException("createStack: First stackId not " + + HOME_STACK_ID); + } StackBox newBox = new StackBox(this, new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)); mStackBoxes.add(newBox); newStack = new TaskStack(stackId, newBox); newBox.mStack = newStack; + mHomeStack = newStack; } else { int stackBoxNdx; for (stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) { @@ -172,10 +181,12 @@ class DisplayContent { || position == StackBox.TASK_STACK_GOES_UNDER) { // Position indicates a new box is added at top level only. if (box.contains(relativeStackId)) { - final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0; StackBox newBox = new StackBox(this, box.mBounds); newStack = new TaskStack(stackId, newBox); newBox.mStack = newStack; + final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0; + if (DEBUG_STACK) Slog.d(TAG, "createStack: inserting stack at " + + (stackBoxNdx + offset)); mStackBoxes.add(stackBoxNdx + offset, newBox); break; } @@ -208,9 +219,11 @@ class DisplayContent { } void removeStackBox(StackBox box) { + if (DEBUG_STACK) Slog.d(TAG, "removeStackBox: box=" + box); final TaskStack stack = box.mStack; if (stack != null && stack.mStackId == HOME_STACK_ID) { // Never delete the home stack, even if it is empty. + if (DEBUG_STACK) Slog.d(TAG, "removeStackBox: Not deleting home stack."); return; } mStackBoxes.remove(box); @@ -223,14 +236,13 @@ class DisplayContent { * @return true if a change was made, false otherwise. */ boolean moveHomeStackBox(boolean toTop) { + if (DEBUG_STACK) Slog.d(TAG, "moveHomeStackBox: toTop=" + toTop); switch (mStackBoxes.size()) { case 0: throw new RuntimeException("moveHomeStackBox: No home StackBox!"); case 1: return false; // Only the home StackBox exists. - case 2: - if (mHomeOnTop != toTop) { - final StackBox home = mStackBoxes.remove(toTop ? 0 : 1); - mStackBoxes.add(toTop ? 1 : 0, home); - mHomeOnTop = toTop; + case 2: + if (homeOnTop() ^ toTop) { + mStackBoxes.add(mStackBoxes.remove(0)); return true; } return false; @@ -262,47 +274,47 @@ class DisplayContent { pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth); pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight); pw.print(subPrefix); pw.print("layoutNeeded="); pw.println(layoutNeeded); - for (int boxNdx = 0; boxNdx < mStackBoxes.size(); ++boxNdx) { - pw.print(prefix); pw.print("StackBox #"); pw.println(boxNdx); - mStackBoxes.get(boxNdx).dump(prefix + " ", pw); - } - int ndx = numTokens(); - if (ndx > 0) { - pw.println(); - pw.println(" Application tokens in Z order:"); - getTasks(); - for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) { - AppTokenList tokens = mTmpTasks.get(taskNdx).mAppTokens; - for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { - final AppWindowToken wtoken = tokens.get(tokenNdx); - pw.print(" App #"); pw.print(ndx--); - pw.print(' '); pw.print(wtoken); pw.println(":"); - wtoken.dump(pw, " "); - } + for (int boxNdx = 0; boxNdx < mStackBoxes.size(); ++boxNdx) { + pw.print(prefix); pw.print("StackBox #"); pw.println(boxNdx); + mStackBoxes.get(boxNdx).dump(prefix + " ", pw); + } + int ndx = numTokens(); + if (ndx > 0) { + pw.println(); + pw.println(" Application tokens in Z order:"); + getTasks(); + for (int taskNdx = mTmpTasks.size() - 1; taskNdx >= 0; --taskNdx) { + AppTokenList tokens = mTmpTasks.get(taskNdx).mAppTokens; + for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { + final AppWindowToken wtoken = tokens.get(tokenNdx); + pw.print(" App #"); pw.print(ndx--); + pw.print(' '); pw.print(wtoken); pw.println(":"); + wtoken.dump(pw, " "); } } - if (mExitingTokens.size() > 0) { - pw.println(); - pw.println(" Exiting tokens:"); - for (int i=mExitingTokens.size()-1; i>=0; i--) { - WindowToken token = mExitingTokens.get(i); - pw.print(" Exiting #"); pw.print(i); - pw.print(' '); pw.print(token); - pw.println(':'); - token.dump(pw, " "); - } + } + if (mExitingTokens.size() > 0) { + pw.println(); + pw.println(" Exiting tokens:"); + for (int i=mExitingTokens.size()-1; i>=0; i--) { + WindowToken token = mExitingTokens.get(i); + pw.print(" Exiting #"); pw.print(i); + pw.print(' '); pw.print(token); + pw.println(':'); + token.dump(pw, " "); } - if (mExitingAppTokens.size() > 0) { - pw.println(); - pw.println(" Exiting application tokens:"); - for (int i=mExitingAppTokens.size()-1; i>=0; i--) { - WindowToken token = mExitingAppTokens.get(i); - pw.print(" Exiting App #"); pw.print(i); - pw.print(' '); pw.print(token); - pw.println(':'); - token.dump(pw, " "); - } + } + if (mExitingAppTokens.size() > 0) { + pw.println(); + pw.println(" Exiting application tokens:"); + for (int i=mExitingAppTokens.size()-1; i>=0; i--) { + WindowToken token = mExitingAppTokens.get(i); + pw.print(" Exiting App #"); pw.print(i); + pw.print(' '); pw.print(token); + pw.println(':'); + token.dump(pw, " "); } + } pw.println(); } } diff --git a/services/java/com/android/server/wm/StackBox.java b/services/java/com/android/server/wm/StackBox.java index 31d1d52..9525d7c 100644 --- a/services/java/com/android/server/wm/StackBox.java +++ b/services/java/com/android/server/wm/StackBox.java @@ -212,7 +212,7 @@ public class StackBox { mParent.absorb(mParent.mFirst); } mParent.makeDirty(); - return getStackId(); + return mParent.getStackId(); } /** TODO: */ @@ -222,19 +222,26 @@ public class StackBox { public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("mParent="); pw.println(mParent); - pw.print(prefix); pw.print("mFirst="); pw.println(mFirst); - pw.print(prefix); pw.print("mSecond="); pw.println(mSecond); pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString()); pw.print(" mVertical="); pw.print(mVertical); pw.print(" layoutNeeded="); pw.println(layoutNeeded); - if (mStack != null) { - pw.print(prefix); pw.print("mStack="); pw.println(mStack); - mStack.dump(prefix + " ", pw); - } else { + if (mFirst != null) { pw.print(prefix); pw.print("mFirst="); pw.println(mStack); mFirst.dump(prefix + " ", pw); pw.print(prefix); pw.print("mSecond="); pw.println(mStack); mSecond.dump(prefix + " ", pw); + } else { + pw.print(prefix); pw.print("mStack="); pw.println(mStack); + mStack.dump(prefix + " ", pw); + } + } + + @Override + public String toString() { + if (mStack != null) { + return "Box{" + hashCode() + " stack=" + mStack.mStackId + "}"; } + return "Box{" + hashCode() + " parent=" + mParent.hashCode() + + " first=" + mFirst.hashCode() + " second=" + mSecond.hashCode() + "}"; } } diff --git a/services/java/com/android/server/wm/Task.java b/services/java/com/android/server/wm/Task.java index 81245c6..2520f31 100644 --- a/services/java/com/android/server/wm/Task.java +++ b/services/java/com/android/server/wm/Task.java @@ -47,6 +47,6 @@ class Task { @Override public String toString() { - return "id=" + taskId + " appTokens=" + mAppTokens; + return "taskId=" + taskId + " appTokens=" + mAppTokens; } } diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java index 683744a..3e5a933 100644 --- a/services/java/com/android/server/wm/TaskStack.java +++ b/services/java/com/android/server/wm/TaskStack.java @@ -85,17 +85,10 @@ public class TaskStack { * Delete a Task from this stack. If it is the last Task in the stack, remove this stack from * its parent StackBox and merge the parent. * @param task The Task to delete. - * @return True if #task was in this stack. */ - boolean removeTask(Task task) { + void removeTask(Task task) { mParent.makeDirty(); - if (mTasks.remove(task)) { - if (mTasks.size() == 0) { - mParent.removeStack(); - } - return true; - } - return false; + mTasks.remove(task); } int remove() { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 8b43537..ad49d0a 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -181,6 +181,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_SURFACE_TRACE = false; static final boolean DEBUG_WINDOW_TRACE = false; static final boolean DEBUG_TASK_MOVEMENT = false; + static final boolean DEBUG_STACK = false; static final boolean SHOW_SURFACE_ALLOC = false; static final boolean SHOW_TRANSACTIONS = false; static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS; @@ -2318,9 +2319,9 @@ public class WindowManagerService extends IWindowManager.Stub if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Remove " + win + " client=" - + Integer.toHexString(System.identityHashCode( - win.mClient.asBinder())) - + ", surface=" + win.mWinAnimator.mSurfaceControl); + + Integer.toHexString(System.identityHashCode(win.mClient.asBinder())) + + ", surface=" + win.mWinAnimator.mSurfaceControl, + new RuntimeException("here").fillInStackTrace()); final long origId = Binder.clearCallingIdentity(); @@ -7676,8 +7677,7 @@ public class WindowManagerService extends IWindowManager.Stub win.mRebuilding = true; mRebuildTmp[numRemoved] = win; mWindowsChanged = true; - if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, - "Rebuild removing window: " + win); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Rebuild removing window: " + win); NW--; numRemoved++; continue; |