Per-display focused stacks in AM

This switches to tracking focused stacks for each display instead of
a single focused stack in the system.

Now ActivityDisplay#getFocusedStack() will return the corresponding
instance, if present on display. Previously it was tracked in
ActivityStackSupervisor with a field. Now the getter searches for a
focusable stack starting from top. Usually it will be the first one
- only PiP stack can be on on top and not focused.

ActivityStackSupervisor#getFocusedStack() will search across displays
for the topmost focusable stack  based on the order provided by WM.

The assumption that there always exists a focused stack is still true
- ActivityStackSupervisor#getFocusedStack() should never return null.
There always exists a focusable home stack on the default display.

Bug: 111541062
Test: atest com.android.server.am
Change-Id: I92922e4298ebbe1c68791bf59406e8f387bcd564
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 5f9f4eb..552ed16 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -339,6 +339,38 @@
                         this, stackId, mSupervisor, windowingMode, activityType, onTop);
     }
 
+    ActivityStack getFocusedStack() {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = mStacks.get(i);
+            if (stack.isFocusable() && stack.shouldBeVisible(null /* starting */)) {
+                return stack;
+            }
+        }
+
+        return null;
+    }
+
+    ActivityRecord getResumedActivity() {
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack == null) {
+            return null;
+        }
+        // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
+        // Check if the focused stack has the resumed activity
+        ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+        if (resumedActivity == null || resumedActivity.app == null) {
+            // If there is no registered resumed activity in the stack or it is not running -
+            // try to use previously resumed one.
+            resumedActivity = focusedStack.mPausingActivity;
+            if (resumedActivity == null || resumedActivity.app == null) {
+                // If previously resumed activity doesn't work either - find the topmost running
+                // activity that can be focused.
+                resumedActivity = focusedStack.topRunningActivityLocked(true /* focusableOnly */);
+            }
+        }
+        return resumedActivity;
+    }
+
     /**
      * Removes stacks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
@@ -585,7 +617,7 @@
 
     /**
      * Get the topmost stack on the display. It may be different from focused stack, because
-     * focus may be on another display.
+     * some stacks are not focusable (e.g. PiP).
      */
     ActivityStack getTopStack() {
         return mStacks.isEmpty() ? null : mStacks.get(mStacks.size() - 1);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f02e8f6..bf9132f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -91,6 +91,7 @@
 import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -12825,8 +12826,8 @@
         boolean needSep = printedAnything;
 
         boolean printed = ActivityStackSupervisor.printThisActivity(pw,
-                mStackSupervisor.getResumedActivityLocked(),
-                dumpPackage, needSep, "  ResumedActivity: ");
+                mStackSupervisor.getTopResumedActivity(),  dumpPackage, needSep,
+                "  ResumedActivity: ");
         if (printed) {
             printedAnything = true;
             needSep = false;
@@ -20642,9 +20643,10 @@
         }
     }
 
-    private final ActivityRecord resumedAppLocked() {
-        final ActivityRecord act =
-                mStackSupervisor != null ? mStackSupervisor.getResumedActivityLocked() : null;
+    // TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
+    // the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
+    private ActivityRecord resumedAppLocked() {
+        final ActivityRecord act = mStackSupervisor.getTopResumedActivity();
         String pkg;
         int uid;
         if (act != null) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 70f638d..427a48e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1792,7 +1792,7 @@
         // considers the resumed activity, as normal means will bring the activity from STOPPED
         // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
         if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
-                || mStackSupervisor.getResumedActivityLocked() == this) {
+                || isResumedActivityOnDisplay()) {
             return false;
         }
 
@@ -3003,6 +3003,15 @@
         return mStackSupervisor.topRunningActivityLocked() == this;
     }
 
+    /**
+     * @return {@code true} if this is the resumed activity on its current display, {@code false}
+     * otherwise.
+     */
+    boolean isResumedActivityOnDisplay() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && this == display.getResumedActivity();
+    }
+
     void registerRemoteAnimations(RemoteAnimationDefinition definition) {
         mWindowContainerController.registerRemoteAnimations(definition);
     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index bebaede..d9efb0f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -847,7 +847,7 @@
         }
     }
 
