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

Commit 84730a0a authored by Bryce Lee's avatar Bryce Lee
Browse files

Ensure ActivityStack's cached resumed activity is updated.

ActivityStack stores a reference to its resumed activity for quicker
future reference. Currently, children must inform the ActivityStack
of any movement in order to properly set this value. We are missing
cases today, causing stale values to persist.

This changelist addresses by updating the stack's paused and resumed
activity references whenever the ancestor stack changes when a task
or activity's parent is changed.

Change-Id: I5ef6db6af3d7e304012c54afa996fbfff28e9110
Fixes: 75992227
Test: atest FrameworksServicesTests:com.android.server.am.ActivityStackTests#testResumedActivityFromActivityReparenting
Test: atest FrameworksServicesTests:com.android.server.am.ActivityStackTests#testPrimarySplitScreenToFullscreenWhenMovedToBack
Test: atest CtsActivityManagerDeviceTestCases:ActivityManagerPinnedStackTests#testFinishPipActivityWithTaskOverlay
parent cfa439f0
Loading
Loading
Loading
Loading
+23 −7
Original line number Diff line number Diff line
@@ -779,11 +779,13 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
     * @param task The new parent {@link TaskRecord}.
     */
    void setTask(TaskRecord task) {
        setTask(task, false /*reparenting*/);
        setTask(task /* task */, false /* reparenting */);
    }

    /**
     * This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}.
     * @param task          The new parent task.
     * @param reparenting   Whether we're in the middle of reparenting.
     */
    void setTask(TaskRecord task, boolean reparenting) {
        // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}.
@@ -791,12 +793,19 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
            return;
        }

        final ActivityStack stack = getStack();
        final ActivityStack oldStack = getStack();
        final ActivityStack newStack = task != null ? task.getStack() : null;

        // Inform old stack (if present) of activity removal and new stack (if set) of activity
        // addition.
        if (oldStack != newStack) {
            if (!reparenting && oldStack != null) {
                oldStack.onActivityRemovedFromStack(this);
            }

        // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this
        // {@link ActivityRecord} from its current {@link ActivityStack}.
        if (!reparenting && stack != null && (task == null || stack != task.getStack())) {
            stack.onActivityRemovedFromStack(this);
            if (newStack != null) {
                newStack.onActivityAddedToStack(this);
            }
        }

        this.task = task;
@@ -1073,6 +1082,13 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
        // Must reparent first in window manager
        mWindowContainerController.reparent(newTask.getWindowContainerController(), position);

        // Reparenting prevents informing the parent stack of activity removal in the case that
        // the new stack has the same parent. we must manually signal here if this is not the case.
        final ActivityStack prevStack = prevTask.getStack();

        if (prevStack != newTask.getStack()) {
            prevStack.onActivityRemovedFromStack(this);
        }
        // Remove the activity from the old task and add it to the new task.
        prevTask.removeActivity(this, true /* reparenting */);

+19 −13
Original line number Diff line number Diff line
@@ -489,13 +489,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
     */
    void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
        if (record == mResumedActivity && state != RESUMED) {
            clearResumedActivity(reason + " - onActivityStateChanged");
            setResumedActivity(null, reason + " - onActivityStateChanged");
        }

        if (state == RESUMED) {
            if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
                    + reason);
            mResumedActivity = record;
            setResumedActivity(record, reason + " - onActivityStateChanged");
            mService.setResumedActivityUncheckLocked(record, reason);
            mStackSupervisor.mRecentTasks.add(record.getTask());
        }
@@ -2314,14 +2314,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
        return mResumedActivity;
    }

    /**
     * Clears reference to currently resumed activity.
     */
    private void clearResumedActivity(String reason) {
        if (DEBUG_STACK) Slog.d(TAG_STACK, "clearResumedActivity: " + mResumedActivity + " reason:"
                + reason);
    private void setResumedActivity(ActivityRecord r, String reason) {
        if (mResumedActivity == r) {
            return;
        }

        mResumedActivity = null;
        if (DEBUG_STACK) Slog.d(TAG_STACK, "setResumedActivity stack:" + this + " + from: "
                + mResumedActivity + " to:" + r + " reason:" + reason);
        mResumedActivity = r;
    }

    @GuardedBy("mService")
@@ -4027,14 +4027,20 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
     * an activity moves away from the stack.
     */
    void onActivityRemovedFromStack(ActivityRecord r) {
        if (mResumedActivity == r) {
            clearResumedActivity("onActivityRemovedFromStack");
        removeTimeoutsForActivityLocked(r);

        if (mResumedActivity != null && mResumedActivity == r) {
            setResumedActivity(null, "onActivityRemovedFromStack");
        }
        if (mPausingActivity == r) {
        if (mPausingActivity != null && mPausingActivity == r) {
            mPausingActivity = null;
        }
    }

        removeTimeoutsForActivityLocked(r);
    void onActivityAddedToStack(ActivityRecord r) {
        if(r.getState() == RESUMED) {
            setResumedActivity(r, "onActivityAddedToStack");
        }
    }

    /**
+22 −2
Original line number Diff line number Diff line
@@ -927,7 +927,26 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
        if (stack != null && !stack.isInStackLocked(this)) {
            throw new IllegalStateException("Task must be added as a Stack child first.");
        }
        final ActivityStack oldStack = mStack;
        mStack = stack;

        // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this
        // {@link ActivityRecord} from its current {@link ActivityStack}.

        if (oldStack != mStack) {
            for (int i = getChildCount() - 1; i >= 0; --i) {
                final ActivityRecord activity = getChildAt(i);

                if (oldStack != null) {
                    oldStack.onActivityRemovedFromStack(activity);
                }

                if (mStack != null) {
                    stack.onActivityAddedToStack(activity);
                }
            }
        }

        onParentChanged();
    }

@@ -1232,6 +1251,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi

        index = Math.min(size, index);
        mActivities.add(index, r);

        updateEffectiveIntent();
        if (r.isPersistable()) {
            mService.notifyTaskPersisterLocked(this, false);
@@ -1266,7 +1286,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
                    "Activity=" + r + " does not belong to task=" + this);
        }

        r.setTask(null /*task*/, reparenting);
        r.setTask(null /* task */, reparenting /* reparenting */);

        if (mActivities.remove(r) && r.fullscreen) {
            // Was previously in list.
+35 −1
Original line number Diff line number Diff line
@@ -103,7 +103,42 @@ public class ActivityStackTests extends ActivityTestsBase {
        assertEquals(mStack.getResumedActivity(), r);
        r.setState(PAUSING, "testResumedActivity");
        assertEquals(mStack.getResumedActivity(), null);
    }

    @Test
    public void testResumedActivityFromTaskReparenting() {
        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
        // Ensure moving task between two stacks updates resumed activity
        r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
        assertEquals(mStack.getResumedActivity(), r);

        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);

        mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
                false /* animate */, true /* deferResume*/,
                "testResumedActivityFromTaskReparenting");

        assertEquals(mStack.getResumedActivity(), null);
        assertEquals(destStack.getResumedActivity(), r);
    }

    @Test
    public void testResumedActivityFromActivityReparenting() {
        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
        // Ensure moving task between two stacks updates resumed activity
        r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
        assertEquals(mStack.getResumedActivity(), r);

        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
        final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();

        mTask.removeActivity(r);
        destTask.addActivityToTop(r);

        assertEquals(mStack.getResumedActivity(), null);
        assertEquals(destStack.getResumedActivity(), r);
    }

    @Test
@@ -543,5 +578,4 @@ public class ActivityStackTests extends ActivityTestsBase {

        assertEquals(expected, mStack.shouldSleepActivities());
    }

}