Skip to content

Commit 79311c4

Browse files
committed
Speedup the accessibility window querying APIs and clean up.
1. Now when an interrogating client requires an AccessibilibtyNodeInfo we aggressively prefetch all the predecessors of that node and its descendants. The number of fetched nodes in one call is limited to keep the APIs responsive. The prefetched nodes infos are cached in the client process. The node info cache is invalidated partially or completely based on the fired accessibility events. For example, TYPE_WINDOW_STATE_CHANGED event clears the cache while TYPE_VIEW_FOCUSED removed the focused node from the cache, etc. Note that the cache is only for the currently active window. The ViewRootImple also keeps track of only the ids of the node infos it has sent to each querying process to avoid duplicating work. Usually only one process will query the screen content but we support the general case. Also all the caches are automatically invalidated so not additional bookkeeping is required. This simple strategy leads to 10X improving the speed of the querying APIs. 2. The Monkey and UI test automation framework were registering a raw event listener for accessibility events and hence perform connection and cache management in similar way to an AccessibilityService. This is fragile and requires the implementer to know internal framework stuff. Now the functionality required by the Monkey and the UI automation is encapsulated in a new UiTestAutomationBridge class. To enable this was requited some refactoring of AccessibilityService. 3. Removed the *doSomethiong*InActiveWindow methods from the AccessibilityInteractionClient and the AccessibilityInteractionConnection. The function of these methods is implemented by the not *InActiveWindow version while passing appropriate constants. 4. Updated the internal window Querying tests to use the new UiTestAutomationBridge. 5. If the ViewRootImple was not initialized the querying APIs of the IAccessibilityInteractionConnection implementation were returning immediately without calling the callback with null. This was causing the client side to wait until it times out. Now the client is notified as soon as the call fails. 6. Added a check to guarantee that Views with AccessibilityNodeProvider do not have children. bug:5879530 Change-Id: I3ee43718748fec6e570992c7073c8f6f1fc269b3
1 parent d4e34d6 commit 79311c4

15 files changed

+1111
-333
lines changed

core/java/android/accessibilityservice/AccessibilityService.java

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
package android.accessibilityservice;
1818

