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

Commit bbb63c26 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Move last focused stack from global to per-display

- Update last focused stack when moving stack to front or back by
  comparing updated focused stack with ideal previous one.
- Print event log am_focused_stack by display.
- Remove setFocusStackUnchecked because it is no longer used to set
  current focus.
- Change allResumedActivitiesComplete to per-display for checking
  whether to execute transition (originally it is global but still
  only check for the top focused).
- Remove checking of finish booting (added in commit f3ea23ad)
  when moving order of stack. That intended to fix a corner case
  when home is idle but it is not the topmost. Currently the case
  won't happen because now:
    1. The restored empty tasks from recent will be put on bottom.
    2. The checking in activityIdleInternalLocked uses
       isTopDisplayFocusedStack that will not be affected by empty
       (non-focusable or invisible) stacks on top.

Bug: 117198947
Test: atest ActivityDisplayTests

Change-Id: I5dcce337b4b6e33807b7a6729720cebd062356e2
parent b4995105
Loading
Loading
Loading
Loading
+59 −4
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
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.RESUMED;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
@@ -120,6 +121,13 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
     */
    private ActivityStack mPreferredTopFocusableStack;

    /**
     * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
     * stack has been resumed. If stacks are changing position this will hold the old stack until
     * the new stack becomes resumed after which it will be set to current focused stack.
     */
    private ActivityStack mLastFocusedStack;

    // Cached reference to some special stacks we tend to get a lot so we don't need to loop
    // through the list to find them.
    private ActivityStack mHomeStack = null;
@@ -182,20 +190,33 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
    }

    void positionChildAtTop(ActivityStack stack, boolean includingParents) {
        positionChildAt(stack, mStacks.size(), includingParents);
        positionChildAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
    }

    void positionChildAtTop(ActivityStack stack, boolean includingParents,
            String updateLastFocusedStackReason) {
        positionChildAt(stack, mStacks.size(), includingParents, updateLastFocusedStackReason);
    }

    void positionChildAtBottom(ActivityStack stack) {
        positionChildAt(stack, 0, false /* includingParents */);
        positionChildAtBottom(stack, null /* updateLastFocusedStackReason */);
    }

    void positionChildAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
        positionChildAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
    }

    private void positionChildAt(ActivityStack stack, int position) {
        positionChildAt(stack, position, false /* includingParents */);
        positionChildAt(stack, position, false /* includingParents */,
                null /* updateLastFocusedStackReason */);
    }

    private void positionChildAt(ActivityStack stack, int position, boolean includingParents) {
    private void positionChildAt(ActivityStack stack, int position, boolean includingParents,
            String updateLastFocusedStackReason) {
        // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
        //       the position internally, also update the logic here
        final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
                ? getFocusedStack() : null;
        final boolean wasContained = mStacks.remove(stack);
        final int insertPosition = getTopInsertPosition(stack, position);
        mStacks.add(insertPosition, stack);
@@ -211,6 +232,17 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
            mPreferredTopFocusableStack = null;
        }

        if (updateLastFocusedStackReason != null) {
            final ActivityStack currentFocusedStack = getFocusedStack();
            if (currentFocusedStack != prevFocusedStack) {
                mLastFocusedStack = prevFocusedStack;
                EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId,
                        currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
                        mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
                        updateLastFocusedStackReason);
            }
        }

        // Since positionChildAt() is called during the creation process of pinned stacks,
        // ActivityStack#getWindowContainerController() can be null. In this special case,
        // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
@@ -458,6 +490,26 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
        return resumedActivity;
    }

    ActivityStack getLastFocusedStack() {
        return mLastFocusedStack;
    }

    boolean allResumedActivitiesComplete() {
        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
            final ActivityRecord r = mStacks.get(stackNdx).getResumedActivity();
            if (r != null && !r.isState(RESUMED)) {
                return false;
            }
        }
        final ActivityStack currentFocusedStack = getFocusedStack();
        if (DEBUG_STACK) {
            Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
                    + mLastFocusedStack + " to=" + currentFocusedStack);
        }
        mLastFocusedStack = currentFocusedStack;
        return true;
    }

    /**
     * Pause all activities in either all of the stacks or just the back stacks.
     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
@@ -1138,6 +1190,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
        if (mPreferredTopFocusableStack != null) {
            pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
        }
        if (mLastFocusedStack != null) {
            pw.println(myPrefix + "mLastFocusedStack=" + mLastFocusedStack);
        }
    }

    public void dumpStacks(PrintWriter pw) {
+16 −19
Original line number Diff line number Diff line
@@ -1105,12 +1105,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
            display.moveHomeStackToFront(reason + " returnToHome");
        }

        display.positionChildAtTop(this, true /* includingParents */);
        mStackSupervisor.setFocusStackUnchecked(reason, this);
        if (task != null) {
        final boolean movingTask = task != null;
        display.positionChildAtTop(this, !movingTask /* includingParents */, reason);
        if (movingTask) {
            // This also moves the entire hierarchy branch to top, including parents
            insertTaskAtTop(task, null);
            return;
            insertTaskAtTop(task, null /* starting */);
        }
    }

