summaryrefslogtreecommitdiffstats
path: root/core/java/android/view/accessibility
diff options
context:
space:
mode:
authorSvetoslav Ganov <svetoslavganov@google.com>2012-01-17 20:24:26 -0800
committerSvetoslav Ganov <svetoslavganov@google.com>2012-01-23 20:13:58 -0800
commit79311c4af8b54d3cd47ab37a120c648bfc990511 (patch)
tree839e7e5c99a3e482a3a86a64f6c11a318ee44ac7 /core/java/android/view/accessibility
parentd4e34d61d01222ff90684b9a1dc4f9c8be560e7c (diff)
downloadframeworks_base-79311c4af8b54d3cd47ab37a120c648bfc990511.zip
frameworks_base-79311c4af8b54d3cd47ab37a120c648bfc990511.tar.gz
frameworks_base-79311c4af8b54d3cd47ab37a120c648bfc990511.tar.bz2
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
Diffstat (limited to 'core/java/android/view/accessibility')
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java141
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java15
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java11
-rw-r--r--core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl8
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl4
5 files changed, 112 insertions, 67 deletions
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 95c070c..072fdd8 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -24,7 +24,9 @@ import android.os.SystemClock;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
+import android.view.AccessibilityNodeInfoCache;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -97,6 +99,11 @@ public final class AccessibilityInteractionClient
private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
new SparseArray<IAccessibilityServiceConnection>();
+ // The connection cache is shared between all interrogating threads since
+ // at any given time there is only one window allowing querying.
+ private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache =
+ AccessibilityNodeInfoCache.newSynchronizedAccessibilityNodeInfoCache();
+
/**
* @return The client for the current thread.
*/
@@ -145,7 +152,9 @@ public final class AccessibilityInteractionClient
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
*
* @param connectionId The id of a connection for interacting with the system.
- * @param accessibilityWindowId A unique window id.
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
* @param accessibilityNodeId A unique node accessibility id
* (accessibility view and virtual descendant id).
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
@@ -155,16 +164,22 @@ public final class AccessibilityInteractionClient
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
+ AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(accessibilityNodeId);
+ if (cachedInfo != null) {
+ return cachedInfo;
+ }
final int interactionId = mInteractionIdCounter.getAndIncrement();
final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
accessibilityWindowId, accessibilityNodeId, interactionId, this,
Thread.currentThread().getId());
// If the scale is zero the call has failed.
if (windowScale > 0) {
- AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+ List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
- return info;
+ finalizeAccessibilityNodeInfos(infos, connectionId, windowScale);
+ if (infos != null && !infos.isEmpty()) {
+ return infos.get(0);
+ }
}
} else {
if (DEBUG) {
@@ -181,22 +196,30 @@ public final class AccessibilityInteractionClient
}
/**
- * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed
- * in the currently active window and starts from the root View in the window.
+ * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
+ * the window whose id is specified and starts from the node whose accessibility
+ * id is specified.
*
* @param connectionId The id of a connection for interacting with the system.
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
+ * @param accessibilityNodeId A unique view id from where to start the search. Use
+ * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
+ * to start from the root.
* @param viewId The id of the view.
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
*/
- public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int connectionId,
- int viewId) {
+ public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int connectionId,
+ int accessibilityWindowId, long accessibilityNodeId, int viewId) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final float windowScale =
- connection.findAccessibilityNodeInfoByViewIdInActiveWindow(viewId,
- interactionId, this, Thread.currentThread().getId());
+ connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId,
+ accessibilityNodeId, viewId, interactionId, this,
+ Thread.currentThread().getId());
// If the scale is zero the call has failed.
if (windowScale > 0) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
@@ -220,64 +243,27 @@ public final class AccessibilityInteractionClient
/**
* Finds {@link AccessibilityNodeInfo}s by View text. The match is case
- * insensitive containment. The search is performed in the currently
- * active window and starts from the root View in the window.
- *
- * @param connectionId The id of a connection for interacting with the system.
- * @param text The searched text.
- * @return A list of found {@link AccessibilityNodeInfo}s.
- */
- public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByTextInActiveWindow(
- int connectionId, String text) {
- try {
- IAccessibilityServiceConnection connection = getConnection(connectionId);
- if (connection != null) {
- final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale =
- connection.findAccessibilityNodeInfosByTextInActiveWindow(text,
- interactionId, this, Thread.currentThread().getId());
- // If the scale is zero the call has failed.
- if (windowScale > 0) {
- List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
- interactionId);
- finalizeAccessibilityNodeInfos(infos, connectionId, windowScale);
- return infos;
- }
- } else {
- if (DEBUG) {
- Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
- }
- }
- } catch (RemoteException re) {
- if (DEBUG) {
- Log.w(LOG_TAG, "Error while calling remote"
- + " findAccessibilityNodeInfosByViewTextInActiveWindow", re);
- }
- }
- return null;
- }
-
- /**
- * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
* insensitive containment. The search is performed in the window whose
- * id is specified and starts from the View whose accessibility id is
+ * id is specified and starts from the node whose accessibility id is
* specified.
*
* @param connectionId The id of a connection for interacting with the system.
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
+ * @param accessibilityNodeId A unique view id from where to start the search. Use
+ * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID}
* @param text The searched text.
- * @param accessibilityWindowId A unique window id.
- * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id) from
- * where to start the search. Use {@link android.view.View#NO_ID} to start from the root.
* @return A list of found {@link AccessibilityNodeInfo}s.
*/
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId,
- String text, int accessibilityWindowId, long accessibilityNodeId) {
+ int accessibilityWindowId, long accessibilityNodeId, String text) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.findAccessibilityNodeInfosByText(text,
- accessibilityWindowId, accessibilityNodeId, interactionId, this,
+ final float windowScale = connection.findAccessibilityNodeInfosByText(
+ accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
Thread.currentThread().getId());
// If the scale is zero the call has failed.
if (windowScale > 0) {
@@ -304,7 +290,9 @@ public final class AccessibilityInteractionClient
* Performs an accessibility action on an {@link AccessibilityNodeInfo}.
*
* @param connectionId The id of a connection for interacting with the system.
- * @param accessibilityWindowId The id of the window.
+ * @param accessibilityWindowId A unique window id. Use
+ * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID}
+ * to query the currently active window.
* @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
* @param action The action to perform.
* @return Whether the action was performed.
@@ -319,7 +307,7 @@ public final class AccessibilityInteractionClient
accessibilityWindowId, accessibilityNodeId, action, interactionId, this,
Thread.currentThread().getId());
if (success) {
- return getPerformAccessibilityActionResult(interactionId);
+ return getPerformAccessibilityActionResultAndClear(interactionId);
}
} else {
if (DEBUG) {
@@ -334,6 +322,24 @@ public final class AccessibilityInteractionClient
return false;
}
+ public void clearCache() {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "clearCache()");
+ }
+ sAccessibilityNodeInfoCache.clear();
+ }
+
+ public void removeCachedNode(long accessibilityNodeId) {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "removeCachedNode(" + accessibilityNodeId +")");
+ }
+ sAccessibilityNodeInfoCache.remove(accessibilityNodeId);
+ }
+
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ sAccessibilityNodeInfoCache.onAccessibilityEvent(event);
+ }
+
/**
* Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
*
@@ -358,6 +364,9 @@ public final class AccessibilityInteractionClient
if (interactionId > mInteractionId) {
mFindAccessibilityNodeInfoResult = info;
mInteractionId = interactionId;
+ if (info != null) {
+ sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info);
+ }
}
mInstanceLock.notifyAll();
}
@@ -386,8 +395,20 @@ public final class AccessibilityInteractionClient
int interactionId) {
synchronized (mInstanceLock) {
if (interactionId > mInteractionId) {
- mFindAccessibilityNodeInfosResult = infos;
+ // If the call is not an IPC, i.e. it is made from the same process, we need to
+ // instantiate new result list to avoid passing internal instances to clients.
+ final boolean isIpcCall = (queryLocalInterface(getInterfaceDescriptor()) == null);
+ if (!isIpcCall) {
+ mFindAccessibilityNodeInfosResult = new ArrayList<AccessibilityNodeInfo>(infos);
+ } else {
+ mFindAccessibilityNodeInfosResult = infos;
+ }
mInteractionId = interactionId;
+ final int infoCount = infos.size();
+ for (int i = 0; i < infoCount; i ++) {
+ AccessibilityNodeInfo info = infos.get(i);
+ sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info);
+ }
}
mInstanceLock.notifyAll();
}
@@ -399,7 +420,7 @@ public final class AccessibilityInteractionClient
* @param interactionId The interaction id to match the result with the request.
* @return Whether the action was performed.
*/
- private boolean getPerformAccessibilityActionResult(int interactionId) {
+ private boolean getPerformAccessibilityActionResultAndClear(int interactionId) {
synchronized (mInstanceLock) {
final boolean success = waitForResultTimedLocked(interactionId);
final boolean result = success ? mPerformAccessibilityActionResult : false;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 6939c2c..d7d6792 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -380,8 +380,8 @@ public class AccessibilityNodeInfo implements Parcelable {
return Collections.emptyList();
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfosByText(mConnectionId, text, mWindowId,
- mSourceNodeId);
+ return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
+ text);
}
/**
@@ -903,6 +903,17 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Gets the id of the source node.
+ *
+ * @return The id.
+ *
+ * @hide
+ */
+ public long getSourceNodeId() {
+ return mSourceNodeId;
+ }
+
+ /**
* Sets if this instance is sealed.
*
* @param sealed Whether is sealed.
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 07aeb9a..b60f50e 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -564,6 +564,17 @@ public class AccessibilityRecord {
}
/**
+ * Gets the id of the source node.
+ *
+ * @return The id.
+ *
+ * @hide
+ */
+ public long getSourceNodeId() {
+ return mSourceNodeId;
+ }
+
+ /**
* Sets the unique id of the IAccessibilityServiceConnection over which
* this instance can send requests to the system.
*
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index a90c427..ae6869c 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -31,13 +31,13 @@ oneway interface IAccessibilityInteractionConnection {
IAccessibilityInteractionConnectionCallback callback,
int interrogatingPid, long interrogatingTid);
- void findAccessibilityNodeInfoByViewId(int id, int interactionId,
+ void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int id, int interactionId,
IAccessibilityInteractionConnectionCallback callback,
int interrogatingPid, long interrogatingTid);
- void findAccessibilityNodeInfosByText(String text, long accessibilityNodeId,
- int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int interrogatingPid, long interrogatingTid);
+ void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+ long interrogatingTid);
void performAccessibilityAction(long accessibilityNodeId, int action, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index c3794be..320c75d 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -49,5 +49,7 @@ interface IAccessibilityManager {
void removeAccessibilityInteractionConnection(IWindow windowToken);
- void registerEventListener(IEventListener client);
+ void registerUiTestAutomationService(IEventListener listener, in AccessibilityServiceInfo info);
+
+ void unregisterUiTestAutomationService(IEventListener listener);
}