-    private ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
+    ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
             if (r != null && (!focusableOnly || r.isFocusable())) {
@@ -1052,12 +1052,14 @@
         if (!isActivityTypeHome() && returnsToHomeStack()) {
             // Make sure the home stack is behind this stack since that is where we should return to
             // when this stack is no longer visible.
+            // TODO(b/111541062): Move home stack on the current display
             mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
         }
 
         display.positionChildAtTop(this);
         mStackSupervisor.setFocusStackUnchecked(reason, this);
         if (task != null) {
+            // This also moves the entire hierarchy branch to top, including parents
             insertTaskAtTop(task, null);
             return;
         }
@@ -1083,6 +1085,8 @@
         getDisplay().positionChildAtBottom(this);
         mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack());
         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;
         }
@@ -1734,7 +1738,17 @@
     }
 
     boolean isTopStackOnDisplay() {
-        return getDisplay().isTopStack(this);
+        final ActivityDisplay display = getDisplay();
+        return display != null && display.isTopStack(this);
+    }
+
+    /**
+     * @return {@code true} if this is the focused stack on its current display, {@code false}
+     * otherwise.
+     */
+    boolean isFocusedStackOnDisplay() {
+        final ActivityDisplay display = getDisplay();
+        return display != null && this == display.getFocusedStack();
     }
 
     boolean isTopActivityVisible() {
@@ -1751,9 +1765,6 @@
         if (!isAttached() || mForceHidden) {
             return false;
         }
-        if (mStackSupervisor.isFocusedStack(this)) {
-            return true;
-        }
 
         final ActivityRecord top = topRunningActivityLocked();
         if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e3e1c48..fe594a8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -336,9 +336,6 @@
      * Display.DEFAULT_DISPLAY. */
     ActivityStack mHomeStack;
 
-    /** The stack currently receiving input or launching the next activity. */
-    ActivityStack mFocusedStack;
-
     /** 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. */
@@ -682,12 +679,62 @@
             calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
         }
 
-        mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack(
+        final ActivityDisplay defaultDisplay = getDefaultDisplay();
+        mHomeStack = mLastFocusedStack = defaultDisplay.getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
     }
 
     ActivityStack getFocusedStack() {
-        return mFocusedStack;
+        mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+
+        for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+            final int displayId = mTmpOrderedDisplayIds.get(i);
+            final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+            // If WindowManagerService has encountered the display before we have, ignore as there
+            // will be no stacks present and therefore no activities.
+            if (display == null) {
+                continue;
+            }
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack != null) {
+                return focusedStack;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord getTopResumedActivity() {
+        if (mWindowManager == null) {
+            return null;
+        }
+
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack == null) {
+            return null;
+        }
+        final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+        if (resumedActivity != null && resumedActivity.app != null) {
+            return resumedActivity;
+        }
+        // The top focused stack might not have a resumed activity yet - look on all displays in
+        // focus order.
+        mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+        for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+            final int displayId = mTmpOrderedDisplayIds.get(i);
+            final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+            // If WindowManagerService has encountered the display before we have, ignore as there
+            // will be no stacks present and therefore no activities.
+            if (display == null) {
+                continue;
+            }
+            final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+            if (resumedActivityOnDisplay != null) {
+                return resumedActivityOnDisplay;
+            }
+        }
+        return null;
     }
 
     boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
