diff options
4 files changed, 182 insertions, 127 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index d6657f1e637b..841527261e29 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -31,6 +31,7 @@ import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.view.IWindow; @@ -83,6 +84,7 @@ public class AccessibilityWindowManager mInteractionConnections = new SparseArray<>(); private final SparseArray<SparseArray<IBinder>> mWindowTokens = new SparseArray<>(); + private final List<WindowInfo> mCachedWindowInfos = new ArrayList<>(); private List<AccessibilityWindowInfo> mWindows; private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection; @@ -173,23 +175,131 @@ public class AccessibilityWindowManager } /** - * Callbacks from from window manager when there's an accessibility change in windows. + * Callbacks from window manager when there's an accessibility change in windows. * + * @param forceSend Send the windows for accessibility even if they haven't changed. * @param windows The windows of current display for accessibility. */ @Override - public void onWindowsForAccessibilityChanged(@NonNull List<WindowInfo> windows) { + public void onWindowsForAccessibilityChanged(boolean forceSend, + @NonNull List<WindowInfo> windows) { synchronized (mLock) { if (DEBUG) { Slog.i(LOG_TAG, "Windows changed: " + windows); } - // Let the policy update the focused and active windows. - updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows); + if (shouldUpdateWindowsLocked(forceSend, windows)) { + cacheWindows(windows); + // Let the policy update the focused and active windows. + updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows); + // Someone may be waiting for the windows - advertise it. + mLock.notifyAll(); + } + } + } + + private boolean shouldUpdateWindowsLocked(boolean forceSend, + @NonNull List<WindowInfo> windows) { + if (forceSend) { + return true; + } + + final int windowCount = windows.size(); + // We computed the windows and if they changed notify the client. + if (mCachedWindowInfos.size() != windowCount) { + // Different size means something changed. + return true; + } else if (!mCachedWindowInfos.isEmpty() || !windows.isEmpty()) { + // Since we always traverse windows from high to low layer + // the old and new windows at the same index should be the + // same, otherwise something changed. + for (int i = 0; i < windowCount; i++) { + WindowInfo oldWindow = mCachedWindowInfos.get(i); + WindowInfo newWindow = windows.get(i); + // We do not care for layer changes given the window + // order does not change. This brings no new information + // to the clients. + if (windowChangedNoLayer(oldWindow, newWindow)) { + return true; + } + } + } + + return false; + } + + private void cacheWindows(List<WindowInfo> windows) { + final int oldWindowCount = mCachedWindowInfos.size(); + for (int i = oldWindowCount - 1; i >= 0; i--) { + mCachedWindowInfos.remove(i).recycle(); + } + final int newWindowCount = windows.size(); + for (int i = 0; i < newWindowCount; i++) { + WindowInfo newWindow = windows.get(i); + mCachedWindowInfos.add(WindowInfo.obtain(newWindow)); + } + } - // Someone may be waiting for the windows - advertise it. - mLock.notifyAll(); + private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) { + if (oldWindow == newWindow) { + return false; + } + if (oldWindow == null) { + return true; + } + if (newWindow == null) { + return true; + } + if (oldWindow.type != newWindow.type) { + return true; + } + if (oldWindow.focused != newWindow.focused) { + return true; + } + if (oldWindow.token == null) { + if (newWindow.token != null) { + return true; + } + } else if (!oldWindow.token.equals(newWindow.token)) { + return true; + } + if (oldWindow.parentToken == null) { + if (newWindow.parentToken != null) { + return true; + } + } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) { + return true; + } + if (oldWindow.activityToken == null) { + if (newWindow.activityToken != null) { + return true; + } + } else if (!oldWindow.activityToken.equals(newWindow.activityToken)) { + return true; + } + if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) { + return true; + } + if (oldWindow.childTokens != null && newWindow.childTokens != null + && !oldWindow.childTokens.equals(newWindow.childTokens)) { + return true; + } + if (!TextUtils.equals(oldWindow.title, newWindow.title)) { + return true; + } + if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) { + return true; + } + if (oldWindow.inPictureInPicture != newWindow.inPictureInPicture) { + return true; + } + if (oldWindow.hasFlagWatchOutsideTouch != newWindow.hasFlagWatchOutsideTouch) { + return true; + } + if (oldWindow.displayId != newWindow.displayId) { + return true; } + return false; } /** diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 6b7187ebcc01..a2891e97f521 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -43,9 +43,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.text.TextUtils; import android.util.ArraySet; -import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; @@ -1036,12 +1034,9 @@ final class AccessibilityController { private static final boolean DEBUG = false; - private final SparseArray<WindowState> mTempWindowStates = - new SparseArray<WindowState>(); + private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>(); - private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>(); - - private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>(); + private final Set<IBinder> mTempBinderSet = new ArraySet<>(); private final RectF mTempRectF = new RectF(); @@ -1098,8 +1093,7 @@ final class AccessibilityController { Slog.i(LOG_TAG, "computeChangedWindows()"); } - boolean windowsChanged = false; - List<WindowInfo> windows = new ArrayList<WindowInfo>(); + List<WindowInfo> windows = new ArrayList<>(); synchronized (mService.mGlobalLock) { // Do not send the windows if there is no current focus as @@ -1169,46 +1163,9 @@ final class AccessibilityController { visibleWindows.clear(); addedWindows.clear(); - - if (!forceSend) { - // We computed the windows and if they changed notify the client. - if (mOldWindows.size() != windows.size()) { - // Different size means something changed. - windowsChanged = true; - } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) { - // Since we always traverse windows from high to low layer - // the old and new windows at the same index should be the - // same, otherwise something changed. - for (int i = 0; i < windowCount; i++) { - WindowInfo oldWindow = mOldWindows.get(i); - WindowInfo newWindow = windows.get(i); - // We do not care for layer changes given the window - // order does not change. This brings no new information - // to the clients. - if (windowChangedNoLayer(oldWindow, newWindow)) { - windowsChanged = true; - break; - } - } - } - } - - if (forceSend || windowsChanged) { - cacheWindows(windows); - } } - // Now we do not hold the lock, so send the windows over. - if (forceSend || windowsChanged) { - if (DEBUG) { - Log.i(LOG_TAG, "Windows changed or force sending:" + windows); - } - mCallback.onWindowsForAccessibilityChanged(windows); - } else { - if (DEBUG) { - Log.i(LOG_TAG, "No windows changed."); - } - } + mCallback.onWindowsForAccessibilityChanged(forceSend, windows); // Recycle the windows as we do not need them. clearAndRecycleWindows(windows); @@ -1313,67 +1270,6 @@ final class AccessibilityController { tokenOut.add(window.token); } - private void cacheWindows(List<WindowInfo> windows) { - final int oldWindowCount = mOldWindows.size(); - for (int i = oldWindowCount - 1; i >= 0; i--) { - mOldWindows.remove(i).recycle(); - } - final int newWindowCount = windows.size(); - for (int i = 0; i < newWindowCount; i++) { - WindowInfo newWindow = windows.get(i); - mOldWindows.add(WindowInfo.obtain(newWindow)); - } - } - - private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) { - if (oldWindow == newWindow) { - return false; - } - if (oldWindow == null) { - return true; - } - if (newWindow == null) { - return true; - } - if (oldWindow.type != newWindow.type) { - return true; - } - if (oldWindow.focused != newWindow.focused) { - return true; - } - if (oldWindow.token == null) { - if (newWindow.token != null) { - return true; - } - } else if (!oldWindow.token.equals(newWindow.token)) { - return true; - } - if (oldWindow.parentToken == null) { - if (newWindow.parentToken != null) { - return true; - } - } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) { - return true; - } - if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) { - return true; - } - if (oldWindow.childTokens != null && newWindow.childTokens != null - && !oldWindow.childTokens.equals(newWindow.childTokens)) { - return true; - } - if (!TextUtils.equals(oldWindow.title, newWindow.title)) { - return true; - } - if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) { - return true; - } - if (oldWindow.displayId != newWindow.displayId) { - return true; - } - return false; - } - private static void clearAndRecycleWindows(List<WindowInfo> windows) { final int windowCount = windows.size(); for (int i = windowCount - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 40bec148b33b..6910ce91cce2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -51,9 +51,10 @@ public abstract class WindowManagerInternal { /** * Called when the windows for accessibility changed. * + * @param forceSend Send the windows for accessibility even if they haven't changed. * @param windows The windows for accessibility. */ - public void onWindowsForAccessibilityChanged(List<WindowInfo> windows); + void onWindowsForAccessibilityChanged(boolean forceSend, List<WindowInfo> windows); } /** diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java index 04ca40e74546..22408cc05b93 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java @@ -70,6 +70,8 @@ import java.util.List; */ public class AccessibilityWindowManagerTest { private static final String PACKAGE_NAME = "com.android.server.accessibility"; + private static final boolean FORCE_SEND = true; + private static final boolean SEND_ON_WINDOW_CHANGES = false; private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM; private static final int NUM_GLOBAL_WINDOWS = 4; private static final int NUM_APP_WINDOWS = 4; @@ -122,7 +124,7 @@ public class AccessibilityWindowManagerTest { mWindowInfos.get(DEFAULT_FOCUSED_INDEX).focused = true; // Turn on windows tracking, and update window info mA11yWindowManager.startTrackingWindows(); - mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos); + mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos); assertEquals(mA11yWindowManager.getWindowListLocked().size(), mWindowInfos.size()); @@ -169,16 +171,16 @@ public class AccessibilityWindowManagerTest { @Test public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() { final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - WindowInfo focuedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX); + WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX); assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, focuedWindowInfo.token)); + USER_SYSTEM_ID, focusedWindowInfo.token)); - focuedWindowInfo.focused = false; - focuedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1); - focuedWindowInfo.focused = true; + focusedWindowInfo.focused = false; + focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1); + focusedWindowInfo.focused = true; mA11yWindowManager.onTouchInteractionStart(); - mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos); + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); } @@ -208,6 +210,52 @@ public class AccessibilityWindowManagerTest { } @Test + public void onWindowsChangedAndForceSend_shouldUpdateWindows() { + final WindowInfo windowInfo = mWindowInfos.get(0); + final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer(); + windowInfo.layer += 1; + + mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos); + assertNotEquals(correctLayer, mA11yWindowManager.getWindowListLocked().get(0).getLayer()); + } + + @Test + public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() { + final WindowInfo windowInfo = mWindowInfos.get(0); + final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer(); + windowInfo.layer += 1; + + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + assertEquals(correctLayer, mA11yWindowManager.getWindowListLocked().get(0).getLayer()); + } + + @Test + public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows() + throws RemoteException { + final AccessibilityWindowInfo oldWindow = mA11yWindowManager.getWindowListLocked().get(0); + final IWindow token = addAccessibilityInteractionConnection(true); + final WindowInfo windowInfo = WindowInfo.obtain(); + windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; + windowInfo.token = token.asBinder(); + windowInfo.layer = 0; + windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + mWindowInfos.set(0, windowInfo); + + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + assertNotEquals(oldWindow, mA11yWindowManager.getWindowListLocked().get(0)); + } + + @Test + public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() { + final WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX); + final WindowInfo windowInfo = mWindowInfos.get(0); + focusedWindowInfo.focused = false; + windowInfo.focused = true; + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + assertTrue(mA11yWindowManager.getWindowListLocked().get(0).isFocused()); + } + + @Test public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() { for (int i = 0; i < NUM_OF_WINDOWS; i++) { final int windowId = mA11yWindowTokens.keyAt(i); @@ -264,7 +312,7 @@ public class AccessibilityWindowManagerTest { windowInfo = mWindowInfos.get(1); windowInfo.boundsInScreen.set(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT); - mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos); + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); @@ -291,7 +339,7 @@ public class AccessibilityWindowManagerTest { windowInfo = mWindowInfos.get(1); windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos); + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); @@ -309,7 +357,7 @@ public class AccessibilityWindowManagerTest { windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); windowInfo = mWindowInfos.get(1); windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos); + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); @@ -498,7 +546,7 @@ public class AccessibilityWindowManagerTest { public void getPictureInPictureWindow_shouldNotNull() { assertNull(mA11yWindowManager.getPictureInPictureWindow()); mWindowInfos.get(1).inPictureInPicture = true; - mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos); + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); assertNotNull(mA11yWindowManager.getPictureInPictureWindow()); } @@ -511,7 +559,7 @@ public class AccessibilityWindowManagerTest { mA11yWindowManager.getConnectionLocked( USER_SYSTEM_ID, outsideWindowId).getRemote(); mWindowInfos.get(0).hasFlagWatchOutsideTouch = true; - mA11yWindowManager.onWindowsForAccessibilityChanged(mWindowInfos); + mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId); verify(mockRemoteConnection).notifyOutsideTouch(); |