Methods Summary |
---|
public void | addConnection(int connectionId, android.accessibilityservice.IAccessibilityServiceConnection connection)Adds a cached accessibility service connection.
synchronized (sConnectionCache) {
sConnectionCache.put(connectionId, connection);
}
|
private void | checkFindAccessibilityNodeInfoResultIntegrity(java.util.List infos)Checks whether the infos are a fully connected tree with no duplicates.
if (infos.size() == 0) {
return;
}
// Find the root node.
AccessibilityNodeInfo root = infos.get(0);
final int infoCount = infos.size();
for (int i = 1; i < infoCount; i++) {
for (int j = i; j < infoCount; j++) {
AccessibilityNodeInfo candidate = infos.get(j);
if (root.getParentNodeId() == candidate.getSourceNodeId()) {
root = candidate;
break;
}
}
}
if (root == null) {
Log.e(LOG_TAG, "No root.");
}
// Check for duplicates.
HashSet<AccessibilityNodeInfo> seen = new HashSet<>();
Queue<AccessibilityNodeInfo> fringe = new LinkedList<>();
fringe.add(root);
while (!fringe.isEmpty()) {
AccessibilityNodeInfo current = fringe.poll();
if (!seen.add(current)) {
Log.e(LOG_TAG, "Duplicate node.");
return;
}
final int childCount = current.getChildCount();
for (int i = 0; i < childCount; i++) {
final long childId = current.getChildId(i);
for (int j = 0; j < infoCount; j++) {
AccessibilityNodeInfo child = infos.get(j);
if (child.getSourceNodeId() == childId) {
fringe.add(child);
}
}
}
}
final int disconnectedCount = infos.size() - seen.size();
if (disconnectedCount > 0) {
Log.e(LOG_TAG, disconnectedCount + " Disconnected nodes.");
}
|
public void | clearCache()
sAccessibilityCache.clear();
|
private void | clearResultLocked()Clears the result state.
mInteractionId = -1;
mFindAccessibilityNodeInfoResult = null;
mFindAccessibilityNodeInfosResult = null;
mPerformAccessibilityActionResult = false;
|
private void | finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId)Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
if (info != null) {
info.setConnectionId(connectionId);
info.setSealed(true);
sAccessibilityCache.add(info);
}
|
private void | finalizeAndCacheAccessibilityNodeInfos(java.util.List infos, int connectionId)Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
if (infos != null) {
final int infosCount = infos.size();
for (int i = 0; i < infosCount; i++) {
AccessibilityNodeInfo info = infos.get(i);
finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
}
}
|
public AccessibilityNodeInfo | findAccessibilityNodeInfoByAccessibilityId(int connectionId, int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache, int prefetchFlags)Finds an {@link AccessibilityNodeInfo} by accessibility id.
if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0
&& (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) {
throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS"
+ " requires FLAG_PREFETCH_PREDECESSORS");
}
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
if (!bypassCache) {
AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode(
accessibilityWindowId, accessibilityNodeId);
if (cachedInfo != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Node cache hit");
}
return cachedInfo;
}
if (DEBUG) {
Log.i(LOG_TAG, "Node cache miss");
}
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
accessibilityWindowId, accessibilityNodeId, interactionId, this,
prefetchFlags, Thread.currentThread().getId());
// If the scale is zero the call has failed.
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
if (infos != null && !infos.isEmpty()) {
return infos.get(0);
}
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while calling remote"
+ " findAccessibilityNodeInfoByAccessibilityId", re);
}
return null;
|
public java.util.List | findAccessibilityNodeInfosByText(int connectionId, int accessibilityWindowId, long accessibilityNodeId, java.lang.String text)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 node whose accessibility id is
specified.
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.findAccessibilityNodeInfosByText(
accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
Thread.currentThread().getId());
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
return infos;
}
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling remote"
+ " findAccessibilityNodeInfosByViewText", re);
}
return Collections.emptyList();
|
public java.util.List | findAccessibilityNodeInfosByViewId(int connectionId, int accessibilityWindowId, long accessibilityNodeId, java.lang.String viewId)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.
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.findAccessibilityNodeInfosByViewId(
accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
Thread.currentThread().getId());
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
return infos;
}
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling remote"
+ " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
}
return Collections.emptyList();
|
public AccessibilityNodeInfo | findFocus(int connectionId, int accessibilityWindowId, long accessibilityNodeId, int focusType)Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the
specified focus type. The search is performed in the window whose id is specified
and starts from the node whose accessibility id is specified.
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.findFocus(accessibilityWindowId,
accessibilityNodeId, focusType, interactionId, this,
Thread.currentThread().getId());
if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
return info;
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling remote findFocus", re);
}
return null;
|
public AccessibilityNodeInfo | focusSearch(int connectionId, int accessibilityWindowId, long accessibilityNodeId, int direction)Finds the accessibility focused {@link android.view.accessibility.AccessibilityNodeInfo}.
The search is performed in the window whose id is specified and starts from the
node whose accessibility id is specified.
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.focusSearch(accessibilityWindowId,
accessibilityNodeId, direction, interactionId, this,
Thread.currentThread().getId());
if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
return info;
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
}
return null;
|
public android.accessibilityservice.IAccessibilityServiceConnection | getConnection(int connectionId)Gets a cached accessibility service connection.
synchronized (sConnectionCache) {
return sConnectionCache.get(connectionId);
}
|
private AccessibilityNodeInfo | getFindAccessibilityNodeInfoResultAndClear(int interactionId)Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
synchronized (mInstanceLock) {
final boolean success = waitForResultTimedLocked(interactionId);
AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null;
clearResultLocked();
return result;
}
|
private java.util.List | getFindAccessibilityNodeInfosResultAndClear(int interactionId)Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s.
synchronized (mInstanceLock) {
final boolean success = waitForResultTimedLocked(interactionId);
List<AccessibilityNodeInfo> result = null;
if (success) {
result = mFindAccessibilityNodeInfosResult;
} else {
result = Collections.emptyList();
}
clearResultLocked();
if (Build.IS_DEBUGGABLE && CHECK_INTEGRITY) {
checkFindAccessibilityNodeInfoResultIntegrity(result);
}
return result;
}
|
public static android.view.accessibility.AccessibilityInteractionClient | getInstance()
final long threadId = Thread.currentThread().getId();
return getInstanceForThread(threadId);
|
public static android.view.accessibility.AccessibilityInteractionClient | getInstanceForThread(long threadId)Note: We keep one instance per interrogating thread since
the instance contains state which can lead to undesired thread interleavings.
We do not have a thread local variable since other threads should be able to
look up the correct client knowing a thread id. See ViewRootImpl for details.
synchronized (sStaticLock) {
AccessibilityInteractionClient client = sClients.get(threadId);
if (client == null) {
client = new AccessibilityInteractionClient();
sClients.put(threadId, client);
}
return client;
}
|
private boolean | getPerformAccessibilityActionResultAndClear(int interactionId)Gets the result of a request to perform an accessibility action.
synchronized (mInstanceLock) {
final boolean success = waitForResultTimedLocked(interactionId);
final boolean result = success ? mPerformAccessibilityActionResult : false;
clearResultLocked();
return result;
}
|
public AccessibilityNodeInfo | getRootInActiveWindow(int connectionId)Gets the root {@link AccessibilityNodeInfo} in the currently active window.
return findAccessibilityNodeInfoByAccessibilityId(connectionId,
AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
|
private android.os.Message | getSameProcessMessageAndClear()Gets the message stored if the interacted and interacting
threads are the same.
synchronized (mInstanceLock) {
Message result = mSameThreadMessage;
mSameThreadMessage = null;
return result;
}
|
public AccessibilityWindowInfo | getWindow(int connectionId, int accessibilityWindowId)Gets the info for a window.
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
AccessibilityWindowInfo window = sAccessibilityCache.getWindow(
accessibilityWindowId);
if (window != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Window cache hit");
}
return window;
}
if (DEBUG) {
Log.i(LOG_TAG, "Window cache miss");
}
window = connection.getWindow(accessibilityWindowId);
if (window != null) {
sAccessibilityCache.addWindow(window);
return window;
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while calling remote getWindow", re);
}
return null;
|
public java.util.List | getWindows(int connectionId)Gets the info for all windows.
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
if (windows != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
}
return windows;
}
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache miss");
}
windows = connection.getWindows();
if (windows != null) {
final int windowCount = windows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = windows.get(i);
sAccessibilityCache.addWindow(window);
}
return windows;
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while calling remote getWindows", re);
}
return Collections.emptyList();
|
public void | onAccessibilityEvent(AccessibilityEvent event)
sAccessibilityCache.onAccessibilityEvent(event);
|
public boolean | performAccessibilityAction(int connectionId, int accessibilityWindowId, long accessibilityNodeId, int action, android.os.Bundle arguments)Performs an accessibility action on an {@link AccessibilityNodeInfo}.
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final boolean success = connection.performAccessibilityAction(
accessibilityWindowId, accessibilityNodeId, action, arguments,
interactionId, this, Thread.currentThread().getId());
if (success) {
return getPerformAccessibilityActionResultAndClear(interactionId);
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
}
return false;
|
public void | removeConnection(int connectionId)Removes a cached accessibility service connection.
synchronized (sConnectionCache) {
sConnectionCache.remove(connectionId);
}
|
public void | setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info, int interactionId){@inheritDoc}
synchronized (mInstanceLock) {
if (interactionId > mInteractionId) {
mFindAccessibilityNodeInfoResult = info;
mInteractionId = interactionId;
}
mInstanceLock.notifyAll();
}
|
public void | setFindAccessibilityNodeInfosResult(java.util.List infos, int interactionId){@inheritDoc}
synchronized (mInstanceLock) {
if (interactionId > mInteractionId) {
if (infos != null) {
// 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 = (Binder.getCallingPid() != Process.myPid());
if (!isIpcCall) {
mFindAccessibilityNodeInfosResult = new ArrayList<>(infos);
} else {
mFindAccessibilityNodeInfosResult = infos;
}
} else {
mFindAccessibilityNodeInfosResult = Collections.emptyList();
}
mInteractionId = interactionId;
}
mInstanceLock.notifyAll();
}
|
public void | setPerformAccessibilityActionResult(boolean succeeded, int interactionId){@inheritDoc}
synchronized (mInstanceLock) {
if (interactionId > mInteractionId) {
mPerformAccessibilityActionResult = succeeded;
mInteractionId = interactionId;
}
mInstanceLock.notifyAll();
}
|
public void | setSameThreadMessage(android.os.Message message)Sets the message to be processed if the interacted view hierarchy
and the interacting client are running in the same thread.
synchronized (mInstanceLock) {
mSameThreadMessage = message;
mInstanceLock.notifyAll();
}
|
private boolean | waitForResultTimedLocked(int interactionId)Waits up to a given bound for a result of a request and returns it.
long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS;
final long startTimeMillis = SystemClock.uptimeMillis();
while (true) {
try {
Message sameProcessMessage = getSameProcessMessageAndClear();
if (sameProcessMessage != null) {
sameProcessMessage.getTarget().handleMessage(sameProcessMessage);
}
if (mInteractionId == interactionId) {
return true;
}
if (mInteractionId > interactionId) {
return false;
}
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
if (waitTimeMillis <= 0) {
return false;
}
mInstanceLock.wait(waitTimeMillis);
} catch (InterruptedException ie) {
/* ignore */
}
}
|