@@ -1131,13 +1130,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
            setWindowingMode(WINDOWING_MODE_UNDEFINED);
        }

        getDisplay().positionChildAtBottom(this);
        mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack());
        getDisplay().positionChildAtBottom(this, reason);
        if (task != null) {
            // TODO(b/111541062): We probably don't want to change display z-order to bottom just
            // because one of its stacks moved to bottom.
            insertTaskAtBottom(task);
            return;
        }
    }

@@ -2431,10 +2428,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
        }

        next.delayedResume = false;
        final ActivityDisplay display = getDisplay();

        // If the top activity is the resumed one, nothing to do.
        if (mResumedActivity == next && next.isState(RESUMED)
                && mStackSupervisor.allResumedActivitiesComplete()) {
                && display.allResumedActivitiesComplete()) {
            // Make sure we have executed any pending transitions, since there
            // should be nothing left to do at this point.
            executeAppTransition(options);
@@ -2500,7 +2498,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai

        boolean lastResumedCanPip = false;
        ActivityRecord lastResumed = null;
        final ActivityStack lastFocusedStack = mStackSupervisor.getTopDisplayLastFocusedStack();
        final ActivityStack lastFocusedStack = display.getLastFocusedStack();
        if (lastFocusedStack != null && lastFocusedStack != this) {
            // So, why aren't we using prev here??? See the param comment on the method. prev doesn't
            // represent the last resumed activity. However, the last focus stack does if it isn't null.
@@ -2545,7 +2543,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
            }
            return true;
        } else if (mResumedActivity == next && next.isState(RESUMED)
                && mStackSupervisor.allResumedActivitiesComplete()) {
                && display.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:
@@ -2661,7 +2659,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai

        mStackSupervisor.mNoAnimActivities.clear();

        ActivityStack lastStack = mStackSupervisor.getTopDisplayLastFocusedStack();
        if (next.attachedToProcess()) {
            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
                    + " stopped=" + next.stopped + " visible=" + next.visible);
@@ -2673,10 +2670,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
            // Launcher is already visible in this case. If we don't add it to opening
            // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
            // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
            final boolean lastActivityTranslucent = lastStack != null
                    && (lastStack.inMultiWindowMode()
                    || (lastStack.mLastPausedActivity != null
                    && !lastStack.mLastPausedActivity.fullscreen));
            final boolean lastActivityTranslucent = lastFocusedStack != null
                    && (lastFocusedStack.inMultiWindowMode()
                    || (lastFocusedStack.mLastPausedActivity != null
                    && !lastFocusedStack.mLastPausedActivity.fullscreen));

            // The contained logic must be synchronized, since we are both changing the visibility
            // and updating the {@link Configuration}. {@link ActivityRecord#setVisibility} will
@@ -2693,7 +2690,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
                next.startLaunchTickingLocked();

                ActivityRecord lastResumedActivity =
                        lastStack == null ? null :lastStack.mResumedActivity;
                        lastFocusedStack == null ? null : lastFocusedStack.mResumedActivity;
                final ActivityState lastState = next.getState();

                mService.updateCpuStats();
@@ -2798,8 +2795,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
                    Slog.i(TAG, "Restarting because process died: " + next);
                    if (!next.hasBeenLaunched) {
                        next.hasBeenLaunched = true;
                    } else  if (SHOW_APP_STARTING_PREVIEW && lastStack != null
                            && lastStack.isTopStackOnDisplay()) {
                    } else  if (SHOW_APP_STARTING_PREVIEW && lastFocusedStack != null
                            && lastFocusedStack.isTopStackOnDisplay()) {
                        next.showStartingWindow(null /* prev */, false /* newTask */,
                                false /* taskSwitch */);
                    }
+2 −65
Original line number Diff line number Diff line
@@ -337,11 +337,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
    /** The current user */
    int mCurrentUser;

    /** If this is the same as mFocusedStack then the activity on the top of the focused stack has
     * been resumed. If stacks are changing position this will hold the old stack until the new
     * stack becomes resumed after which it will be set to mFocusedStack. */
    private ActivityStack mLastFocusedStack;

    /** List of activities that are waiting for a new activity to become visible before completing
     * whatever operation they are supposed to do. */
    // TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from
@@ -692,8 +687,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D

        final ActivityDisplay defaultDisplay = getDefaultDisplay();

        mLastFocusedStack = defaultDisplay.getOrCreateStack(
                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
        defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
        positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
    }

@@ -761,43 +755,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
    }

    ActivityStack getTopDisplayLastFocusedStack() {
        return mLastFocusedStack;
    }

    boolean isTopDisplayFocusedStack(ActivityStack stack) {
        return stack != null && stack == getTopDisplayFocusedStack();
    }

    /** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
    void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) {
        if (!focusCandidate.isFocusable()) {
            // The focus candidate isn't focusable. Move focus to the top stack that is focusable.
            focusCandidate = getNextFocusableStackLocked(focusCandidate, false /* ignoreCurrent */);
            if (focusCandidate == null) {
                Slog.w(TAG,
                        "setFocusStackUnchecked: No focusable stack found, focus home as default");
                focusCandidate = getDefaultDisplay().getHomeStack();
            }
        }

        final ActivityStack currentFocusedStack = getTopDisplayFocusedStack();
        if (currentFocusedStack != focusCandidate) {
            mLastFocusedStack = currentFocusedStack;
            // TODO(b/111541062): Update event log to include focus movements on all displays
            EventLogTags.writeAmFocusedStack(
                    mCurrentUser, focusCandidate == null ? -1 : focusCandidate.getStackId(),
                    mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
        }

        final ActivityRecord r = topRunningActivityLocked();
        if (mService.isBooting() || !mService.isBooted()) {
            if (r != null && r.idle) {
                checkFinishBootingLocked();
            }
        }
    }

    void moveRecentsStackToFront(String reason) {
        final ActivityStack recentsStack = getDefaultDisplay().getStack(
                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
@@ -1091,28 +1052,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        return true;
    }

    boolean allResumedActivitiesComplete() {
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getChildAt(stackNdx);
                if (isTopDisplayFocusedStack(stack)) {
                    final ActivityRecord r = stack.getResumedActivity();
                    if (r != null && !r.isState(RESUMED)) {
                        return false;
                    }
                }
            }
        }
        // TODO: Not sure if this should check if all Paused are complete too.
        final ActivityStack focusedStack = getTopDisplayFocusedStack();
        if (DEBUG_STACK) Slog.d(TAG_STACK,
                "allResumedActivitiesComplete: mLastFocusedStack changing from="
                        + mLastFocusedStack + " to=" + focusedStack);
        mLastFocusedStack = focusedStack;
        return true;
    }

    private boolean allResumedActivitiesVisible() {
        boolean foundResumed = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -3673,7 +3612,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        if (isTopDisplayFocusedStack(stack)) {
            mService.updateUsageStats(r, true);
        }
        if (allResumedActivitiesComplete()) {
        if (stack.getDisplay().allResumedActivitiesComplete()) {
            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
            mWindowManager.executeAppTransition();
            return true;
@@ -3984,8 +3923,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
        pw.print(prefix);
        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
        pw.print(prefix);
        pw.println("mLastFocusedStack=" + mLastFocusedStack);
        pw.print(prefix);
        pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
        pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+1 −1
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ option java_package com.android.server.am
30043 am_set_resumed_activity (User|1|5),(Component Name|3),(Reason|3)

# Stack focus
30044 am_focused_stack (User|1|5),(Focused Stack Id|1|5),(Last Focused Stack Id|1|5),(Reason|3)
30044 am_focused_stack (User|1|5),(Display Id|1|5),(Focused Stack Id|1|5),(Last Focused Stack Id|1|5),(Reason|3)

# Running pre boot receiver
30045 am_pre_boot (User|1|5),(Package|3)
+19 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;

import static com.android.server.am.ActivityStackSupervisor.ON_TOP;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import android.platform.test.annotations.Presubmit;
@@ -52,6 +53,24 @@ public class ActivityDisplayTests extends ActivityTestsBase {
        setupActivityTaskManagerService();
    }

    @Test
    public void testLastFocusedStackIsUpdatedWhenMovingStack() {
        // Create a stack at bottom.
        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
        final ActivityStack stack = display.createStack(
                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, !ON_TOP);
        final ActivityStack prevFocusedStack = display.getFocusedStack();

        stack.moveToFront("moveStackToFront");
        // After moving the stack to front, the previous focused should be the last focused.
        assertTrue(stack.isFocusedStackOnDisplay());
        assertEquals(prevFocusedStack, display.getLastFocusedStack());

        stack.moveToBack("moveStackToBack", null /* task */);
        // After moving the stack to back, the stack should be the last focused.
        assertEquals(stack, display.getLastFocusedStack());
    }

    /**
     * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
     * stack. The fullscreen stack should be the top focused for resuming correctly.