diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index d23200bd3c802..656fdbd7131a0 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -151,7 +151,8 @@ public final class AccessibilityInteractionController { if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId && mHandler.hasAccessibilityCallback(message)) { AccessibilityInteractionClient.getInstanceForThread( - interrogatingTid).setSameThreadMessage(message); + interrogatingTid, /* initializeCache= */true) + .setSameThreadMessage(message); } else { // For messages without callback of interrogating client, just handle the // message immediately if this is UI thread. diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java index d14dc6e71c005..dc019909129d6 100644 --- a/core/java/android/view/accessibility/AccessibilityCache.java +++ b/core/java/android/view/accessibility/AccessibilityCache.java @@ -16,6 +16,9 @@ package android.view.accessibility; + +import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY; + import android.os.Build; import android.util.ArraySet; import android.util.Log; @@ -70,6 +73,7 @@ public class AccessibilityCache { private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; private int mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; + private int mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; private boolean mIsAllWindowsCached; @@ -190,6 +194,7 @@ public class AccessibilityCache { removeCachedNodeLocked(event.getWindowId(), mInputFocus); } mInputFocus = event.getSourceNodeId(); + mInputFocusWindow = event.getWindowId(); nodeToRefresh = removeCachedNodeLocked(event.getWindowId(), mInputFocus); } break; @@ -439,6 +444,7 @@ public class AccessibilityCache { } if (clone.isFocused()) { mInputFocus = sourceId; + mInputFocusWindow = windowId; } } } @@ -462,6 +468,7 @@ public class AccessibilityCache { mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; + mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } } @@ -485,6 +492,87 @@ public class AccessibilityCache { mIsAllWindowsCached = false; } + /** + * Gets a cached {@link AccessibilityNodeInfo} with focus according to focus type. + * + * Note: {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} will return + * null. + * + * @param focusType The focus type. + * @param windowId A unique window id. + * @param initialNodeId A unique view id or virtual descendant id from where to start the + * search. + * @return The cached {@link AccessibilityNodeInfo} if it has a11y focus or null if such not + * found. + */ + public AccessibilityNodeInfo getFocus(int focusType, long initialNodeId, int windowId) { + synchronized (mLock) { + int currentFocusWindowId; + long currentFocusId; + if (focusType == FOCUS_ACCESSIBILITY) { + currentFocusWindowId = mAccessibilityFocusedWindow; + currentFocusId = mAccessibilityFocus; + } else { + currentFocusWindowId = mInputFocusWindow; + currentFocusId = mInputFocus; + } + + if (currentFocusWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { + return null; + } + + if (windowId != AccessibilityWindowInfo.ANY_WINDOW_ID + && windowId != currentFocusWindowId) { + return null; + } + + LongSparseArray nodes = + mNodeCache.get(currentFocusWindowId); + if (nodes == null) { + return null; + } + + final AccessibilityNodeInfo currentFocusedNode = nodes.get(currentFocusId); + if (currentFocusedNode == null) { + return null; + } + + if (initialNodeId == currentFocusId || (isCachedNodeOrDescendantLocked( + currentFocusedNode.getParentNodeId(), initialNodeId, nodes))) { + if (VERBOSE) { + Log.i(LOG_TAG, "getFocus(0x" + Long.toHexString(currentFocusId) + ") = " + + currentFocusedNode + " with type: " + + (focusType == FOCUS_ACCESSIBILITY + ? "FOCUS_ACCESSIBILITY" + : "FOCUS_INPUT")); + } + // Return a copy since the client calls to AccessibilityNodeInfo#recycle() + // will wipe the data of the cached info. + return new AccessibilityNodeInfo(currentFocusedNode); + } + + if (VERBOSE) { + Log.i(LOG_TAG, "getFocus is null with type: " + + (focusType == FOCUS_ACCESSIBILITY + ? "FOCUS_ACCESSIBILITY" + : "FOCUS_INPUT")); + } + return null; + } + } + + private boolean isCachedNodeOrDescendantLocked(long nodeId, long ancestorId, + LongSparseArray nodes) { + if (ancestorId == nodeId) { + return true; + } + AccessibilityNodeInfo node = nodes.get(nodeId); + if (node == null) { + return false; + } + return isCachedNodeOrDescendantLocked(node.getParentNodeId(), ancestorId, nodes); + } + /** * Clears nodes for the window with the given id */ diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 80ef6cff55700..6975bb2ed603f 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -22,6 +22,7 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILIT import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; @@ -114,8 +115,7 @@ public final class AccessibilityInteractionClient from a window, mapping from windowId -> timestamp. */ private static final SparseLongArray sScrollingWindows = new SparseLongArray(); - private static AccessibilityCache sAccessibilityCache = - new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher()); + private static AccessibilityCache sAccessibilityCache; private final AtomicInteger mInteractionIdCounter = new AtomicInteger(); @@ -150,7 +150,7 @@ public final class AccessibilityInteractionClient @UnsupportedAppUsage() public static AccessibilityInteractionClient getInstance() { final long threadId = Thread.currentThread().getId(); - return getInstanceForThread(threadId); + return getInstanceForThread(threadId, true); } /** @@ -161,11 +161,17 @@ public final class AccessibilityInteractionClient * * @return The client for a given threadId. */ - public static AccessibilityInteractionClient getInstanceForThread(long threadId) { + public static AccessibilityInteractionClient getInstanceForThread(long threadId, + boolean initializeCache) { synchronized (sStaticLock) { AccessibilityInteractionClient client = sClients.get(threadId); if (client == null) { - client = new AccessibilityInteractionClient(); + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + // Don't initialize a cache for the system process + client = new AccessibilityInteractionClient(false); + } else { + client = new AccessibilityInteractionClient(initializeCache); + } sClients.put(threadId, client); } return client; @@ -176,11 +182,20 @@ public final class AccessibilityInteractionClient * @return The client for the current thread. */ public static AccessibilityInteractionClient getInstance(Context context) { + return getInstance(/* initializeCache= */true, context); + } + + /** + * @param initializeCache whether to initialize the cache in a new client instance + * @return The client for the current thread. + */ + public static AccessibilityInteractionClient getInstance(boolean initializeCache, + Context context) { final long threadId = Thread.currentThread().getId(); if (context != null) { - return getInstanceForThread(threadId, context); + return getInstanceForThread(threadId, initializeCache, context); } - return getInstanceForThread(threadId); + return getInstanceForThread(threadId, initializeCache); } /** @@ -189,14 +204,19 @@ public final class AccessibilityInteractionClient * We do not have a thread local variable since other threads should be able to * look up the correct client knowing a thread id. See ViewRootImpl for details. * + * @param initializeCache whether to initialize the cache in a new client instance * @return The client for a given threadId. */ public static AccessibilityInteractionClient getInstanceForThread( - long threadId, Context context) { + long threadId, boolean initializeCache, Context context) { synchronized (sStaticLock) { AccessibilityInteractionClient client = sClients.get(threadId); if (client == null) { - client = new AccessibilityInteractionClient(context); + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + client = new AccessibilityInteractionClient(false, context); + } else { + client = new AccessibilityInteractionClient(initializeCache, context); + } sClients.put(threadId, client); } return client; @@ -249,13 +269,26 @@ public final class AccessibilityInteractionClient private AccessibilityInteractionClient() { /* reducing constructor visibility */ + this(true); + } + + private AccessibilityInteractionClient(boolean initializeCache) { + initializeCache(initializeCache); mAccessibilityManager = null; } - private AccessibilityInteractionClient(Context context) { + private AccessibilityInteractionClient(boolean initializeCache, Context context) { + initializeCache(initializeCache); mAccessibilityManager = context.getSystemService(AccessibilityManager.class); } + private static void initializeCache(boolean initialize) { + if (initialize && sAccessibilityCache == null) { + sAccessibilityCache = new AccessibilityCache( + new AccessibilityCache.AccessibilityNodeRefresher()); + } + } + /** * Sets the message to be processed if the interacted view hierarchy * and the interacting client are running in the same thread. @@ -311,7 +344,7 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { AccessibilityWindowInfo window; - if (!bypassCache) { + if (!bypassCache && sAccessibilityCache != null) { window = sAccessibilityCache.getWindow(accessibilityWindowId); if (window != null) { if (DEBUG) { @@ -341,7 +374,7 @@ public final class AccessibilityInteractionClient + bypassCache); } - if (window != null) { + if (window != null && sAccessibilityCache != null) { if (!bypassCache) { sAccessibilityCache.addWindow(window); } @@ -384,21 +417,24 @@ public final class AccessibilityInteractionClient try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { - SparseArray> windows = - sAccessibilityCache.getWindowsOnAllDisplays(); - if (windows != null) { + SparseArray> windows; + if (sAccessibilityCache != null) { + windows = sAccessibilityCache.getWindowsOnAllDisplays(); + if (windows != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Windows cache hit"); + } + if (shouldTraceClient()) { + logTraceClient( + connection, "getWindows cache", "connectionId=" + connectionId); + } + return windows; + } if (DEBUG) { - Log.i(LOG_TAG, "Windows cache hit"); + Log.i(LOG_TAG, "Windows cache miss"); } - if (shouldTraceClient()) { - logTraceClient( - connection, "getWindows cache", "connectionId=" + connectionId); - } - return windows; - } - if (DEBUG) { - Log.i(LOG_TAG, "Windows cache miss"); } + final long identityToken = Binder.clearCallingIdentity(); try { windows = connection.getWindows(); @@ -409,7 +445,9 @@ public final class AccessibilityInteractionClient logTraceClient(connection, "getWindows", "connectionId=" + connectionId); } if (windows != null) { - sAccessibilityCache.setWindowsOnAllDisplays(windows); + if (sAccessibilityCache != null) { + sAccessibilityCache.setWindowsOnAllDisplays(windows); + } return windows; } } else { @@ -493,7 +531,7 @@ public final class AccessibilityInteractionClient try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { - if (!bypassCache) { + if (!bypassCache && sAccessibilityCache != null) { AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode( accessibilityWindowId, accessibilityNodeId); if (cachedInfo != null) { @@ -725,7 +763,9 @@ public final class AccessibilityInteractionClient * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} - * to query the currently active window. + * to query the currently active window. Use + * {@link android.view.accessibility.AccessibilityWindowInfo#ANY_WINDOW_ID} to query all + * windows * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} @@ -733,11 +773,28 @@ public final class AccessibilityInteractionClient * @param focusType The focus type. * @return The accessibility focused {@link AccessibilityNodeInfo}. */ + @SuppressLint("LongLogTag") public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId, long accessibilityNodeId, int focusType) { try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { + if (sAccessibilityCache != null) { + AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getFocus(focusType, + accessibilityNodeId, accessibilityWindowId); + if (cachedInfo != null) { + if (DEBUG) { + Log.i(LOG_TAG, "Focused node cache hit retrieved" + + idToString(cachedInfo.getWindowId(), + cachedInfo.getSourceNodeId())); + } + return cachedInfo; + } + if (DEBUG) { + Log.i(LOG_TAG, "Focused node cache miss with " + + idToString(accessibilityWindowId, accessibilityNodeId)); + } + } final int interactionId = mInteractionIdCounter.getAndIncrement(); if (shouldTraceClient()) { logTraceClient(connection, "findFocus", @@ -901,7 +958,9 @@ public final class AccessibilityInteractionClient */ @UnsupportedAppUsage() public void clearCache() { - sAccessibilityCache.clear(); + if (sAccessibilityCache != null) { + sAccessibilityCache.clear(); + } } public void onAccessibilityEvent(AccessibilityEvent event) { @@ -917,7 +976,9 @@ public final class AccessibilityInteractionClient default: break; } - sAccessibilityCache.onAccessibilityEvent(event); + if (sAccessibilityCache != null) { + sAccessibilityCache.onAccessibilityEvent(event); + } } /** @@ -1153,7 +1214,7 @@ public final class AccessibilityInteractionClient } } info.setSealed(true); - if (!bypassCache) { + if (!bypassCache && sAccessibilityCache != null) { sAccessibilityCache.add(info); } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java index d2b52ba350736..0e78f8789eee5 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java @@ -16,6 +16,12 @@ package android.view.accessibility; +import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY; +import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_INPUT; +import static android.view.accessibility.AccessibilityNodeInfo.ROOT_ITEM_ID; +import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID; +import static android.view.accessibility.AccessibilityWindowInfo.ANY_WINDOW_ID; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -596,6 +602,97 @@ public class AccessibilityCacheTest { } } + @Test + public void getFocus_focusedNodeAsInitialNode_returnsNodeWithA11yFocus() { + AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_ACCESSIBILITY); + assertFocus(nodeInfo, FOCUS_ACCESSIBILITY, nodeInfo.getSourceNodeId(), + nodeInfo.getWindowId()); + } + + @Test + public void getFocus_focusedNodeAsInitialNode_returnsNodeWithInputFocus() { + AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_INPUT); + assertFocus(nodeInfo, FOCUS_INPUT, nodeInfo.getSourceNodeId(), nodeInfo.getWindowId()); + } + + + @Test + public void getFocus_fromAnyWindow_returnsNodeWithA11yFocus() { + AccessibilityNodeInfo parentNodeInfo = + getNodeWithA11yAndWindowId(ROOT_ITEM_ID, WINDOW_ID_1); + AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); + nodeInfo.setParent(getMockViewWithA11yAndWindowIds(ROOT_ITEM_ID, WINDOW_ID_1)); + setFocus(nodeInfo, FOCUS_ACCESSIBILITY); + mAccessibilityCache.add(parentNodeInfo); + mAccessibilityCache.add(nodeInfo); + + AccessibilityNodeInfo focusedNodeInfo = + mAccessibilityCache.getFocus(FOCUS_ACCESSIBILITY, ROOT_NODE_ID, ANY_WINDOW_ID); + try { + assertEquals(focusedNodeInfo, nodeInfo); + } finally { + nodeInfo.recycle(); + parentNodeInfo.recycle(); + } + } + + @Test + public void getFocus_fromAnyWindow_returnsNodeWithInputFocus() { + AccessibilityNodeInfo parentNodeInfo = + getNodeWithA11yAndWindowId(ROOT_ITEM_ID, WINDOW_ID_1); + AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); + nodeInfo.setParent(getMockViewWithA11yAndWindowIds(ROOT_ITEM_ID, WINDOW_ID_1)); + setFocus(nodeInfo, FOCUS_INPUT); + mAccessibilityCache.add(parentNodeInfo); + mAccessibilityCache.add(nodeInfo); + + AccessibilityNodeInfo focusedNodeInfo = + mAccessibilityCache.getFocus(FOCUS_INPUT, ROOT_NODE_ID, ANY_WINDOW_ID); + try { + assertEquals(focusedNodeInfo, nodeInfo); + } finally { + nodeInfo.recycle(); + parentNodeInfo.recycle(); + } + } + + + @Test + public void getFocus_parentNodeAsInitialNode_returnsNodeWithA11yFocus() { + findFocusReturnsFocus_initialNodeIsParent(FOCUS_ACCESSIBILITY); + } + + @Test + public void getFocus_parentNodeAsInitialNode_returnsNodeWithInputFocus() { + findFocusReturnsFocus_initialNodeIsParent(FOCUS_INPUT); + + } + + @Test + public void getFocus_nonFocusedChildNodeAsInitialNode_returnsNullA11yFocus() { + findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(FOCUS_ACCESSIBILITY); + } + + @Test + public void getFocus_nonFocusedChildNodeAsInitialNode_returnsNullInputFocus() { + findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(FOCUS_INPUT); + } + + @Test + public void getFocus_cacheCleared_returnsNullA11yFocus() { + AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_ACCESSIBILITY); + mAccessibilityCache.clear(); + assertFocus(null, FOCUS_ACCESSIBILITY, nodeInfo.getSourceNodeId(), + nodeInfo.getWindowId()); + } + + @Test + public void getFocus_cacheCleared_returnsNullInputFocus() { + AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_INPUT); + mAccessibilityCache.clear(); + assertFocus(null, FOCUS_INPUT, nodeInfo.getSourceNodeId(), nodeInfo.getWindowId()); + } + @Test public void nodeSourceOfInputFocusEvent_getsRefreshed() { AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); @@ -786,6 +883,86 @@ public class AccessibilityCacheTest { } } + private AccessibilityNodeInfo addFocusedNode(int focusType) { + AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); + setFocus(nodeInfo, focusType); + mAccessibilityCache.add(nodeInfo); + return nodeInfo; + } + + private void assertFocus(AccessibilityNodeInfo focus, int focusType, long nodeId, + int windowId) { + AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus( + focusType, nodeId, windowId); + try { + assertEquals(focusedNodeInfo, focus); + } finally { + if (focus != null) { + focus.recycle(); + } + if (focusedNodeInfo != null) { + focusedNodeInfo.recycle(); + } + } + } + + + private void findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(int focusType) { + AccessibilityNodeInfo parentNodeInfo = + getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); + AccessibilityNodeInfo childNodeInfo = + getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); + AccessibilityNodeInfo otherChildNodeInfo = + getNodeWithA11yAndWindowId(OTHER_CHILD_VIEW_ID, WINDOW_ID_1); + childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); + otherChildNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); + setFocus(childNodeInfo, focusType); + mAccessibilityCache.add(parentNodeInfo); + mAccessibilityCache.add(childNodeInfo); + mAccessibilityCache.add(otherChildNodeInfo); + + AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus( + focusType, otherChildNodeInfo.getSourceNodeId(), WINDOW_ID_1); + + try { + assertNull(focusedNodeInfo); + + } finally { + parentNodeInfo.recycle(); + childNodeInfo.recycle(); + } + } + + private void findFocusReturnsFocus_initialNodeIsParent(int focusType) { + AccessibilityNodeInfo parentNodeInfo = + getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); + AccessibilityNodeInfo childNodeInfo = + getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); + childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); + setFocus(childNodeInfo, focusType); + mAccessibilityCache.add(parentNodeInfo); + mAccessibilityCache.add(childNodeInfo); + + AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus( + focusType, parentNodeInfo.getSourceNodeId(), WINDOW_ID_1); + + try { + assertEquals(focusedNodeInfo, childNodeInfo); + + } finally { + parentNodeInfo.recycle(); + childNodeInfo.recycle(); + } + } + + private void setFocus(AccessibilityNodeInfo info, int focusType) { + if (focusType == FOCUS_ACCESSIBILITY) { + info.setAccessibilityFocused(true); + } else { + info.setFocused(true); + } + } + private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) { AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain(); windowInfo.setId(windowId); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index fd05c231fa49b..a9cd0ed2d5292 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3432,7 +3432,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mConnectionId = service.mId; - mClient = AccessibilityInteractionClient.getInstance(mContext); + mClient = AccessibilityInteractionClient.getInstance(/* initializeCache= */false, + mContext); mClient.addConnection(mConnectionId, service); //TODO: (multi-display) We need to support multiple displays.