summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java67
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java14
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java57
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java10
6 files changed, 117 insertions, 42 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2f7311b77886..f0397df288fc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -317,10 +317,15 @@ final class ActivityRecord extends ConfigurationContainer {
UriPermissionOwner uriPermissions; // current special URI access perms.
WindowProcessController app; // if non-null, hosting application
private ActivityState mState; // current state we are in
- Bundle icicle; // last saved activity state
- PersistableBundle persistentState; // last persistently saved activity state
+ private Bundle mIcicle; // last saved activity state
+ private PersistableBundle mPersistentState; // last persistently saved activity state
+ private boolean mHaveState = true; // Indicates whether the last saved state of activity is
+ // preserved. This starts out 'true', since the initial state
+ // of an activity is that we have everything, and we should
+ // never consider it lacking in state to be removed if it
+ // dies. After an activity is launched it follows the value
+ // of #mIcicle.
boolean launchFailed; // set if a launched failed, to abort on 2nd try
- boolean haveState; // have we gotten the last activity state?
boolean stopped; // is activity pause finished?
boolean delayedResume; // not yet resumed because of stopped app switches?
boolean finishing; // activity in pending finish list?
@@ -550,8 +555,8 @@ final class ActivityRecord extends ConfigurationContainer {
if (lastLaunchTime == 0) pw.print("0");
else TimeUtils.formatDuration(lastLaunchTime, now, pw);
pw.println();
- pw.print(prefix); pw.print("haveState="); pw.print(haveState);
- pw.print(" icicle="); pw.println(icicle);
+ pw.print(prefix); pw.print("mHaveState="); pw.print(mHaveState);
+ pw.print(" mIcicle="); pw.println(mIcicle);
pw.print(prefix); pw.print("state="); pw.print(mState);
pw.print(" stopped="); pw.print(stopped);
pw.print(" delayedResume="); pw.print(delayedResume);
@@ -612,6 +617,34 @@ final class ActivityRecord extends ConfigurationContainer {
}
}
+ /** Update the saved state of an activity. */
+ void setSavedState(@Nullable Bundle savedState) {
+ mIcicle = savedState;
+ mHaveState = mIcicle != null;
+ }
+
+ /**
+ * Get the actual Bundle instance of the saved state.
+ * @see #hasSavedState() for checking if the record has saved state.
+ */
+ @Nullable Bundle getSavedState() {
+ return mIcicle;
+ }
+
+ /**
+ * Check if the activity has saved state.
+ * @return {@code true} if the client reported a non-empty saved state from last onStop(), or
+ * if this record was just created and the client is yet to be launched and resumed.
+ */
+ boolean hasSavedState() {
+ return mHaveState;
+ }
+
+ /** @return The actual PersistableBundle instance of the saved persistent state. */
+ @Nullable PersistableBundle getPersistentSavedState() {
+ return mPersistentState;
+ }
+
void updateApplicationInfo(ApplicationInfo aInfo) {
info.applicationInfo = aInfo;
}
@@ -969,10 +1002,6 @@ final class ActivityRecord extends ConfigurationContainer {
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 lacking in state to be removed if it dies.
- haveState = true;
-
// If the class name in the intent doesn't match that of the target, this is
// probably an alias. We have to create a new ComponentName object to keep track
// of the real activity name, so that FLAG_ACTIVITY_CLEAR_TOP is handled properly.
@@ -2182,8 +2211,7 @@ final class ActivityRecord extends ConfigurationContainer {
// been removed (e.g. destroy timeout), so the token could be null.
return;
}
- r.icicle = null;
- r.haveState = false;
+ r.setSavedState(null /* savedState */);
final ActivityDisplay display = r.getDisplay();
if (display != null) {
@@ -2256,19 +2284,18 @@ final class ActivityRecord extends ConfigurationContainer {
return;
}
if (newPersistentState != null) {
- persistentState = newPersistentState;
+ mPersistentState = newPersistentState;
mAtmService.notifyTaskPersisterLocked(task, false);
}
- if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + icicle);
if (newIcicle != null) {
// If icicle is null, this is happening due to a timeout, so we haven't really saved
// the state.
- icicle = newIcicle;
- haveState = true;
+ setSavedState(newIcicle);
launchCount = 0;
updateTaskDescription(description);
}
+ if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + mIcicle);
if (!stopped) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)");
stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
@@ -2588,7 +2615,7 @@ final class ActivityRecord extends ConfigurationContainer {
}
final ActivityStack stack = getActivityStack();
if (stack == null || this == stack.getResumedActivity() || this == stack.mPausingActivity
- || !haveState || !stopped) {
+ || !mHaveState || !stopped) {
// We're not ready for this kind of thing.
return false;
}
@@ -3523,7 +3550,7 @@ final class ActivityRecord extends ConfigurationContainer {
// The restarting state avoids removing this record when process is died.
setState(RESTARTING_PROCESS, "restartActivityProcess");
- if (!visible || haveState) {
+ if (!visible || mHaveState) {
// Kill its process immediately because the activity should be in background.
// The activity state will be update to {@link #DESTROYED} in
// {@link ActivityStack#cleanUpActivityLocked} when handling process died.
@@ -3612,9 +3639,9 @@ final class ActivityRecord extends ConfigurationContainer {
intent.saveToXml(out);
out.endTag(null, TAG_INTENT);
- if (isPersistable() && persistentState != null) {
+ if (isPersistable() && mPersistentState != null) {
out.startTag(null, TAG_PERSISTABLEBUNDLE);
- persistentState.saveToXml(out);
+ mPersistentState.saveToXml(out);
out.endTag(null, TAG_PERSISTABLEBUNDLE);
}
}
@@ -3695,7 +3722,7 @@ final class ActivityRecord extends ConfigurationContainer {
0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
stackSupervisor, null /* options */, null /* sourceRecord */);
- r.persistentState = persistentState;
+ r.mPersistentState = persistentState;
r.taskDescription = taskDescription;
r.createTime = createTime;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 235fd2ef3864..ccc2dfe58a9c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -78,7 +78,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAIN
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -1499,8 +1498,6 @@ class ActivityStack extends ConfigurationContainer {
+ " callers=" + Debug.getCallers(5));
r.setState(RESUMED, "minimalResumeActivityLocked");
r.completeResumeLocked();
- if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
- "Launch completed; removing icicle of " + r.icicle);
}
private void clearLaunchTime(ActivityRecord r) {
@@ -3983,7 +3980,7 @@ class ActivityStack extends ConfigurationContainer {
r.results = null;
r.pendingResults = null;
r.newIntents = null;
- r.icicle = null;
+ r.setSavedState(null /* savedState */);
}
/**
@@ -4800,7 +4797,7 @@ class ActivityStack extends ConfigurationContainer {
// it has failed more than twice. Skip activities that's already finishing
// cleanly by itself.
remove = false;
- } else if ((!r.haveState && !r.stateNotNeeded
+ } else if ((!r.hasSavedState() && !r.stateNotNeeded
&& !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
// Don't currently have state for the activity, or
// it is finishing -- always remove it.
@@ -4820,7 +4817,7 @@ class ActivityStack extends ConfigurationContainer {
if (remove) {
if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
"Removing activity " + r + " from stack at " + i
- + ": haveState=" + r.haveState
+ + ": hasSavedState=" + r.hasSavedState()
+ " stateNotNeeded=" + r.stateNotNeeded
+ " finishing=" + r.finishing
+ " state=" + r.getState() + " callers=" + Debug.getCallers(5));
@@ -4843,11 +4840,6 @@ class ActivityStack extends ConfigurationContainer {
// This is needed when user later tap on the dead window, we need to stop
// other apps when user transfers focus to the restarted activity.
r.nowVisible = r.visible;
- if (!r.haveState) {
- if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
- "App died, clearing saved state of " + r);
- r.icicle = null;
- }
}
cleanUpActivityLocked(r, true, true);
if (remove) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index eb170bdeee2d..1314bfd83cf8 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -813,8 +813,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
newIntents = r.newIntents;
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Launching: " + r + " icicle=" + r.icicle + " with results=" + results
- + " newIntents=" + newIntents + " andResume=" + andResume);
+ "Launching: " + r + " savedState=" + r.getSavedState()
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.mUserId,
System.identityHashCode(r), task.taskId, r.shortComponentName);
if (r.isActivityTypeHome()) {
@@ -836,7 +837,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
proc.getConfiguration(), r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
- logIfTransactionTooLarge(r.intent, r.icicle);
+ logIfTransactionTooLarge(r.intent, r.getSavedState());
// Create activity launch transaction.
@@ -851,7 +852,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
- r.icicle, r.persistentState, results, newIntents,
+ r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
r.assistToken));
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 932e44e7b8c2..e65712c7f7a4 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -734,7 +734,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
// Don't consider any activities that are currently not in a state where they
// can be destroyed.
- if (r.visible || !r.stopped || !r.haveState
+ if (r.visible || !r.stopped || !r.hasSavedState()
|| r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
continue;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index deca57e98f7c..ef11a437fcb1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -36,6 +36,7 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
@@ -62,6 +63,8 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.util.MutableBoolean;
@@ -200,7 +203,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
public void testRestartProcessIfVisible() {
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
mActivity.visible = true;
- mActivity.haveState = false;
+ mActivity.setSavedState(null /* savedState */);
mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart");
prepareFixedAspectRatioUnresizableActivity();
@@ -626,6 +629,58 @@ public class ActivityRecordTests extends ActivityTestsBase {
assertThat(mActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue();
}
+ /**
+ * Verify that an {@link ActivityRecord} reports that it has saved state after creation, and
+ * that it is cleared after activity is resumed.
+ */
+ @Test
+ public void testHasSavedState() {
+ assertTrue(mActivity.hasSavedState());
+
+ ActivityRecord.activityResumedLocked(mActivity.appToken);
+ assertFalse(mActivity.hasSavedState());
+ assertNull(mActivity.getSavedState());
+ }
+
+ /** Verify the behavior of {@link ActivityRecord#setSavedState(Bundle)}. */
+ @Test
+ public void testUpdateSavedState() {
+ mActivity.setSavedState(null /* savedState */);
+ assertFalse(mActivity.hasSavedState());
+ assertNull(mActivity.getSavedState());
+
+ final Bundle savedState = new Bundle();
+ savedState.putString("test", "string");
+ mActivity.setSavedState(savedState);
+ assertTrue(mActivity.hasSavedState());
+ assertEquals(savedState, mActivity.getSavedState());
+ }
+
+ /** Verify the correct updates of saved state when activity client reports stop. */
+ @Test
+ public void testUpdateSavedState_activityStopped() {
+ final Bundle savedState = new Bundle();
+ savedState.putString("test", "string");
+ final PersistableBundle persistentSavedState = new PersistableBundle();
+ persistentSavedState.putString("persist", "string");
+
+ // Set state to STOPPING, or ActivityRecord#activityStoppedLocked() call will be ignored.
+ mActivity.setState(STOPPING, "test");
+ mActivity.activityStoppedLocked(savedState, persistentSavedState, "desc");
+ assertTrue(mActivity.hasSavedState());
+ assertEquals(savedState, mActivity.getSavedState());
+ assertEquals(persistentSavedState, mActivity.getPersistentSavedState());
+
+ // Sending 'null' for saved state can only happen due to timeout, so previously stored saved
+ // states should not be overridden.
+ mActivity.setState(STOPPING, "test");
+ mActivity.activityStoppedLocked(null /* savedState */, null /* persistentSavedState */,
+ "desc");
+ assertTrue(mActivity.hasSavedState());
+ assertEquals(savedState, mActivity.getSavedState());
+ assertEquals(persistentSavedState, mActivity.getPersistentSavedState());
+ }
+
/** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
private void prepareFixedAspectRatioUnresizableActivity() {
setupDisplayContentForCompatDisplayInsets();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index bde0ef6aa39e..0bf7446ccfef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -892,7 +892,7 @@ public class ActivityStackTests extends ActivityTestsBase {
firstActivity.app = null;
// second activity will be immediately removed as it has no state.
- secondActivity.haveState = false;
+ secondActivity.setSavedState(null /* savedState */);
assertEquals(2, mTask.mActivities.size());
@@ -908,7 +908,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
activity.launchCount = 1;
- activity.haveState = false;
+ activity.setSavedState(null /* savedState */);
mStack.handleAppDiedLocked(activity.app);
@@ -922,7 +922,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
activity.launchCount = 3;
- activity.haveState = false;
+ activity.setSavedState(null /* savedState */);
mStack.handleAppDiedLocked(activity.app);
@@ -936,7 +936,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
activity.launchCount = 1;
- activity.haveState = false;
+ activity.setSavedState(null /* savedState */);
mStack.handleAppDiedLocked(activity.app);
@@ -950,7 +950,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE;
activity.launchCount = 3;
- activity.haveState = false;
+ activity.setSavedState(null /* savedState */);
mStack.handleAppDiedLocked(activity.app);