Skip to content

Commit 8bd6961

Browse files
committed
Intra-process view hierarchy interrogation does not work.
The content retrieval APIs are synchronous from a client's perspective but internally they are asynchronous. The client thread calls into the system requesting an action and providing a callback to receive the result after which it waits up to a timeout for that result. The system enforces security and then delegates the request to a given view hierarchy where a message is posted (from a binder thread) describing what to be performed by the main UI thread the result of which it delivered via the mentioned callback. However, the blocked client thread and the main UI thread of the target view hierarchy can be the same one, for example an accessibility service and an activity run in the same process, thus they are executed on the same main thread. In such a case the retrieval will fail since the UI thread that has to process the message describing the work to be done is blocked waiting for a result is has to compute! To avoid this scenario when making a call the client also passes its process and thread ids so the accessed view hierarchy can detect if the client making the request is running in its main UI thread. In such a case the view hierarchy, specifically the binder thread performing the IPC to it, does not post a message to be run on the UI thread but passes it to the singleton interaction client through which all interactions occur and the latter is responsible to execute the message before starting to wait for the asynchronous result delivered via the callback. In this case the expected result is already received so no waiting is performed. bug:5138933 Change-Id: I382e2d8689f5189110226613c2387f553df98bd3
1 parent 69314e7 commit 8bd6961

File tree

11 files changed

+753
-316
lines changed

11 files changed

+753
-316
lines changed

core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package android.accessibilityservice;
1818

1919
import android.accessibilityservice.AccessibilityServiceInfo;
2020
import android.view.accessibility.AccessibilityNodeInfo;
21+
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
2122

2223
/**
2324
* Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
@@ -30,84 +31,79 @@ interface IAccessibilityServiceConnection {
3031

3132
/**
3233
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
33-
* <p>
34-
* <strong>
35-
* It is a client responsibility to recycle the received info by
36-
* calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
37-
* of multiple instances.
38-
* </strong>
39-
* </p>
4034
*
4135
* @param accessibilityWindowId A unique window id.
4236
* @param accessibilityViewId A unique View accessibility id.
43-
* @return The node info.
37+
* @param interactionId The id of the interaction for matching with the callback result.
38+
* @param callback Callback which to receive the result.
39+
* @param threadId The id of the calling thread.
40+
* @return The current window scale, where zero means a failure.
4441
*/
45-
AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
46-
int accessibilityViewId);
42+
float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
43+
int accessibilityViewId, int interactionId,
44+
IAccessibilityInteractionConnectionCallback callback, long threadId);
4745

4846
/**
4947
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
5048
* insensitive containment. The search is performed in the window whose
5149
* id is specified and starts from the View whose accessibility id is
5250
* specified.
53-
* <p>
54-
* <strong>
55-
* It is a client responsibility to recycle the received infos by
56-
* calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
57-
* of multiple instances.
58-
* </strong>
59-
* </p>
6051
*
6152
* @param text The searched text.
62-
* @param accessibilityId The id of the view from which to start searching.
53+
* @param accessibilityWindowId A unique window id.
54+
* @param accessibilityViewId A unique View accessibility id from where to start the search.
6355
* Use {@link android.view.View#NO_ID} to start from the root.
64-
* @return A list of node info.
56+
* @param interactionId The id of the interaction for matching with the callback result.
57+
* @param callback Callback which to receive the result.
58+
* @param threadId The id of the calling thread.
59+
* @return The current window scale, where zero means a failure.
6560
*/
66-
List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text,
67-
int accessibilityWindowId, int accessibilityViewId);
61+
float findAccessibilityNodeInfosByViewText(String text, int accessibilityWindowId,
62+
int accessibilityViewId, int interractionId,
63+
IAccessibilityInteractionConnectionCallback callback, long threadId);
6864

6965
/**
7066
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
7167
* insensitive containment. The search is performed in the currently
7268
* active window and start from the root View in the window.
73-
* <p>
74-
* <strong>
75-
* It is a client responsibility to recycle the received infos by
76-
* calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
77-
* of multiple instances.
78-
* </strong>
79-
* </p>
8069
*
8170
* @param text The searched text.
8271
* @param accessibilityId The id of the view from which to start searching.
8372
* Use {@link android.view.View#NO_ID} to start from the root.
84-
* @return A list of node info.
73+
* @param interactionId The id of the interaction for matching with the callback result.
74+
* @param callback Callback which to receive the result.
75+
* @param threadId The id of the calling thread.
76+
* @return The current window scale, where zero means a failure.
8577
*/
86-
List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(String text);
78+
float findAccessibilityNodeInfosByViewTextInActiveWindow(String text,
79+
int interactionId, IAccessibilityInteractionConnectionCallback callback,
80+
long threadId);
8781

