diff options
4 files changed, 207 insertions, 34 deletions
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 2db0dcbce45e..7d8f363bf81c 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -1332,6 +1332,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * Convenience method to obtain a {@link #TYPE_WINDOWS_CHANGED} event for a specific window and * change set. * + * @param displayId The ID of the display from which the event comes from * @param windowId The ID of the window that changed * @param windowChangeTypes The changes to populate * @return An instance of a TYPE_WINDOWS_CHANGED, populated with the requested fields and with @@ -1340,8 +1341,9 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @hide */ public static AccessibilityEvent obtainWindowsChangedEvent( - int windowId, int windowChangeTypes) { + int displayId, int windowId, int windowChangeTypes) { final AccessibilityEvent event = new AccessibilityEvent(TYPE_WINDOWS_CHANGED); + event.setDisplayId(displayId); event.setWindowId(windowId); event.setWindowChanges(windowChangeTypes); event.setImportantForAccessibility(true); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 6a6d2bb44d48..098e0245c32d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -952,10 +952,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // current state of the windows as the window manager may be delaying // the computation for performance reasons. boolean shouldComputeWindows = false; - int displayId = Display.INVALID_DISPLAY; + int displayId = event.getDisplayId(); synchronized (mLock) { final int windowId = event.getWindowId(); - if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { + if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID + && displayId == Display.INVALID_DISPLAY) { displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked( resolvedUserId, windowId); event.setDisplayId(displayId); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index d33e7b2e4d87..7c68c8a5bdb1 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -19,7 +19,6 @@ package com.android.server.accessibility; import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION; import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; -import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -275,22 +274,23 @@ public class AccessibilityWindowManager { * Sets the active flag of the window according to given windowId, others set to inactive. * * @param windowId The windowId + * @return {@code true} if the window is in this display, {@code false} otherwise. */ - void setActiveWindowLocked(int windowId) { + boolean setActiveWindowLocked(int windowId) { + boolean foundWindow = false; if (mWindows != null) { final int windowCount = mWindows.size(); for (int i = 0; i < windowCount; i++) { AccessibilityWindowInfo window = mWindows.get(i); if (window.getId() == windowId) { window.setActive(true); - mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( - AccessibilityEvent.obtainWindowsChangedEvent(windowId, - AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)); + foundWindow = true; } else { window.setActive(false); } } } + return foundWindow; } /** @@ -298,24 +298,23 @@ public class AccessibilityWindowManager { * unfocused. * * @param windowId The windowId + * @return {@code true} if the window is in this display, {@code false} otherwise. */ - void setAccessibilityFocusedWindowLocked(int windowId) { + boolean setAccessibilityFocusedWindowLocked(int windowId) { + boolean foundWindow = false; if (mWindows != null) { final int windowCount = mWindows.size(); for (int i = 0; i < windowCount; i++) { AccessibilityWindowInfo window = mWindows.get(i); if (window.getId() == windowId) { - mAccessibilityFocusedDisplayId = mDisplayId; window.setAccessibilityFocused(true); - mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( - AccessibilityEvent.obtainWindowsChangedEvent( - windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); - + foundWindow = true; } else { window.setAccessibilityFocused(false); } } } + return foundWindow; } /** @@ -704,7 +703,7 @@ public class AccessibilityWindowManager { final AccessibilityWindowInfo window = oldWindows.get(i); if (mA11yWindowInfoById.get(window.getId()) == null) { events.add(AccessibilityEvent.obtainWindowsChangedEvent( - window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED)); + mDisplayId, window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED)); } } @@ -714,13 +713,13 @@ public class AccessibilityWindowManager { final AccessibilityWindowInfo newWindow = mWindows.get(i); final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId()); if (oldWindow == null) { - events.add(AccessibilityEvent.obtainWindowsChangedEvent( + events.add(AccessibilityEvent.obtainWindowsChangedEvent(mDisplayId, newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED)); } else { int changes = newWindow.differenceFrom(oldWindow); if (changes != 0) { events.add(AccessibilityEvent.obtainWindowsChangedEvent( - newWindow.getId(), changes)); + mDisplayId, newWindow.getId(), changes)); } } } @@ -1522,38 +1521,59 @@ public class AccessibilityWindowManager { private void setActiveWindowLocked(int windowId) { if (mActiveWindowId != windowId) { - mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( - AccessibilityEvent.obtainWindowsChangedEvent( + List<AccessibilityEvent> events = new ArrayList<>(2); + if (mActiveWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { + final DisplayWindowsObserver observer = + getDisplayWindowObserverByWindowIdLocked(mActiveWindowId); + if (observer != null) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId, mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)); + } + } mActiveWindowId = windowId; // Goes through all windows for each display. final int count = mDisplayWindowsObservers.size(); for (int i = 0; i < count; i++) { final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); - if (observer != null) { - observer.setActiveWindowLocked(windowId); + if (observer != null && observer.setActiveWindowLocked(windowId)) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId, + windowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)); } } + + for (final AccessibilityEvent event : events) { + mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(event); + } } } private void setAccessibilityFocusedWindowLocked(int windowId) { if (mAccessibilityFocusedWindowId != windowId) { - mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( - AccessibilityEvent.obtainWindowsChangedEvent( - mAccessibilityFocusedWindowId, - WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); + List<AccessibilityEvent> events = new ArrayList<>(2); + if (mAccessibilityFocusedDisplayId != Display.INVALID_DISPLAY + && mAccessibilityFocusedWindowId + != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent( + mAccessibilityFocusedDisplayId, mAccessibilityFocusedWindowId, + AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); + } mAccessibilityFocusedWindowId = windowId; // Goes through all windows for each display. final int count = mDisplayWindowsObservers.size(); for (int i = 0; i < count; i++) { final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); - if (observer != null) { - observer.setAccessibilityFocusedWindowLocked(windowId); + if (observer != null && observer.setAccessibilityFocusedWindowLocked(windowId)) { + mAccessibilityFocusedDisplayId = observer.mDisplayId; + events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId, + windowId, AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); } } + + for (final AccessibilityEvent event : events) { + mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(event); + } } } 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 c7757f7832fa..acbcad54626d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java @@ -16,6 +16,7 @@ package com.android.server.accessibility; +import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId; import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges; import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId; @@ -587,10 +588,12 @@ public class AccessibilityWindowManagerTest { verify(mMockA11yEventSender, times(2)) .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), - allOf(a11yWindowId(currentActiveWindowId), + allOf(displayId(Display.DEFAULT_DISPLAY), + a11yWindowId(currentActiveWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); assertThat(captor.getAllValues().get(1), - allOf(a11yWindowId(eventWindowId), + allOf(displayId(Display.DEFAULT_DISPLAY), + a11yWindowId(eventWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); } @@ -600,10 +603,59 @@ public class AccessibilityWindowManagerTest { DEFAULT_FOCUSED_INDEX); final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); - assertThat(currentA11yFocusedWindowId, is(not(eventWindowId))); + assertThat(currentA11yFocusedWindowId, is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); + + final int noUse = 0; + mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, + eventWindowId, + AccessibilityNodeInfo.ROOT_NODE_ID, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, + noUse); + assertThat(mA11yWindowManager.getFocusedWindowId( + AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); + final ArgumentCaptor<AccessibilityEvent> captor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + verify(mMockA11yEventSender, times(1)) + .sendAccessibilityEventForCurrentUserLocked(captor.capture()); + assertThat(captor.getAllValues().get(0), + allOf(displayId(Display.DEFAULT_DISPLAY), + a11yWindowId(eventWindowId), + a11yWindowChanges( + AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); + } + + @Test + public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary() + throws RemoteException { + runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( + Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID); + } + + @Test + public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault() + throws RemoteException { + runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( + SECONDARY_DISPLAY_ID, Display.DEFAULT_DISPLAY); + } + private void runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( + int initialDisplayId, int eventDisplayId) throws RemoteException { + startTrackingPerDisplay(SECONDARY_DISPLAY_ID); + final int initialWindowId = getWindowIdFromWindowInfosForDisplay( + initialDisplayId, DEFAULT_FOCUSED_INDEX); final int noUse = 0; mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, + initialWindowId, + AccessibilityNodeInfo.ROOT_NODE_ID, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, + noUse); + assertThat(mA11yWindowManager.getFocusedWindowId( + AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(initialWindowId)); + Mockito.reset(mMockA11yEventSender); + + final int eventWindowId = getWindowIdFromWindowInfosForDisplay( + eventDisplayId, DEFAULT_FOCUSED_INDEX); + mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, eventWindowId, AccessibilityNodeInfo.ROOT_NODE_ID, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, @@ -615,11 +667,13 @@ public class AccessibilityWindowManagerTest { verify(mMockA11yEventSender, times(2)) .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), - allOf(a11yWindowId(currentA11yFocusedWindowId), + allOf(displayId(initialDisplayId), + a11yWindowId(initialWindowId), a11yWindowChanges( AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); assertThat(captor.getAllValues().get(1), - allOf(a11yWindowId(eventWindowId), + allOf(displayId(eventDisplayId), + a11yWindowId(eventWindowId), a11yWindowChanges( AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); } @@ -674,10 +728,12 @@ public class AccessibilityWindowManagerTest { verify(mMockA11yEventSender, times(2)) .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), - allOf(a11yWindowId(eventWindowId), + allOf(displayId(Display.DEFAULT_DISPLAY), + a11yWindowId(eventWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); assertThat(captor.getAllValues().get(1), - allOf(a11yWindowId(currentActiveWindowId), + allOf(displayId(Display.DEFAULT_DISPLAY), + a11yWindowId(currentActiveWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); } @@ -861,6 +917,77 @@ public class AccessibilityWindowManagerTest { assertTrue(TextUtils.equals(layoutParams.accessibilityTitle, a11yWindow.getTitle())); } + @Test + public void sendAccessibilityEventOnWindowRemoval() { + final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); + + // Removing index 0 because it's not focused, and avoids unnecessary layer change. + final int windowId = + getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); + infos.remove(0); + for (WindowInfo info : infos) { + // Adjust layer number because it should start from 0. + info.layer--; + } + + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + + final ArgumentCaptor<AccessibilityEvent> captor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + verify(mMockA11yEventSender, times(1)) + .sendAccessibilityEventForCurrentUserLocked(captor.capture()); + assertThat(captor.getAllValues().get(0), + allOf(displayId(Display.DEFAULT_DISPLAY), + a11yWindowId(windowId), + a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED))); + } + + @Test + public void sendAccessibilityEventOnWindowAddition() throws RemoteException { + final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); + + for (WindowInfo info : infos) { + // Adjust layer number because new window will have 0 so that layer number in + // A11yWindowInfo in window won't be changed. + info.layer++; + } + + final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, + false, USER_SYSTEM_ID); + addWindowInfo(infos, token, 0); + final int windowId = + getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1); + + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + + final ArgumentCaptor<AccessibilityEvent> captor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + verify(mMockA11yEventSender, times(1)) + .sendAccessibilityEventForCurrentUserLocked(captor.capture()); + assertThat(captor.getAllValues().get(0), + allOf(displayId(Display.DEFAULT_DISPLAY), + a11yWindowId(windowId), + a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED))); + } + + @Test + public void sendAccessibilityEventOnWindowChange() { + final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); + infos.get(0).title = "new title"; + final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); + + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + + final ArgumentCaptor<AccessibilityEvent> captor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + verify(mMockA11yEventSender, times(1)) + .sendAccessibilityEventForCurrentUserLocked(captor.capture()); + assertThat(captor.getAllValues().get(0), + allOf(displayId(Display.DEFAULT_DISPLAY), + a11yWindowId(windowId), + a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE))); + } + private void registerLeashedTokenAndWindowId() { mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID); mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID); @@ -1018,6 +1145,29 @@ public class AccessibilityWindowManagerTest { } } + static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { + private final int mDisplayId; + + DisplayIdMatcher(int displayId) { + super(); + mDisplayId = displayId; + } + + static DisplayIdMatcher displayId(int displayId) { + return new DisplayIdMatcher(displayId); + } + + @Override + protected boolean matchesSafely(AccessibilityEvent event) { + return event.getDisplayId() == mDisplayId; + } + + @Override + public void describeTo(Description description) { + description.appendText("Matching to displayId " + mDisplayId); + } + } + static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { private int mWindowId; |