diff options
| author | 2018-02-23 18:38:37 +0000 | |
|---|---|---|
| committer | 2018-02-23 18:38:37 +0000 | |
| commit | e4c5e3b55259b0787ac2eec2fc95e6bca9c64bb2 (patch) | |
| tree | d2294900f8c409fed9a19a572a79848527830c50 | |
| parent | a8e26b2e16f1bf800072194055ade22e1f1df4be (diff) | |
| parent | 7ace395d65edb4764fc537ac1c9ed3c07bc72c33 (diff) | |
Merge "Always finish activity when moving to a destroyed state."
7 files changed, 195 insertions, 109 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index efe51723612e..6fcdc3ee0d42 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5689,8 +5689,7 @@ public class ActivityManagerService extends IActivityManager.Stub final long origId = Binder.clearCallingIdentity(); - if (self.state == ActivityState.RESUMED - || self.state == ActivityState.PAUSING) { + if (self.isState(ActivityState.RESUMED, ActivityState.PAUSING)) { mWindowManager.overridePendingAppTransition(packageName, enterAnim, exitAnim, null); } @@ -22774,7 +22773,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } break; - } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) { + } else if (r.isState(ActivityState.PAUSING, ActivityState.PAUSED)) { if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { adj = ProcessList.PERCEPTIBLE_APP_ADJ; app.adjType = "pause-activity"; @@ -22789,7 +22788,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.cached = false; app.empty = false; foregroundActivities = true; - } else if (r.state == ActivityState.STOPPING) { + } else if (r.isState(ActivityState.STOPPING)) { if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { adj = ProcessList.PERCEPTIBLE_APP_ADJ; app.adjType = "stop-activity"; @@ -23183,9 +23182,8 @@ public class ActivityManagerService extends IActivityManager.Stub } final ActivityRecord a = cr.activity; if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { - if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && - (a.visible || a.state == ActivityState.RESUMED || - a.state == ActivityState.PAUSING)) { + if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && (a.visible + || a.isState(ActivityState.RESUMED, ActivityState.PAUSING))) { adj = ProcessList.FOREGROUND_APP_ADJ; if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { if ((cr.flags&Context.BIND_IMPORTANT) != 0) { diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index e6c1cd5ccfc0..06faeb9398b3 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -288,7 +288,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold UriPermissionOwner uriPermissions; // current special URI access perms. ProcessRecord app; // if non-null, hosting application - ActivityState state; // current state we are in + private ActivityState mState; // current state we are in Bundle icicle; // last saved activity state PersistableBundle persistentState; // last persistently saved activity state // TODO: See if this is still needed. @@ -381,8 +381,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo String getLifecycleDescription(String reason) { return "name= " + this + ", component=" + intent.getComponent().flattenToShortString() - + ", package=" + packageName + ", state=" + state + ", reason=" + reason + ", time=" - + System.currentTimeMillis(); + + ", package=" + packageName + ", state=" + mState + ", reason=" + reason + + ", time=" + System.currentTimeMillis(); } void dump(PrintWriter pw, String prefix) { @@ -503,7 +503,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo pw.println(); pw.print(prefix); pw.print("haveState="); pw.print(haveState); pw.print(" icicle="); pw.println(icicle); - pw.print(prefix); pw.print("state="); pw.print(state); + pw.print(prefix); pw.print("state="); pw.print(mState); pw.print(" stopped="); pw.print(stopped); pw.print(" delayedResume="); pw.print(delayedResume); pw.print(" finishing="); pw.println(finishing); @@ -841,7 +841,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo resultTo = _resultTo; resultWho = _resultWho; requestCode = _reqCode; - state = INITIALIZING; + setState(INITIALIZING, "ActivityRecord ctor"); frontOfTask = false; launchFailed = false; stopped = false; @@ -1000,6 +1000,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void removeWindowContainer() { + // Do not try to remove a window container if we have already removed it. + if (mWindowContainerController == null) { + return; + } + // Resume key dispatching if it is currently paused before we remove the container. resumeKeyDispatchingLocked(); @@ -1259,7 +1264,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return false; } - switch (state) { + switch (mState) { case RESUMED: // When visible, allow entering PiP if the app is not locked. If it is over the // keyguard, then we will prompt to unlock in the caller before entering PiP. @@ -1385,13 +1390,13 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // - It is currently resumed or paused. i.e. it is currently visible to the user and we want // the user to see the visual effects caused by the intent delivery now. // - The device is sleeping and it is the top activity behind the lock screen (b/6700897). - if ((state == RESUMED || state == PAUSED + if ((mState == RESUMED || mState == PAUSED || isTopActivityWhileSleeping) && app != null && app.thread != null) { try { ArrayList<ReferrerIntent> ar = new ArrayList<>(1); ar.add(rintent); service.mLifecycleManager.scheduleTransaction(app.thread, appToken, - NewIntentItem.obtain(ar, state == PAUSED)); + NewIntentItem.obtain(ar, mState == PAUSED)); unsent = false; } catch (RemoteException e) { Slog.w(TAG, "Exception thrown sending new intent to " + this, e); @@ -1573,6 +1578,63 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true; } + void setState(ActivityState state, String reason) { + if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState() + + " to:" + state + " reason:" + reason); + final boolean stateChanged = mState != state; + mState = state; + + if (stateChanged && isState(DESTROYING, DESTROYED)) { + makeFinishingLocked(); + + // When moving to the destroyed state, immediately destroy the activity in the + // associated stack. Most paths for finishing an activity will handle an activity's path + // to destroy through mechanisms such as ActivityStackSupervisor#mFinishingActivities. + // However, moving to the destroyed state directly (as in the case of an app dying) and + // marking it as finished will lead to cleanup steps that will prevent later handling + // from happening. + if (isState(DESTROYED)) { + final ActivityStack stack = getStack(); + if (stack != null) { + stack.activityDestroyedLocked(this, reason); + } + } + } + } + + ActivityState getState() { + return mState; + } + + /** + * Returns {@code true} if the Activity is in the specified state. + */ + boolean isState(ActivityState state) { + return state == mState; + } + + /** + * Returns {@code true} if the Activity is in one of the specified states. + */ + boolean isState(ActivityState state1, ActivityState state2) { + return state1 == mState || state2 == mState; + } + + /** + * Returns {@code true} if the Activity is in one of the specified states. + */ + boolean isState(ActivityState state1, ActivityState state2, ActivityState state3) { + return state1 == mState || state2 == mState || state3 == mState; + } + + /** + * Returns {@code true} if the Activity is in one of the specified states. + */ + boolean isState(ActivityState state1, ActivityState state2, ActivityState state3, + ActivityState state4) { + return state1 == mState || state2 == mState || state3 == mState || state4 == mState; + } + void notifyAppResumed(boolean wasStopped) { mWindowContainerController.notifyAppResumed(wasStopped); } @@ -1602,9 +1664,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void makeVisibleIfNeeded(ActivityRecord starting) { // This activity is not currently visible, but is running. Tell it to become visible. - if (state == RESUMED || this == starting) { + if (mState == RESUMED || this == starting) { if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, - "Not making visible, r=" + this + " state=" + state + " starting=" + starting); + "Not making visible, r=" + this + " state=" + mState + " starting=" + starting); return; } @@ -1627,13 +1689,13 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo mStackSupervisor.mGoingToSleepActivities.remove(this); // If the activity is stopped or stopping, cycle to the paused state. - if (state == STOPPED || state == STOPPING) { + if (isState(STOPPED, STOPPING)) { // Capture reason before state change final String reason = getLifecycleDescription("makeVisibleIfNeeded"); // An activity must be in the {@link PAUSING} state for the system to validate // the move to {@link PAUSED}. - state = PAUSING; + setState(PAUSING, "makeVisibleIfNeeded"); service.mLifecycleManager.scheduleTransaction(app.thread, appToken, PauseActivityItem.obtain(finishing, false /* userLeaving */, configChangeFlags, false /* dontReport */) @@ -1654,7 +1716,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } catch(RemoteException e) { } - return state == RESUMED; + return mState == RESUMED; } static void activityResumedLocked(IBinder token) { @@ -1728,7 +1790,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState, CharSequence description) { final ActivityStack stack = getStack(); - if (state != STOPPING) { + if (mState != STOPPING) { Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this); stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this); return; @@ -1751,7 +1813,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)"); stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this); stopped = true; - state = STOPPED; + setState(STOPPED, "activityStoppedLocked"); mWindowContainerController.notifyAppStopped(); @@ -2021,8 +2083,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo * currently pausing, or is resumed. */ public boolean isInterestingToUserLocked() { - return visible || nowVisible || state == PAUSING || - state == RESUMED; + return visible || nowVisible || mState == PAUSING || mState == RESUMED; } void setSleeping(boolean _sleeping) { @@ -2084,8 +2145,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } final boolean isDestroyable() { - if (finishing || app == null || state == DESTROYING - || state == DESTROYED) { + if (finishing || app == null) { // This would be redundant. return false; } @@ -2151,7 +2211,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(), allowTaskSnapshot(), - state.ordinal() >= RESUMED.ordinal() && state.ordinal() <= STOPPED.ordinal(), + mState.ordinal() >= RESUMED.ordinal() && mState.ordinal() <= STOPPED.ordinal(), fromRecents); if (shown) { mStartingWindowState = STARTING_WINDOW_SHOWN; @@ -2328,7 +2388,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } // Skip updating configuration for activity that are stopping or stopped. - if (state == STOPPING || state == STOPPED) { + if (mState == STOPPING || mState == STOPPED) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Skipping config check stopped or stopping: " + this); return true; @@ -2378,7 +2438,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig); - if (state == INITIALIZING) { + if (mState == INITIALIZING) { // No need to relaunch or schedule new config for activity that hasn't been launched // yet. We do, however, return after applying the config to activity record, so that // it will use it for launch transaction. @@ -2431,7 +2491,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is destroying non-running " + this); stack.destroyActivityLocked(this, true, "config"); - } else if (state == PAUSING) { + } else if (mState == PAUSING) { // A little annoying: we are waiting for this activity to finish pausing. Let's not // do anything now, but just flag that it needs to be restarted when done pausing. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, @@ -2439,7 +2499,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo deferRelaunchUntilPaused = true; preserveWindowOnDeferredRelaunch = preserveWindow; return true; - } else if (state == RESUMED) { + } else if (mState == RESUMED) { // Try to optimize this case: the configuration is changing and we need to restart // the top, resumed activity. Instead of doing the normal handshaking, just say // "restart!". @@ -2609,7 +2669,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo service.showAskCompatModeDialogLocked(this); } else { service.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this); - state = PAUSED; + setState(PAUSED, "relaunchActivityLocked"); // if the app is relaunched when it's stopped, and we're not resuming, // put it back into stopped state. if (stopped) { @@ -2853,7 +2913,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo final long token = proto.start(fieldId); super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */); writeIdentifierToProto(proto, IDENTIFIER); - proto.write(STATE, state.toString()); + proto.write(STATE, mState.toString()); proto.write(VISIBLE, visible); proto.write(FRONT_OF_TASK, frontOfTask); if (app != null) { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index fe1067096609..817b69964f24 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -70,7 +70,12 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAV import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityStack.ActivityState.DESTROYED; +import static com.android.server.am.ActivityStack.ActivityState.DESTROYING; +import static com.android.server.am.ActivityStack.ActivityState.FINISHING; import static com.android.server.am.ActivityStack.ActivityState.PAUSED; +import static com.android.server.am.ActivityStack.ActivityState.PAUSING; +import static com.android.server.am.ActivityStack.ActivityState.RESUMED; import static com.android.server.am.ActivityStack.ActivityState.STOPPED; import static com.android.server.am.ActivityStack.ActivityState.STOPPING; import static com.android.server.am.ActivityStackSupervisor.FindTaskResult; @@ -1353,8 +1358,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); - if (r.state == STOPPING || r.state == STOPPED - || r.state == ActivityState.PAUSED || r.state == ActivityState.PAUSING) { + if (r.isState(STOPPING, STOPPED, PAUSED, PAUSING)) { r.setSleeping(true); } } @@ -1401,7 +1405,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai ActivityRecord resuming, boolean pauseImmediately) { if (mPausingActivity != null) { Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity - + " state=" + mPausingActivity.state); + + " state=" + mPausingActivity.getState()); if (!shouldSleepActivities()) { // Avoid recursion among check for sleep and complete pause during sleeping. // Because activity will be paused immediately after resume, just let pause @@ -1431,7 +1435,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mLastPausedActivity = prev; mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null; - prev.state = ActivityState.PAUSING; + prev.setState(PAUSING, "startPausingLocked"); prev.getTask().touchActiveTime(); clearLaunchTime(prev); final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(); @@ -1525,8 +1529,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.userId, System.identityHashCode(r), r.shortComponentName, mPausingActivity != null ? mPausingActivity.shortComponentName : "(none)"); - if (r.state == ActivityState.PAUSING) { - r.state = ActivityState.PAUSED; + if (r.isState(PAUSING)) { + r.setState(PAUSED, "activityPausedLocked"); if (r.finishing) { if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of failed to pause activity: " + r); @@ -1544,8 +1548,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev); if (prev != null) { - final boolean wasStopping = prev.state == STOPPING; - prev.state = ActivityState.PAUSED; + final boolean wasStopping = prev.isState(STOPPING); + prev.setState(PAUSED, "completePausedLocked"); if (prev.finishing) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev); prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false, @@ -1566,7 +1570,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // We are also stopping, the stop request must have gone soon after the pause. // We can't clobber it, because the stop confirmation will not be handled. // We don't need to schedule another stop, we only need to let it happen. - prev.state = STOPPING; + prev.setState(STOPPING, "completePausedLocked"); } else if (!prev.visible || shouldSleepOrShutDownActivities()) { // Clear out any deferred client hide we might currently have. prev.setDeferHidingClient(false); @@ -1843,7 +1847,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (reallyVisible) { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r - + " finishing=" + r.finishing + " state=" + r.state); + + " finishing=" + r.finishing + " state=" + r.getState()); // First: if this is not the current activity being started, make // sure it matches the current configuration. if (r != starting) { @@ -1875,7 +1879,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai configChanges |= r.configChangeFlags; } else { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r - + " finishing=" + r.finishing + " state=" + r.state + + " finishing=" + r.finishing + " state=" + r.getState() + " stackShouldBeVisible=" + stackShouldBeVisible + " behindFullscreenActivity=" + behindFullscreenActivity + " mLaunchTaskBehind=" + r.mLaunchTaskBehind); @@ -2059,7 +2063,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } // Now for any activities that aren't visible to the user, make sure they no longer are // keeping the screen frozen. - if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state); + if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.getState()); try { final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( "makeInvisible", true /* beforeStopping */); @@ -2071,11 +2075,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // the current contract for "auto-Pip" is that the app should enter it before onPause // returns. Just need to confirm this reasoning makes sense. final boolean deferHidingClient = canEnterPictureInPicture - && r.state != STOPPING && r.state != STOPPED && r.state != PAUSED; + && !r.isState(STOPPING, STOPPED, PAUSED); r.setDeferHidingClient(deferHidingClient); r.setVisible(false); - switch (r.state) { + switch (r.getState()) { case STOPPING: case STOPPED: if (r.app != null && r.app.thread != null) { @@ -2255,7 +2259,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // TODO: move mResumedActivity to stack supervisor, // there should only be 1 global copy of resumed activity. mResumedActivity = r; - r.state = ActivityState.RESUMED; + r.setState(RESUMED, "setResumedActivityLocked"); mService.setResumedActivityUncheckLocked(r, reason); mStackSupervisor.mRecentTasks.add(r.getTask()); } @@ -2294,8 +2298,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.delayedResume = false; // If the top activity is the resumed one, nothing to do. - if (mResumedActivity == next && next.state == ActivityState.RESUMED && - mStackSupervisor.allResumedActivitiesComplete()) { + if (mResumedActivity == next && next.isState(RESUMED) + && mStackSupervisor.allResumedActivitiesComplete()) { // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. executeAppTransition(options); @@ -2389,8 +2393,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; - } else if (mResumedActivity == next && next.state == ActivityState.RESUMED && - mStackSupervisor.allResumedActivitiesComplete()) { + } else if (mResumedActivity == next && next.isState(RESUMED) + && mStackSupervisor.allResumedActivitiesComplete()) { // It is possible for the activity to be resumed when we paused back stacks above if the // next activity doesn't have to wait for pause to complete. // So, nothing else to-do except: @@ -2539,7 +2543,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai ActivityRecord lastResumedActivity = lastStack == null ? null :lastStack.mResumedActivity; - ActivityState lastState = next.state; + final ActivityState lastState = next.getState(); mService.updateCpuStats(); @@ -2645,7 +2649,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Whoops, need to restart this activity! if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to " + lastState + ": " + next); - next.state = lastState; + next.setState(lastState, "resumeTopActivityInnerLocked"); if (lastStack != null) { lastStack.mResumedActivity = lastResumedActivity; } @@ -3408,7 +3412,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.stopped = false; if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: " + r + " (stop requested)"); - r.state = STOPPING; + r.setState(STOPPING, "stopActivityLocked"); if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Stopping visible=" + r.visible + " for " + r); if (!r.visible) { @@ -3432,7 +3436,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Just in case, assume it to be stopped. r.stopped = true; if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r); - r.state = STOPPED; + r.setState(STOPPED, "stopActivityLocked"); if (r.deferRelaunchUntilPaused) { destroyActivityLocked(r, true, "stop-except"); } @@ -3505,9 +3509,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (activityNdx >= 0) { r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx); - if (r.state == ActivityState.RESUMED - || r.state == ActivityState.PAUSING - || r.state == ActivityState.PAUSED) { + if (r.isState(RESUMED, PAUSING, PAUSED)) { if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) { Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); @@ -3671,7 +3673,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (endTask) { mService.mLockTaskController.clearLockedTask(task); } - } else if (r.state != ActivityState.PAUSING) { + } else if (!r.isState(PAUSING)) { // If the activity is PAUSING, we will complete the finish once // it is done pausing; else we can just directly finish it here. if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r); @@ -3738,7 +3740,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: "+ r + " (finish requested)"); - r.state = STOPPING; + r.setState(STOPPING, "finishCurrentActivityLocked"); if (oomAdj) { mService.updateOomAdjLocked(); } @@ -3752,15 +3754,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (mResumedActivity == r) { mResumedActivity = null; } - final ActivityState prevState = r.state; + final ActivityState prevState = r.getState(); if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r); - r.state = ActivityState.FINISHING; + r.setState(FINISHING, "finishCurrentActivityLocked"); final boolean finishingActivityInNonFocusedStack = r.getStack() != mStackSupervisor.getFocusedStack() - && prevState == ActivityState.PAUSED && mode == FINISH_AFTER_VISIBLE; + && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE; if (mode == FINISH_IMMEDIATELY - || (prevState == ActivityState.PAUSED + || (prevState == PAUSED && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode())) || finishingActivityInNonFocusedStack || prevState == STOPPING @@ -3981,7 +3983,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (setState) { if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)"); - r.state = ActivityState.DESTROYED; + r.setState(DESTROYED, "cleanupActivityLocked"); if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r); r.app = null; } @@ -4030,7 +4032,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai removeTimeoutsForActivityLocked(r); if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (removed from history)"); - r.state = ActivityState.DESTROYED; + r.setState(DESTROYED, "removeActivityFromHistoryLocked"); if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r); r.app = null; r.removeWindowContainer(); @@ -4114,7 +4116,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai continue; } if (r.isDestroyable()) { - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.state + if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r + + " in state " + r.getState() + " resumed=" + mResumedActivity + " pausing=" + mPausingActivity + " for reason " + reason); if (destroyActivityLocked(r, true, reason)) { @@ -4131,7 +4134,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) { if (r.isDestroyable()) { if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, - "Destroying " + r + " in state " + r.state + " resumed=" + mResumedActivity + "Destroying " + r + " in state " + r.getState() + " resumed=" + mResumedActivity + " pausing=" + mPausingActivity + " for reason " + reason); return destroyActivityLocked(r, true, reason); } @@ -4159,7 +4162,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ActivityRecord activity = activities.get(actNdx); if (activity.app == app && activity.isDestroyable()) { if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity - + " in state " + activity.state + " resumed=" + mResumedActivity + + " in state " + activity.getState() + " resumed=" + mResumedActivity + " pausing=" + mPausingActivity + " for reason " + reason); destroyActivityLocked(activity, true, reason); if (activities.get(actNdx) != activity) { @@ -4253,13 +4256,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (r.finishing && !skipDestroy) { if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYING: " + r + " (destroy requested)"); - r.state = ActivityState.DESTROYING; + r.setState(DESTROYING, + "destroyActivityLocked. finishing and not skipping destroy"); Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r); mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT); } else { if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (destroy skipped)"); - r.state = ActivityState.DESTROYED; + r.setState(DESTROYED, + "destroyActivityLocked. not finishing or skipping destroy"); if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r); r.app = null; } @@ -4270,7 +4275,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai removedFromHistory = true; } else { if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (no app)"); - r.state = ActivityState.DESTROYED; + r.setState(DESTROYED, "destroyActivityLocked. not finishing and had no app"); if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r); r.app = null; } @@ -4288,24 +4293,29 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final void activityDestroyedLocked(IBinder token, String reason) { final long origId = Binder.clearCallingIdentity(); try { - ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r != null) { - mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r); - } - if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + r); - - if (isInStackLocked(r) != null) { - if (r.state == ActivityState.DESTROYING) { - cleanUpActivityLocked(r, true, false); - removeActivityFromHistoryLocked(r, reason); - } - } - mStackSupervisor.resumeFocusedStackTopActivityLocked(); + activityDestroyedLocked(ActivityRecord.forTokenLocked(token), reason); } finally { Binder.restoreCallingIdentity(origId); } } + final void activityDestroyedLocked(ActivityRecord record, String reason) { + if (record != null) { + mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record); + } + + if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + record); + + if (isInStackLocked(record) != null) { + if (record.isState(DESTROYING, DESTROYED)) { + cleanUpActivityLocked(record, true, false); + removeActivityFromHistoryLocked(record, reason); + } + } + + mStackSupervisor.resumeFocusedStackTopActivityLocked(); + } + private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list, ProcessRecord app, String listName) { int i = list.size(); @@ -4374,14 +4384,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai + ": haveState=" + r.haveState + " stateNotNeeded=" + r.stateNotNeeded + " finishing=" + r.finishing - + " state=" + r.state + " callers=" + Debug.getCallers(5)); + + " state=" + r.getState() + " callers=" + Debug.getCallers(5)); if (!r.finishing) { Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), r.getTask().taskId, r.shortComponentName, "proc died without state saved"); - if (r.state == ActivityState.RESUMED) { + if (r.getState() == RESUMED) { mService.updateUsageStats(r, false); } } @@ -4417,7 +4427,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai private void updateTransitLocked(int transit, ActivityOptions options) { if (options != null) { ActivityRecord r = topRunningActivityLocked(); - if (r != null && r.state != ActivityState.RESUMED) { + if (r != null && !r.isState(RESUMED)) { r.updateOptionsLocked(options); } else { ActivityOptions.abort(options); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 9a3b10299155..55771867302c 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1005,7 +1005,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ActivityStack stack = display.getChildAt(stackNdx); if (isFocusedStack(stack)) { final ActivityRecord r = stack.mResumedActivity; - if (r != null && r.state != RESUMED) { + if (r != null && !r.isState(RESUMED)) { return false; } } @@ -1069,10 +1069,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = display.getChildAt(stackNdx); final ActivityRecord r = stack.mPausingActivity; - if (r != null && r.state != PAUSED && r.state != STOPPED && r.state != STOPPING) { + if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) { if (DEBUG_STATES) { Slog.d(TAG_STATES, - "allPausedActivitiesComplete: r=" + r + " state=" + r.state); + "allPausedActivitiesComplete: r=" + r + " state=" + r.getState()); pausing = false; } else { return false; @@ -1522,7 +1522,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // current icicle and other state. if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r + " (starting in paused state)"); - r.state = PAUSED; + r.setState(PAUSED, "realStartActivityLocked"); } // Launch the new version setup screen if needed. We do this -after- @@ -2099,9 +2099,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } final ActivityRecord r = mFocusedStack.topRunningActivityLocked(); - if (r == null || r.state != RESUMED) { + if (r == null || !r.isState(RESUMED)) { mFocusedStack.resumeTopActivityUncheckedLocked(null, null); - } else if (r.state == RESUMED) { + } else if (r.isState(RESUMED)) { // Kick off any lingering app transitions form the MoveTaskToFront operation. mFocusedStack.executeAppTransition(targetOptions); } @@ -3540,14 +3540,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // First, if we find an activity that is in the process of being destroyed, // then we just aren't going to do anything for now; we want things to settle // down before we try to prune more activities. - if (r.finishing || r.state == DESTROYING || r.state == DESTROYED) { + if (r.finishing || r.isState(DESTROYING, DESTROYED)) { if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r); return; } // Don't consider any activies that are currently not in a state where they // can be destroyed. - if (r.visible || !r.stopped || !r.haveState || r.state == RESUMED || r.state == PAUSING - || r.state == PAUSED || r.state == STOPPING) { + if (r.visible || !r.stopped || !r.haveState + || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) { if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r); continue; } @@ -3683,7 +3683,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ? stack.shouldSleepOrShutDownActivities() : mService.isSleepingOrShuttingDownLocked(); if (!waitingVisible || shouldSleepOrShutDown) { - if (!processPausingActivities && s.state == PAUSING) { + if (!processPausingActivities && s.isState(PAUSING)) { // Defer processing pausing activities in this iteration and reschedule // a delayed idle to reprocess it again removeTimeoutsForActivityLocked(idleActivity); @@ -3710,7 +3710,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = display.getChildAt(stackNdx); final ActivityRecord r = stack.topRunningActivityLocked(); - final ActivityState state = r == null ? DESTROYED : r.state; + final ActivityState state = r == null ? DESTROYED : r.getState(); if (isFocusedStack(stack)) { if (r == null) Slog.e(TAG, "validateTop...: null top activity, stack=" + stack); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 8205265ba047..fcdf3d2918ed 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1107,7 +1107,7 @@ class ActivityStarter { case START_TASK_TO_FRONT: { // ActivityRecord may represent a different activity, but it should not be // in the resumed state. - if (r.nowVisible && r.state == RESUMED) { + if (r.nowVisible && r.isState(RESUMED)) { outResult.timeout = false; outResult.who = r.realActivity; outResult.totalTime = 0; @@ -1516,7 +1516,7 @@ class ActivityStarter { final TaskRecord task = mSupervisor.anyTaskForIdLocked( mOptions.getLaunchTaskId()); final ActivityRecord top = task != null ? task.getTopActivity() : null; - if (top != null && top.state != RESUMED) { + if (top != null && !top.isState(RESUMED)) { // The caller specifies that we'd like to be avoided to be moved to the // front, so be it! diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index d679439d3b7d..a07afde44db2 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -1103,7 +1103,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi // Increment the total number of non-finishing activities reportOut.numActivities++; - if (reportOut.top == null || (reportOut.top.state == ActivityState.INITIALIZING)) { + if (reportOut.top == null || (reportOut.top.isState(ActivityState.INITIALIZING))) { reportOut.top = r; // Reset the number of running activities until we hit the first non-initializing // activity diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index d3df9241283a..5161114baf0f 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -22,6 +22,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.server.am.ActivityStack.ActivityState.DESTROYED; +import static com.android.server.am.ActivityStack.ActivityState.DESTROYING; import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.am.ActivityStack.ActivityState.PAUSING; import static com.android.server.am.ActivityStack.ActivityState.STOPPED; @@ -119,20 +121,20 @@ public class ActivityRecordTests extends ActivityTestsBase { } return null; }).when(mActivity.app.thread).scheduleTransaction(any()); - mActivity.state = STOPPED; + mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); mActivity.makeVisibleIfNeeded(null /* starting */); - assertEquals(mActivity.state, PAUSING); + assertTrue(mActivity.isState(PAUSING)); assertTrue(pauseFound.value); // Make sure that the state does not change for current non-stopping states. - mActivity.state = INITIALIZING; + mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped"); mActivity.makeVisibleIfNeeded(null /* starting */); - assertEquals(mActivity.state, INITIALIZING); + assertTrue(mActivity.isState(INITIALIZING)); } @Test @@ -197,7 +199,23 @@ public class ActivityRecordTests extends ActivityTestsBase { record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY); - verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected), anyInt(), anyInt(), - eq(record.info)); + verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected), + anyInt(), anyInt(), eq(record.info)); + } + + @Test + public void testFinishingAfterDestroying() throws Exception { + assertFalse(mActivity.finishing); + mActivity.setState(DESTROYING, "testFinishingAfterDestroying"); + assertTrue(mActivity.isState(DESTROYING)); + assertTrue(mActivity.finishing); + } + + @Test + public void testFinishingAfterDestroyed() throws Exception { + assertFalse(mActivity.finishing); + mActivity.setState(DESTROYED, "testFinishingAfterDestroyed"); + assertTrue(mActivity.isState(DESTROYED)); + assertTrue(mActivity.finishing); } } |