[英]Infinite recursion in Android accessibility service when traversing AccessibilityNodeInfo
Users of my app are experiencing crashes since a few days.几天以来,我的应用程序的用户遇到了崩溃。 I was able to collect a logcat on my device, with some added debugging output.
我能够在我的设备上收集一个 logcat,并添加了一些调试输出。 So here's what's happening:
所以这是发生了什么:
In my handleAccessibilityEvent()
, I call在我的
handleAccessibilityEvent()
,我打电话
AccessibilityNodeInfo root = getRootInActiveWindow();
int eventWindowId = event.getWindowId();
if (ExistsNodeOrChildren(root, new WindowIdCondition(eventWindowId)))
{
}
which walks through the node tree recursively:它递归地遍历节点树:
private boolean ExistsNodeOrChildren(AccessibilityNodeInfo n, NodeCondition condition)
{
Log.d(_logTag, "ExistsNodeOrChildren" + n.toString());
if (n == null) return false;
if (condition.check(n))
return true;
for (int i = 0; i < n.getChildCount(); i++)
{
Log.d(_logTag, "ExistsNodeOrChildren child" + i);
if (ExistsNodeOrChildren(n.getChild(i), condition))
return true;
}
return false;
}
The NodeCondition is a simple interface for predicate-like checks on a node. NodeCondition 是一个简单的接口,用于对节点进行类似谓词的检查。
ExistsNodeOrChildren
runs into infinite recursion. ExistsNodeOrChildren
遇到无限递归。 From the logs (see below), it looks to me like a node is returning itself as its own child.从日志(见下文)来看,在我看来,一个节点将自己作为自己的子节点返回。
My main question is: Is this allowed and must be handeled in my Accessibility service?我的主要问题是:这是否允许并且必须在我的辅助功能服务中处理? It seems like this was introduced with some recent updates, maybe concerning WebView or Chrome.
似乎这是在最近的一些更新中引入的,可能与 WebView 或 Chrome 相关。
If yes: How should I compare AccessibilityNodeInfo
objects to check if they refer to the same node?如果是:我应该如何比较
AccessibilityNodeInfo
对象以检查它们是否引用同一个节点?
(latest events are on top) (最新活动在上)
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildrenandroid.view.accessibility.AccessibilityNodeInfo@c723; boundsInParent: Rect(0, 0 - 1080, 1605); boundsInScreen: Rect(0, 210 - 1080, 1668); packageName: com.opera.mini.native; className: android.webkit.WebView; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; importantForAccessibility: false; actions: null
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildren child0
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildrenandroid.view.accessibility.AccessibilityNodeInfo@c723; boundsInParent: Rect(0, 0 - 1080, 1605); boundsInScreen: Rect(0, 210 - 1080, 1668); packageName: com.opera.mini.native; className: android.webkit.WebView; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; importantForAccessibility: false; actions: null
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildren child0
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildrenandroid.view.accessibility.AccessibilityNodeInfo@c723; boundsInParent: Rect(0, 0 - 1080, 1605); boundsInScreen: Rect(0, 210 - 1080, 1668); packageName: com.opera.mini.native; className: android.webkit.WebView; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; importantForAccessibility: false; actions: null
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildren child0
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildren child0
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildren child0
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildrenandroid.view.accessibility.AccessibilityNodeInfo@c723; boundsInParent: Rect(0, 0 - 1080, 1605); boundsInScreen: Rect(0, 210 - 1080, 1668); packageName: com.opera.mini.native; className: android.webkit.WebView; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; importantForAccessibility: false; actions: null
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildrenandroid.view.accessibility.AccessibilityNodeInfo@c723; boundsInParent: Rect(0, 0 - 1080, 1605); boundsInScreen: Rect(0, 210 - 1080, 1668); packageName: com.opera.mini.native; className: android.webkit.WebView; text: null; error: null; maxTextLength: -1; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; contextClickable: false; enabled: true; password: false; scrollable: false; importantForAccessibility: false; actions: null
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildren child0
02-08 10:49:01.074 Google Pixel Debug 9314 KP2AAF ExistsNodeOrChildren child0
If this isn't caused by some other recursion in your NodeCondition function (which you probably should have provided), and this is actually a bug in the Android OS virtual view hierarchy, you could attempt to remediate it by doing something like this:如果这不是由 NodeCondition 函数中的其他递归引起的(您可能应该提供),并且这实际上是 Android 操作系统虚拟视图层次结构中的一个错误,您可以尝试通过执行以下操作来修复它:
private boolean ExistsNodeOrChildren(AccessibilityNodeInfo n, NodeCondition condition) {
//For God sake do this first if you think n might actually be null!!!
//Or just don't do it, and let n.toString() throw a NPE. (BAD IDEA)
if (n == null) return false;
Log.d(_logTag, "ExistsNodeOrChildren" + n.toString());
//NOTE: This could also cause recursion, you didn't provide this code.
if (condition.check(n)) return true;
for (int i = 0; i < n.getChildCount(); i++) {
AccessibilityNodeInfo child = n.getChild(i);
//Skip recursion the times n.getChild() returns n.
//The check really is this simple, because we can only skip this
//When the child is literally the same object, otherwise it might be
//a node that has identical properties, which can happen.
if (child != n) {
Log.d(_logTag, "ExistsNodeOrChildren child" + i);
if (ExistsNodeOrChildren(n.getChild(i), condition)) return true;
} else {
log.e("We should report this as a bug in the AOSP");
}
}
return false;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.