From 63d4ecc7a5c23f1ebbd3d71e5054041d90df9762 Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Thu, 8 Sep 2016 18:48:26 -0700 Subject: [PATCH] Moved window focus calculation to DisplayContent In prep. for using WindowContainer. Bug: 30060889 Change-Id: Ide3022b3129b3d190f631a07d7589a27c434e0c3 --- .../com/android/server/wm/DisplayContent.java | 64 ++++++++++++++++++ .../core/java/com/android/server/wm/Task.java | 5 ++ .../java/com/android/server/wm/TaskStack.java | 11 +++ .../android/server/wm/WindowContainer.java | 67 ++++++++++++++++++- .../server/wm/WindowManagerService.java | 66 +----------------- .../server/wm/WindowSurfacePlacer.java | 3 +- .../server/wm/WindowContainerTests.java | 43 ++++++++++++ 7 files changed, 192 insertions(+), 67 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 69d01ed88108b..aa00084b4bbb9 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -26,7 +26,10 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -764,4 +767,65 @@ class DisplayContent { } } } + + WindowState findFocusedWindow() { + final AppWindowToken focusedApp = mService.mFocusedApp; + + for (int i = mWindows.size() - 1; i >= 0; i--) { + final WindowState win = mWindows.get(i); + + if (DEBUG_FOCUS) Slog.v(TAG_WM, "Looking for focus: " + i + " = " + win + + ", flags=" + win.mAttrs.flags + ", canReceive=" + win.canReceiveKeys()); + + if (!win.canReceiveKeys()) { + continue; + } + + final AppWindowToken wtoken = win.mAppToken; + + // If this window's application has been removed, just skip it. + if (wtoken != null && (wtoken.removed || wtoken.sendingToBottom)) { + if (DEBUG_FOCUS) Slog.v(TAG_WM, "Skipping " + wtoken + " because " + + (wtoken.removed ? "removed" : "sendingToBottom")); + continue; + } + + if (focusedApp == null) { + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: focusedApp=null" + + " using new focus @ " + i + " = " + win); + return win; + } + + if (!focusedApp.windowsAreFocusable()) { + // Current focused app windows aren't focusable... + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: focusedApp windows not" + + " focusable using new focus @ " + i + " = " + win); + return win; + } + + // Descend through all of the app tokens and find the first that either matches + // win.mAppToken (return win) or mFocusedApp (return null). + if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING) { + final TaskStack focusedAppStack = focusedApp.mTask.mStack; + final TaskStack appStack = wtoken.mTask.mStack; + + // TODO: Use WindowContainer.compareTo() once everything is using WindowContainer + if ((focusedAppStack == appStack + && appStack.isFirstGreaterThanSecond(focusedApp, wtoken)) + || mStacks.indexOf(focusedAppStack) > mStacks.indexOf(appStack)) { + // App stack below focused app stack. No focus for you!!! + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, + "findFocusedWindow: Reached focused app=" + focusedApp); + return null; + } + } + + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Found new focus @ " + + i + " = " + win); + return win; + } + + if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: No focusable windows."); + return null; + } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f2f24748761e9..a759d5633ca94 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -684,6 +684,11 @@ class Task implements DimLayer.DimLayerUser { return false; } + // TODO: Use WindowContainer.compareTo() once everything is using WindowContainer + boolean isFirstGreaterThanSecond(AppWindowToken first, AppWindowToken second) { + return mAppTokens.indexOf(first) > mAppTokens.indexOf(second); + } + boolean fillsParent() { return mFullscreen || !StackId.isTaskResizeAllowed(mStack.mStackId); } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index f5fa9fd47f65a..534d908bcf875 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1309,6 +1309,17 @@ public class TaskStack implements DimLayer.DimLayerUser, } } + // TODO: Use WindowContainer.compareTo() once everything is using WindowContainer + boolean isFirstGreaterThanSecond(AppWindowToken first, AppWindowToken second) { + final Task firstTask = first.mTask; + final Task secondTask = second.mTask; + + if (firstTask == secondTask) { + return firstTask.isFirstGreaterThanSecond(first, second); + } + return mTasks.indexOf(first) > mTasks.indexOf(second); + } + // TODO: Remove once switched to use WindowContainer int getOrientation() { if (!StackId.canSpecifyOrientation(mStackId)) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 0ed3a33dfbe39..176663d754ad4 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -31,7 +31,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; * The test class is {@link WindowContainerTests} which must be kept up-to-date and ran anytime * changes are made to this class. */ -class WindowContainer { +class WindowContainer implements Comparable { // The parent of this window container. private WindowContainer mParent = null; @@ -47,6 +47,10 @@ class WindowContainer { return mParent; } + // Temp. holders for a chain of containers we are currently processing. + private final LinkedList mTmpChain1 = new LinkedList(); + private final LinkedList mTmpChain2 = new LinkedList(); + /** * Adds the input window container has a child of this container in order based on the input * comparator. @@ -311,4 +315,65 @@ class WindowContainer { boolean fillsParent() { return false; } + + /** + * Returns -1, 0, or 1 depending on if the input container is greater than, equal to, or lesser + * than the input container in terms of z-order. + */ + @Override + public int compareTo(WindowContainer other) { + if (this == other) { + return 0; + } + + if (mParent != null && mParent == other.mParent) { + final LinkedList list = mParent.mChildren; + return list.indexOf(this) > list.indexOf(other) ? 1 : -1; + } + + final LinkedList thisParentChain = mTmpChain1; + final LinkedList otherParentChain = mTmpChain2; + getParents(thisParentChain); + other.getParents(otherParentChain); + + // Find the common ancestor of both containers. + WindowContainer commonAncestor = null; + WindowContainer thisTop = thisParentChain.peekLast(); + WindowContainer otherTop = otherParentChain.peekLast(); + while (thisTop != null && otherTop != null && thisTop == otherTop) { + commonAncestor = thisParentChain.removeLast(); + otherParentChain.removeLast(); + thisTop = thisParentChain.peekLast(); + otherTop = otherParentChain.peekLast(); + } + + // Containers don't belong to the same hierarchy??? + if (commonAncestor == null) { + throw new IllegalArgumentException("No in the same hierarchy this=" + + thisParentChain + " other=" + otherParentChain); + } + + // Children are always considered greater than their parents, so if one of the containers + // we are comparing it the parent of the other then whichever is the child is greater. + if (commonAncestor == this) { + return -1; + } else if (commonAncestor == other) { + return 1; + } + + // The position of the first non-common ancestor in the common ancestor list determines + // which is greater the which. + final LinkedList list = commonAncestor.mChildren; + return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast()) + ? 1 : -1; + } + + private void getParents(LinkedList parents) { + parents.clear(); + WindowContainer current = this; + do { + parents.addLast(current); + current = current.mParent; + } while (current != null); + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5e279fe4f7aa2..85cbd72c86620 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3638,8 +3638,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) { // We're launchingBehind, add the launching activity to mOpeningApps. - final WindowState win = - findFocusedWindowLocked(getDefaultDisplayContentLocked()); + final WindowState win = getDefaultDisplayContentLocked().findFocusedWindow(); if (win != null) { final AppWindowToken focusedToken = win.mAppToken; if (focusedToken != null) { @@ -8772,7 +8771,7 @@ public class WindowManagerService extends IWindowManager.Stub final int displayCount = mDisplayContents.size(); for (int i = 0; i < displayCount; i++) { final DisplayContent displayContent = mDisplayContents.valueAt(i); - WindowState win = findFocusedWindowLocked(displayContent); + final WindowState win = displayContent.findFocusedWindow(); if (win != null) { return win; } @@ -8780,67 +8779,6 @@ public class WindowManagerService extends IWindowManager.Stub return null; } - WindowState findFocusedWindowLocked(DisplayContent displayContent) { - final WindowList windows = displayContent.getWindowList(); - for (int i = windows.size() - 1; i >= 0; i--) { - final WindowState win = windows.get(i); - - if (localLOGV || DEBUG_FOCUS) Slog.v( - TAG_WM, "Looking for focus: " + i - + " = " + win - + ", flags=" + win.mAttrs.flags - + ", canReceive=" + win.canReceiveKeys()); - - if (!win.canReceiveKeys()) { - continue; - } - - AppWindowToken wtoken = win.mAppToken; - - // If this window's application has been removed, just skip it. - if (wtoken != null && (wtoken.removed || wtoken.sendingToBottom)) { - if (DEBUG_FOCUS) Slog.v(TAG_WM, "Skipping " + wtoken + " because " - + (wtoken.removed ? "removed" : "sendingToBottom")); - continue; - } - - // Descend through all of the app tokens and find the first that either matches - // win.mAppToken (return win) or mFocusedApp (return null). - if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING && - mFocusedApp != null) { - ArrayList tasks = displayContent.getTasks(); - for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { - AppTokenList tokens = tasks.get(taskNdx).mAppTokens; - int tokenNdx = tokens.size() - 1; - for ( ; tokenNdx >= 0; --tokenNdx) { - final AppWindowToken token = tokens.get(tokenNdx); - if (wtoken == token) { - break; - } - if (mFocusedApp == token && token.windowsAreFocusable()) { - // Whoops, we are below the focused app whose windows are focusable... - // No focus for you!!! - if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, - "findFocusedWindow: Reached focused app=" + mFocusedApp); - return null; - } - } - if (tokenNdx >= 0) { - // Early exit from loop, must have found the matching token. - break; - } - } - } - - if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Found new focus @ " + i + - " = " + win); - return win; - } - - if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: No focusable windows."); - return null; - } - void startFreezingDisplayLocked(boolean inTransaction, int exitAnim, int enterAnim) { if (mDisplayFrozen) { return; diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index a2d02a3be7a42..8bfcf52ab0a26 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -1569,8 +1569,7 @@ class WindowSurfacePlacer { private void processApplicationsAnimatingInPlace(int transit) { if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) { // Find the focused window - final WindowState win = mService.findFocusedWindowLocked( - mService.getDefaultDisplayContentLocked()); + final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow(); if (win != null) { final AppWindowToken wtoken = win.mAppToken; final AppWindowAnimator appAnimator = wtoken.mAppAnimator; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java index 354aa3420f5ea..eb7c7f19f3ad2 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java @@ -326,6 +326,49 @@ public class WindowContainerTests { assertEquals(SCREEN_ORIENTATION_PORTRAIT, root.getOrientation()); } + @Test + public void testCompareTo() throws Exception { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(); + + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child21 = child2.addChildWindow(); + final TestWindowContainer child22 = child2.addChildWindow(); + final TestWindowContainer child23 = child2.addChildWindow(); + final TestWindowContainer child221 = child22.addChildWindow(); + final TestWindowContainer child222 = child22.addChildWindow(); + final TestWindowContainer child223 = child22.addChildWindow(); + final TestWindowContainer child2221 = child222.addChildWindow(); + final TestWindowContainer child2222 = child222.addChildWindow(); + final TestWindowContainer child2223 = child222.addChildWindow(); + + final TestWindowContainer root2 = builder.setLayer(0).build(); + + assertEquals(0, root.compareTo(root)); + assertEquals(-1, child1.compareTo(child2)); + assertEquals(1, child2.compareTo(child1)); + + boolean inTheSameTree = true; + try { + root.compareTo(root2); + } catch (IllegalArgumentException e) { + inTheSameTree = false; + } + assertFalse(inTheSameTree); + + assertEquals(-1, child1.compareTo(child11)); + assertEquals(1, child21.compareTo(root)); + assertEquals(1, child21.compareTo(child12)); + assertEquals(-1, child11.compareTo(child2)); + assertEquals(1, child2221.compareTo(child11)); + assertEquals(-1, child2222.compareTo(child223)); + assertEquals(1, child2223.compareTo(child21)); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private class TestWindowContainer extends WindowContainer { private final int mLayer;