Skip to content

Commit 57c7fd5

Browse files
committed
Fixing issues with the AccessibilityNodeInfo cache.
1. Before there were two caches one in the app process that kept track only the ids of infos that were given to a querying client and one in the querying client that holds the infos. This design requires precise sync between the caches. Doing that is somehow complicated since the app has cache for each window and it has to intercept all accessibility events from that window to manage the cache. Each app has to have a cache for each querying client. This approach would guarantee that no infos are fetched twice but due to its stateful nature and the two caches is tricky to implement and adds unnecessary complexity. Now there is only one cache in the client and the apps are stateless. The client is passing flags to the app that are a clue what nodes to prefetch. This approach may occasionally fetch a node twice but it is considerably simpler and stateless from the app perspective - there is only one cache. Fetching a node more than once does not cause much overhead compared to the IPC. Change-Id: Ia02f6fe4f82cff9a9c2e21f4a36747de0f414c6f
1 parent 0d04e24 commit 57c7fd5

File tree

9 files changed

+310
-176
lines changed

9 files changed

+310
-176
lines changed

core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@ interface IAccessibilityServiceConnection {
4242
* @param interactionId The id of the interaction for matching with the callback result.
4343
* @param callback Callback which to receive the result.
4444
* @param threadId The id of the calling thread.
45+
* @param prefetchFlags flags to guide prefetching.
4546
* @return The current window scale, where zero means a failure.
4647
*/
4748
float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
4849
long accessibilityNodeId, int interactionId,
49-
IAccessibilityInteractionConnectionCallback callback, long threadId);
50+
IAccessibilityInteractionConnectionCallback callback, long threadId, int prefetchFlags);
5051

