Displays that have focused apps can also be top focused
There was no focus window during activity switches.
- While starting an activity, the previous top activity was paused and set
the AppWindowToken.hiddenRequested to true (while #setVisibility)
- While finishing current top activity, the focused window was updated a
bit early, which was before the next top activity resumed.
In both two cases, the focused window was set to null for a small amount of
time. So, we tried to look up the focus in other displays and resulted
the extra onWindowFocusChanged() calls for activities on other displays.
Bug: 131374329
Test: DisplayContentTests
Change-Id: Ia77bf697c237696cad7b42ca6b38157eff497b23
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 652f7ee..7a98903 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -78,6 +78,8 @@
import static com.android.server.wm.DisplayContentProto.ABOVE_APP_WINDOWS;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
import static com.android.server.wm.DisplayContentProto.BELOW_APP_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
+import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
@@ -85,14 +87,12 @@
import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
import static com.android.server.wm.DisplayContentProto.ID;
import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.PINNED_STACK_CONTROLLER;
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
-import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
-import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
-import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
@@ -2945,9 +2945,16 @@
forAllWindows(mScheduleToastTimeout, false /* traverseTopToBottom */);
}
- WindowState findFocusedWindowIfNeeded() {
- return (mWmService.mPerDisplayFocusEnabled
- || mWmService.mRoot.mTopFocusedAppByProcess.isEmpty()) ? findFocusedWindow() : null;
+ /**
+ * Looking for the focused window on this display if the top focused display hasn't been
+ * found yet (topFocusedDisplayId is INVALID_DISPLAY) or per-display focused was allowed.
+ *
+ * @param topFocusedDisplayId Id of the top focused display.
+ * @return The focused window or null if there isn't any or no need to seek.
+ */
+ WindowState findFocusedWindowIfNeeded(int topFocusedDisplayId) {
+ return (mWmService.mPerDisplayFocusEnabled || topFocusedDisplayId == INVALID_DISPLAY)
+ ? findFocusedWindow() : null;
}
WindowState findFocusedWindow() {
@@ -2971,10 +2978,12 @@
* {@link WindowManagerService#UPDATE_FOCUS_WILL_PLACE_SURFACES},
* {@link WindowManagerService#UPDATE_FOCUS_REMOVING_FOCUS}
* @param updateInputWindows Whether to sync the window information to the input module.
+ * @param topFocusedDisplayId Display id of current top focused display.
* @return {@code true} if the focused window has changed.
*/
- boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
- WindowState newFocus = findFocusedWindowIfNeeded();
+ boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
+ int topFocusedDisplayId) {
+ WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
if (mCurrentFocus == newFocus) {
return false;
}
@@ -2994,7 +3003,7 @@
if (imWindowChanged) {
mWmService.mWindowsChanged = true;
setLayoutNeeded();
- newFocus = findFocusedWindowIfNeeded();
+ newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
}
if (mCurrentFocus != newFocus) {
mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9f42324..8a5f52f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -170,7 +170,7 @@
int topFocusedDisplayId = INVALID_DISPLAY;
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
- changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows);
+ changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
final WindowState newFocus = dc.mCurrentFocus;
if (newFocus != null) {
final int pidOfNewFocus = newFocus.mSession.mPid;
@@ -180,6 +180,11 @@
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = dc.getDisplayId();
}
+ } else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
+ // The top-most display that has a focused app should still be the top focused
+ // display even when the app window is not ready yet (process not attached or
+ // window not added yet).
+ topFocusedDisplayId = dc.getDisplayId();
}
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e60e54c..04f897e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -380,6 +380,14 @@
assertTrue(window1.isFocused());
assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window2.isFocused());
assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+
+ // Make sure top focused display not changed if there is a focused app.
+ window1.mAppToken.hiddenRequested = true;
+ window1.getDisplayContent().setFocusedApp(window1.mAppToken);
+ updateFocusedWindow();
+ assertTrue(!window1.isFocused());
+ assertEquals(window1.getDisplayId(),
+ mWm.mRoot.getTopFocusedDisplayContent().getDisplayId());
}
/**