@@ -703,7 +750,7 @@
     }
 
     boolean isFocusedStack(ActivityStack stack) {
-        return stack != null && stack == mFocusedStack;
+        return stack != null && stack == getFocusedStack();
     }
 
     /** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
@@ -718,12 +765,12 @@
             }
         }
 
-        if (focusCandidate != mFocusedStack) {
-            mLastFocusedStack = mFocusedStack;
-            mFocusedStack = focusCandidate;
-
+        final ActivityStack currentFocusedStack = getFocusedStack();
+        if (currentFocusedStack != focusCandidate) {
+            mLastFocusedStack = currentFocusedStack;
+            // TODO(b/111541062): Update event log to include focus movements on all displays
             EventLogTags.writeAmFocusedStack(
-                    mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
+                    mCurrentUser, focusCandidate == null ? -1 : focusCandidate.getStackId(),
                     mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
         }
 
@@ -961,21 +1008,6 @@
         return candidateTaskId;
     }
 
-    ActivityRecord getResumedActivityLocked() {
-        ActivityStack stack = mFocusedStack;
-        if (stack == null) {
-            return null;
-        }
-        ActivityRecord resumedActivity = stack.getResumedActivity();
-        if (resumedActivity == null || resumedActivity.app == null) {
-            resumedActivity = stack.mPausingActivity;
-            if (resumedActivity == null || resumedActivity.app == null) {
-                resumedActivity = stack.topRunningActivityLocked();
-            }
-        }
-        return resumedActivity;
-    }
-
     boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
         final String processName = app.processName;
         boolean didSomething = false;
@@ -1048,10 +1080,11 @@
             }
         }
         // TODO: Not sure if this should check if all Paused are complete too.
+        final ActivityStack focusedStack = getFocusedStack();
         if (DEBUG_STACK) Slog.d(TAG_STACK,
-                "allResumedActivitiesComplete: mLastFocusedStack changing from=" +
-                mLastFocusedStack + " to=" + mFocusedStack);
-        mLastFocusedStack = mFocusedStack;
+                "allResumedActivitiesComplete: mLastFocusedStack changing from="
+                        + mLastFocusedStack + " to=" + focusedStack);
+        mLastFocusedStack = focusedStack;
         return true;
     }
 
@@ -1235,7 +1268,7 @@
      * @return The top running activity. {@code null} if none is available.
      */
     ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) {
-        final ActivityStack focusedStack = mFocusedStack;
+        final ActivityStack focusedStack = getFocusedStack();
         ActivityRecord r = focusedStack.topRunningActivityLocked();
         if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) {
             return r;
@@ -1724,12 +1757,27 @@
         boolean sendHint = forceSend;
 
         if (!sendHint) {
-            // If not forced, send power hint when the activity's process is different than the
-            // current resumed activity.
-            final ActivityRecord resumedActivity = getResumedActivityLocked();
-            sendHint = resumedActivity == null
-                    || resumedActivity.app == null
-                    || !resumedActivity.app.equals(targetActivity.app);
+            // Send power hint if we don't know what we're launching yet
+            sendHint = targetActivity == null || targetActivity.app == null;
+        }
+
+        if (!sendHint) { // targetActivity != null
+            // Send power hint when the activity's process is different than the current resumed
+            // activity on all displays, or if there are no resumed activities in the system.
+            boolean noResumedActivities = true;
+            boolean allFocusedProcessesDiffer = true;
+            for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+                final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+                final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+                final WindowProcessController resumedActivityProcess =
+                    resumedActivity == null ? null : resumedActivity.app;
+
+                noResumedActivities &= resumedActivityProcess == null;
+                if (resumedActivityProcess != null) {
+                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+                }
+            }
+            sendHint = noResumedActivities || allFocusedProcessesDiffer;
         }
 
         if (sendHint && mService.mAm.mLocalPowerManager != null) {
@@ -2233,12 +2281,13 @@
             return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
         }
 
-        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
+        final ActivityStack focusedStack = getFocusedStack();
+        final ActivityRecord r = focusedStack.topRunningActivityLocked();
         if (r == null || !r.isState(RESUMED)) {
-            mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
+            focusedStack.resumeTopActivityUncheckedLocked(null, null);
         } else if (r.isState(RESUMED)) {
             // Kick off any lingering app transitions form the MoveTaskToFront operation.
-            mFocusedStack.executeAppTransition(targetOptions);
+            focusedStack.executeAppTransition(targetOptions);
         }
 
         return false;
@@ -3404,7 +3453,7 @@
             return false;
         }
 
-        if (stack == mFocusedStack && stack.topRunningActivityLocked() == r) {
+        if (r == getTopResumedActivity()) {
             if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
                     "moveActivityStackToFront: already on top, r=" + r);
             return false;
@@ -3792,11 +3841,11 @@
     }
 
     boolean switchUserLocked(int userId, UserState uss) {
-        final int focusStackId = mFocusedStack.getStackId();
+        final int focusStackId = getFocusedStack().getStackId();
         // We dismiss the docked stack whenever we switch users.
         final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
         if (dockedStack != null) {
-            moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack);
+            moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
         }
         // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
         // also cause all tasks to be moved to the fullscreen stack at a position that is