8882
/**
8983
* Finds an {@link AccessibilityNodeInfo} by View id. The search is performed
90-
* in the currently active window and start from the root View in the window.
91-
* <p>
92-
* <strong>
93-
* It is a client responsibility to recycle the received info by
94-
* calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
95-
* of multiple instances.
96-
* </strong>
97-
* </p>
84+
* in the currently active window and starts from the root View in the window.
9885
*
9986
* @param id The id of the node.
100-
* @return The node info.
87+
* @param interactionId The id of the interaction for matching with the callback result.
88+
* @param callback Callback which to receive the result.
89+
* @param threadId The id of the calling thread.
90+
* @return The current window scale, where zero means a failure.
10191
*/
102-
AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId);
92+
float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId, int interactionId,
93+
IAccessibilityInteractionConnectionCallback callback, long threadId);
10394

10495
/**
10596
* Performs an accessibility action on an {@link AccessibilityNodeInfo}.
10697
*
10798
* @param accessibilityWindowId The id of the window.
108-
* @param accessibilityViewId The of a view in the .
99+
* @param accessibilityViewId A unique View accessibility id.
100+
* @param action The action to perform.
101+
* @param interactionId The id of the interaction for matching with the callback result.
102+
* @param callback Callback which to receive the result.
103+
* @param threadId The id of the calling thread.
109104
* @return Whether the action was performed.
110105
*/
111106
boolean performAccessibilityAction(int accessibilityWindowId, int accessibilityViewId,
112-
int action);
107+
int action, int interactionId, IAccessibilityInteractionConnectionCallback callback,
108+
long threadId);
113109
}

core/java/android/view/ViewRootImpl.java

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import android.util.TypedValue;
6262
import android.view.View.MeasureSpec;
6363
import android.view.accessibility.AccessibilityEvent;
64+
import android.view.accessibility.AccessibilityInteractionClient;
6465
import android.view.accessibility.AccessibilityManager;
6566
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
6667
import android.view.accessibility.AccessibilityNodeInfo;
@@ -4359,37 +4360,42 @@ final class AccessibilityInteractionConnection
43594360
}
43604361

43614362
public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId,
4362-
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
4363+
int interactionId, IAccessibilityInteractionConnectionCallback callback,
4364+
int interrogatingPid, long interrogatingTid) {
43634365
if (mViewAncestor.get() != null) {
43644366
getAccessibilityInteractionController()
43654367
.findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId,
4366-
interactionId, callback);
4368+
interactionId, callback, interrogatingPid, interrogatingTid);
43674369
}
43684370
}
43694371

43704372
public void performAccessibilityAction(int accessibilityId, int action,
4371-
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
4373+
int interactionId, IAccessibilityInteractionConnectionCallback callback,
4374+
int interogatingPid, long interrogatingTid) {
43724375
if (mViewAncestor.get() != null) {
43734376
getAccessibilityInteractionController()
43744377
.performAccessibilityActionClientThread(accessibilityId, action, interactionId,
4375-
callback);
4378+
callback, interogatingPid, interrogatingTid);
43764379
}
43774380
}
43784381

43794382
public void findAccessibilityNodeInfoByViewId(int viewId,
4380-
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
4383+
int interactionId, IAccessibilityInteractionConnectionCallback callback,
4384+
int interrogatingPid, long interrogatingTid) {
43814385
if (mViewAncestor.get() != null) {
43824386
getAccessibilityInteractionController()
4383-
.findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback);
4387+
.findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback,
4388+
interrogatingPid, interrogatingTid);
43844389
}
43854390
}
43864391

43874392
public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId,
4388-
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
4393+
int interactionId, IAccessibilityInteractionConnectionCallback callback,
4394+
int interrogatingPid, long interrogatingTid) {
43894395
if (mViewAncestor.get() != null) {
43904396
getAccessibilityInteractionController()
43914397
.findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId,
4392-
interactionId, callback);
4398+
interactionId, callback, interrogatingPid, interrogatingTid);
43934399
}
43944400
}
43954401
}
@@ -4465,13 +4471,24 @@ private void clear() {
44654471
}
44664472

