Skip to content

Commit 749e796

Browse files
committed
UI test automation cannot get the root node and gets null children.
1. The AccessibilityInteractionController was using an incorrect looper i.e. not the UI thread looper which was causing getting the root node to fail. 2. The AccessibilityNodeInfo was populated by a ViewGroup with the children for accessibility without checking whether these children are really displayed. bug:6362875 Change-Id: I7906d89571eb9d57d10f971639f88632926dd077
1 parent caf7f3d commit 749e796

File tree

2 files changed

+43
-45
lines changed

2 files changed

+43
-45
lines changed

core/java/android/view/AccessibilityInteractionController.java

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,14 @@ final class AccessibilityInteractionController {
5858

5959
private final AccessibilityNodePrefetcher mPrefetcher;
6060

61+
private final long mMyLooperThreadId;
62+
63+
private final int mMyProcessId;
64+
6165
public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
62-
// mView is never null - the caller has already checked.
63-
Looper looper = viewRootImpl.mView.mContext.getMainLooper();
66+
Looper looper = viewRootImpl.mHandler.getLooper();
67+
mMyLooperThreadId = looper.getThread().getId();
68+
mMyProcessId = Process.myPid();
6469
mHandler = new PrivateHandler(looper);
6570
mViewRootImpl = viewRootImpl;
6671
mPrefetcher = new AccessibilityNodePrefetcher();
@@ -137,8 +142,7 @@ public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
137142
// thread in this process, set the message as a static reference so
138143
// after this call completes the same thread but in the interrogating
139144
// client can handle the message to generate the result.
140-
if (interrogatingPid == Process.myPid()
141-
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
145+
if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
142146
AccessibilityInteractionClient.getInstanceForThread(
143147
interrogatingTid).setSameThreadMessage(message);
144148
} else {
@@ -169,7 +173,7 @@ private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message)
169173
} else {
170174
root = findViewByAccessibilityId(accessibilityViewId);
171175
}
172-
if (root != null && isDisplayedOnScreen(root)) {
176+
if (root != null && root.isDisplayedOnScreen()) {
173177
mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos);
174178
}
175179
} finally {
@@ -199,8 +203,7 @@ public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNode
199203
// thread in this process, set the message as a static reference so
200204
// after this call completes the same thread but in the interrogating
201205
// client can handle the message to generate the result.
202-
if (interrogatingPid == Process.myPid()
203-
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
206+
if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
204207
AccessibilityInteractionClient.getInstanceForThread(
205208
interrogatingTid).setSameThreadMessage(message);
206209
} else {
@@ -232,7 +235,7 @@ private void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
232235
}
233236
if (root != null) {
234237
View target = root.findViewById(viewId);
235-
if (target != null && isDisplayedOnScreen(target)) {
238+
if (target != null && target.isDisplayedOnScreen()) {
236239
info = target.createAccessibilityNodeInfo();
237240
}
238241
}
@@ -263,8 +266,7 @@ public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeI
263266
// thread in this process, set the message as a static reference so
264267
// after this call completes the same thread but in the interrogating
265268
// client can handle the message to generate the result.
266-
if (interrogatingPid == Process.myPid()
267-
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
269+
if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
268270
AccessibilityInteractionClient.getInstanceForThread(
269271
interrogatingTid).setSameThreadMessage(message);
270272
} else {
@@ -295,7 +297,7 @@ private void findAccessibilityNodeInfosByTextUiThread(Message message) {
295297
} else {
296298
root = mViewRootImpl.mView;
297299
}
298-
if (root != null && isDisplayedOnScreen(root)) {
300+
if (root != null && root.isDisplayedOnScreen()) {
299301
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
300302
if (provider != null) {
301303
infos = provider.findAccessibilityNodeInfosByText(text,
@@ -312,7 +314,7 @@ private void findAccessibilityNodeInfosByTextUiThread(Message message) {
312314
final int viewCount = foundViews.size();
313315
for (int i = 0; i < viewCount; i++) {
314316
View foundView = foundViews.get(i);
315-
if (isDisplayedOnScreen(foundView)) {
317+
if (foundView.isDisplayedOnScreen()) {
316318
provider = foundView.getAccessibilityNodeProvider();
317319
if (provider != null) {
318320
List<AccessibilityNodeInfo> infosFromProvider =
@@ -356,8 +358,7 @@ public void findFocusClientThread(long accessibilityNodeId, int interactionId, i
356358
// thread in this process, set the message as a static reference so
357359
// after this call completes the same thread but in the interrogating
358360
// client can handle the message to generate the result.
359-
if (interogatingPid == Process.myPid()
360-
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
361+
if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
361362
AccessibilityInteractionClient.getInstanceForThread(
362363
interrogatingTid).setSameThreadMessage(message);
363364
} else {
@@ -388,7 +389,7 @@ private void findFocusUiThread(Message message) {
388389
} else {
389390
root = mViewRootImpl.mView;
390391
}
391-
if (root != null && isDisplayedOnScreen(root)) {
392+
if (root != null && root.isDisplayedOnScreen()) {
392393
switch (focusType) {
393394
case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
394395
View host = mViewRootImpl.mAccessibilityFocusedHost;
@@ -409,7 +410,7 @@ private void findFocusUiThread(Message message) {
409410
case AccessibilityNodeInfo.FOCUS_INPUT: {
410411
// Input focus cannot go to virtual views.
411412
View target = root.findFocus();
412-
if (target != null && isDisplayedOnScreen(target)) {
413+
if (target != null && target.isDisplayedOnScreen()) {
413414
focused = target.createAccessibilityNodeInfo();
414415
}
415416
} break;
@@ -444,8 +445,7 @@ public void focusSearchClientThread(long accessibilityNodeId, int interactionId,
444445
// thread in this process, set the message as a static reference so
445446
// after this call completes the same thread but in the interrogating
446447
// client can handle the message to generate the result.
447-
if (interogatingPid == Process.myPid()
448-
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
448+
if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
449449
AccessibilityInteractionClient.getInstanceForThread(
450450
interrogatingTid).setSameThreadMessage(message);
451451
} else {
@@ -476,7 +476,7 @@ private void focusSearchUiThread(Message message) {
476476
} else {
477477
root = mViewRootImpl.mView;
478478
}
479-
if (root != null && isDisplayedOnScreen(root)) {
479+
if (root != null && root.isDisplayedOnScreen()) {
480480
if ((direction & View.FOCUS_ACCESSIBILITY) == View.FOCUS_ACCESSIBILITY) {
481481
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
482482
if (provider != null) {
@@ -530,8 +530,7 @@ public void performAccessibilityActionClientThread(long accessibilityNodeId, int
530530
// thread in this process, set the message as a static reference so
531531
// after this call completes the same thread but in the interrogating
532532
// client can handle the message to generate the result.
533-
if (interogatingPid == Process.myPid()
534-
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
533+
if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
535534
AccessibilityInteractionClient.getInstanceForThread(
536535
interrogatingTid).setSameThreadMessage(message);
537536
} else {
@@ -562,7 +561,7 @@ private void perfromAccessibilityActionUiThread(Message message) {
562561
} else {
563562
target = mViewRootImpl.mView;
564563
}
565-
if (target != null && isDisplayedOnScreen(target)) {
564+
if (target != null && target.isDisplayedOnScreen()) {
566565
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
567566
if (provider != null) {
568567
succeeded = provider.performAccessibilityAction(action, virtualDescendantId);
@@ -586,30 +585,12 @@ private View findViewByAccessibilityId(int accessibilityId) {
586585
return null;
587586
}
588587
View foundView = root.findViewByAccessibilityId(accessibilityId);
589-
if (foundView != null && !isDisplayedOnScreen(foundView)) {
588+
if (foundView != null && !foundView.isDisplayedOnScreen()) {
590589
return null;
591590
}
592591
return foundView;
593592
}
594593

595-
/**
596-
* Computes whether a view is visible on the screen.
597-
*
598-
* @param view The view to check.
599-
* @return Whether the view is visible on the screen.
600-
*/
601-
private boolean isDisplayedOnScreen(View view) {
602-
// The first two checks are made also made by isShown() which
603-
// however traverses the tree up to the parent to catch that.
604-
// Therefore, we do some fail fast check to minimize the up
605-
// tree traversal.
606-
return (view.mAttachInfo != null
607-
&& view.mAttachInfo.mWindowVisibility == View.VISIBLE
608-
&& view.getAlpha() > 0
609-
&& view.isShown()
610-
&& view.getGlobalVisibleRect(mViewRootImpl.mTempRect));
611-
}
612-
613594
/**
614595
* This class encapsulates a prefetching strategy for the accessibility APIs for
615596
* querying window content. It is responsible to prefetch a batch of
@@ -684,7 +665,7 @@ private void prefetchSiblingsOfRealNode(View current,
684665
}
685666
View child = children.getChildAt(i);
686667
if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
687-
&& isDisplayedOnScreen(child)) {
668+
&& child.isDisplayedOnScreen()) {
688669
AccessibilityNodeInfo info = null;
689670
AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
690671
if (provider == null) {
@@ -718,7 +699,7 @@ private void prefetchDescendantsOfRealNode(View root,
718699
return;
719700
}
720701
View child = children.getChildAt(i);
721-
if ( isDisplayedOnScreen(child)) {
702+
if (child.isDisplayedOnScreen()) {
722703
AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
723704
if (provider == null) {
724705
AccessibilityNodeInfo info = child.createAccessibilityNodeInfo();

core/java/android/view/View.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4679,6 +4679,23 @@ void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
46794679
}
46804680
}
46814681

4682+
/**
4683+
* Computes whether this view is visible on the screen.
4684+
*
4685+
* @return Whether the view is visible on the screen.
4686+
*/
4687+
boolean isDisplayedOnScreen() {
4688+
// The first two checks are made also made by isShown() which
4689+
// however traverses the tree up to the parent to catch that.
4690+
// Therefore, we do some fail fast check to minimize the up
4691+
// tree traversal.
4692+
return (mAttachInfo != null
4693+
&& mAttachInfo.mWindowVisibility == View.VISIBLE
4694+
&& getAlpha() > 0
4695+
&& isShown()
4696+
&& getGlobalVisibleRect(mAttachInfo.mTmpInvalRect));
4697+
}
4698+
46824699
/**
46834700
* Sets a delegate for implementing accessibility support via compositon as
46844701
* opposed to inheritance. The delegate's primary use is for implementing
@@ -6301,9 +6318,9 @@ public void addChildrenForAccessibility(ArrayList<View> children) {
63016318
boolean includeForAccessibility() {
63026319
if (mAttachInfo != null) {
63036320
if (!mAttachInfo.mIncludeNotImportantViews) {
6304-
return isImportantForAccessibility();
6321+
return isImportantForAccessibility() && isDisplayedOnScreen();
63056322
} else {
6306-
return true;
6323+
return isDisplayedOnScreen();
63076324
}
63086325
}
63096326
return false;

0 commit comments

Comments
 (0)