From 749e796eb3a42e21613a3b360000373601a8f50d Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Thu, 19 Apr 2012 17:13:46 -0700 Subject: 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 --- .../view/AccessibilityInteractionController.java | 67 ++++++++-------------- core/java/android/view/View.java | 21 ++++++- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 54c62ee83a12..e3f5b968781f 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -58,9 +58,14 @@ final class AccessibilityInteractionController { private final AccessibilityNodePrefetcher mPrefetcher; + private final long mMyLooperThreadId; + + private final int mMyProcessId; + public AccessibilityInteractionController(ViewRootImpl viewRootImpl) { - // mView is never null - the caller has already checked. - Looper looper = viewRootImpl.mView.mContext.getMainLooper(); + Looper looper = viewRootImpl.mHandler.getLooper(); + mMyLooperThreadId = looper.getThread().getId(); + mMyProcessId = Process.myPid(); mHandler = new PrivateHandler(looper); mViewRootImpl = viewRootImpl; mPrefetcher = new AccessibilityNodePrefetcher(); @@ -137,8 +142,7 @@ final class AccessibilityInteractionController { // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating // client can handle the message to generate the result. - if (interrogatingPid == Process.myPid() - && interrogatingTid == Looper.getMainLooper().getThread().getId()) { + if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { @@ -169,7 +173,7 @@ final class AccessibilityInteractionController { } else { root = findViewByAccessibilityId(accessibilityViewId); } - if (root != null && isDisplayedOnScreen(root)) { + if (root != null && root.isDisplayedOnScreen()) { mPrefetcher.prefetchAccessibilityNodeInfos(root, virtualDescendantId, flags, infos); } } finally { @@ -199,8 +203,7 @@ final class AccessibilityInteractionController { // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating // client can handle the message to generate the result. - if (interrogatingPid == Process.myPid() - && interrogatingTid == Looper.getMainLooper().getThread().getId()) { + if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { @@ -232,7 +235,7 @@ final class AccessibilityInteractionController { } if (root != null) { View target = root.findViewById(viewId); - if (target != null && isDisplayedOnScreen(target)) { + if (target != null && target.isDisplayedOnScreen()) { info = target.createAccessibilityNodeInfo(); } } @@ -263,8 +266,7 @@ final class AccessibilityInteractionController { // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating // client can handle the message to generate the result. - if (interrogatingPid == Process.myPid() - && interrogatingTid == Looper.getMainLooper().getThread().getId()) { + if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { @@ -295,7 +297,7 @@ final class AccessibilityInteractionController { } else { root = mViewRootImpl.mView; } - if (root != null && isDisplayedOnScreen(root)) { + if (root != null && root.isDisplayedOnScreen()) { AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider(); if (provider != null) { infos = provider.findAccessibilityNodeInfosByText(text, @@ -312,7 +314,7 @@ final class AccessibilityInteractionController { final int viewCount = foundViews.size(); for (int i = 0; i < viewCount; i++) { View foundView = foundViews.get(i); - if (isDisplayedOnScreen(foundView)) { + if (foundView.isDisplayedOnScreen()) { provider = foundView.getAccessibilityNodeProvider(); if (provider != null) { List infosFromProvider = @@ -356,8 +358,7 @@ final class AccessibilityInteractionController { // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating // client can handle the message to generate the result. - if (interogatingPid == Process.myPid() - && interrogatingTid == Looper.getMainLooper().getThread().getId()) { + if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { @@ -388,7 +389,7 @@ final class AccessibilityInteractionController { } else { root = mViewRootImpl.mView; } - if (root != null && isDisplayedOnScreen(root)) { + if (root != null && root.isDisplayedOnScreen()) { switch (focusType) { case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: { View host = mViewRootImpl.mAccessibilityFocusedHost; @@ -409,7 +410,7 @@ final class AccessibilityInteractionController { case AccessibilityNodeInfo.FOCUS_INPUT: { // Input focus cannot go to virtual views. View target = root.findFocus(); - if (target != null && isDisplayedOnScreen(target)) { + if (target != null && target.isDisplayedOnScreen()) { focused = target.createAccessibilityNodeInfo(); } } break; @@ -444,8 +445,7 @@ final class AccessibilityInteractionController { // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating // client can handle the message to generate the result. - if (interogatingPid == Process.myPid() - && interrogatingTid == Looper.getMainLooper().getThread().getId()) { + if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { @@ -476,7 +476,7 @@ final class AccessibilityInteractionController { } else { root = mViewRootImpl.mView; } - if (root != null && isDisplayedOnScreen(root)) { + if (root != null && root.isDisplayedOnScreen()) { if ((direction & View.FOCUS_ACCESSIBILITY) == View.FOCUS_ACCESSIBILITY) { AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider(); if (provider != null) { @@ -530,8 +530,7 @@ final class AccessibilityInteractionController { // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating // client can handle the message to generate the result. - if (interogatingPid == Process.myPid() - && interrogatingTid == Looper.getMainLooper().getThread().getId()) { + if (interogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { @@ -562,7 +561,7 @@ final class AccessibilityInteractionController { } else { target = mViewRootImpl.mView; } - if (target != null && isDisplayedOnScreen(target)) { + if (target != null && target.isDisplayedOnScreen()) { AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); if (provider != null) { succeeded = provider.performAccessibilityAction(action, virtualDescendantId); @@ -586,30 +585,12 @@ final class AccessibilityInteractionController { return null; } View foundView = root.findViewByAccessibilityId(accessibilityId); - if (foundView != null && !isDisplayedOnScreen(foundView)) { + if (foundView != null && !foundView.isDisplayedOnScreen()) { return null; } return foundView; } - /** - * Computes whether a view is visible on the screen. - * - * @param view The view to check. - * @return Whether the view is visible on the screen. - */ - private boolean isDisplayedOnScreen(View view) { - // The first two checks are made also made by isShown() which - // however traverses the tree up to the parent to catch that. - // Therefore, we do some fail fast check to minimize the up - // tree traversal. - return (view.mAttachInfo != null - && view.mAttachInfo.mWindowVisibility == View.VISIBLE - && view.getAlpha() > 0 - && view.isShown() - && view.getGlobalVisibleRect(mViewRootImpl.mTempRect)); - } - /** * This class encapsulates a prefetching strategy for the accessibility APIs for * querying window content. It is responsible to prefetch a batch of @@ -684,7 +665,7 @@ final class AccessibilityInteractionController { } View child = children.getChildAt(i); if (child.getAccessibilityViewId() != current.getAccessibilityViewId() - && isDisplayedOnScreen(child)) { + && child.isDisplayedOnScreen()) { AccessibilityNodeInfo info = null; AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider(); if (provider == null) { @@ -718,7 +699,7 @@ final class AccessibilityInteractionController { return; } View child = children.getChildAt(i); - if ( isDisplayedOnScreen(child)) { + if (child.isDisplayedOnScreen()) { AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider(); if (provider == null) { AccessibilityNodeInfo info = child.createAccessibilityNodeInfo(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ab3413e60f6d..ce0211328093 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4679,6 +4679,23 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } } + /** + * Computes whether this view is visible on the screen. + * + * @return Whether the view is visible on the screen. + */ + boolean isDisplayedOnScreen() { + // The first two checks are made also made by isShown() which + // however traverses the tree up to the parent to catch that. + // Therefore, we do some fail fast check to minimize the up + // tree traversal. + return (mAttachInfo != null + && mAttachInfo.mWindowVisibility == View.VISIBLE + && getAlpha() > 0 + && isShown() + && getGlobalVisibleRect(mAttachInfo.mTmpInvalRect)); + } + /** * Sets a delegate for implementing accessibility support via compositon as * opposed to inheritance. The delegate's primary use is for implementing @@ -6301,9 +6318,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal boolean includeForAccessibility() { if (mAttachInfo != null) { if (!mAttachInfo.mIncludeNotImportantViews) { - return isImportantForAccessibility(); + return isImportantForAccessibility() && isDisplayedOnScreen(); } else { - return true; + return isDisplayedOnScreen(); } } return false; -- cgit v1.2.3-59-g8ed1b