44674473
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId,
4468-
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
4474+
int interactionId, IAccessibilityInteractionConnectionCallback callback,
4475+
int interrogatingPid, long interrogatingTid) {
44694476
Message message = Message.obtain();
44704477
message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
44714478
message.arg1 = accessibilityId;
44724479
message.arg2 = interactionId;
44734480
message.obj = callback;
4474-
sendMessage(message);
4481+
// If the interrogation is performed by the same thread as the main UI
4482+
// thread in this process, set the message as a static reference so
4483+
// after this call completes the same thread but in the interrogating
4484+
// client can handle the message to generate the result.
4485+
if (interrogatingPid == Process.myPid()
4486+
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
4487+
message.setTarget(ViewRootImpl.this);
4488+
AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
4489+
} else {
4490+
sendMessage(message);
4491+
}
44754492
}
44764493

44774494
public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
@@ -4499,13 +4516,24 @@ public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message)
44994516
}
45004517

45014518
public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId,
4502-
IAccessibilityInteractionConnectionCallback callback) {
4519+
IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
4520+
long interrogatingTid) {
45034521
Message message = Message.obtain();
45044522
message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
45054523
message.arg1 = viewId;
45064524
message.arg2 = interactionId;
45074525
message.obj = callback;
4508-
sendMessage(message);
4526+
// If the interrogation is performed by the same thread as the main UI
4527+
// thread in this process, set the message as a static reference so
4528+
// after this call completes the same thread but in the interrogating
4529+
// client can handle the message to generate the result.
4530+
if (interrogatingPid == Process.myPid()
4531+
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
4532+
message.setTarget(ViewRootImpl.this);
4533+
AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
4534+
} else {
4535+
sendMessage(message);
4536+
}
45094537
}
45104538

45114539
public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
@@ -4532,7 +4560,8 @@ public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
45324560

45334561
public void findAccessibilityNodeInfosByViewTextClientThread(String text,
45344562
int accessibilityViewId, int interactionId,
4535-
IAccessibilityInteractionConnectionCallback callback) {
4563+
IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
4564+
long interrogatingTid) {
45364565
Message message = Message.obtain();
45374566
message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
45384567
SomeArgs args = mPool.acquire();
@@ -4541,7 +4570,17 @@ public void findAccessibilityNodeInfosByViewTextClientThread(String text,
45414570
args.argi2 = interactionId;
45424571
args.arg2 = callback;
45434572
message.obj = args;
4544-
sendMessage(message);
4573+
// If the interrogation is performed by the same thread as the main UI
4574+
// thread in this process, set the message as a static reference so
4575+
// after this call completes the same thread but in the interrogating
4576+
// client can handle the message to generate the result.
4577+
if (interrogatingPid == Process.myPid()
4578+
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
4579+
message.setTarget(ViewRootImpl.this);
4580+
AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
4581+
} else {
4582+
sendMessage(message);
4583+
}
45454584
}
45464585

45474586
public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
@@ -4594,7 +4633,8 @@ public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
45944633
}
45954634

45964635
public void performAccessibilityActionClientThread(int accessibilityId, int action,
4597-
int interactionId, IAccessibilityInteractionConnectionCallback callback) {
4636+
int interactionId, IAccessibilityInteractionConnectionCallback callback,
4637+
int interogatingPid, long interrogatingTid) {
45984638
Message message = Message.obtain();
45994639
message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
46004640
SomeArgs args = mPool.acquire();
@@ -4603,7 +4643,17 @@ public void performAccessibilityActionClientThread(int accessibilityId, int acti
46034643
args.argi3 = interactionId;
46044644
args.arg1 = callback;
46054645
message.obj = args;
4606-
sendMessage(message);
4646+
// If the interrogation is performed by the same thread as the main UI
4647+
// thread in this process, set the message as a static reference so
4648+
// after this call completes the same thread but in the interrogating
4649+
// client can handle the message to generate the result.
4650+
if (interogatingPid == Process.myPid()
4651+
&& interrogatingTid == Looper.getMainLooper().getThread().getId()) {
4652+
message.setTarget(ViewRootImpl.this);
4653+
AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
4654+
} else {
4655+
sendMessage(message);
4656+
}
46074657
}
46084658

46094659
public void perfromAccessibilityActionUiThread(Message message) {

0 commit comments

Comments
 (0)