Skip to content

Commit 67d10a5

Browse files
sganovAndroid (Google) Code Review
authored andcommitted
Merge "Prefetching of accessibility node infos getting incorrect views." into jb-dev
2 parents 0763e01 + 4528b4e commit 67d10a5

File tree

4 files changed

+80
-15
lines changed

4 files changed

+80
-15
lines changed

core/java/android/view/AccessibilityInteractionController.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import android.util.PoolableManager;
3030
import android.util.Pools;
3131
import android.util.SparseLongArray;
32-
import android.view.ViewGroup.ChildListForAccessibility;
3332
import android.view.accessibility.AccessibilityInteractionClient;
3433
import android.view.accessibility.AccessibilityNodeInfo;
3534
import android.view.accessibility.AccessibilityNodeProvider;
@@ -623,6 +622,8 @@ private class AccessibilityNodePrefetcher {
623622

624623
private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
625624

625+
private final ArrayList<View> mTempViewList = new ArrayList<View>();
626+
626627
public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
627628
List<AccessibilityNodeInfo> outInfos) {
628629
AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
@@ -663,8 +664,6 @@ private void prefetchPredecessorsOfRealNode(View view,
663664
while (parent instanceof View
664665
&& outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
665666
View parentView = (View) parent;
666-
final long parentNodeId = AccessibilityNodeInfo.makeNodeId(
667-
parentView.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
668667
AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
669668
if (info != null) {
670669
outInfos.add(info);
@@ -678,19 +677,21 @@ private void prefetchSiblingsOfRealNode(View current,
678677
ViewParent parent = current.getParentForAccessibility();
679678
if (parent instanceof ViewGroup) {
680679
ViewGroup parentGroup = (ViewGroup) parent;
681-
ChildListForAccessibility children = ChildListForAccessibility.obtain(parentGroup,
682-
false);
680+
ArrayList<View> children = mTempViewList;
681+
children.clear();
683682
try {
684-
final int childCount = children.getChildCount();
683+
parentGroup.addChildrenForAccessibility(children);
684+
final int childCount = children.size();
685685
for (int i = 0; i < childCount; i++) {
686686
if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
687687
return;
688688
}
689-
View child = children.getChildAt(i);
689+
View child = children.get(i);
690690
if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
691691
&& isShown(child)) {
692692
AccessibilityNodeInfo info = null;
693-
AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
693+
AccessibilityNodeProvider provider =
694+
child.getAccessibilityNodeProvider();
694695
if (provider == null) {
695696
info = child.createAccessibilityNodeInfo();
696697
} else {
@@ -703,7 +704,7 @@ && isShown(child)) {
703704
}
704705
}
705706
} finally {
706-
children.recycle();
707+
children.clear();
707708
}
708709
}
709710
}
@@ -716,14 +717,16 @@ private void prefetchDescendantsOfRealNode(View root,
716717
ViewGroup rootGroup = (ViewGroup) root;
717718
HashMap<View, AccessibilityNodeInfo> addedChildren =
718719
new HashMap<View, AccessibilityNodeInfo>();
719-
ChildListForAccessibility children = ChildListForAccessibility.obtain(rootGroup, false);
720+
ArrayList<View> children = mTempViewList;
721+
children.clear();
720722
try {
721-
final int childCount = children.getChildCount();
723+
root.addChildrenForAccessibility(children);
724+
final int childCount = children.size();
722725
for (int i = 0; i < childCount; i++) {
723726
if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
724727
return;
725728
}
726-
View child = children.getChildAt(i);
729+
View child = children.get(i);
727730
if (isShown(child)) {
728731
AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
729732
if (provider == null) {
@@ -743,7 +746,7 @@ private void prefetchDescendantsOfRealNode(View root,
743746
}
744747
}
745748
} finally {
746-
children.recycle();
749+
children.clear();
747750
}
748751
if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
749752
for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {

core/java/android/view/accessibility/AccessibilityInteractionClient.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.accessibilityservice.IAccessibilityServiceConnection;
2020
import android.graphics.Rect;
2121
import android.os.Binder;
22+
import android.os.Build;
2223
import android.os.Bundle;
2324
import android.os.Message;
2425
import android.os.Process;
@@ -27,10 +28,14 @@
2728
import android.util.Log;
2829
import android.util.LongSparseArray;
2930
import android.util.SparseArray;
31+
import android.util.SparseLongArray;
3032

3133
import java.util.ArrayList;
3234
import java.util.Collections;
35+
import java.util.HashSet;
36+
import java.util.LinkedList;
3337
import java.util.List;
38+
import java.util.Queue;
3439
import java.util.concurrent.atomic.AtomicInteger;
3540

3641
/**
@@ -74,6 +79,8 @@ public final class AccessibilityInteractionClient
7479

7580
private static final boolean DEBUG = false;
7681

82+
private static final boolean CHECK_INTEGRITY = true;
83+
7784
private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
7885

7986
private static final Object sStaticLock = new Object();
@@ -491,6 +498,9 @@ private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
491498
result = Collections.emptyList();
492499
}
493500
clearResultLocked();
501+
if (Build.IS_DEBUGGABLE && CHECK_INTEGRITY) {
502+
checkFindAccessibilityNodeInfoResultIntegrity(result);
503+
}
494504
return result;
495505
}
496506
}
@@ -696,4 +706,56 @@ public void removeConnection(int connectionId) {
696706
sConnectionCache.remove(connectionId);
697707
}
698708
}
709+
710+
/**
711+
* Checks whether the infos are a fully connected tree with no duplicates.
712+
*
713+
* @param infos The result list to check.
714+
*/
715+
private void checkFindAccessibilityNodeInfoResultIntegrity(List<AccessibilityNodeInfo> infos) {
716+
if (infos.size() == 0) {
717+
return;
718+
}
719+
// Find the root node.
720+
AccessibilityNodeInfo root = infos.get(0);
721+
final int infoCount = infos.size();
722+
for (int i = 1; i < infoCount; i++) {
723+
for (int j = i; j < infoCount; j++) {
724+
AccessibilityNodeInfo candidate = infos.get(j);
725+
if (root.getParentNodeId() == candidate.getSourceNodeId()) {
726+
root = candidate;
727+
break;
728+
}
729+
}
730+
}
731+
if (root == null) {
732+
Log.e(LOG_TAG, "No root.");
733+
}
734+
// Check for duplicates.
735+
HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
736+
Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
737+
fringe.add(root);
738+
while (!fringe.isEmpty()) {
739+
AccessibilityNodeInfo current = fringe.poll();
740+
if (!seen.add(current)) {
741+
Log.e(LOG_TAG, "Duplicate node.");
742+
return;
743+
}
744+
SparseLongArray childIds = current.getChildNodeIds();
745+
final int childCount = childIds.size();
746+
for (int i = 0; i < childCount; i++) {
747+
final long childId = childIds.valueAt(i);
748+
for (int j = 0; j < infoCount; j++) {
749+
AccessibilityNodeInfo child = infos.get(j);
750+
if (child.getSourceNodeId() == childId) {
751+
fringe.add(child);
752+
}
753+
}
754+
}
755+
}
756+
final int disconnectedCount = infos.size() - seen.size();
757+
if (disconnectedCount > 0) {
758+
Log.e(LOG_TAG, disconnectedCount + " Disconnected nodes.");
759+
}
760+
}
699761
}

core/java/android/view/accessibility/AccessibilityNodeInfoCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ private void clearSubtreeWithOldInputFocusLocked(long currentInputFocusId) {
244244
/**
245245
* We are enforcing the invariant for a single accessibility focus.
246246
*
247-
* @param currentInputFocusId The current input focused node.
247+
* @param currentAccessibilityFocusId The current input focused node.
248248
*/
249249
private void clearSubtreeWithOldAccessibilityFocusLocked(long currentAccessibilityFocusId) {
250250
final int cacheSize = mCacheImpl.size();

core/java/android/widget/NumberPicker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2490,7 +2490,7 @@ private AccessibilityNodeInfo createAccessibilityNodeInfoForNumberPicker(int lef
24902490
info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
24912491
}
24922492

2493-
info.setParent((View) getParent());
2493+
info.setParent((View) getParentForAccessibility());
24942494
info.setEnabled(NumberPicker.this.isEnabled());
24952495
info.setScrollable(true);
24962496
Rect boundsInParent = mTempRect;

0 commit comments

Comments
 (0)