Loading core/java/android/view/AccessibilityInteractionController.java +221 −130 Original line number Diff line number Diff line Loading @@ -86,12 +86,19 @@ public final class AccessibilityInteractionController { // accessibility from hanging private static final long REQUEST_PREPARER_TIMEOUT_MS = 500; // Callbacks should have the same configuration of the flags below to allow satisfying a pending // node request on prefetch private static final int FLAGS_AFFECTING_REPORTED_DATA = AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS | AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS; private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = new ArrayList<AccessibilityNodeInfo>(); private final Object mLock = new Object(); private final PrivateHandler mHandler; @VisibleForTesting public final PrivateHandler mHandler; private final ViewRootImpl mViewRootImpl; Loading @@ -113,6 +120,9 @@ public final class AccessibilityInteractionController { private AddNodeInfosForViewId mAddNodeInfosForViewId; @GuardedBy("mLock") private ArrayList<Message> mPendingFindNodeByIdMessages; @GuardedBy("mLock") private int mNumActiveRequestPreparers; @GuardedBy("mLock") Loading @@ -128,6 +138,7 @@ public final class AccessibilityInteractionController { mViewRootImpl = viewRootImpl; mPrefetcher = new AccessibilityNodePrefetcher(); mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class); mPendingFindNodeByIdMessages = new ArrayList<>(); } private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid, Loading Loading @@ -177,6 +188,9 @@ public final class AccessibilityInteractionController { args.arg4 = arguments; message.obj = args; synchronized (mLock) { mPendingFindNodeByIdMessages.add(message); } scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS); } Loading Loading @@ -315,6 +329,9 @@ public final class AccessibilityInteractionController { } private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { synchronized (mLock) { mPendingFindNodeByIdMessages.remove(message); } final int flags = message.arg1; SomeArgs args = (SomeArgs) message.obj; Loading @@ -329,22 +346,58 @@ public final class AccessibilityInteractionController { args.recycle(); List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; infos.clear(); View rootView = null; AccessibilityNodeInfo rootNode = null; try { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); if (root != null && isShown(root)) { mPrefetcher.prefetchAccessibilityNodeInfos( root, virtualDescendantId, flags, infos, arguments); rootView = findViewByAccessibilityId(accessibilityViewId); if (rootView != null && isShown(rootView)) { rootNode = populateAccessibilityNodeInfoForView( rootView, arguments, virtualDescendantId); } } finally { updateInfosForViewportAndReturnFindNodeResult( infos, callback, interactionId, spec, interactiveRegion); updateInfoForViewportAndReturnFindNodeResult( rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode), callback, interactionId, spec, interactiveRegion); } ArrayList<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; infos.clear(); mPrefetcher.prefetchAccessibilityNodeInfos( rootView, rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode), virtualDescendantId, flags, infos); mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewPort(infos, spec, interactiveRegion); returnPrefetchResult(interactionId, infos, callback); returnPendingFindAccessibilityNodeInfosInPrefetch(rootNode, infos, flags); } private AccessibilityNodeInfo populateAccessibilityNodeInfoForView( View view, Bundle arguments, int virtualViewId) { AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); // Determine if we'll be populating extra data final String extraDataRequested = (arguments == null) ? null : arguments.getString(EXTRA_DATA_REQUESTED_KEY); AccessibilityNodeInfo root = null; if (provider == null) { root = view.createAccessibilityNodeInfo(); if (root != null) { if (extraDataRequested != null) { view.addExtraDataToAccessibilityNodeInfo(root, extraDataRequested, arguments); } } } else { root = provider.createAccessibilityNodeInfo(virtualViewId); if (root != null) { if (extraDataRequested != null) { provider.addExtraDataToAccessibilityNodeInfo( virtualViewId, root, extraDataRequested, arguments); } } } return root; } public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId, Loading Loading @@ -402,6 +455,7 @@ public final class AccessibilityInteractionController { mAddNodeInfosForViewId.reset(); } } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewportAndReturnFindNodeResult( infos, callback, interactionId, spec, interactiveRegion); } Loading Loading @@ -484,6 +538,7 @@ public final class AccessibilityInteractionController { } } } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewportAndReturnFindNodeResult( infos, callback, interactionId, spec, interactiveRegion); } Loading Loading @@ -575,6 +630,7 @@ public final class AccessibilityInteractionController { } } } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfoForViewportAndReturnFindNodeResult( focused, callback, interactionId, spec, interactiveRegion); } Loading Loading @@ -629,6 +685,7 @@ public final class AccessibilityInteractionController { } } } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfoForViewportAndReturnFindNodeResult( next, callback, interactionId, spec, interactiveRegion); } Loading Loading @@ -785,33 +842,6 @@ public final class AccessibilityInteractionController { } } private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos, MagnificationSpec spec) { if (infos == null) { return; } final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { AccessibilityNodeInfo info = infos.get(i); applyAppScaleAndMagnificationSpecIfNeeded(info, spec); } } } private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos, Region interactiveRegion) { if (interactiveRegion == null || infos == null) { return; } final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { AccessibilityNodeInfo info = infos.get(i); adjustIsVisibleToUserIfNeeded(info, interactiveRegion); } } private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info, Region interactiveRegion) { if (interactiveRegion == null || info == null) { Loading @@ -832,17 +862,6 @@ public final class AccessibilityInteractionController { return false; } private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) { if (infos == null || shouldBypassAdjustBoundsInScreen()) { return; } final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { final AccessibilityNodeInfo info = infos.get(i); adjustBoundsInScreenIfNeeded(info); } } private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) { if (info == null || shouldBypassAdjustBoundsInScreen()) { return; Loading Loading @@ -890,17 +909,6 @@ public final class AccessibilityInteractionController { return screenMatrix == null || screenMatrix.isIdentity(); } private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) { if (infos == null || shouldBypassAssociateLeashedParent()) { return; } final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { final AccessibilityNodeInfo info = infos.get(i); associateLeashedParentIfNeeded(info); } } private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) { if (info == null || shouldBypassAssociateLeashedParent()) { return; Loading Loading @@ -974,18 +982,46 @@ public final class AccessibilityInteractionController { return (appScale != 1.0f || (spec != null && !spec.isNop())); } private void updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec, Region interactiveRegion) { for (int i = 0; i < infos.size(); i++) { updateInfoForViewPort(infos.get(i), spec, interactiveRegion); } } private void updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec, Region interactiveRegion) { associateLeashedParentIfNeeded(info); applyScreenMatrixIfNeeded(info); adjustBoundsInScreenIfNeeded(info); // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, // then impact the visibility result, we need to adjust visibility before apply scale. adjustIsVisibleToUserIfNeeded(info, interactiveRegion); applyAppScaleAndMagnificationSpecIfNeeded(info, spec); } private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion) { if (infos != null) { updateInfosForViewPort(infos, spec, interactiveRegion); } returnFindNodesResult(infos, callback, interactionId); } private void returnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId) { try { callback.setFindAccessibilityNodeInfoResult(info, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ } } private void returnFindNodesResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId) { try { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; associateLeashedParentIfNeeded(infos); applyScreenMatrixIfNeeded(infos); adjustBoundsInScreenIfNeeded(infos); // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, // then impact the visibility result, we need to adjust visibility before apply scale. adjustIsVisibleToUserIfNeeded(infos, interactiveRegion); applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); callback.setFindAccessibilityNodeInfosResult(infos, interactionId); if (infos != null) { infos.clear(); Loading @@ -995,24 +1031,82 @@ public final class AccessibilityInteractionController { } } private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion) { private void returnPendingFindAccessibilityNodeInfosInPrefetch(AccessibilityNodeInfo rootNode, List<AccessibilityNodeInfo> infos, int flags) { AccessibilityNodeInfo satisfiedPendingRequestPrefetchedNode = null; IAccessibilityInteractionConnectionCallback satisfiedPendingRequestCallback = null; int satisfiedPendingRequestInteractionId = AccessibilityInteractionClient.NO_ID; synchronized (mLock) { for (int i = 0; i < mPendingFindNodeByIdMessages.size(); i++) { final Message pendingMessage = mPendingFindNodeByIdMessages.get(i); final int pendingFlags = pendingMessage.arg1; if ((pendingFlags & FLAGS_AFFECTING_REPORTED_DATA) != (flags & FLAGS_AFFECTING_REPORTED_DATA)) { continue; } SomeArgs args = (SomeArgs) pendingMessage.obj; final int accessibilityViewId = args.argi1; final int virtualDescendantId = args.argi2; satisfiedPendingRequestPrefetchedNode = nodeWithIdFromList(rootNode, infos, AccessibilityNodeInfo.makeNodeId( accessibilityViewId, virtualDescendantId)); if (satisfiedPendingRequestPrefetchedNode != null) { satisfiedPendingRequestCallback = (IAccessibilityInteractionConnectionCallback) args.arg1; satisfiedPendingRequestInteractionId = args.argi3; mHandler.removeMessages( PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID, pendingMessage.obj); args.recycle(); break; } } mPendingFindNodeByIdMessages.clear(); } if (satisfiedPendingRequestPrefetchedNode != null) { returnFindNodeResult( AccessibilityNodeInfo.obtain(satisfiedPendingRequestPrefetchedNode), satisfiedPendingRequestCallback, satisfiedPendingRequestInteractionId); } } private AccessibilityNodeInfo nodeWithIdFromList(AccessibilityNodeInfo rootNode, List<AccessibilityNodeInfo> infos, long nodeId) { if (rootNode != null && rootNode.getSourceNodeId() == nodeId) { return rootNode; } for (int j = 0; j < infos.size(); j++) { AccessibilityNodeInfo info = infos.get(j); if (info.getSourceNodeId() == nodeId) { return info; } } return null; } private void returnPrefetchResult(int interactionId, List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback) { if (infos.size() > 0) { try { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; associateLeashedParentIfNeeded(info); applyScreenMatrixIfNeeded(info); adjustBoundsInScreenIfNeeded(info); // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, // then impact the visibility result, we need to adjust visibility before apply scale. adjustIsVisibleToUserIfNeeded(info, interactiveRegion); applyAppScaleAndMagnificationSpecIfNeeded(info, spec); callback.setFindAccessibilityNodeInfoResult(info, interactionId); callback.setPrefetchAccessibilityNodeInfoResult(infos, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ /* ignore - other side isn't too bothered if this doesn't arrive */ } } } private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion) { updateInfoForViewPort(info, spec, interactiveRegion); returnFindNodeResult(info, callback, interactionId); } private boolean handleClickableSpanActionUiThread( View view, int virtualDescendantId, Bundle arguments) { Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN); Loading Loading @@ -1053,20 +1147,13 @@ public final class AccessibilityInteractionController { private final ArrayList<View> mTempViewList = new ArrayList<View>(); public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos, Bundle arguments) { public void prefetchAccessibilityNodeInfos(View view, AccessibilityNodeInfo root, int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos) { if (root == null) { return; } AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); // Determine if we'll be populating extra data final String extraDataRequested = (arguments == null) ? null : arguments.getString(EXTRA_DATA_REQUESTED_KEY); if (provider == null) { AccessibilityNodeInfo root = view.createAccessibilityNodeInfo(); if (root != null) { if (extraDataRequested != null) { view.addExtraDataToAccessibilityNodeInfo( root, extraDataRequested, arguments); } outInfos.add(root); if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { prefetchPredecessorsOfRealNode(view, outInfos); } Loading @@ -1076,16 +1163,7 @@ public final class AccessibilityInteractionController { if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { prefetchDescendantsOfRealNode(view, outInfos); } } } else { final AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId); if (root != null) { if (extraDataRequested != null) { provider.addExtraDataToAccessibilityNodeInfo( virtualViewId, root, extraDataRequested, arguments); } outInfos.add(root); if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos); } Loading @@ -1096,13 +1174,18 @@ public final class AccessibilityInteractionController { prefetchDescendantsOfVirtualNode(root, provider, outInfos); } } } if (ENFORCE_NODE_TREE_CONSISTENT) { enforceNodeTreeConsistent(outInfos); enforceNodeTreeConsistent(root, outInfos); } } private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) { private boolean shouldStopPrefetching(List prefetchededInfos) { return mHandler.hasUserInteractiveMessagesWaiting() || prefetchededInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE; } private void enforceNodeTreeConsistent( AccessibilityNodeInfo root, List<AccessibilityNodeInfo> nodes) { LongSparseArray<AccessibilityNodeInfo> nodeMap = new LongSparseArray<AccessibilityNodeInfo>(); final int nodeCount = nodes.size(); Loading @@ -1113,7 +1196,6 @@ public final class AccessibilityInteractionController { // If the nodes are a tree it does not matter from // which node we start to search for the root. AccessibilityNodeInfo root = nodeMap.valueAt(0); AccessibilityNodeInfo parent = root; while (parent != null) { root = parent; Loading Loading @@ -1180,9 +1262,11 @@ public final class AccessibilityInteractionController { private void prefetchPredecessorsOfRealNode(View view, List<AccessibilityNodeInfo> outInfos) { if (shouldStopPrefetching(outInfos)) { return; } ViewParent parent = view.getParentForAccessibility(); while (parent instanceof View && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { while (parent instanceof View && !shouldStopPrefetching(outInfos)) { View parentView = (View) parent; AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo(); if (info != null) { Loading @@ -1194,6 +1278,9 @@ public final class AccessibilityInteractionController { private void prefetchSiblingsOfRealNode(View current, List<AccessibilityNodeInfo> outInfos) { if (shouldStopPrefetching(outInfos)) { return; } ViewParent parent = current.getParentForAccessibility(); if (parent instanceof ViewGroup) { ViewGroup parentGroup = (ViewGroup) parent; Loading @@ -1203,7 +1290,7 @@ public final class AccessibilityInteractionController { parentGroup.addChildrenForAccessibility(children); final int childCount = children.size(); for (int i = 0; i < childCount; i++) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } View child = children.get(i); Loading Loading @@ -1231,7 +1318,7 @@ public final class AccessibilityInteractionController { private void prefetchDescendantsOfRealNode(View root, List<AccessibilityNodeInfo> outInfos) { if (!(root instanceof ViewGroup)) { if (shouldStopPrefetching(outInfos) || !(root instanceof ViewGroup)) { return; } HashMap<View, AccessibilityNodeInfo> addedChildren = Loading @@ -1242,7 +1329,7 @@ public final class AccessibilityInteractionController { root.addChildrenForAccessibility(children); final int childCount = children.size(); for (int i = 0; i < childCount; i++) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } View child = children.get(i); Loading @@ -1267,7 +1354,7 @@ public final class AccessibilityInteractionController { } finally { children.clear(); } if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (!shouldStopPrefetching(outInfos)) { for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) { View addedChild = entry.getKey(); AccessibilityNodeInfo virtualRoot = entry.getValue(); Loading @@ -1289,7 +1376,7 @@ public final class AccessibilityInteractionController { long parentNodeId = root.getParentNodeId(); int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId); while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } final int virtualDescendantId = Loading Loading @@ -1334,7 +1421,7 @@ public final class AccessibilityInteractionController { if (parent != null) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } final long childNodeId = parent.getChildId(i); Loading @@ -1359,7 +1446,7 @@ public final class AccessibilityInteractionController { final int initialOutInfosSize = outInfos.size(); final int childCount = root.getChildCount(); for (int i = 0; i < childCount; i++) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } final long childNodeId = root.getChildId(i); Loading @@ -1369,7 +1456,7 @@ public final class AccessibilityInteractionController { outInfos.add(child); } } if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (!shouldStopPrefetching(outInfos)) { final int addedChildCount = outInfos.size() - initialOutInfosSize; for (int i = 0; i < addedChildCount; i++) { AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i); Loading Loading @@ -1478,6 +1565,10 @@ public final class AccessibilityInteractionController { boolean hasAccessibilityCallback(Message message) { return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false; } boolean hasUserInteractiveMessagesWaiting() { return hasMessagesOrCallbacks(); } } private final class AddNodeInfosForViewId implements Predicate<View> { Loading Loading
core/java/android/view/AccessibilityInteractionController.java +221 −130 Original line number Diff line number Diff line Loading @@ -86,12 +86,19 @@ public final class AccessibilityInteractionController { // accessibility from hanging private static final long REQUEST_PREPARER_TIMEOUT_MS = 500; // Callbacks should have the same configuration of the flags below to allow satisfying a pending // node request on prefetch private static final int FLAGS_AFFECTING_REPORTED_DATA = AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS | AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS; private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = new ArrayList<AccessibilityNodeInfo>(); private final Object mLock = new Object(); private final PrivateHandler mHandler; @VisibleForTesting public final PrivateHandler mHandler; private final ViewRootImpl mViewRootImpl; Loading @@ -113,6 +120,9 @@ public final class AccessibilityInteractionController { private AddNodeInfosForViewId mAddNodeInfosForViewId; @GuardedBy("mLock") private ArrayList<Message> mPendingFindNodeByIdMessages; @GuardedBy("mLock") private int mNumActiveRequestPreparers; @GuardedBy("mLock") Loading @@ -128,6 +138,7 @@ public final class AccessibilityInteractionController { mViewRootImpl = viewRootImpl; mPrefetcher = new AccessibilityNodePrefetcher(); mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class); mPendingFindNodeByIdMessages = new ArrayList<>(); } private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid, Loading Loading @@ -177,6 +188,9 @@ public final class AccessibilityInteractionController { args.arg4 = arguments; message.obj = args; synchronized (mLock) { mPendingFindNodeByIdMessages.add(message); } scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS); } Loading Loading @@ -315,6 +329,9 @@ public final class AccessibilityInteractionController { } private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { synchronized (mLock) { mPendingFindNodeByIdMessages.remove(message); } final int flags = message.arg1; SomeArgs args = (SomeArgs) message.obj; Loading @@ -329,22 +346,58 @@ public final class AccessibilityInteractionController { args.recycle(); List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; infos.clear(); View rootView = null; AccessibilityNodeInfo rootNode = null; try { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; final View root = findViewByAccessibilityId(accessibilityViewId); if (root != null && isShown(root)) { mPrefetcher.prefetchAccessibilityNodeInfos( root, virtualDescendantId, flags, infos, arguments); rootView = findViewByAccessibilityId(accessibilityViewId); if (rootView != null && isShown(rootView)) { rootNode = populateAccessibilityNodeInfoForView( rootView, arguments, virtualDescendantId); } } finally { updateInfosForViewportAndReturnFindNodeResult( infos, callback, interactionId, spec, interactiveRegion); updateInfoForViewportAndReturnFindNodeResult( rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode), callback, interactionId, spec, interactiveRegion); } ArrayList<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; infos.clear(); mPrefetcher.prefetchAccessibilityNodeInfos( rootView, rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode), virtualDescendantId, flags, infos); mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewPort(infos, spec, interactiveRegion); returnPrefetchResult(interactionId, infos, callback); returnPendingFindAccessibilityNodeInfosInPrefetch(rootNode, infos, flags); } private AccessibilityNodeInfo populateAccessibilityNodeInfoForView( View view, Bundle arguments, int virtualViewId) { AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); // Determine if we'll be populating extra data final String extraDataRequested = (arguments == null) ? null : arguments.getString(EXTRA_DATA_REQUESTED_KEY); AccessibilityNodeInfo root = null; if (provider == null) { root = view.createAccessibilityNodeInfo(); if (root != null) { if (extraDataRequested != null) { view.addExtraDataToAccessibilityNodeInfo(root, extraDataRequested, arguments); } } } else { root = provider.createAccessibilityNodeInfo(virtualViewId); if (root != null) { if (extraDataRequested != null) { provider.addExtraDataToAccessibilityNodeInfo( virtualViewId, root, extraDataRequested, arguments); } } } return root; } public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId, Loading Loading @@ -402,6 +455,7 @@ public final class AccessibilityInteractionController { mAddNodeInfosForViewId.reset(); } } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewportAndReturnFindNodeResult( infos, callback, interactionId, spec, interactiveRegion); } Loading Loading @@ -484,6 +538,7 @@ public final class AccessibilityInteractionController { } } } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewportAndReturnFindNodeResult( infos, callback, interactionId, spec, interactiveRegion); } Loading Loading @@ -575,6 +630,7 @@ public final class AccessibilityInteractionController { } } } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfoForViewportAndReturnFindNodeResult( focused, callback, interactionId, spec, interactiveRegion); } Loading Loading @@ -629,6 +685,7 @@ public final class AccessibilityInteractionController { } } } finally { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfoForViewportAndReturnFindNodeResult( next, callback, interactionId, spec, interactiveRegion); } Loading Loading @@ -785,33 +842,6 @@ public final class AccessibilityInteractionController { } } private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos, MagnificationSpec spec) { if (infos == null) { return; } final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { AccessibilityNodeInfo info = infos.get(i); applyAppScaleAndMagnificationSpecIfNeeded(info, spec); } } } private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos, Region interactiveRegion) { if (interactiveRegion == null || infos == null) { return; } final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { AccessibilityNodeInfo info = infos.get(i); adjustIsVisibleToUserIfNeeded(info, interactiveRegion); } } private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info, Region interactiveRegion) { if (interactiveRegion == null || info == null) { Loading @@ -832,17 +862,6 @@ public final class AccessibilityInteractionController { return false; } private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) { if (infos == null || shouldBypassAdjustBoundsInScreen()) { return; } final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { final AccessibilityNodeInfo info = infos.get(i); adjustBoundsInScreenIfNeeded(info); } } private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) { if (info == null || shouldBypassAdjustBoundsInScreen()) { return; Loading Loading @@ -890,17 +909,6 @@ public final class AccessibilityInteractionController { return screenMatrix == null || screenMatrix.isIdentity(); } private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) { if (infos == null || shouldBypassAssociateLeashedParent()) { return; } final int infoCount = infos.size(); for (int i = 0; i < infoCount; i++) { final AccessibilityNodeInfo info = infos.get(i); associateLeashedParentIfNeeded(info); } } private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) { if (info == null || shouldBypassAssociateLeashedParent()) { return; Loading Loading @@ -974,18 +982,46 @@ public final class AccessibilityInteractionController { return (appScale != 1.0f || (spec != null && !spec.isNop())); } private void updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec, Region interactiveRegion) { for (int i = 0; i < infos.size(); i++) { updateInfoForViewPort(infos.get(i), spec, interactiveRegion); } } private void updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec, Region interactiveRegion) { associateLeashedParentIfNeeded(info); applyScreenMatrixIfNeeded(info); adjustBoundsInScreenIfNeeded(info); // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, // then impact the visibility result, we need to adjust visibility before apply scale. adjustIsVisibleToUserIfNeeded(info, interactiveRegion); applyAppScaleAndMagnificationSpecIfNeeded(info, spec); } private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion) { if (infos != null) { updateInfosForViewPort(infos, spec, interactiveRegion); } returnFindNodesResult(infos, callback, interactionId); } private void returnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId) { try { callback.setFindAccessibilityNodeInfoResult(info, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ } } private void returnFindNodesResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId) { try { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; associateLeashedParentIfNeeded(infos); applyScreenMatrixIfNeeded(infos); adjustBoundsInScreenIfNeeded(infos); // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, // then impact the visibility result, we need to adjust visibility before apply scale. adjustIsVisibleToUserIfNeeded(infos, interactiveRegion); applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); callback.setFindAccessibilityNodeInfosResult(infos, interactionId); if (infos != null) { infos.clear(); Loading @@ -995,24 +1031,82 @@ public final class AccessibilityInteractionController { } } private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion) { private void returnPendingFindAccessibilityNodeInfosInPrefetch(AccessibilityNodeInfo rootNode, List<AccessibilityNodeInfo> infos, int flags) { AccessibilityNodeInfo satisfiedPendingRequestPrefetchedNode = null; IAccessibilityInteractionConnectionCallback satisfiedPendingRequestCallback = null; int satisfiedPendingRequestInteractionId = AccessibilityInteractionClient.NO_ID; synchronized (mLock) { for (int i = 0; i < mPendingFindNodeByIdMessages.size(); i++) { final Message pendingMessage = mPendingFindNodeByIdMessages.get(i); final int pendingFlags = pendingMessage.arg1; if ((pendingFlags & FLAGS_AFFECTING_REPORTED_DATA) != (flags & FLAGS_AFFECTING_REPORTED_DATA)) { continue; } SomeArgs args = (SomeArgs) pendingMessage.obj; final int accessibilityViewId = args.argi1; final int virtualDescendantId = args.argi2; satisfiedPendingRequestPrefetchedNode = nodeWithIdFromList(rootNode, infos, AccessibilityNodeInfo.makeNodeId( accessibilityViewId, virtualDescendantId)); if (satisfiedPendingRequestPrefetchedNode != null) { satisfiedPendingRequestCallback = (IAccessibilityInteractionConnectionCallback) args.arg1; satisfiedPendingRequestInteractionId = args.argi3; mHandler.removeMessages( PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID, pendingMessage.obj); args.recycle(); break; } } mPendingFindNodeByIdMessages.clear(); } if (satisfiedPendingRequestPrefetchedNode != null) { returnFindNodeResult( AccessibilityNodeInfo.obtain(satisfiedPendingRequestPrefetchedNode), satisfiedPendingRequestCallback, satisfiedPendingRequestInteractionId); } } private AccessibilityNodeInfo nodeWithIdFromList(AccessibilityNodeInfo rootNode, List<AccessibilityNodeInfo> infos, long nodeId) { if (rootNode != null && rootNode.getSourceNodeId() == nodeId) { return rootNode; } for (int j = 0; j < infos.size(); j++) { AccessibilityNodeInfo info = infos.get(j); if (info.getSourceNodeId() == nodeId) { return info; } } return null; } private void returnPrefetchResult(int interactionId, List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback) { if (infos.size() > 0) { try { mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; associateLeashedParentIfNeeded(info); applyScreenMatrixIfNeeded(info); adjustBoundsInScreenIfNeeded(info); // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, // then impact the visibility result, we need to adjust visibility before apply scale. adjustIsVisibleToUserIfNeeded(info, interactiveRegion); applyAppScaleAndMagnificationSpecIfNeeded(info, spec); callback.setFindAccessibilityNodeInfoResult(info, interactionId); callback.setPrefetchAccessibilityNodeInfoResult(infos, interactionId); } catch (RemoteException re) { /* ignore - the other side will time out */ /* ignore - other side isn't too bothered if this doesn't arrive */ } } } private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion) { updateInfoForViewPort(info, spec, interactiveRegion); returnFindNodeResult(info, callback, interactionId); } private boolean handleClickableSpanActionUiThread( View view, int virtualDescendantId, Bundle arguments) { Parcelable span = arguments.getParcelable(ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN); Loading Loading @@ -1053,20 +1147,13 @@ public final class AccessibilityInteractionController { private final ArrayList<View> mTempViewList = new ArrayList<View>(); public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos, Bundle arguments) { public void prefetchAccessibilityNodeInfos(View view, AccessibilityNodeInfo root, int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos) { if (root == null) { return; } AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); // Determine if we'll be populating extra data final String extraDataRequested = (arguments == null) ? null : arguments.getString(EXTRA_DATA_REQUESTED_KEY); if (provider == null) { AccessibilityNodeInfo root = view.createAccessibilityNodeInfo(); if (root != null) { if (extraDataRequested != null) { view.addExtraDataToAccessibilityNodeInfo( root, extraDataRequested, arguments); } outInfos.add(root); if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { prefetchPredecessorsOfRealNode(view, outInfos); } Loading @@ -1076,16 +1163,7 @@ public final class AccessibilityInteractionController { if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { prefetchDescendantsOfRealNode(view, outInfos); } } } else { final AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId); if (root != null) { if (extraDataRequested != null) { provider.addExtraDataToAccessibilityNodeInfo( virtualViewId, root, extraDataRequested, arguments); } outInfos.add(root); if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos); } Loading @@ -1096,13 +1174,18 @@ public final class AccessibilityInteractionController { prefetchDescendantsOfVirtualNode(root, provider, outInfos); } } } if (ENFORCE_NODE_TREE_CONSISTENT) { enforceNodeTreeConsistent(outInfos); enforceNodeTreeConsistent(root, outInfos); } } private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) { private boolean shouldStopPrefetching(List prefetchededInfos) { return mHandler.hasUserInteractiveMessagesWaiting() || prefetchededInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE; } private void enforceNodeTreeConsistent( AccessibilityNodeInfo root, List<AccessibilityNodeInfo> nodes) { LongSparseArray<AccessibilityNodeInfo> nodeMap = new LongSparseArray<AccessibilityNodeInfo>(); final int nodeCount = nodes.size(); Loading @@ -1113,7 +1196,6 @@ public final class AccessibilityInteractionController { // If the nodes are a tree it does not matter from // which node we start to search for the root. AccessibilityNodeInfo root = nodeMap.valueAt(0); AccessibilityNodeInfo parent = root; while (parent != null) { root = parent; Loading Loading @@ -1180,9 +1262,11 @@ public final class AccessibilityInteractionController { private void prefetchPredecessorsOfRealNode(View view, List<AccessibilityNodeInfo> outInfos) { if (shouldStopPrefetching(outInfos)) { return; } ViewParent parent = view.getParentForAccessibility(); while (parent instanceof View && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { while (parent instanceof View && !shouldStopPrefetching(outInfos)) { View parentView = (View) parent; AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo(); if (info != null) { Loading @@ -1194,6 +1278,9 @@ public final class AccessibilityInteractionController { private void prefetchSiblingsOfRealNode(View current, List<AccessibilityNodeInfo> outInfos) { if (shouldStopPrefetching(outInfos)) { return; } ViewParent parent = current.getParentForAccessibility(); if (parent instanceof ViewGroup) { ViewGroup parentGroup = (ViewGroup) parent; Loading @@ -1203,7 +1290,7 @@ public final class AccessibilityInteractionController { parentGroup.addChildrenForAccessibility(children); final int childCount = children.size(); for (int i = 0; i < childCount; i++) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } View child = children.get(i); Loading Loading @@ -1231,7 +1318,7 @@ public final class AccessibilityInteractionController { private void prefetchDescendantsOfRealNode(View root, List<AccessibilityNodeInfo> outInfos) { if (!(root instanceof ViewGroup)) { if (shouldStopPrefetching(outInfos) || !(root instanceof ViewGroup)) { return; } HashMap<View, AccessibilityNodeInfo> addedChildren = Loading @@ -1242,7 +1329,7 @@ public final class AccessibilityInteractionController { root.addChildrenForAccessibility(children); final int childCount = children.size(); for (int i = 0; i < childCount; i++) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } View child = children.get(i); Loading @@ -1267,7 +1354,7 @@ public final class AccessibilityInteractionController { } finally { children.clear(); } if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (!shouldStopPrefetching(outInfos)) { for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) { View addedChild = entry.getKey(); AccessibilityNodeInfo virtualRoot = entry.getValue(); Loading @@ -1289,7 +1376,7 @@ public final class AccessibilityInteractionController { long parentNodeId = root.getParentNodeId(); int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId); while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } final int virtualDescendantId = Loading Loading @@ -1334,7 +1421,7 @@ public final class AccessibilityInteractionController { if (parent != null) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } final long childNodeId = parent.getChildId(i); Loading @@ -1359,7 +1446,7 @@ public final class AccessibilityInteractionController { final int initialOutInfosSize = outInfos.size(); final int childCount = root.getChildCount(); for (int i = 0; i < childCount; i++) { if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (shouldStopPrefetching(outInfos)) { return; } final long childNodeId = root.getChildId(i); Loading @@ -1369,7 +1456,7 @@ public final class AccessibilityInteractionController { outInfos.add(child); } } if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { if (!shouldStopPrefetching(outInfos)) { final int addedChildCount = outInfos.size() - initialOutInfosSize; for (int i = 0; i < addedChildCount; i++) { AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i); Loading Loading @@ -1478,6 +1565,10 @@ public final class AccessibilityInteractionController { boolean hasAccessibilityCallback(Message message) { return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false; } boolean hasUserInteractiveMessagesWaiting() { return hasMessagesOrCallbacks(); } } private final class AddNodeInfosForViewId implements Predicate<View> { Loading