From db2997c756abca8ec1f400e449b2b4a4a16bdf4d Mon Sep 17 00:00:00 2001 From: sallyyuen Date: Thu, 5 Jan 2023 11:40:57 -0800 Subject: [PATCH] Expose A11yDisplayProxy#findFocus A proxy can retrieve input or accessibility focus. - Make sure a proxy and non-proxy user cannot obtain each other's nodes. - Remove ACTIVE_WINDOW code when finding focus, since ACTIVE_WINDOW is not used. What this CL does not do: - separate input focus between proxy and non-proxy users. This can't be done properly until V - separate a11y focus between proxy and non-proxy users. This will be in a future CL. Bug: 264594384 Test: atest AccessibiltiyDisplayProxyTest Change-Id: Iefd88806a5a33deb7ef2ec2460ee79167c82519d --- core/api/system-current.txt | 1 + .../AccessibilityDisplayProxy.java | 32 ++++++++++++------- .../AccessibilityInteractionClient.java | 2 -- ...bstractAccessibilityServiceConnection.java | 24 ++++++++++---- .../AccessibilityWindowManager.java | 24 ++++++++++++++ 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index b4594e00e5c63..9c808bc1573da 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -16499,6 +16499,7 @@ package android.view.accessibility { public abstract class AccessibilityDisplayProxy { ctor public AccessibilityDisplayProxy(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.List); + method @Nullable public android.view.accessibility.AccessibilityNodeInfo findFocus(int); method public int getDisplayId(); method @NonNull public final java.util.List getInstalledAndEnabledServices(); method @NonNull public java.util.List getWindows(); diff --git a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java index a757236f92fda..dd320e196e8b6 100644 --- a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java +++ b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java @@ -83,7 +83,7 @@ public abstract class AccessibilityDisplayProxy { * @param displayId the id of the display to proxy. * @param executor the executor used to execute proxy callbacks. * @param installedAndEnabledServices the list of infos representing the installed and - * enabled a11y services. + * enabled accessibility services. */ public AccessibilityDisplayProxy(int displayId, @NonNull Executor executor, @NonNull List installedAndEnabledServices) { @@ -147,19 +147,27 @@ public abstract class AccessibilityDisplayProxy { } /** - * Gets the focus of the window specified by {@code windowInfo}. + * Gets the node with focus, in this display. * - * @param windowInfo the window to search - * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or + *

For {@link AccessibilityNodeInfo#FOCUS_INPUT}, this returns the input-focused node in the + * proxy display if this display can receive unspecified input events (input that does not + * specify a target display.) + * + *

For {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}, this returns the + * accessibility-focused node in the proxy display if the display has accessibility focus. + * @param focusType The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. * @return The node info of the focused view or null. - * @hide - * TODO(254545943): Do not expose until support for accessibility focus and/or input is in place + */ @Nullable - public AccessibilityNodeInfo findFocus(@NonNull AccessibilityWindowInfo windowInfo, int focus) { - AccessibilityNodeInfo windowRoot = windowInfo.getRoot(); - return windowRoot != null ? windowRoot.findFocus(focus) : null; + public AccessibilityNodeInfo findFocus(int focusType) { + // TODO(264423198): Support querying the focused node of the proxy's display even if it is + // not the top-focused display and can't receive untargeted input events. + // TODO(254545943): Separate accessibility focus between proxy and phone state. + return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, + AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, + focusType); } /** @@ -177,10 +185,10 @@ public abstract class AccessibilityDisplayProxy { * Sets the list of {@link AccessibilityServiceInfo}s describing the services interested in the * {@link AccessibilityDisplayProxy}'s display. * - *

These represent a11y features and services that are installed and running. These should - * not include {@link AccessibilityService}s installed on the phone. + *

These represent accessibility features and services that are installed and running. These + * should not include {@link AccessibilityService}s installed on the phone. * - * @param installedAndEnabledServices the list of installed and running a11y services. + * @param installedAndEnabledServices the list of installed and running accessibility services. */ public void setInstalledAndEnabledServices( @NonNull List installedAndEnabledServices) { diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 52eda0a19c55c..83a6c5a2e8a67 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -921,8 +921,6 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link AccessibilityWindowInfo#ACTIVE_WINDOW_ID} - * to query the currently active window. Use * {@link AccessibilityWindowInfo#ANY_WINDOW_ID} to query all * windows * @param accessibilityNodeId A unique view id or virtual descendant id from diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 05e305c437ec4..f4c6cc3de0b1e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -132,7 +132,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private static final String TRACE_WM = "WindowManagerInternal"; private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000; - /** Display type for displays associated with the default user of th device. */ + /** Display type for displays associated with the default user of the device. */ public static final int DISPLAY_TYPE_DEFAULT = 1 << 0; /** Display type for displays associated with an AccessibilityDisplayProxy user. */ public static final int DISPLAY_TYPE_PROXY = 1 << 1; @@ -1993,17 +1993,29 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private int resolveAccessibilityWindowIdLocked(int accessibilityWindowId) { if (accessibilityWindowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) { - return mA11yWindowManager.getActiveWindowId(mSystemSupport.getCurrentUserIdLocked()); + final int focusedWindowId = + mA11yWindowManager.getActiveWindowId(mSystemSupport.getCurrentUserIdLocked()); + if (!mA11yWindowManager.windowIdBelongsToDisplayType(focusedWindowId, mDisplayTypes)) { + return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; + } + return focusedWindowId; } return accessibilityWindowId; } private int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) { - if (windowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) { - return mA11yWindowManager.getActiveWindowId(mSystemSupport.getCurrentUserIdLocked()); - } if (windowId == AccessibilityWindowInfo.ANY_WINDOW_ID) { - return mA11yWindowManager.getFocusedWindowId(focusType); + final int focusedWindowId = mA11yWindowManager.getFocusedWindowId(focusType); + // If the caller is a proxy and the found window doesn't belong to a proxy display + // (or vice versa), then return null. This doesn't work if there are multiple active + // proxys, but in the future this code shouldn't be needed if input and a11y focus are + // properly split. (so we will deal with the issues if we see them). + //TODO(254545943): Remove this when there is user and proxy separation of input and a11y + // focus + if (!mA11yWindowManager.windowIdBelongsToDisplayType(focusedWindowId, mDisplayTypes)) { + return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; + } + return focusedWindowId; } return windowId; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index c050449e01d97..c61e86489f04a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -155,6 +155,30 @@ public class AccessibilityWindowManager { } } + /** + * Returns {@code true} if the window belongs to a display of {@code displayTypes}. + */ + public boolean windowIdBelongsToDisplayType(int focusedWindowId, int displayTypes) { + // UIAutomation wants focus from any display type. + final int displayTypeMask = DISPLAY_TYPE_PROXY | DISPLAY_TYPE_DEFAULT; + if ((displayTypes & displayTypeMask) == displayTypeMask) { + return true; + } + synchronized (mLock) { + final int count = mDisplayWindowsObservers.size(); + for (int i = 0; i < count; i++) { + final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); + if (observer != null + && observer.findA11yWindowInfoByIdLocked(focusedWindowId) != null) { + return observer.mIsProxy + ? ((displayTypes & DISPLAY_TYPE_PROXY) != 0) + : (displayTypes & DISPLAY_TYPE_DEFAULT) != 0; + } + } + } + return false; + } + /** * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to * receive {@link WindowInfo}s from window manager when there's an accessibility change in