5152
/**
5253
* Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by View text.

core/java/android/accessibilityservice/UiTestAutomationBridge.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public class UiTestAutomationBridge {
5757

5858
public static final int UNDEFINED = -1;
5959

60+
private static final int FIND_ACCESSIBILITY_NODE_INFO_PREFETCH_FLAGS =
61+
AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS
62+
| AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS
63+
| AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
64+
6065
private final Object mLock = new Object();
6166

6267
private volatile int mConnectionId = AccessibilityInteractionClient.NO_ID;
@@ -351,13 +356,15 @@ public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
351356
ensureValidConnection(connectionId);
352357
return AccessibilityInteractionClient.getInstance()
353358
.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
354-
accessibilityWindowId, accessibilityNodeId);
359+
accessibilityWindowId, accessibilityNodeId,
360+
FIND_ACCESSIBILITY_NODE_INFO_PREFETCH_FLAGS);
355361
}
356362

357363
/**
358364
* Finds an {@link AccessibilityNodeInfo} by View id in the active
359365
* window. The search is performed from the root node.
360366
*
367+
* @param viewId The id of a View.
361368
* @return The current window scale, where zero means a failure.
362369
*/
363370
public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId) {
@@ -373,6 +380,7 @@ public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int
373380
* {@link #ACTIVE_WINDOW_ID} to query the currently active window.
374381
* @param accessibilityNodeId A unique view id or virtual descendant id from
375382
* where to start the search. Use {@link #ROOT_NODE_ID} to start from the root.
383+
* @param viewId The id of a View.
376384
* @return The current window scale, where zero means a failure.
377385
*/
378386
public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int accessibilityWindowId,
@@ -460,7 +468,7 @@ public AccessibilityNodeInfo getRootAccessibilityNodeInfoInActiveWindow() {
460468
ensureValidConnection(connectionId);
461469
return AccessibilityInteractionClient.getInstance()
462470
.findAccessibilityNodeInfoByAccessibilityId(connectionId, ACTIVE_WINDOW_ID,
463-
ROOT_NODE_ID);
471+
ROOT_NODE_ID, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
464472
}
465473

466474
private void ensureValidConnection(int connectionId) {

core/java/android/view/ViewRootImpl.java

Lines changed: 212 additions & 87 deletions
Large diffs are not rendered by default.

core/java/android/view/accessibility/AccessibilityInteractionClient.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import android.util.Log;
2525
import android.util.LongSparseArray;
2626
import android.util.SparseArray;
27-
import android.view.AccessibilityNodeInfoCache;
2827

2928
import java.util.ArrayList;
3029
import java.util.Collections;
@@ -102,7 +101,7 @@ public final class AccessibilityInteractionClient
102101
// The connection cache is shared between all interrogating threads since
103102
// at any given time there is only one window allowing querying.
104103
private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache =
105-
AccessibilityNodeInfoCache.newSynchronizedAccessibilityNodeInfoCache();
104+
new AccessibilityNodeInfoCache();
106105

107106
/**
108107
* @return The client for the current thread.
@@ -159,10 +158,11 @@ public void setSameThreadMessage(Message message) {
159158
* where to start the search. Use
160159
* {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
161160
* to start from the root.
161+
* @param prefetchFlags flags to guide prefetching.
162162
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
163163
*/
164164
public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
165-
int accessibilityWindowId, long accessibilityNodeId) {
165+
int accessibilityWindowId, long accessibilityNodeId, int prefetchFlags) {
166166
try {
167167
IAccessibilityServiceConnection connection = getConnection(connectionId);
168168
if (connection != null) {
@@ -174,7 +174,7 @@ public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int conn
174174
final int interactionId = mInteractionIdCounter.getAndIncrement();
175175
final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
176176
accessibilityWindowId, accessibilityNodeId, interactionId, this,
177-
Thread.currentThread().getId());
177+
Thread.currentThread().getId(), prefetchFlags);
178178
// If the scale is zero the call has failed.
179179
if (windowScale > 0) {
180180
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(

core/java/android/view/accessibility/AccessibilityNodeInfo.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ public class AccessibilityNodeInfo implements Parcelable {
6161
/** @hide */
6262
public static final int ACTIVE_WINDOW_ID = UNDEFINED;
6363

64+
/** @hide */
65+
public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
66+
67+
/** @hide */
68+
public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
69+
70+
/** @hide */
71+
public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000003;
72+
6473
// Actions.
6574

6675
/**
@@ -181,7 +190,7 @@ public static long makeNodeId(int accessibilityViewId, int virtualDescendantId)
181190
private CharSequence mText;
182191
private CharSequence mContentDescription;
183192

184-
private SparseLongArray mChildIds = new SparseLongArray();
193+
private SparseLongArray mChildNodeIds = new SparseLongArray();
185194
private int mActions;
186195

187196
private int mConnectionId = UNDEFINED;
@@ -243,13 +252,22 @@ public int getWindowId() {
243252
return mWindowId;
244253
}
245254

255+
/**
256+
* @return The ids of the children.
257+
*
258+
* @hide
259+
*/
260+
public SparseLongArray getChildNodeIds() {
261+
return mChildNodeIds;
262+
}
263+
246264
/**
247265
* Gets the number of children.
248266
*
249267
* @return The child count.
250268
*/
251269
public int getChildCount() {
252-
return mChildIds.size();
270+
return mChildNodeIds.size();
253271
}
254272

255273
/**
@@ -271,9 +289,10 @@ public AccessibilityNodeInfo getChild(int index) {
271289
if (!canPerformRequestOverConnection(mSourceNodeId)) {
272290
return null;
273291
}
274-
final long childId = mChildIds.get(index);
292+
final long childId = mChildNodeIds.get(index);
275293
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
276-
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, childId);
294+
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
295+
childId, FLAG_PREFETCH_DESCENDANTS);
277296
}
278297

279298
/**
@@ -308,11 +327,11 @@ public void addChild(View child) {
308327
*/
309328
public void addChild(View root, int virtualDescendantId) {
310329
enforceNotSealed();
311-
final int index = mChildIds.size();
330+
final int index = mChildNodeIds.size();
312331
final int rootAccessibilityViewId =
313332
(root != null) ? root.getAccessibilityViewId() : UNDEFINED;
314333
final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
315-
mChildIds.put(index, childNodeId);
334+
mChildNodeIds.put(index, childNodeId);
316335
}
317336

318337
/**
@@ -408,7 +427,16 @@ public AccessibilityNodeInfo getParent() {
408427
}
409428
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
410429
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
411-
mWindowId, mParentNodeId);
430+
mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
431+
}
432+
433+
/**
434+
* @return The parent node id.
435+
*
436+
* @hide
437+
*/
438+
public long getParentNodeId() {
439+
return mParentNodeId;
412440
}
413441

414442
/**
@@ -1070,7 +1098,7 @@ public void writeToParcel(Parcel parcel, int flags) {
10701098
parcel.writeLong(mParentNodeId);
10711099
parcel.writeInt(mConnectionId);
10721100

1073-
SparseLongArray childIds = mChildIds;
1101+
SparseLongArray childIds = mChildNodeIds;
10741102
final int childIdsSize = childIds.size();
10751103
parcel.writeInt(childIdsSize);
10761104
for (int i = 0; i < childIdsSize; i++) {
@@ -1120,7 +1148,7 @@ private void init(AccessibilityNodeInfo other) {
11201148
mContentDescription = other.mContentDescription;
11211149
mActions= other.mActions;
11221150
mBooleanProperties = other.mBooleanProperties;
1123-
mChildIds = other.mChildIds.clone();
1151+
mChildNodeIds = other.mChildNodeIds.clone();
11241152
}
11251153

11261154
/**
@@ -1135,7 +1163,7 @@ private void initFromParcel(Parcel parcel) {
11351163
mParentNodeId = parcel.readLong();
11361164
mConnectionId = parcel.readInt();
11371165

1138-
SparseLongArray childIds = mChildIds;
1166+
SparseLongArray childIds = mChildNodeIds;
11391167
final int childrenSize = parcel.readInt();
11401168
for (int i = 0; i < childrenSize; i++) {
11411169
final long childId = parcel.readLong();
@@ -1171,7 +1199,7 @@ private void clear() {
11711199
mParentNodeId = ROOT_NODE_ID;
11721200
mWindowId = UNDEFINED;
11731201
mConnectionId = UNDEFINED;
1174-
mChildIds.clear();
1202+
mChildNodeIds.clear();
11751203
mBoundsInParent.set(0, 0, 0, 0);
11761204
mBoundsInScreen.set(0, 0, 0, 0);
11771205
mBooleanProperties = 0;
@@ -1249,7 +1277,7 @@ public String toString() {
12491277
builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
12501278
builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
12511279
builder.append("; mParentNodeId: " + mParentNodeId);
1252-
SparseLongArray childIds = mChildIds;
1280+
SparseLongArray childIds = mChildNodeIds;
12531281
builder.append("; childAccessibilityIds: [");
12541282
for (int i = 0, count = childIds.size(); i < count; i++) {
12551283
builder.append(childIds.valueAt(i));

core/java/android/view/AccessibilityNodeInfoCache.java renamed to core/java/android/view/accessibility/AccessibilityNodeInfoCache.java

Lines changed: 32 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17-
package android.view;
17+
package android.view.accessibility;
1818

1919
import android.os.Process;
2020
import android.util.Log;
2121
import android.util.LongSparseArray;
22-
import android.view.accessibility.AccessibilityEvent;
23-
import android.view.accessibility.AccessibilityNodeInfo;
2422

2523
/**
2624
* Simple cache for AccessibilityNodeInfos. The cache is mapping an
@@ -38,53 +36,11 @@ public class AccessibilityNodeInfoCache {
3836

3937
private static final boolean DEBUG = false;
4038

41-
/**
42-
* @return A new <strong>not synchronized</strong> AccessibilityNodeInfoCache.
43-
*/
44-
public static AccessibilityNodeInfoCache newAccessibilityNodeInfoCache() {
45-
return new AccessibilityNodeInfoCache();
46-
}
47-
48-
/**
49-
* @return A new <strong>synchronized</strong> AccessibilityNodeInfoCache.
50-
*/
51-
public static AccessibilityNodeInfoCache newSynchronizedAccessibilityNodeInfoCache() {
52-
return new AccessibilityNodeInfoCache() {
53-
private final Object mLock = new Object();
54-
55-
@Override
56-
public void clear() {
57-
synchronized(mLock) {
58-
super.clear();
59-
}
60-
}
61-
62-
@Override
63-
public AccessibilityNodeInfo get(long accessibilityNodeId) {
64-
synchronized(mLock) {
65-
return super.get(accessibilityNodeId);
66-
}
67-
}
68-
69-
@Override
70-
public void put(long accessibilityNodeId, AccessibilityNodeInfo info) {
71-
synchronized(mLock) {
72-
super.put(accessibilityNodeId, info);
73-
}
74-
}
75-
76-
@Override
77-
public void remove(long accessibilityNodeId) {
78-
synchronized(mLock) {
79-
super.remove(accessibilityNodeId);
80-
}
81-
}
82-
};
83-
}
39+
private final Object mLock = new Object();
8440

8541
private final LongSparseArray<AccessibilityNodeInfo> mCacheImpl;
8642

87-
private AccessibilityNodeInfoCache() {
43+
public AccessibilityNodeInfoCache() {
8844
if (ENABLED) {
8945
mCacheImpl = new LongSparseArray<AccessibilityNodeInfo>();
9046
} else {
@@ -124,13 +80,15 @@ public void onAccessibilityEvent(AccessibilityEvent event) {
12480
*/
12581
public AccessibilityNodeInfo get(long accessibilityNodeId) {
12682
if (ENABLED) {
127-
if (DEBUG) {
128-
AccessibilityNodeInfo info = mCacheImpl.get(accessibilityNodeId);
129-
Log.i(LOG_TAG, "Process: " + Process.myPid() +
130-
" get(" + accessibilityNodeId + ") = " + info);
131-
return info;
132-
} else {
133-
return mCacheImpl.get(accessibilityNodeId);
83+
synchronized(mLock) {
84+
if (DEBUG) {
85+
AccessibilityNodeInfo info = mCacheImpl.get(accessibilityNodeId);
86+
Log.i(LOG_TAG, "Process: " + Process.myPid() +
87+
" get(" + accessibilityNodeId + ") = " + info);
88+
return info;
89+
} else {
90+
return mCacheImpl.get(accessibilityNodeId);
91+
}
13492
}
13593
} else {
13694
return null;
@@ -145,11 +103,13 @@ public AccessibilityNodeInfo get(long accessibilityNodeId) {
145103
*/
146104
public void put(long accessibilityNodeId, AccessibilityNodeInfo info) {
147105
if (ENABLED) {
148-
if (DEBUG) {
149-
Log.i(LOG_TAG, "Process: " + Process.myPid()
150-
+ " put(" + accessibilityNodeId + ", " + info + ")");
106+
synchronized(mLock) {
107+
if (DEBUG) {
108+
Log.i(LOG_TAG, "Process: " + Process.myPid()
109+
+ " put(" + accessibilityNodeId + ", " + info + ")");
110+
}
111+
mCacheImpl.put(accessibilityNodeId, info);
151112
}
152-
mCacheImpl.put(accessibilityNodeId, info);
153113
}
154114
}
155115

@@ -161,7 +121,9 @@ public void put(long accessibilityNodeId, AccessibilityNodeInfo info) {
161121
*/
162122
public boolean containsKey(long accessibilityNodeId) {
163123
if (ENABLED) {
164-
return (mCacheImpl.indexOfKey(accessibilityNodeId) >= 0);
124+
synchronized(mLock) {
125+
return (mCacheImpl.indexOfKey(accessibilityNodeId) >= 0);
126+
}
165127
} else {
166128
return false;
167129
}
@@ -174,11 +136,13 @@ public boolean containsKey(long accessibilityNodeId) {
174136
*/
175137
public void remove(long accessibilityNodeId) {
176138
if (ENABLED) {
177-
if (DEBUG) {
178-
Log.i(LOG_TAG, "Process: " + Process.myPid()
179-
+ " remove(" + accessibilityNodeId + ")");
139+
synchronized(mLock) {
140+
if (DEBUG) {
141+
Log.i(LOG_TAG, "Process: " + Process.myPid()
142+
+ " remove(" + accessibilityNodeId + ")");
143+
}
144+
mCacheImpl.remove(accessibilityNodeId);
180145
}
181-
mCacheImpl.remove(accessibilityNodeId);
182146
}
183147
}
184148

@@ -187,10 +151,12 @@ public void remove(long accessibilityNodeId) {
187151
*/
188152
public void clear() {
189153
if (ENABLED) {
190-
if (DEBUG) {
191-
Log.i(LOG_TAG, "Process: " + Process.myPid() + "clear()");
154+
synchronized(mLock) {
155+
if (DEBUG) {
156+
Log.i(LOG_TAG, "Process: " + Process.myPid() + "clear()");
157+
}
158+
mCacheImpl.clear();
192159
}
193-
mCacheImpl.clear();
194160
}
195161
}
196162
}

0 commit comments

Comments
 (0)