Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 037cacae authored by Andrii Kulian's avatar Andrii Kulian Committed by Android (Google) Code Review
Browse files

Merge "Encapsulate savedState updates in ActivityRecord"

parents 11385f52 06b8dcb5
Loading
Loading
Loading
Loading
+47 −20
Original line number Diff line number Diff line
@@ -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;

+3 −11
Original line number Diff line number Diff line
@@ -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) {
+5 −4
Original line number Diff line number Diff line
@@ -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));

+1 −1
Original line number Diff line number Diff line
@@ -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;
+56 −1
Original line number Diff line number Diff line
@@ -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();
Loading