1919
import android.app.Service;
20+
import android.content.Context;
2021
import android.content.Intent;
2122
import android.os.IBinder;
23+
import android.os.Looper;
2224
import android.os.Message;
2325
import android.os.RemoteException;
2426
import android.util.Log;
@@ -218,10 +220,17 @@ public abstract class AccessibilityService extends Service {
218220

219221
private static final String LOG_TAG = "AccessibilityService";
220222

221-
private AccessibilityServiceInfo mInfo;
223+
interface Callbacks {
224+
public void onAccessibilityEvent(AccessibilityEvent event);
225+
public void onInterrupt();
226+
public void onServiceConnected();
227+
public void onSetConnectionId(int connectionId);
228+
}
222229

223230
private int mConnectionId;
224231

232+
private AccessibilityServiceInfo mInfo;
233+
225234
/**
226235
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
227236
*
@@ -282,27 +291,49 @@ private void sendServiceInfo() {
282291
*/
283292
@Override
284293
public final IBinder onBind(Intent intent) {
285-
return new IEventListenerWrapper(this);
294+
return new IEventListenerWrapper(this, getMainLooper(), new Callbacks() {
295+
@Override
296+
public void onServiceConnected() {
297+
AccessibilityService.this.onServiceConnected();
298+
}
299+
300+
@Override
301+
public void onInterrupt() {
302+
AccessibilityService.this.onInterrupt();
303+
}
304+
305+
@Override
306+
public void onAccessibilityEvent(AccessibilityEvent event) {
307+
AccessibilityService.this.onAccessibilityEvent(event);
308+
}
309+
310+
@Override
311+
public void onSetConnectionId( int connectionId) {
312+
mConnectionId = connectionId;
313+
}
314+
});
286315
}
287316

288317
/**
289318
* Implements the internal {@link IEventListener} interface to convert
290319
* incoming calls to it back to calls on an {@link AccessibilityService}.
291320
*/
292-
class IEventListenerWrapper extends IEventListener.Stub
321+
static class IEventListenerWrapper extends IEventListener.Stub
293322
implements HandlerCaller.Callback {
294323

324+
static final int NO_ID = -1;
325+
295326
private static final int DO_SET_SET_CONNECTION = 10;
296327
private static final int DO_ON_INTERRUPT = 20;
297328
private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
298329

299330
private final HandlerCaller mCaller;
300331

301-
private final AccessibilityService mTarget;
332+
private final Callbacks mCallback;
302333

303-
public IEventListenerWrapper(AccessibilityService context) {
304-
mTarget = context;
305-
mCaller = new HandlerCaller(context, this);
334+
public IEventListenerWrapper(Context context, Looper looper, Callbacks callback) {
335+
mCallback = callback;
336+
mCaller = new HandlerCaller(context, looper, this);
306337
}
307338

308339
public void setConnection(IAccessibilityServiceConnection connection, int connectionId) {
@@ -326,12 +357,13 @@ public void executeMessage(Message message) {
326357
case DO_ON_ACCESSIBILITY_EVENT :
327358
AccessibilityEvent event = (AccessibilityEvent) message.obj;
328359
if (event != null) {
329-
mTarget.onAccessibilityEvent(event);
360+
AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
361+
mCallback.onAccessibilityEvent(event);
330362
event.recycle();
331363
}
332364
return;
333365
case DO_ON_INTERRUPT :
334-
mTarget.onInterrupt();
366+
mCallback.onInterrupt();
335367
return;
336368
case DO_SET_SET_CONNECTION :
337369
final int connectionId = message.arg1;
@@ -340,12 +372,11 @@ public void executeMessage(Message message) {
340372
if (connection != null) {
341373
AccessibilityInteractionClient.getInstance().addConnection(connectionId,
342374
connection);
343-
mConnectionId = connectionId;
344-
mTarget.onServiceConnected();
375+
mCallback.onSetConnectionId(connectionId);
376+
mCallback.onServiceConnected();
345377
} else {
346378
AccessibilityInteractionClient.getInstance().removeConnection(connectionId);
347-
mConnectionId = AccessibilityInteractionClient.NO_ID;
348-
// TODO: Do we need a onServiceDisconnected callback?
379+
mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
349380
}
350381
return;
351382
default :

core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,13 @@ interface IAccessibilityServiceConnection {
3232
/**
3333
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
3434
*
35-
* @param accessibilityWindowId A unique window id.
36-
* @param accessibilityNodeId A unique view id or virtual descendant id.
35+
* @param accessibilityWindowId A unique window id. Use
36+
* {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
37+
* to query the currently active window.
38+
* @param accessibilityNodeId A unique view id or virtual descendant id from
39+
* where to start the search. Use
40+
* {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
41+
* to start from the root.
3742
* @param interactionId The id of the interaction for matching with the callback result.
3843
* @param callback Callback which to receive the result.
3944
* @param threadId The id of the calling thread.
@@ -46,57 +51,58 @@ interface IAccessibilityServiceConnection {
4651
/**
4752
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
4853
* insensitive containment. The search is performed in the window whose
49-
* id is specified and starts from the View whose accessibility id is
54+
* id is specified and starts from the node whose accessibility id is
5055
* specified.
5156
*
52-
* @param text The searched text.
53-
* @param accessibilityWindowId A unique window id.
57+
* @param accessibilityWindowId A unique window id. Use
58+
* {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
59+
* to query the currently active window.
5460
* @param accessibilityNodeId A unique view id or virtual descendant id from
55-
* where to start the search. Use {@link android.view.View#NO_ID} to start from the root.
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.
60-
*/
61-
float findAccessibilityNodeInfosByText(String text, int accessibilityWindowId,
62-
long accessibilityNodeId, int interractionId,
63-
IAccessibilityInteractionConnectionCallback callback, long threadId);
64-
65-
/**
66-
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
67-
* insensitive containment. The search is performed in the currently
68-
* active window and start from the root View in the window.
69-
*
61+
* where to start the search. Use
62+
* {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
63+
* to start from the root.
7064
* @param text The searched text.
71-
* @param accessibilityId The id of the view from which to start searching.
72-
* Use {@link android.view.View#NO_ID} to start from the root.
7365
* @param interactionId The id of the interaction for matching with the callback result.
7466
* @param callback Callback which to receive the result.
7567
* @param threadId The id of the calling thread.
7668
* @return The current window scale, where zero means a failure.
7769
*/
78-
float findAccessibilityNodeInfosByTextInActiveWindow(String text,
79-
int interactionId, IAccessibilityInteractionConnectionCallback callback,
70+
float findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
71+
String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
8072
long threadId);
8173

8274
/**
83-
* Finds an {@link AccessibilityNodeInfo} by View id. The search is performed
84-
* in the currently active window and starts from the root View in the window.
75+
* Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
76+
* the window whose id is specified and starts from the node whose accessibility
77+
* id is specified.
8578
*
79+
* @param accessibilityWindowId A unique window id. Use
80+
* {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
81+
* to query the currently active window.
82+
* @param accessibilityNodeId A unique view id or virtual descendant id from
83+
* where to start the search. Use
84+
* {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
85+
* to start from the root.
8686
* @param id The id of the node.
8787
* @param interactionId The id of the interaction for matching with the callback result.
8888
* @param callback Callback which to receive the result.
8989
* @param threadId The id of the calling thread.
9090
* @return The current window scale, where zero means a failure.
9191
*/
92-
float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId, int interactionId,
93-
IAccessibilityInteractionConnectionCallback callback, long threadId);
92+
float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId,
93+
int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
94+
long threadId);
9495

9596
/**
9697
* Performs an accessibility action on an {@link AccessibilityNodeInfo}.
9798
*
98-
* @param accessibilityWindowId The id of the window.
99-
* @param accessibilityNodeId A unique view id or virtual descendant id.
99+
* @param accessibilityWindowId A unique window id. Use
100+
* {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
101+
* to query the currently active window.
102+
* @param accessibilityNodeId A unique view id or virtual descendant id from
103+
* where to start the search. Use
104+
* {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
105+
* to start from the root.
100106
* @param action The action to perform.
101107
* @param interactionId The id of the interaction for matching with the callback result.
102108
* @param callback Callback which to receive the result.

0 commit comments

Comments
 (0)