diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 2db7f0e108b46..de32a300bfb84 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -396,7 +396,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } - AccessibilityWindowInfo window = mA11yWindowManager.findA11yWindowInfoById(windowId); + AccessibilityWindowInfo window = + mA11yWindowManager.findA11yWindowInfoByIdLocked(windowId); if (window != null) { AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window); windowClone.setConnectionId(mId); @@ -1356,11 +1357,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ || (action == ACTION_CLEAR_ACCESSIBILITY_FOCUS); if (!isA11yFocusAction) { final WindowInfo windowInfo = - mA11yWindowManager.findWindowInfoById(resolvedWindowId); + mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId); if (windowInfo != null) activityToken = windowInfo.activityToken; } final AccessibilityWindowInfo a11yWindowInfo = - mA11yWindowManager.findA11yWindowInfoById(resolvedWindowId); + mA11yWindowManager.findA11yWindowInfoByIdLocked(resolvedWindowId); if (a11yWindowInfo != null && a11yWindowInfo.isInPictureInPictureMode() && mA11yWindowManager.getPictureInPictureActionReplacingConnection() != null && !isA11yFocusAction) { @@ -1413,11 +1414,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ int interactionId, int interrogatingPid, long interrogatingTid) { final RemoteAccessibilityConnection pipActionReplacingConnection = mA11yWindowManager.getPictureInPictureActionReplacingConnection(); - final AccessibilityWindowInfo windowInfo = - mA11yWindowManager.findA11yWindowInfoById(resolvedWindowId); - if ((windowInfo == null) || !windowInfo.isInPictureInPictureMode() + synchronized (mLock) { + final AccessibilityWindowInfo windowInfo = + mA11yWindowManager.findA11yWindowInfoByIdLocked(resolvedWindowId); + if ((windowInfo == null) || !windowInfo.isInPictureInPictureMode() || (pipActionReplacingConnection == null)) { - return originalCallback; + return originalCallback; + } } return new ActionReplacingCallback(originalCallback, pipActionReplacingConnection.getRemote(), interactionId, diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index a2204518e762f..ae5fd56cd10ac 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -541,7 +541,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (event.getWindowId() == AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID) { // The replacer window isn't shown to services. Move its events into the pip. - AccessibilityWindowInfo pip = mA11yWindowManager.getPictureInPictureWindow(); + AccessibilityWindowInfo pip = mA11yWindowManager.getPictureInPictureWindowLocked(); if (pip != null) { int pipId = pip.getId(); event.setWindowId(pipId); @@ -770,7 +770,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (resolvedUserId != mCurrentUserId) { return null; } - if (mA11yWindowManager.findA11yWindowInfoById(windowId) == null) { + if (mA11yWindowManager.findA11yWindowInfoByIdLocked(windowId) == null) { return null; } return mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(userId, windowId); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index 1e224cfaea85f..315d6fa287f2a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -429,7 +429,7 @@ public class AccessibilitySecurityPolicy { if (windowId == mAccessibilityWindowManager.getActiveWindowId(userId)) { return true; } - return mAccessibilityWindowManager.findA11yWindowInfoById(windowId) != null; + return mAccessibilityWindowManager.findA11yWindowInfoByIdLocked(windowId) != null; } private boolean isShellAllowedToRetrieveWindowLocked(int userId, int windowId) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index c129291537818..9687098b6f3fd 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -52,13 +52,10 @@ import java.util.Collections; import java.util.List; /** - * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to receive - * {@link WindowInfo}s from window manager when there's an accessibility change in window. It also - * provides APIs for accessibility manager to manage {@link AccessibilityWindowInfo}s and + * This class provides APIs for accessibility manager to manage {@link AccessibilityWindowInfo}s and * {@link WindowInfo}s. */ -public class AccessibilityWindowManager - implements WindowManagerInternal.WindowsForAccessibilityCallback { +public class AccessibilityWindowManager { private static final String LOG_TAG = "AccessibilityWindowManager"; private static final boolean DEBUG = false; @@ -71,9 +68,6 @@ public class AccessibilityWindowManager private final AccessibilitySecurityPolicy mSecurityPolicy; private final AccessibilityUserManager mAccessibilityUserManager; - private final SparseArray mA11yWindowInfoById = new SparseArray<>(); - private final SparseArray mWindowInfoById = new SparseArray<>(); - // Connections and window tokens for cross-user windows private final SparseArray mGlobalInteractionConnections = new SparseArray<>(); @@ -84,9 +78,6 @@ public class AccessibilityWindowManager mInteractionConnections = new SparseArray<>(); private final SparseArray> mWindowTokens = new SparseArray<>(); - private final List mCachedWindowInfos = new ArrayList<>(); - private List mWindows; - private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection; private int mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; @@ -95,15 +86,641 @@ public class AccessibilityWindowManager private long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; private boolean mTouchInteractionInProgress; - private boolean mHasWatchOutsideTouchWindow; - private boolean mTrackingWindows = false; + // TO-DO [Multi-Display] : make DisplayWindowObserver to plural + private DisplayWindowsObserver mDisplayWindowsObserver; + + /** + * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to + * receive {@link WindowInfo}s from window manager when there's an accessibility change in + * window and holds window lists information per display. + */ + private final class DisplayWindowsObserver implements + WindowManagerInternal.WindowsForAccessibilityCallback { + + private final int mDisplayId; + private final SparseArray mA11yWindowInfoById = + new SparseArray<>(); + private final SparseArray mWindowInfoById = new SparseArray<>(); + private final List mCachedWindowInfos = new ArrayList<>(); + private List mWindows; + private boolean mTrackingWindows = false; + private boolean mHasWatchOutsideTouchWindow; + + /** + * Constructor for DisplayWindowsObserver. + */ + DisplayWindowsObserver(int displayId) { + mDisplayId = displayId; + } + + /** + * Starts tracking windows changes from window manager by registering callback. + * + * @return true if callback registers successful. + */ + boolean startTrackingWindowsLocked() { + boolean result = true; + + if (!mTrackingWindows) { + // Turns on the flag before setup the callback. + // In some cases, onWindowsForAccessibilityChanged will be called immediately in + // setWindowsForAccessibilityCallback. We'll lost windows if flag is false. + mTrackingWindows = true; + result = mWindowManagerInternal.setWindowsForAccessibilityCallback( + mDisplayId, this); + if (!result) { + mTrackingWindows = false; + Slog.w(LOG_TAG, "set windowsObserver callbacks fail, displayId:" + + mDisplayId); + } + } + return result; + } + + /** + * Stops tracking windows changes from window manager, and clear all windows info. + */ + void stopTrackingWindowsLocked() { + if (mTrackingWindows) { + mWindowManagerInternal.setWindowsForAccessibilityCallback( + mDisplayId, null); + mTrackingWindows = false; + clearWindowsLocked(); + } + } + + /** + * Returns true if windows changes tracking. + * + * @return true if windows changes tracking + */ + boolean isTrackingWindowsLocked() { + return mTrackingWindows; + } + + /** + * Returns accessibility windows. + * @return accessibility windows. + */ + @Nullable + List getWindowListLocked() { + return mWindows; + } + + /** + * Returns accessibility window info according to given windowId. + * + * @param windowId The windowId + * @return The accessibility window info + */ + @Nullable + AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) { + return mA11yWindowInfoById.get(windowId); + } + + /** + * Returns the window info according to given windowId. + * + * @param windowId The windowId + * @return The window info + */ + @Nullable + WindowInfo findWindowInfoByIdLocked(int windowId) { + return mWindowInfoById.get(windowId); + } + + /** + * Returns {@link AccessibilityWindowInfo} of PIP window. + * + * @return PIP accessibility window info + */ + @Nullable + AccessibilityWindowInfo getPictureInPictureWindowLocked() { + if (mWindows != null) { + final int windowCount = mWindows.size(); + for (int i = 0; i < windowCount; i++) { + final AccessibilityWindowInfo window = mWindows.get(i); + if (window.isInPictureInPictureMode()) { + return window; + } + } + } + return null; + } + + /** + * Sets the active flag of the window according to given windowId, others set to inactive. + * + * @param windowId The windowId + */ + void setActiveWindowLocked(int windowId) { + 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)); + } else { + window.setActive(false); + } + } + } + } + + /** + * Sets the window accessibility focused according to given windowId, others set + * unfocused. + * + * @param windowId The windowId + */ + void setAccessibilityFocusedWindowLocked(int windowId) { + 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.setAccessibilityFocused(true); + mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( + AccessibilityEvent.obtainWindowsChangedEvent( + windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); + + } else { + window.setAccessibilityFocused(false); + } + } + } + } + + /** + * Computes partial interactive region of given windowId. + * + * @param windowId The windowId + * @param outRegion The output to which to write the bounds. + * @return true if outRegion is not empty. + */ + boolean computePartialInteractiveRegionForWindowLocked(int windowId, + @NonNull Region outRegion) { + if (mWindows == null) { + return false; + } + + // Windows are ordered in z order so start from the bottom and find + // the window of interest. After that all windows that cover it should + // be subtracted from the resulting region. Note that for accessibility + // we are returning only interactive windows. + Region windowInteractiveRegion = null; + boolean windowInteractiveRegionChanged = false; + + final int windowCount = mWindows.size(); + final Region currentWindowRegions = new Region(); + for (int i = windowCount - 1; i >= 0; i--) { + AccessibilityWindowInfo currentWindow = mWindows.get(i); + if (windowInteractiveRegion == null) { + if (currentWindow.getId() == windowId) { + currentWindow.getRegionInScreen(currentWindowRegions); + outRegion.set(currentWindowRegions); + windowInteractiveRegion = outRegion; + continue; + } + } else if (currentWindow.getType() + != AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY) { + currentWindow.getRegionInScreen(currentWindowRegions); + if (windowInteractiveRegion.op(currentWindowRegions, Region.Op.DIFFERENCE)) { + windowInteractiveRegionChanged = true; + } + } + } + + return windowInteractiveRegionChanged; + } + + List getWatchOutsideTouchWindowIdLocked(int targetWindowId) { + final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId); + if (targetWindow != null && mHasWatchOutsideTouchWindow) { + final List outsideWindowsId = new ArrayList<>(); + for (int i = 0; i < mWindowInfoById.size(); i++) { + final WindowInfo window = mWindowInfoById.valueAt(i); + if (window != null && window.layer < targetWindow.layer + && window.hasFlagWatchOutsideTouch) { + outsideWindowsId.add(mWindowInfoById.keyAt(i)); + } + } + return outsideWindowsId; + } + return Collections.emptyList(); + } + + /** + * 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 for accessibility. + */ + @Override + public void onWindowsForAccessibilityChanged(boolean forceSend, + @NonNull List windows) { + synchronized (mLock) { + if (DEBUG) { + Slog.i(LOG_TAG, "Display Id = " + mDisplayId); + Slog.i(LOG_TAG, "Windows changed: " + windows); + } + if (shouldUpdateWindowsLocked(forceSend, windows)) { + cacheWindows(windows); + // Lets 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 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 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)); + } + } + + 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.regionInScreen.equals(newWindow.regionInScreen)) { + 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; + } + + /** + * Clears all {@link AccessibilityWindowInfo}s and {@link WindowInfo}s. + */ + private void clearWindowsLocked() { + final List windows = Collections.emptyList(); + final int activeWindowId = mActiveWindowId; + // UserId is useless in updateWindowsLocked, when we update a empty window list. + // Just pass current userId here. + updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows); + // Do not reset mActiveWindowId here. mActiveWindowId will be clear after accessibility + // interaction connection removed. + mActiveWindowId = activeWindowId; + mWindows = null; + } + + /** + * Updates windows info according to specified userId and windows. + * + * @param userId The userId to update + * @param windows The windows to update + */ + private void updateWindowsLocked(int userId, @NonNull List windows) { + if (mWindows == null) { + mWindows = new ArrayList<>(); + } + + final List oldWindowList = new ArrayList<>(mWindows); + final SparseArray oldWindowsById = mA11yWindowInfoById.clone(); + + mWindows.clear(); + mA11yWindowInfoById.clear(); + + for (int i = 0; i < mWindowInfoById.size(); i++) { + mWindowInfoById.valueAt(i).recycle(); + } + mWindowInfoById.clear(); + mHasWatchOutsideTouchWindow = false; + mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; + if (!mTouchInteractionInProgress) { + mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; + } + + // If the active window goes away while the user is touch exploring we + // reset the active window id and wait for the next hover event from + // under the user's finger to determine which one is the new one. It + // is possible that the finger is not moving and the input system + // filters out such events. + boolean activeWindowGone = true; + + final int windowCount = windows.size(); + + // We'll clear accessibility focus if the window with focus is no longer visible to + // accessibility services + boolean shouldClearAccessibilityFocus = + mAccessibilityFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; + if (windowCount > 0) { + for (int i = 0; i < windowCount; i++) { + final WindowInfo windowInfo = windows.get(i); + final AccessibilityWindowInfo window; + if (isTrackingWindowsLocked()) { + window = populateReportedWindowLocked(userId, windowInfo); + } else { + window = null; + } + if (window != null) { + + // Flip layers in list to be consistent with AccessibilityService#getWindows + window.setLayer(windowCount - 1 - window.getLayer()); + + final int windowId = window.getId(); + if (window.isFocused()) { + mFocusedWindowId = windowId; + if (!mTouchInteractionInProgress) { + mActiveWindowId = windowId; + window.setActive(true); + } else if (windowId == mActiveWindowId) { + activeWindowGone = false; + } + } + if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) { + mHasWatchOutsideTouchWindow = true; + } + mWindows.add(window); + mA11yWindowInfoById.put(windowId, window); + mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo)); + } + } + + if (mTouchInteractionInProgress && activeWindowGone) { + mActiveWindowId = mFocusedWindowId; + } + + // Focused window may change the active one, so set the + // active window once we decided which it is. + final int accessibilityWindowCount = mWindows.size(); + for (int i = 0; i < accessibilityWindowCount; i++) { + final AccessibilityWindowInfo window = mWindows.get(i); + if (window.getId() == mActiveWindowId) { + window.setActive(true); + } + if (window.getId() == mAccessibilityFocusedWindowId) { + window.setAccessibilityFocused(true); + shouldClearAccessibilityFocus = false; + } + } + } + + sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById); + + final int oldWindowCount = oldWindowList.size(); + for (int i = oldWindowCount - 1; i >= 0; i--) { + oldWindowList.remove(i).recycle(); + } + + if (shouldClearAccessibilityFocus) { + clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId); + } + } + + private void sendEventsForChangedWindowsLocked(List oldWindows, + SparseArray oldWindowsById) { + List events = new ArrayList<>(); + // Sends events for all removed windows. + final int oldWindowsCount = oldWindows.size(); + for (int i = 0; i < oldWindowsCount; i++) { + final AccessibilityWindowInfo window = oldWindows.get(i); + if (mA11yWindowInfoById.get(window.getId()) == null) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent( + window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED)); + } + } + + // Looks for other changes. + final int newWindowCount = mWindows.size(); + for (int i = 0; i < newWindowCount; i++) { + final AccessibilityWindowInfo newWindow = mWindows.get(i); + final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId()); + if (oldWindow == null) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent( + newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED)); + } else { + int changes = newWindow.differenceFrom(oldWindow); + if (changes != 0) { + events.add(AccessibilityEvent.obtainWindowsChangedEvent( + newWindow.getId(), changes)); + } + } + } + + final int numEvents = events.size(); + for (int i = 0; i < numEvents; i++) { + mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(events.get(i)); + } + } + + private AccessibilityWindowInfo populateReportedWindowLocked(int userId, + WindowInfo window) { + final int windowId = findWindowIdLocked(userId, window.token); + if (windowId < 0) { + return null; + } + + final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); + + reportedWindow.setId(windowId); + reportedWindow.setType(getTypeForWindowManagerWindowType(window.type)); + reportedWindow.setLayer(window.layer); + reportedWindow.setFocused(window.focused); + reportedWindow.setRegionInScreen(window.regionInScreen); + reportedWindow.setTitle(window.title); + reportedWindow.setAnchorId(window.accessibilityIdOfAnchor); + reportedWindow.setPictureInPicture(window.inPictureInPicture); + reportedWindow.setDisplayId(window.displayId); + + final int parentId = findWindowIdLocked(userId, window.parentToken); + if (parentId >= 0) { + reportedWindow.setParentId(parentId); + } + + if (window.childTokens != null) { + final int childCount = window.childTokens.size(); + for (int i = 0; i < childCount; i++) { + final IBinder childToken = window.childTokens.get(i); + final int childId = findWindowIdLocked(userId, childToken); + if (childId >= 0) { + reportedWindow.addChild(childId); + } + } + } + + return reportedWindow; + } + + private int getTypeForWindowManagerWindowType(int windowType) { + switch (windowType) { + case WindowManager.LayoutParams.TYPE_APPLICATION: + case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: + case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING: + case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL: + case WindowManager.LayoutParams.TYPE_BASE_APPLICATION: + case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: + case WindowManager.LayoutParams.TYPE_PHONE: + case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: + case WindowManager.LayoutParams.TYPE_TOAST: + case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: { + return AccessibilityWindowInfo.TYPE_APPLICATION; + } + + case WindowManager.LayoutParams.TYPE_INPUT_METHOD: + case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: { + return AccessibilityWindowInfo.TYPE_INPUT_METHOD; + } + + case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR: + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: + case WindowManager.LayoutParams.TYPE_SEARCH_BAR: + case WindowManager.LayoutParams.TYPE_STATUS_BAR: + case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL: + case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL: + case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: + case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: + case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: + case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: + case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: + case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: + case WindowManager.LayoutParams.TYPE_SCREENSHOT: { + return AccessibilityWindowInfo.TYPE_SYSTEM; + } + + case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: { + return AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER; + } + + case TYPE_ACCESSIBILITY_OVERLAY: { + return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY; + } + + default: { + return -1; + } + } + } + + /** + * Dumps all {@link AccessibilityWindowInfo}s here. + */ + void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) { + if (mWindows != null) { + final int windowCount = mWindows.size(); + for (int j = 0; j < windowCount; j++) { + if (j == 0) { + pw.append("Display["); + pw.append(Integer.toString(mDisplayId)); + pw.append("] : "); + pw.println(); + } + if (j > 0) { + pw.append(','); + pw.println(); + } + pw.append("Window["); + AccessibilityWindowInfo window = mWindows.get(j); + pw.append(window.toString()); + pw.append(']'); + } + pw.println(); + } + } + } /** * Interface to send {@link AccessibilityEvent}. */ public interface AccessibilityEventSender { /** - * Send {@link AccessibilityEvent} for current user. + * Sends {@link AccessibilityEvent} for current user. */ void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event); } @@ -171,166 +788,24 @@ public class AccessibilityWindowManager mAccessibilityEventSender = accessibilityEventSender; mSecurityPolicy = securityPolicy; mAccessibilityUserManager = accessibilityUserManager; - + mDisplayWindowsObserver = new DisplayWindowsObserver(Display.DEFAULT_DISPLAY); } /** - * 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(boolean forceSend, - @NonNull List windows) { - synchronized (mLock) { - if (DEBUG) { - Slog.i(LOG_TAG, "Windows changed: " + 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 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 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)); - } - } - - 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.regionInScreen.equals(newWindow.regionInScreen)) { - 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; - } - - /** - * Start tracking windows changes from window manager. + * Starts tracking windows changes from window manager. */ public void startTrackingWindows() { synchronized (mLock) { - if (!mTrackingWindows) { - // Turn on the flag before setup the callback. - // In some cases, onWindowsForAccessibilityChanged will be called immediately in - // setWindowsForAccessibilityCallback. We'll lost windows if flag is false. - mTrackingWindows = true; - // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY - mWindowManagerInternal.setWindowsForAccessibilityCallback(Display.DEFAULT_DISPLAY, - this); - } + mDisplayWindowsObserver.startTrackingWindowsLocked(); } } /** - * stop tracking windows changes from window manager, and clear all windows info. + * Stops tracking windows changes from window manager, and clear all windows info. */ public void stopTrackingWindows() { synchronized (mLock) { - if (mTrackingWindows) { - // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY - mWindowManagerInternal.setWindowsForAccessibilityCallback(Display.DEFAULT_DISPLAY, - null); - mTrackingWindows = false; - clearWindowsLocked(); - } + mDisplayWindowsObserver.stopTrackingWindowsLocked(); } } @@ -340,127 +815,7 @@ public class AccessibilityWindowManager * @return true if windows changes tracking */ public boolean isTrackingWindowsLocked() { - return mTrackingWindows; - } - - /** - * Clears all {@link AccessibilityWindowInfo}s and {@link WindowInfo}s. - */ - private void clearWindowsLocked() { - final List windows = Collections.emptyList(); - final int activeWindowId = mActiveWindowId; - // userId is useless in updateWindowsLocked, when we update a empty window list. Just pass - // current userId here. - updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows); - // Do not reset mActiveWindowId here. mActiveWindowId will be clear after accessibility - // interaction connection removed. - mActiveWindowId = activeWindowId; - mWindows = null; - } - - /** - * Update windows info according to specified userId and windows. - * - * @param userId The userId to update - * @param windows The windows to update - */ - private void updateWindowsLocked(int userId, @NonNull List windows) { - if (mWindows == null) { - mWindows = new ArrayList<>(); - } - - final List oldWindowList = new ArrayList<>(mWindows); - final SparseArray oldWindowsById = mA11yWindowInfoById.clone(); - - mWindows.clear(); - mA11yWindowInfoById.clear(); - - for (int i = 0; i < mWindowInfoById.size(); i++) { - mWindowInfoById.valueAt(i).recycle(); - } - mWindowInfoById.clear(); - mHasWatchOutsideTouchWindow = false; - - mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; - if (!mTouchInteractionInProgress) { - mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; - } - - // If the active window goes away while the user is touch exploring we - // reset the active window id and wait for the next hover event from - // under the user's finger to determine which one is the new one. It - // is possible that the finger is not moving and the input system - // filters out such events. - boolean activeWindowGone = true; - - final int windowCount = windows.size(); - - // We'll clear accessibility focus if the window with focus is no longer visible to - // accessibility services - boolean shouldClearAccessibilityFocus = - mAccessibilityFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; - if (windowCount > 0) { - for (int i = 0; i < windowCount; i++) { - final WindowInfo windowInfo = windows.get(i); - final AccessibilityWindowInfo window; - if (isTrackingWindowsLocked()) { - window = populateReportedWindowLocked(userId, windowInfo); - } else { - window = null; - } - if (window != null) { - - // Flip layers in list to be consistent with AccessibilityService#getWindows - window.setLayer(windowCount - 1 - window.getLayer()); - - final int windowId = window.getId(); - if (window.isFocused()) { - mFocusedWindowId = windowId; - if (!mTouchInteractionInProgress) { - mActiveWindowId = windowId; - window.setActive(true); - } else if (windowId == mActiveWindowId) { - activeWindowGone = false; - } - } - if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) { - mHasWatchOutsideTouchWindow = true; - } - mWindows.add(window); - mA11yWindowInfoById.put(windowId, window); - mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo)); - } - } - - if (mTouchInteractionInProgress && activeWindowGone) { - mActiveWindowId = mFocusedWindowId; - } - - // Focused window may change the active one, so set the - // active window once we decided which it is. - final int accessibilityWindowCount = mWindows.size(); - for (int i = 0; i < accessibilityWindowCount; i++) { - final AccessibilityWindowInfo window = mWindows.get(i); - if (window.getId() == mActiveWindowId) { - window.setActive(true); - } - if (window.getId() == mAccessibilityFocusedWindowId) { - window.setAccessibilityFocused(true); - shouldClearAccessibilityFocus = false; - } - } - } - - sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById); - - final int oldWindowCount = oldWindowList.size(); - for (int i = oldWindowCount - 1; i >= 0; i--) { - oldWindowList.remove(i).recycle(); - } - - if (shouldClearAccessibilityFocus) { - clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId); - } + return mDisplayWindowsObserver.isTrackingWindowsLocked(); } /** @@ -468,7 +823,7 @@ public class AccessibilityWindowManager */ @Nullable public List getWindowListLocked() { - return mWindows; + return mDisplayWindowsObserver.getWindowListLocked(); } /** @@ -494,7 +849,7 @@ public class AccessibilityWindowManager .resolveCallingUserIdEnforcingPermissionsLocked(userId); final int resolvedUid = UserHandle.getUid(resolvedUserId, UserHandle.getCallingAppId()); - // Make sure the reported package is one the caller has access to. + // Makes sure the reported package is one the caller has access to. packageName = mSecurityPolicy.resolveValidReportedPackageLocked( packageName, UserHandle.getCallingAppId(), resolvedUserId); @@ -576,7 +931,7 @@ public class AccessibilityWindowManager } /** - * Resolve a connection wrapper for a window id + * Resolves a connection wrapper for a window id. * * @param userId The user id for any user-specific windows * @param windowId The id of the window of interest @@ -672,7 +1027,7 @@ public class AccessibilityWindowManager } /** - * Return the userId that owns the given window token, {@link UserHandle#USER_NULL} + * Returns the userId that owns the given window token, {@link UserHandle#USER_NULL} * if not found. * * @param windowToken The winodw token @@ -703,42 +1058,6 @@ public class AccessibilityWindowManager return -1; } - private void sendEventsForChangedWindowsLocked(List oldWindows, - SparseArray oldWindowsById) { - List events = new ArrayList<>(); - // Send events for all removed windows - final int oldWindowsCount = oldWindows.size(); - for (int i = 0; i < oldWindowsCount; i++) { - final AccessibilityWindowInfo window = oldWindows.get(i); - if (mA11yWindowInfoById.get(window.getId()) == null) { - events.add(AccessibilityEvent.obtainWindowsChangedEvent( - window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED)); - } - } - - // Look for other changes - final int newWindowCount = mWindows.size(); - for (int i = 0; i < newWindowCount; i++) { - final AccessibilityWindowInfo newWindow = mWindows.get(i); - final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId()); - if (oldWindow == null) { - events.add(AccessibilityEvent.obtainWindowsChangedEvent( - newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED)); - } else { - int changes = newWindow.differenceFrom(oldWindow); - if (changes != 0) { - events.add(AccessibilityEvent.obtainWindowsChangedEvent( - newWindow.getId(), changes)); - } - } - } - - final int numEvents = events.size(); - for (int i = 0; i < numEvents; i++) { - mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(events.get(i)); - } - } - /** * Computes partial interactive region of given windowId. * @@ -748,38 +1067,8 @@ public class AccessibilityWindowManager */ public boolean computePartialInteractiveRegionForWindowLocked(int windowId, @NonNull Region outRegion) { - if (mWindows == null) { - return false; - } - - // Windows are ordered in z order so start from the bottom and find - // the window of interest. After that all windows that cover it should - // be subtracted from the resulting region. Note that for accessibility - // we are returning only interactive windows. - Region windowInteractiveRegion = null; - boolean windowInteractiveRegionChanged = false; - - final int windowCount = mWindows.size(); - final Region currentWindowRegions = new Region(); - for (int i = windowCount - 1; i >= 0; i--) { - AccessibilityWindowInfo currentWindow = mWindows.get(i); - if (windowInteractiveRegion == null) { - if (currentWindow.getId() == windowId) { - currentWindow.getRegionInScreen(currentWindowRegions); - outRegion.set(currentWindowRegions); - windowInteractiveRegion = outRegion; - continue; - } - } else if (currentWindow.getType() - != AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY) { - currentWindow.getRegionInScreen(currentWindowRegions); - if (windowInteractiveRegion.op(currentWindowRegions, Region.Op.DIFFERENCE)) { - windowInteractiveRegionChanged = true; - } - } - } - - return windowInteractiveRegionChanged; + return mDisplayWindowsObserver.computePartialInteractiveRegionForWindowLocked(windowId, + outRegion); } /** @@ -846,7 +1135,7 @@ public class AccessibilityWindowManager mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; } // Clear the window with focus if it no longer has focus and we aren't - // just moving focus from one view to the other in the same window + // just moving focus from one view to the other in the same window. if ((mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) && (mAccessibilityFocusedWindowId == windowId) && (eventAction != AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)) { @@ -904,7 +1193,7 @@ public class AccessibilityWindowManager } /** - * Get the id of the current active window. + * Gets the id of the current active window. * * @return The userId */ @@ -923,20 +1212,7 @@ public class AccessibilityWindowManager mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)); mActiveWindowId = windowId; - 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)); - } else { - window.setActive(false); - } - } - } + mDisplayWindowsObserver.setActiveWindowLocked(windowId); } } @@ -948,21 +1224,7 @@ public class AccessibilityWindowManager WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); mAccessibilityFocusedWindowId = windowId; - 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.setAccessibilityFocused(true); - mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( - AccessibilityEvent.obtainWindowsChangedEvent( - windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); - - } else { - window.setAccessibilityFocused(false); - } - } - } + mDisplayWindowsObserver.setAccessibilityFocusedWindowLocked(windowId); } } @@ -973,8 +1235,8 @@ public class AccessibilityWindowManager * @return The accessibility window info */ @Nullable - public AccessibilityWindowInfo findA11yWindowInfoById(int windowId) { - return mA11yWindowInfoById.get(windowId); + public AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) { + return mDisplayWindowsObserver.findA11yWindowInfoByIdLocked(windowId); } /** @@ -984,8 +1246,8 @@ public class AccessibilityWindowManager * @return The window info */ @Nullable - public WindowInfo findWindowInfoById(int windowId) { - return mWindowInfoById.get(windowId); + public WindowInfo findWindowInfoByIdLocked(int windowId) { + return mDisplayWindowsObserver.findWindowInfoByIdLocked(windowId); } /** @@ -1010,21 +1272,12 @@ public class AccessibilityWindowManager * @return PIP accessibility window info */ @Nullable - public AccessibilityWindowInfo getPictureInPictureWindow() { - if (mWindows != null) { - final int windowCount = mWindows.size(); - for (int i = 0; i < windowCount; i++) { - final AccessibilityWindowInfo window = mWindows.get(i); - if (window.isInPictureInPictureMode()) { - return window; - } - } - } - return null; + public AccessibilityWindowInfo getPictureInPictureWindowLocked() { + return mDisplayWindowsObserver.getPictureInPictureWindowLocked(); } /** - * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture + * Sets an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture * window. */ public void setPictureInPictureActionReplacingConnection( @@ -1060,7 +1313,8 @@ public class AccessibilityWindowManager final List outsideWindowsIds; final List connectionList = new ArrayList<>(); synchronized (mLock) { - outsideWindowsIds = getWatchOutsideTouchWindowIdLocked(targetWindowId); + outsideWindowsIds = + mDisplayWindowsObserver.getWatchOutsideTouchWindowIdLocked(targetWindowId); for (int i = 0; i < outsideWindowsIds.size(); i++) { connectionList.add(getConnectionLocked(userId, outsideWindowsIds.get(i))); } @@ -1079,22 +1333,6 @@ public class AccessibilityWindowManager } } - private List getWatchOutsideTouchWindowIdLocked(int targetWindowId) { - final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId); - if (targetWindow != null && mHasWatchOutsideTouchWindow) { - final List outsideWindowsId = new ArrayList<>(); - for (int i = 0; i < mWindowInfoById.size(); i++) { - final WindowInfo window = mWindowInfoById.valueAt(i); - if (window != null && window.layer < targetWindow.layer - && window.hasFlagWatchOutsideTouch) { - outsideWindowsId.add(mWindowInfoById.keyAt(i)); - } - } - return outsideWindowsId; - } - return Collections.emptyList(); - } - /** * Gets current input focused window token from window manager, and returns its windowId. * @@ -1108,96 +1346,6 @@ public class AccessibilityWindowManager } } - private AccessibilityWindowInfo populateReportedWindowLocked(int userId, WindowInfo window) { - final int windowId = findWindowIdLocked(userId, window.token); - if (windowId < 0) { - return null; - } - - final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); - - reportedWindow.setId(windowId); - reportedWindow.setType(getTypeForWindowManagerWindowType(window.type)); - reportedWindow.setLayer(window.layer); - reportedWindow.setFocused(window.focused); - reportedWindow.setRegionInScreen(window.regionInScreen); - reportedWindow.setTitle(window.title); - reportedWindow.setAnchorId(window.accessibilityIdOfAnchor); - reportedWindow.setPictureInPicture(window.inPictureInPicture); - reportedWindow.setDisplayId(window.displayId); - - final int parentId = findWindowIdLocked(userId, window.parentToken); - if (parentId >= 0) { - reportedWindow.setParentId(parentId); - } - - if (window.childTokens != null) { - final int childCount = window.childTokens.size(); - for (int i = 0; i < childCount; i++) { - final IBinder childToken = window.childTokens.get(i); - final int childId = findWindowIdLocked(userId, childToken); - if (childId >= 0) { - reportedWindow.addChild(childId); - } - } - } - - return reportedWindow; - } - - private int getTypeForWindowManagerWindowType(int windowType) { - switch (windowType) { - case WindowManager.LayoutParams.TYPE_APPLICATION: - case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: - case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: - case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING: - case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: - case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL: - case WindowManager.LayoutParams.TYPE_BASE_APPLICATION: - case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: - case WindowManager.LayoutParams.TYPE_PHONE: - case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: - case WindowManager.LayoutParams.TYPE_TOAST: - case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: { - return AccessibilityWindowInfo.TYPE_APPLICATION; - } - - case WindowManager.LayoutParams.TYPE_INPUT_METHOD: - case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: { - return AccessibilityWindowInfo.TYPE_INPUT_METHOD; - } - - case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: - case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR: - case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: - case WindowManager.LayoutParams.TYPE_SEARCH_BAR: - case WindowManager.LayoutParams.TYPE_STATUS_BAR: - case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL: - case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL: - case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: - case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: - case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: - case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: - case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: - case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: - case WindowManager.LayoutParams.TYPE_SCREENSHOT: { - return AccessibilityWindowInfo.TYPE_SYSTEM; - } - - case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: { - return AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER; - } - - case TYPE_ACCESSIBILITY_OVERLAY: { - return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY; - } - - default: { - return -1; - } - } - } - private boolean isValidUserForInteractionConnectionsLocked(int userId) { return mInteractionConnections.indexOfKey(userId) >= 0; } @@ -1251,22 +1399,9 @@ public class AccessibilityWindowManager } /** - * Dump all {@link AccessibilityWindowInfo}s here. + * Dumps all {@link AccessibilityWindowInfo}s here. */ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { - if (mWindows != null) { - final int windowCount = mWindows.size(); - for (int j = 0; j < windowCount; j++) { - if (j > 0) { - pw.append(','); - pw.println(); - } - pw.append("Window["); - AccessibilityWindowInfo window = mWindows.get(j); - pw.append(window.toString()); - pw.append(']'); - } - pw.println(); - } + mDisplayWindowsObserver.dumpLocked(fd, pw, args); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index a4267b86b3599..1084d625f8a3c 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -179,9 +179,9 @@ public class AbstractAccessibilityServiceConnectionTest { addA11yWindowInfo(mA11yWindowInfos, WINDOWID, false); addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true); when(mMockA11yWindowManager.getWindowListLocked()).thenReturn(mA11yWindowInfos); - when(mMockA11yWindowManager.findA11yWindowInfoById(WINDOWID)) + when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID)) .thenReturn(mA11yWindowInfos.get(0)); - when(mMockA11yWindowManager.findA11yWindowInfoById(PIP_WINDOWID)) + when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(PIP_WINDOWID)) .thenReturn(mA11yWindowInfos.get(1)); final RemoteAccessibilityConnection conn = getRemoteA11yConnection( WINDOWID, mMockIA11yInteractionConnection, PACKAGE_NAME1); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java index bb35776ffcd61..04ac7fe910085 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java @@ -166,7 +166,7 @@ public class AccessibilitySecurityPolicyTest { public void canDispatchAccessibilityEvent_otherEvents_windowIdExist_returnTrue() { when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM)) .thenReturn(WINDOWID2); - when(mMockA11yWindowManager.findA11yWindowInfoById(WINDOWID)) + when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID)) .thenReturn(AccessibilityWindowInfo.obtain()); for (int i = 0; i < OTHER_EVENTS.length; i++) { final AccessibilityEvent event = AccessibilityEvent.obtain(OTHER_EVENTS[i]); @@ -287,7 +287,7 @@ public class AccessibilitySecurityPolicyTest { .thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT); when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM)) .thenReturn(WINDOWID2); - when(mMockA11yWindowManager.findA11yWindowInfoById(WINDOWID)) + when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID)) .thenReturn(AccessibilityWindowInfo.obtain()); assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(UserHandle.USER_SYSTEM, 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 7e64cafaf7a2b..7887d5b76bcac 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java @@ -34,6 +34,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -53,6 +54,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; import com.android.server.wm.WindowManagerInternal; +import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; @@ -75,9 +77,12 @@ public class AccessibilityWindowManagerTest { 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; + // TO-DO [Multi-Display] : change the display count to 2 + private static final int DISPLAY_COUNT = 1; private static final int NUM_GLOBAL_WINDOWS = 4; private static final int NUM_APP_WINDOWS = 4; - private static final int NUM_OF_WINDOWS = NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS; + private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS) + * DISPLAY_COUNT; private static final int DEFAULT_FOCUSED_INDEX = 1; private static final int SCREEN_WIDTH = 1080; private static final int SCREEN_HEIGHT = 1920; @@ -86,7 +91,13 @@ public class AccessibilityWindowManagerTest { // List of window token, mapping from windowId -> window token. private final SparseArray mA11yWindowTokens = new SparseArray<>(); - private final ArrayList mWindowInfos = new ArrayList<>(); + // List of window info lists, mapping from displayId -> window info lists. + private final SparseArray> mWindowInfos = + new SparseArray<>(); + // List of callback, mapping from displayId -> callback. + private final SparseArray mCallbackOfWindows = + new SparseArray<>(); + private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null); @Mock private WindowManagerInternal mMockWindowManagerInternal; @@ -109,29 +120,14 @@ public class AccessibilityWindowManagerTest { mMockA11ySecurityPolicy, mMockA11yUserManager); - // Add RemoteAccessibilityConnection into AccessibilityWindowManager, and copy - // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos - // for the test. - int layer = 0; - for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) { - final IWindow token = addAccessibilityInteractionConnection(true); - addWindowInfo(token, layer++); - + for (int i = 0; i < DISPLAY_COUNT; i++) { + when(mMockWindowManagerInternal.setWindowsForAccessibilityCallback(eq(i), any())) + .thenReturn(true); + startTrackingPerDisplay(i); } - for (int i = 0; i < NUM_APP_WINDOWS; i++) { - final IWindow token = addAccessibilityInteractionConnection(false); - addWindowInfo(token, layer++); - } - // setup default focus - mWindowInfos.get(DEFAULT_FOCUSED_INDEX).focused = true; - // Turn on windows tracking, and update window info - mA11yWindowManager.startTrackingWindows(); - mA11yWindowManager.onWindowsForAccessibilityChanged(FORCE_SEND, mWindowInfos); - assertEquals(mA11yWindowManager.getWindowListLocked().size(), - mWindowInfos.size()); // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged. - // Reset it for mockito verify of further test case. + // Resets it for mockito verify of further test case. Mockito.reset(mMockA11yEventSender); } @@ -142,11 +138,12 @@ public class AccessibilityWindowManagerTest { @Test public void startTrackingWindows_shouldEnableWindowManagerCallback() { - // AccessibilityWindowManager#startTrackingWindows already invoked in setup + // AccessibilityWindowManager#startTrackingWindows already invoked in setup. assertTrue(mA11yWindowManager.isTrackingWindowsLocked()); - // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY + final WindowsForAccessibilityCallback callbacks = + mCallbackOfWindows.get(Display.DEFAULT_DISPLAY); verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( - eq(Display.DEFAULT_DISPLAY), any()); + eq(Display.DEFAULT_DISPLAY), eq(callbacks)); } @Test @@ -156,9 +153,9 @@ public class AccessibilityWindowManagerTest { mA11yWindowManager.stopTrackingWindows(); assertFalse(mA11yWindowManager.isTrackingWindowsLocked()); - // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( - eq(Display.DEFAULT_DISPLAY), any()); + eq(Display.DEFAULT_DISPLAY), isNull()); + } @Test @@ -177,87 +174,102 @@ public class AccessibilityWindowManagerTest { @Test public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() { final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - WindowInfo focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX); + WindowInfo focusedWindowInfo = + mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, focusedWindowInfo.token)); focusedWindowInfo.focused = false; - focusedWindowInfo = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1); + focusedWindowInfo = + mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1); focusedWindowInfo.focused = true; mA11yWindowManager.onTouchInteractionStart(); - mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1); + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); } @Test public void onWindowsChanged_shouldReportCorrectLayer() { - // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup - List a11yWindows = mA11yWindowManager.getWindowListLocked(); + // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. + List a11yWindows = + mA11yWindowManager.getWindowListLocked(); for (int i = 0; i < a11yWindows.size(); i++) { final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); - final WindowInfo windowInfo = mWindowInfos.get(i); - assertThat(mWindowInfos.size() - windowInfo.layer - 1, + final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); + assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1, is(a11yWindow.getLayer())); } } @Test public void onWindowsChanged_shouldReportCorrectOrder() { - // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup - List a11yWindows = mA11yWindowManager.getWindowListLocked(); + // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. + List a11yWindows = + mA11yWindowManager.getWindowListLocked(); for (int i = 0; i < a11yWindows.size(); i++) { final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); final IBinder windowToken = mA11yWindowManager .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId()); - final WindowInfo windowInfo = mWindowInfos.get(i); + final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); assertThat(windowToken, is(windowInfo.token)); } } @Test public void onWindowsChangedAndForceSend_shouldUpdateWindows() { - final WindowInfo windowInfo = mWindowInfos.get(0); - final int correctLayer = mA11yWindowManager.getWindowListLocked().get(0).getLayer(); + final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).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()); + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + 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(); + final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).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()); + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + 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 AccessibilityWindowInfo oldWindow = + mA11yWindowManager.getWindowListLocked().get(0); + final IWindow token = + addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, true); final WindowInfo windowInfo = WindowInfo.obtain(); windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; windowInfo.token = token.asBinder(); windowInfo.layer = 0; windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - mWindowInfos.set(0, windowInfo); + mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo); - mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); - assertNotEquals(oldWindow, mA11yWindowManager.getWindowListLocked().get(0)); + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + 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); + final WindowInfo focusedWindowInfo = + mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); + final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); focusedWindowInfo.focused = false; windowInfo.focused = true; - mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertTrue(mA11yWindowManager.getWindowListLocked().get(0).isFocused()); } @@ -288,7 +300,8 @@ public class AccessibilityWindowManagerTest { @Test public void getWindowTokenForUserAndWindowId_shouldNotNull() { - final List windows = mA11yWindowManager.getWindowListLocked(); + final List windows = + mA11yWindowManager.getWindowListLocked(); for (int i = 0; i < windows.size(); i++) { final int windowId = windows.get(i).getId(); @@ -299,7 +312,8 @@ public class AccessibilityWindowManagerTest { @Test public void findWindowId() { - final List windows = mA11yWindowManager.getWindowListLocked(); + final List windows = + mA11yWindowManager.getWindowListLocked(); for (int i = 0; i < windows.size(); i++) { final int windowId = windows.get(i).getId(); final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( @@ -313,13 +327,15 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() { // Updates top 2 z-order WindowInfo are whole visible. - WindowInfo windowInfo = mWindowInfos.get(0); + WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); - windowInfo = mWindowInfos.get(1); - windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT); - mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1); + windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2, + SCREEN_WIDTH, SCREEN_HEIGHT); + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - final List a11yWindows = mA11yWindowManager.getWindowListLocked(); + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); int windowId = a11yWindows.get(0).getId(); @@ -336,12 +352,13 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() { - // Updates z-order #1 WindowInfo is half visible - WindowInfo windowInfo = mWindowInfos.get(0); + // Updates z-order #1 WindowInfo is half visible. + WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); - mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); - final List a11yWindows = mA11yWindowManager.getWindowListLocked(); + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); @@ -353,7 +370,8 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() { // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. - final List a11yWindows = mA11yWindowManager.getWindowListLocked(); + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); @@ -366,11 +384,12 @@ public class AccessibilityWindowManagerTest { // Updates z-order #0 WindowInfo to have two interact-able areas. Region region = new Region(0, 0, SCREEN_WIDTH, 200); region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION); - WindowInfo windowInfo = mWindowInfos.get(0); + WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); windowInfo.regionInScreen.set(region); - mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - final List a11yWindows = mA11yWindowManager.getWindowListLocked(); + final List a11yWindows = + mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); @@ -382,7 +401,8 @@ public class AccessibilityWindowManagerTest { @Test public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() { - final IBinder eventWindowToken = mWindowInfos.get(DEFAULT_FOCUSED_INDEX + 1).token; + final IBinder eventWindowToken = + mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token; final int eventWindowId = mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, eventWindowToken); when(mMockWindowManagerInternal.getFocusedWindowToken()) @@ -402,7 +422,8 @@ public class AccessibilityWindowManagerTest { @Test public void updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow() { - final int eventWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX + 1); + final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, + DEFAULT_FOCUSED_INDEX + 1); final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); assertThat(currentActiveWindowId, is(not(eventWindowId))); @@ -428,7 +449,8 @@ public class AccessibilityWindowManagerTest { @Test public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus() { - final int eventWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX); + final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, + DEFAULT_FOCUSED_INDEX); final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); assertThat(currentA11yFocusedWindowId, is(not(eventWindowId))); @@ -457,7 +479,8 @@ public class AccessibilityWindowManagerTest { @Test public void updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus() { - final int eventWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX); + final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, + DEFAULT_FOCUSED_INDEX); final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); assertThat(currentA11yFocusedWindowId, is(not(eventWindowId))); @@ -482,7 +505,8 @@ public class AccessibilityWindowManagerTest { @Test public void onTouchInteractionEnd_shouldRollbackActiveWindow() { - final int eventWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX + 1); + final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, + DEFAULT_FOCUSED_INDEX + 1); final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); assertThat(currentActiveWindowId, is(not(eventWindowId))); @@ -513,12 +537,14 @@ public class AccessibilityWindowManagerTest { @Test public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus() throws RemoteException { - final IBinder defaultFocusWinToken = mWindowInfos.get(DEFAULT_FOCUSED_INDEX).token; + final IBinder defaultFocusWinToken = + mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token; final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, defaultFocusWinToken); when(mMockWindowManagerInternal.getFocusedWindowToken()) .thenReturn(defaultFocusWinToken); - final int newFocusWindowId = getWindowIdFromWindowInfos(DEFAULT_FOCUSED_INDEX + 1); + final int newFocusWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, + DEFAULT_FOCUSED_INDEX + 1); final IAccessibilityInteractionConnection mockNewFocusConnection = mA11yWindowManager.getConnectionLocked( USER_SYSTEM_ID, newFocusWindowId).getRemote(); @@ -556,28 +582,72 @@ public class AccessibilityWindowManagerTest { @Test public void getPictureInPictureWindow_shouldNotNull() { - assertNull(mA11yWindowManager.getPictureInPictureWindow()); - mWindowInfos.get(1).inPictureInPicture = true; - mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + assertNull(mA11yWindowManager.getPictureInPictureWindowLocked()); + mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true; + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - assertNotNull(mA11yWindowManager.getPictureInPictureWindow()); + assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked()); } @Test public void notifyOutsideTouch() throws RemoteException { - final int targetWindowId = getWindowIdFromWindowInfos(1); - final int outsideWindowId = getWindowIdFromWindowInfos(0); + final int targetWindowId = + getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 1); + final int outsideWindowId = + getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); final IAccessibilityInteractionConnection mockRemoteConnection = mA11yWindowManager.getConnectionLocked( USER_SYSTEM_ID, outsideWindowId).getRemote(); - mWindowInfos.get(0).hasFlagWatchOutsideTouch = true; - mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); + mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true; + onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId); verify(mockRemoteConnection).notifyOutsideTouch(); } - private IWindow addAccessibilityInteractionConnection(boolean bGlobal) + private void startTrackingPerDisplay(int displayId) throws RemoteException { + ArrayList windowInfosForDisplay = new ArrayList<>(); + // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy + // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos + // for the test. + int layer = 0; + for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) { + final IWindow token = addAccessibilityInteractionConnection(displayId, true); + addWindowInfo(windowInfosForDisplay, token, layer++); + + } + for (int i = 0; i < NUM_APP_WINDOWS; i++) { + final IWindow token = addAccessibilityInteractionConnection(displayId, false); + addWindowInfo(windowInfosForDisplay, token, layer++); + } + // Setups default focus. + windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true; + // Turns on windows tracking, and update window info. + mA11yWindowManager.startTrackingWindows(); + // Puts window lists into array. + mWindowInfos.put(displayId, windowInfosForDisplay); + // Sets the default display as the top focused display. + if (displayId == Display.DEFAULT_DISPLAY) { + setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX); + } + // Invokes callback for sending window lists to A11y framework. + onWindowsForAccessibilityChanged(displayId, FORCE_SEND); + + assertEquals(mA11yWindowManager.getWindowListLocked().size(), + windowInfosForDisplay.size()); + } + + private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) { + ArgumentCaptor windowsForAccessibilityCallbacksCaptor = + ArgumentCaptor.forClass( + WindowManagerInternal.WindowsForAccessibilityCallback.class); + verify(mMockWindowManagerInternal) + .setWindowsForAccessibilityCallback(eq(displayId), + windowsForAccessibilityCallbacksCaptor.capture()); + return windowsForAccessibilityCallbacksCaptor.getValue(); + } + + private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal) throws RemoteException { final IWindow mockWindowToken = Mockito.mock(IWindow.class); final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock( @@ -588,6 +658,8 @@ public class AccessibilityWindowManagerTest { when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder); when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(USER_SYSTEM_ID)) .thenReturn(bGlobal); + when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder())) + .thenReturn(displayId); int windowId = mA11yWindowManager.addAccessibilityInteractionConnection( mockWindowToken, mockA11yConnection, PACKAGE_NAME, USER_SYSTEM_ID); @@ -595,21 +667,40 @@ public class AccessibilityWindowManagerTest { return mockWindowToken; } - private void addWindowInfo(IWindow windowToken, int layer) { + private void addWindowInfo(ArrayList windowInfos, IWindow windowToken, int layer) { final WindowInfo windowInfo = WindowInfo.obtain(); windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; windowInfo.token = windowToken.asBinder(); windowInfo.layer = layer; windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - mWindowInfos.add(windowInfo); + windowInfos.add(windowInfo); } - private int getWindowIdFromWindowInfos(int index) { - final IBinder windowToken = mWindowInfos.get(index).token; + private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) { + final IBinder windowToken = mWindowInfos.get(displayId).get(index).token; return mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, windowToken); } + private void setTopFocusedWindowAndDisplay(int displayId, int index) { + // Sets the top focus window. + final IBinder eventWindowToken = mWindowInfos.get(displayId).get(index).token; + when(mMockWindowManagerInternal.getFocusedWindowToken()) + .thenReturn(eventWindowToken); + // Sets the top focused display. + when(mMockWindowManagerInternal.getDisplayIdForWindow(eventWindowToken)) + .thenReturn(displayId); + } + + private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) { + WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId); + if (callbacks == null) { + callbacks = getWindowsForAccessibilityCallbacks(displayId); + mCallbackOfWindows.put(displayId, callbacks); + } + callbacks.onWindowsForAccessibilityChanged(forceSend, mWindowInfos.get(displayId)); + } + static class WindowIdMatcher extends TypeSafeMatcher { private int mWindowId;