@@ -3952,7 +4001,7 @@
     }
 
     public void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
+        pw.print(prefix); pw.print("mFocusedStack=" + getFocusedStack());
                 pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
         pw.print(prefix);
         pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
@@ -3978,13 +4027,15 @@
         final long token = proto.start(fieldId);
         super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-            ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+            final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
             activityDisplay.writeToProto(proto, DISPLAYS);
         }
         getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
-        if (mFocusedStack != null) {
-            proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId);
-            ActivityRecord focusedActivity = getResumedActivityLocked();
+        // TODO(b/111541062): Update tests to look for resumed activities on all displays
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack != null) {
+            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
             if (focusedActivity != null) {
                 focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
             }
@@ -4017,7 +4068,7 @@
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
             boolean dumpFocusedStackOnly) {
         if (dumpFocusedStackOnly) {
-            return mFocusedStack.getDumpActivitiesLocked(name);
+            return getFocusedStack().getDumpActivitiesLocked(name);
         } else {
             ArrayList<ActivityRecord> activities = new ArrayList<>();
             int numDisplays = mActivityDisplays.size();
@@ -4099,6 +4150,8 @@
                 }
                 needSep = printed;
             }
+            printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+                    " ResumedActivity:");
         }
 
         printed |= dumpHistoryList(fd, pw, mFinishingActivities, "  ", "Fin", false, !dumpAll,
@@ -4584,9 +4637,12 @@
     }
 
     void setDockedStackMinimized(boolean minimized) {
+        // Get currently focused stack before setting mIsDockMinimized. We do this because if
+        // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+        // still occluding other stacks. This will cause getFocusedStack() to return null.
+        final ActivityStack current = getFocusedStack();
         mIsDockMinimized = minimized;
         if (mIsDockMinimized) {
-            final ActivityStack current = getFocusedStack();
             if (current.inSplitScreenPrimaryWindowingMode()) {
                 // The primary split-screen stack can't be focused while it is minimize, so move
                 // focus to something else.
@@ -4850,6 +4906,7 @@
      */
     List<IBinder> getTopVisibleActivities() {
         final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+        final ActivityStack topFocusedStack = getFocusedStack();
         // Traverse all displays.
         for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
             final ActivityDisplay display = mActivityDisplays.valueAt(i);
@@ -4860,7 +4917,7 @@
                 if (stack.shouldBeVisible(null /* starting */)) {
                     final ActivityRecord top = stack.getTopActivity();
                     if (top != null) {
-                        if (stack == mFocusedStack) {
+                        if (stack == topFocusedStack) {
                             topActivityTokens.add(0, top.appToken);
                         } else {
                             topActivityTokens.add(top.appToken);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index d59a651..a131e4a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -802,10 +802,10 @@
                         null /*profilerInfo*/);
 
                 if (DEBUG_PERMISSIONS_REVIEW) {
+                    final ActivityStack focusedStack = mSupervisor.getFocusedStack();
                     Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
                             true, false) + "} from uid " + callingUid + " on display "
-                            + (mSupervisor.mFocusedStack == null
-                            ? DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId));
+                            + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
                 }
             }
         }
@@ -839,7 +839,7 @@
             r.appTimeTracker = sourceRecord.appTimeTracker;
         }
 
-        final ActivityStack stack = mSupervisor.mFocusedStack;
+        final ActivityStack stack = mSupervisor.getFocusedStack();
 
         // If we are starting an activity that is not from the same uid as the currently resumed
         // one, check whether app switches are allowed.
@@ -1013,7 +1013,7 @@
         ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
 
         synchronized (mService.mGlobalLock) {
-            final ActivityStack stack = mSupervisor.mFocusedStack;
+            final ActivityStack stack = mSupervisor.getFocusedStack();
             stack.mConfigWillChange = globalConfig != null
                     && mService.getGlobalConfiguration().diff(globalConfig) != 0;
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1354,7 +1354,7 @@
 
         // If the activity being launched is the same as the one currently at the top, then
         // we need to check if it should only be launched once.
-        final ActivityStack topStack = mSupervisor.mFocusedStack;
+        final ActivityStack topStack = mSupervisor.getFocusedStack();
         final ActivityRecord topFocused = topStack.getTopActivity();
         final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
         final boolean dontStart = top != null && mStartActivity.resultTo == null
@@ -1609,7 +1609,7 @@
         if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
             ActivityRecord checkedCaller = sourceRecord;
             if (checkedCaller == null) {
-                checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
+                checkedCaller = mSupervisor.getFocusedStack().topRunningNonDelayedActivityLocked(
                         mNotTop);
             }
             if (!checkedCaller.realActivity.equals(r.realActivity)) {
@@ -2304,23 +2304,23 @@
         }
 
         final ActivityStack currentStack = task != null ? task.getStack() : null;
+        final ActivityStack focusedStack = mSupervisor.getFocusedStack();
         if (currentStack != null) {
-            if (mSupervisor.mFocusedStack != currentStack) {
+            if (focusedStack != currentStack) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                         "computeStackFocus: Setting " + "focused stack to r=" + r
                                 + " task=" + task);
             } else {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Focused stack already="
-                                + mSupervisor.mFocusedStack);
+                        "computeStackFocus: Focused stack already=" + focusedStack);
             }
             return currentStack;
         }
 
         if (canLaunchIntoFocusedStack(r, newTask)) {
             if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                    "computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
-            return mSupervisor.mFocusedStack;
+                    "computeStackFocus: Have a focused stack=" + focusedStack);
+            return focusedStack;
         }
 
         if (mPreferredDisplayId != DEFAULT_DISPLAY) {
@@ -2356,7 +2356,7 @@
     /** Check if provided activity record can launch in currently focused stack. */
     // TODO: This method can probably be consolidated into getLaunchStack() below.
     private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
-        final ActivityStack focusedStack = mSupervisor.mFocusedStack;
+        final ActivityStack focusedStack = mSupervisor.getFocusedStack();
         final boolean canUseFocusedStack;
         if (focusedStack.isActivityTypeAssistant()) {
             canUseFocusedStack = r.isActivityTypeAssistant();
@@ -2406,18 +2406,19 @@
         }
         // Otherwise handle adjacent launch.
 
+        final ActivityStack focusedStack = mSupervisor.getFocusedStack();
         // The parent activity doesn't want to launch the activity on top of itself, but
         // instead tries to put it onto other side in side-by-side mode.
-        final ActivityStack parentStack = task != null ? task.getStack(): mSupervisor.mFocusedStack;
+        final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
 
-        if (parentStack != mSupervisor.mFocusedStack) {
+        if (parentStack != focusedStack) {
             // If task's parent stack is not focused - use it during adjacent launch.
             return parentStack;
         } else {
-            if (mSupervisor.mFocusedStack != null && task == mSupervisor.mFocusedStack.topTask()) {
+            if (focusedStack != null && task == focusedStack.topTask()) {
                 // If task is already on top of focused stack - use it. We don't want to move the
                 // existing focused task to adjacent stack, just deliver new intent in this case.
-                return mSupervisor.mFocusedStack;
+                return focusedStack;
             }
 
             if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 3ed2875..461aaaf 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -1371,7 +1371,7 @@
             r.immersive = immersive;
 
             // update associated state if we're frontmost
-            if (r == mStackSupervisor.getResumedActivityLocked()) {
+            if (r.isResumedActivityOnDisplay()) {
                 if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
                 applyUpdateLockStateLocked(r);
             }
@@ -3703,7 +3703,7 @@
                 r.requestedVrComponent = (enabled) ? packageName : null;
 
                 // Update associated state if this activity is currently focused
-                if (r == mStackSupervisor.getResumedActivityLocked()) {
+                if (r.isResumedActivityOnDisplay()) {
                     applyUpdateVrModeLocked(r);
                 }
                 return 0;
@@ -4720,8 +4720,9 @@
         mH.post(mAmInternal::updateOomAdj);
     }
 
+    // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
     private void startTimeTrackingFocusedActivityLocked() {
-        final ActivityRecord resumedActivity = mStackSupervisor.getResumedActivityLocked();
+        final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
         if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
             mCurAppTimeTracker.start(resumedActivity.packageName);
         }
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index efde70d..9bfb56f 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -381,7 +381,7 @@
                 return;
             }
             mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
-                    mStackSupervisor.mFocusedStack == stack);
+                    stack.isFocusedStackOnDisplay());
         }
     }