summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java33
-rw-r--r--core/java/android/accessibilityservice/IEventListener.aidl2
-rw-r--r--core/java/android/util/SparseArray.java1
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java36
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java244
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java72
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java53
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java195
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java112
10 files changed, 425 insertions, 325 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 64a2755..211be52 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,8 +16,6 @@
package android.accessibilityservice;
-import com.android.internal.os.HandlerCaller;
-
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
@@ -25,8 +23,11 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
+import com.android.internal.os.HandlerCaller;
+
/**
* An accessibility service runs in the background and receives callbacks by the system
* when {@link AccessibilityEvent}s are fired. Such events denote some state transition
@@ -219,7 +220,7 @@ public abstract class AccessibilityService extends Service {
private AccessibilityServiceInfo mInfo;
- IAccessibilityServiceConnection mConnection;
+ private int mConnectionId;
/**
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
@@ -264,9 +265,11 @@ public abstract class AccessibilityService extends Service {
* AccessibilityManagerService.
*/
private void sendServiceInfo() {
- if (mInfo != null && mConnection != null) {
+ IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
+ if (mInfo != null && connection != null) {
try {
- mConnection.setServiceInfo(mInfo);
+ connection.setServiceInfo(mInfo);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
}
@@ -302,8 +305,9 @@ public abstract class AccessibilityService extends Service {
mCaller = new HandlerCaller(context, this);
}
- public void setConnection(IAccessibilityServiceConnection connection) {
- Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection);
+ public void setConnection(IAccessibilityServiceConnection connection, int connectionId) {
+ Message message = mCaller.obtainMessageIO(DO_SET_SET_CONNECTION, connectionId,
+ connection);
mCaller.sendMessage(message);
}
@@ -330,8 +334,19 @@ public abstract class AccessibilityService extends Service {
mTarget.onInterrupt();
return;
case DO_SET_SET_CONNECTION :
- mConnection = ((IAccessibilityServiceConnection) message.obj);
- mTarget.onServiceConnected();
+ final int connectionId = message.arg1;
+ IAccessibilityServiceConnection connection =
+ (IAccessibilityServiceConnection) message.obj;
+ if (connection != null) {
+ AccessibilityInteractionClient.getInstance().addConnection(connectionId,
+ connection);
+ mConnectionId = connectionId;
+ mTarget.onServiceConnected();
+ } else {
+ AccessibilityInteractionClient.getInstance().removeConnection(connectionId);
+ mConnectionId = AccessibilityInteractionClient.NO_ID;
+ // TODO: Do we need a onServiceDisconnected callback?
+ }
return;
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
diff --git a/core/java/android/accessibilityservice/IEventListener.aidl b/core/java/android/accessibilityservice/IEventListener.aidl
index 5b849f1..5536b3c 100644
--- a/core/java/android/accessibilityservice/IEventListener.aidl
+++ b/core/java/android/accessibilityservice/IEventListener.aidl
@@ -26,7 +26,7 @@ import android.view.accessibility.AccessibilityEvent;
*/
oneway interface IEventListener {
- void setConnection(in IAccessibilityServiceConnection connection);
+ void setConnection(in IAccessibilityServiceConnection connection, int connectionId);
void onAccessibilityEvent(in AccessibilityEvent event);
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 7cf4579..366abd3 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -134,6 +134,7 @@ public class SparseArray<E> implements Cloneable {
if (i != o) {
keys[o] = keys[i];
values[o] = val;
+ values[i] = null;
}
o++;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 34b4dcc..75b875a 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,7 +16,6 @@
package android.view.accessibility;
-import android.accessibilityservice.IAccessibilityServiceConnection;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -590,24 +589,6 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
}
/**
- * Sets the connection for interacting with the AccessibilityManagerService.
- *
- * @param connection The connection.
- *
- * @hide
- */
- @Override
- public void setConnection(IAccessibilityServiceConnection connection) {
- super.setConnection(connection);
- List<AccessibilityRecord> records = mRecords;
- final int recordCount = records.size();
- for (int i = 0; i < recordCount; i++) {
- AccessibilityRecord record = records.get(i);
- record.setConnection(connection);
- }
- }
-
- /**
* Sets if this instance is sealed.
*
* @param sealed Whether is sealed.
@@ -821,23 +802,19 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
*/
public void initFromParcel(Parcel parcel) {
- if (parcel.readInt() == 1) {
- mConnection = IAccessibilityServiceConnection.Stub.asInterface(
- parcel.readStrongBinder());
- }
- setSealed(parcel.readInt() == 1);
+ mSealed = (parcel.readInt() == 1);
mEventType = parcel.readInt();
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
mEventTime = parcel.readLong();
+ mConnectionId = parcel.readInt();
readAccessibilityRecordFromParcel(this, parcel);
// Read the records.
final int recordCount = parcel.readInt();
for (int i = 0; i < recordCount; i++) {
AccessibilityRecord record = AccessibilityRecord.obtain();
- // Do this to write the connection only once.
- record.setConnection(mConnection);
readAccessibilityRecordFromParcel(record, parcel);
+ record.mConnectionId = mConnectionId;
mRecords.add(record);
}
}
@@ -875,16 +852,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* {@inheritDoc}
*/
public void writeToParcel(Parcel parcel, int flags) {
- if (mConnection == null) {
- parcel.writeInt(0);
- } else {
- parcel.writeInt(1);
- parcel.writeStrongBinder(mConnection.asBinder());
- }
parcel.writeInt(isSealed() ? 1 : 0);
parcel.writeInt(mEventType);
TextUtils.writeToParcel(mPackageName, parcel, 0);
parcel.writeLong(mEventTime);
+ parcel.writeInt(mConnectionId);
writeAccessibilityRecordToParcel(this, parcel, flags);
// Write the records.
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 5f2990a..9f4f172 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -16,12 +16,12 @@
package android.view.accessibility;
-import android.accessibilityservice.IAccessibilityServiceConnection;
import android.graphics.Rect;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.util.LongSparseArray;
+import android.util.Log;
+import android.util.SparseArray;
import java.util.Collections;
import java.util.List;
@@ -62,6 +62,12 @@ import java.util.concurrent.atomic.AtomicInteger;
public final class AccessibilityInteractionClient
extends IAccessibilityInteractionConnectionCallback.Stub {
+ public static final int NO_ID = -1;
+
+ private static final String LOG_TAG = "AccessibilityInteractionClient";
+
+ private static final boolean DEBUG = false;
+
private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
private static final Object sStaticLock = new Object();
@@ -85,6 +91,9 @@ public final class AccessibilityInteractionClient
private final Rect mTempBounds = new Rect();
+ private final SparseArray<IAccessibilityServiceConnection> mConnectionCache =
+ new SparseArray<IAccessibilityServiceConnection>();
+
/**
* @return The client for the current thread.
*/
@@ -132,29 +141,38 @@ public final class AccessibilityInteractionClient
/**
* Finds an {@link AccessibilityNodeInfo} by accessibility id.
*
- * @param connection A connection for interacting with the system.
+ * @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id.
* @param accessibilityNodeId A unique node accessibility id
* (accessibility view and virtual descendant id).
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
*/
- public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
- IAccessibilityServiceConnection connection, int accessibilityWindowId,
- long accessibilityNodeId) {
+ public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
+ int accessibilityWindowId, int accessibilityNodeId) {
try {
- 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(
- interactionId);
- finalizeAccessibilityNodeInfo(info, connection, windowScale);
- return info;
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ 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(
+ interactionId);
+ finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
+ return info;
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
}
} catch (RemoteException re) {
- /* ignore */
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfoByAccessibilityId", re);
+ }
}
return null;
}
@@ -163,25 +181,36 @@ 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.
*
- * @param connection A connection for interacting with the system.
+ * @param connectionId The id of a connection for interacting with the system.
* @param viewId The id of the view.
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
*/
- public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(
- IAccessibilityServiceConnection connection, int viewId) {
+ public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int connectionId,
+ int viewId) {
try {
- final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.findAccessibilityNodeInfoByViewIdInActiveWindow(
- viewId, interactionId, this, Thread.currentThread().getId());
- // If the scale is zero the call has failed.
- if (windowScale > 0) {
- AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
- interactionId);
- finalizeAccessibilityNodeInfo(info, connection, windowScale);
- return info;
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ final float windowScale =
+ connection.findAccessibilityNodeInfoByViewIdInActiveWindow(viewId,
+ interactionId, this, Thread.currentThread().getId());
+ // If the scale is zero the call has failed.
+ if (windowScale > 0) {
+ AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+ interactionId);
+ finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
+ return info;
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
}
} catch (RemoteException re) {
- /* ignore */
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
+ }
}
return null;
}
@@ -191,28 +220,36 @@ public final class AccessibilityInteractionClient
* insensitive containment. The search is performed in the currently
* active window and starts from the root View in the window.
*
- * @param connection A connection for interacting with the system.
+ * @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(
- IAccessibilityServiceConnection connection, String text) {
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(
+ int connectionId, String text) {
try {
- 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);
- if (infos == null) {
- return Collections.emptyList();
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ final float windowScale =
+ connection.findAccessibilityNodeInfosByViewTextInActiveWindow(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);
}
- finalizeAccessibilityNodeInfos(infos, connection, windowScale);
- return infos;
}
} catch (RemoteException re) {
- /* ignore */
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfosByViewTextInActiveWindow", re);
+ }
}
return null;
}
@@ -223,30 +260,39 @@ public final class AccessibilityInteractionClient
* id is specified and starts from the View whose accessibility id is
* specified.
*
- * @param connection A connection for interacting with the system.
+ * @param connectionId The id of a connection for interacting with the system.
* @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(
- IAccessibilityServiceConnection connection, String text, int accessibilityWindowId,
- long accessibilityNodeId) {
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(int connectionId,
+ String text, int accessibilityWindowId, int accessibilityNodeId) {
try {
- final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.findAccessibilityNodeInfosByText(text,
- accessibilityWindowId, accessibilityNodeId, interactionId, this,
- Thread.currentThread().getId());
- // If the scale is zero the call has failed.
- if (windowScale > 0) {
- List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
- interactionId);
- finalizeAccessibilityNodeInfos(infos, connection, windowScale);
- return infos;
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ final float windowScale = connection.findAccessibilityNodeInfosByViewText(text,
+ accessibilityWindowId, accessibilityNodeId, 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) {
- /* ignore */
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Error while calling remote"
+ + " findAccessibilityNodeInfosByViewText", re);
+ }
}
return Collections.emptyList();
}
@@ -254,24 +300,33 @@ public final class AccessibilityInteractionClient
/**
* Performs an accessibility action on an {@link AccessibilityNodeInfo}.
*
- * @param connection A connection for interacting with the system.
+ * @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId The id of the window.
* @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
* @param action The action to perform.
* @return Whether the action was performed.
*/
- public boolean performAccessibilityAction(IAccessibilityServiceConnection connection,
- int accessibilityWindowId, long accessibilityNodeId, int action) {
+ public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId,
+ int accessibilityNodeId, int action) {
try {
- final int interactionId = mInteractionIdCounter.getAndIncrement();
- final boolean success = connection.performAccessibilityAction(
- accessibilityWindowId, accessibilityNodeId, action, interactionId, this,
- Thread.currentThread().getId());
- if (success) {
- return getPerformAccessibilityActionResult(interactionId);
+ IAccessibilityServiceConnection connection = getConnection(connectionId);
+ if (connection != null) {
+ final int interactionId = mInteractionIdCounter.getAndIncrement();
+ final boolean success = connection.performAccessibilityAction(
+ accessibilityWindowId, accessibilityNodeId, action, interactionId, this,
+ Thread.currentThread().getId());
+ if (success) {
+ return getPerformAccessibilityActionResult(interactionId);
+ }
+ } else {
+ if (DEBUG) {
+ Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+ }
}
} catch (RemoteException re) {
- /* ignore */
+ if (DEBUG) {
+ Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
+ }
}
return false;
}
@@ -431,14 +486,14 @@ public final class AccessibilityInteractionClient
* Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
*
* @param info The info.
- * @param connection The current connection to the system.
+ * @param connectionId The id of the connection to the system.
* @param windowScale The source window compatibility scale.
*/
- private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info,
- IAccessibilityServiceConnection connection, float windowScale) {
+ private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId,
+ float windowScale) {
if (info != null) {
applyCompatibilityScaleIfNeeded(info, windowScale);
- info.setConnection(connection);
+ info.setConnectionId(connectionId);
info.setSealed(true);
}
}
@@ -447,16 +502,16 @@ public final class AccessibilityInteractionClient
* Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
*
* @param infos The {@link AccessibilityNodeInfo}s.
- * @param connection The current connection to the system.
+ * @param connectionId The id of the connection to the system.
* @param windowScale The source window compatibility scale.
*/
private void finalizeAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
- IAccessibilityServiceConnection connection, float windowScale) {
+ int connectionId, float windowScale) {
if (infos != null) {
final int infosCount = infos.size();
for (int i = 0; i < infosCount; i++) {
AccessibilityNodeInfo info = infos.get(i);
- finalizeAccessibilityNodeInfo(info, connection, windowScale);
+ finalizeAccessibilityNodeInfo(info, connectionId, windowScale);
}
}
}
@@ -474,4 +529,39 @@ public final class AccessibilityInteractionClient
return result;
}
}
+
+ /**
+ * Gets a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ * @return The cached connection if such.
+ */
+ public IAccessibilityServiceConnection getConnection(int connectionId) {
+ synchronized (mConnectionCache) {
+ return mConnectionCache.get(connectionId);
+ }
+ }
+
+ /**
+ * Adds a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ * @param connection The connection.
+ */
+ public void addConnection(int connectionId, IAccessibilityServiceConnection connection) {
+ synchronized (mConnectionCache) {
+ mConnectionCache.put(connectionId, connection);
+ }
+ }
+
+ /**
+ * Removes a cached accessibility service connection.
+ *
+ * @param connectionId The connection id.
+ */
+ public void removeConnection(int connectionId) {
+ synchronized (mConnectionCache) {
+ mConnectionCache.remove(connectionId);
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index d65c0a7..6939c2c 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -16,7 +16,6 @@
package android.view.accessibility;
-import android.accessibilityservice.IAccessibilityServiceConnection;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -53,6 +52,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private static final boolean DEBUG = false;
+ private static final int UNDEFINED = -1;
+
// Actions.
/**
@@ -160,9 +161,10 @@ public class AccessibilityNodeInfo implements Parcelable {
private boolean mSealed;
// Data.
- private long mSourceNodeId = makeNodeId(View.NO_ID, View.NO_ID);
- private int mWindowId = View.NO_ID;
- private long mParentNodeId = makeNodeId(View.NO_ID, View.NO_ID);
+ private int mWindowId = UNDEFINED;
+ private long mSourceNodeId = makeNodeId(UNDEFINED, UNDEFINED);
+ private long mParentNodeId = makeNodeId(UNDEFINED, UNDEFINED);
+
private int mBooleanProperties;
private final Rect mBoundsInParent = new Rect();
private final Rect mBoundsInScreen = new Rect();
@@ -175,7 +177,7 @@ public class AccessibilityNodeInfo implements Parcelable {
private SparseLongArray mChildIds = new SparseLongArray();
private int mActions;
- private IAccessibilityServiceConnection mConnection;
+ private int mConnectionId = UNDEFINED;
/**
* Hide constructor from clients.
@@ -195,7 +197,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param source The info source.
*/
public void setSource(View source) {
- setSource(source, View.NO_ID);
+ setSource(source, UNDEFINED);
}
/**
@@ -219,9 +221,9 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
public void setSource(View root, int virtualDescendantId) {
enforceNotSealed();
- mWindowId = (root != null) ? root.getAccessibilityWindowId() : View.NO_ID;
+ mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : View.NO_ID;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -264,7 +266,7 @@ public class AccessibilityNodeInfo implements Parcelable {
}
final long childId = mChildIds.get(index);
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, mWindowId, childId);
+ return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId, childId);
}
/**
@@ -280,7 +282,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void addChild(View child) {
- addChild(child, View.NO_ID);
+ addChild(child, UNDEFINED);
}
/**
@@ -301,7 +303,7 @@ public class AccessibilityNodeInfo implements Parcelable {
enforceNotSealed();
final int index = mChildIds.size();
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : View.NO_ID;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
mChildIds.put(index, childNodeId);
}
@@ -355,7 +357,7 @@ public class AccessibilityNodeInfo implements Parcelable {
return false;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.performAccessibilityAction(mConnection, mWindowId, mSourceNodeId, action);
+ return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId, action);
}
/**
@@ -378,7 +380,8 @@ public class AccessibilityNodeInfo implements Parcelable {
return Collections.emptyList();
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfosByText(mConnection, text, mWindowId, mSourceNodeId);
+ return client.findAccessibilityNodeInfosByText(mConnectionId, text, mWindowId,
+ mSourceNodeId);
}
/**
@@ -397,7 +400,7 @@ public class AccessibilityNodeInfo implements Parcelable {
return null;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfoByAccessibilityId(mConnection,
+ return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
mWindowId, mParentNodeId);
}
@@ -414,7 +417,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void setParent(View parent) {
- setParent(parent, View.NO_ID);
+ setParent(parent, UNDEFINED);
}
/**
@@ -439,7 +442,7 @@ public class AccessibilityNodeInfo implements Parcelable {
public void setParent(View root, int virtualDescendantId) {
enforceNotSealed();
final int rootAccessibilityViewId =
- (root != null) ? root.getAccessibilityViewId() : View.NO_ID;
+ (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
}
@@ -880,15 +883,16 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
- * Sets the connection for interacting with the system.
+ * Sets the unique id of the IAccessibilityServiceConnection over which
+ * this instance can send requests to the system.
*
- * @param connection The client token.
+ * @param connectionId The connection id.
*
* @hide
*/
- public final void setConnection(IAccessibilityServiceConnection connection) {
+ public void setConnectionId(int connectionId) {
enforceNotSealed();
- mConnection = connection;
+ mConnectionId = connectionId;
}
/**
@@ -1042,16 +1046,11 @@ public class AccessibilityNodeInfo implements Parcelable {
* </p>
*/
public void writeToParcel(Parcel parcel, int flags) {
- if (mConnection == null) {
- parcel.writeInt(0);
- } else {
- parcel.writeInt(1);
- parcel.writeStrongBinder(mConnection.asBinder());
- }
parcel.writeInt(isSealed() ? 1 : 0);
parcel.writeLong(mSourceNodeId);
parcel.writeInt(mWindowId);
parcel.writeLong(mParentNodeId);
+ parcel.writeInt(mConnectionId);
SparseLongArray childIds = mChildIds;
final int childIdsSize = childIds.size();
@@ -1091,10 +1090,10 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
private void init(AccessibilityNodeInfo other) {
mSealed = other.mSealed;
- mConnection = other.mConnection;
mSourceNodeId = other.mSourceNodeId;
mParentNodeId = other.mParentNodeId;
mWindowId = other.mWindowId;
+ mConnectionId = other.mConnectionId;
mBoundsInParent.set(other.mBoundsInParent);
mBoundsInScreen.set(other.mBoundsInScreen);
mPackageName = other.mPackageName;
@@ -1112,14 +1111,11 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
*/
private void initFromParcel(Parcel parcel) {
- if (parcel.readInt() == 1) {
- mConnection = IAccessibilityServiceConnection.Stub.asInterface(
- parcel.readStrongBinder());
- }
mSealed = (parcel.readInt() == 1);
mSourceNodeId = parcel.readLong();
mWindowId = parcel.readInt();
mParentNodeId = parcel.readLong();
+ mConnectionId = parcel.readInt();
SparseLongArray childIds = mChildIds;
final int childrenSize = parcel.readInt();
@@ -1153,10 +1149,10 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
private void clear() {
mSealed = false;
- mConnection = null;
- mSourceNodeId = makeNodeId(View.NO_ID, View.NO_ID);
- mParentNodeId = makeNodeId(View.NO_ID, View.NO_ID);
- mWindowId = View.NO_ID;
+ mSourceNodeId = makeNodeId(UNDEFINED, UNDEFINED);
+ mParentNodeId = makeNodeId(UNDEFINED, UNDEFINED);
+ mWindowId = UNDEFINED;
+ mConnectionId = UNDEFINED;
mChildIds.clear();
mBoundsInParent.set(0, 0, 0, 0);
mBoundsInScreen.set(0, 0, 0, 0);
@@ -1190,9 +1186,9 @@ public class AccessibilityNodeInfo implements Parcelable {
}
private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
- return (mWindowId != View.NO_ID
- && getAccessibilityViewId(accessibilityNodeId) != View.NO_ID
- && mConnection != null);
+ return (mWindowId != UNDEFINED
+ && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
+ && mConnectionId != UNDEFINED);
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index f3ca0d5..07aeb9a 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -16,7 +16,6 @@
package android.view.accessibility;
-import android.accessibilityservice.IAccessibilityServiceConnection;
import android.os.Parcelable;
import android.view.View;
@@ -78,8 +77,8 @@ public class AccessibilityRecord {
int mAddedCount= UNDEFINED;
int mRemovedCount = UNDEFINED;
- long mSourceNodeId = AccessibilityNodeInfo.makeNodeId(View.NO_ID, View.NO_ID);
- int mSourceWindowId = View.NO_ID;
+ long mSourceNodeId = AccessibilityNodeInfo.makeNodeId(UNDEFINED, UNDEFINED);
+ int mSourceWindowId = UNDEFINED;
CharSequence mClassName;
CharSequence mContentDescription;
@@ -87,7 +86,8 @@ public class AccessibilityRecord {
Parcelable mParcelableData;
final List<CharSequence> mText = new ArrayList<CharSequence>();
- IAccessibilityServiceConnection mConnection;
+
+ int mConnectionId = UNDEFINED;
/*
* Hide constructor.
@@ -103,7 +103,7 @@ public class AccessibilityRecord {
* @throws IllegalStateException If called from an AccessibilityService.
*/
public void setSource(View source) {
- setSource(source, View.NO_ID);
+ setSource(source, UNDEFINED);
}
/**
@@ -122,8 +122,8 @@ public class AccessibilityRecord {
*/
public void setSource(View root, int virtualDescendantId) {
enforceNotSealed();
- mSourceWindowId = (root != null) ? root.getAccessibilityWindowId() : View.NO_ID;
- final int rootViewId = (root != null) ? root.getAccessibilityViewId() : View.NO_ID;
+ mSourceWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
+ final int rootViewId = (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId);
}
@@ -133,34 +133,21 @@ public class AccessibilityRecord {
* <strong>Note:</strong> It is a client responsibility to recycle the received info
* by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()}
* to avoid creating of multiple instances.
- *
* </p>
* @return The info of the source.
*/
public AccessibilityNodeInfo getSource() {
enforceSealed();
- if (mConnection == null || mSourceWindowId == View.NO_ID
- || AccessibilityNodeInfo.getAccessibilityViewId(mSourceNodeId) == View.NO_ID) {
+ if (mConnectionId == UNDEFINED || mSourceWindowId == UNDEFINED
+ || AccessibilityNodeInfo.getAccessibilityViewId(mSourceNodeId) == UNDEFINED) {
return null;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
- return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, mSourceWindowId,
+ return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId,
mSourceNodeId);
}
/**
- * Sets the connection for interacting with the AccessibilityManagerService.
- *
- * @param connection The connection.
- *
- * @hide
- */
- public void setConnection(IAccessibilityServiceConnection connection) {
- enforceNotSealed();
- mConnection = connection;
- }
-
- /**
* Gets the id of the window from which the event comes from.
*
* @return The window id.
@@ -577,6 +564,19 @@ public class AccessibilityRecord {
}
/**
+ * Sets the unique id of the IAccessibilityServiceConnection over which
+ * this instance can send requests to the system.
+ *
+ * @param connectionId The connection id.
+ *
+ * @hide
+ */
+ public void setConnectionId(int connectionId) {
+ enforceNotSealed();
+ mConnectionId = connectionId;
+ }
+
+ /**
* Sets if this instance is sealed.
*
* @param sealed Whether is sealed.
@@ -724,7 +724,7 @@ public class AccessibilityRecord {
mText.addAll(record.mText);
mSourceWindowId = record.mSourceWindowId;
mSourceNodeId = record.mSourceNodeId;
- mConnection = record.mConnection;
+ mConnectionId = record.mConnectionId;
}
/**
@@ -748,8 +748,9 @@ public class AccessibilityRecord {
mBeforeText = null;
mParcelableData = null;
mText.clear();
- mSourceNodeId = AccessibilityNodeInfo.makeNodeId(View.NO_ID, View.NO_ID);
- mSourceWindowId = View.NO_ID;
+ mSourceNodeId = AccessibilityNodeInfo.makeNodeId(UNDEFINED, UNDEFINED);
+ mSourceWindowId = UNDEFINED;
+ mConnectionId = UNDEFINED;
}
@Override
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index c621ff6..c3794be 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -49,5 +49,5 @@ interface IAccessibilityManager {
void removeAccessibilityInteractionConnection(IWindow windowToken);
- IAccessibilityServiceConnection registerEventListener(IEventListener client);
+ void registerEventListener(IEventListener client);
}
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
index 1ed54cb..a41a9a3 100644
--- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -26,11 +26,11 @@ import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.IAccessibilityManager;
import com.android.frameworks.coretests.R;
@@ -54,28 +54,31 @@ public class InterrogationActivityTest
// Timeout before give up wait for the system to process an accessibility setting change.
private static final int TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING = 2000;
+ // Timeout for the accessibility state of an Activity to be fully initialized.
+ private static final int TIMEOUT_ACCESSIBLITY_STATE_INITIALIZED_MILLIS = 100;
+
// Handle to a connection to the AccessibilityManagerService
- private static IAccessibilityServiceConnection sConnection;
+ private static int sConnectionId = View.NO_ID;
// The last received accessibility event
- private static volatile AccessibilityEvent sLastFocusAccessibilityEvent;
+ private volatile AccessibilityEvent mLastAccessibilityEvent;
public InterrogationActivityTest() {
super(InterrogationActivity.class);
}
+ @Override
+ public void setUp() throws Exception {
+ ensureConnection();
+ bringUpActivityWithInitalizedAccessbility();
+ }
+
@LargeTest
public void testFindAccessibilityNodeInfoByViewId() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertNotNull(button);
assertEquals(0, button.getChildCount());
@@ -120,15 +123,9 @@ public class InterrogationActivityTest
public void testFindAccessibilityNodeInfoByViewText() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view by text
List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfosByTextInActiveWindow(connection, "butto");
+ .findAccessibilityNodeInfosByTextInActiveWindow(sConnectionId, "butto");
assertEquals(9, buttons.size());
} finally {
if (DEBUG) {
@@ -143,15 +140,11 @@ public class InterrogationActivityTest
public void testFindAccessibilityNodeInfoByViewTextContentDescription() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
+ bringUpActivityWithInitalizedAccessbility();
// find a view by text
List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfosByTextInActiveWindow(connection,
+ .findAccessibilityNodeInfosByTextInActiveWindow(sConnectionId,
"contentDescription");
assertEquals(1, buttons.size());
} finally {
@@ -167,12 +160,6 @@ public class InterrogationActivityTest
public void testTraverseAllViews() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// make list of expected nodes
List<String> classNameAndTextList = new ArrayList<String>();
classNameAndTextList.add("android.widget.LinearLayout");
@@ -190,7 +177,7 @@ public class InterrogationActivityTest
classNameAndTextList.add("android.widget.ButtonButton9");
AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.root);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.root);
assertNotNull("We must find the existing root.", root);
Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
@@ -227,23 +214,17 @@ public class InterrogationActivityTest
public void testPerformAccessibilityActionFocus() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not focused
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isFocused());
// focus the view
assertTrue(button.performAction(ACTION_FOCUS));
// find the view again and make sure it is focused
- button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ button = AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertTrue(button.isFocused());
} finally {
if (DEBUG) {
@@ -257,15 +238,9 @@ public class InterrogationActivityTest
public void testPerformAccessibilityActionClearFocus() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not focused
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isFocused());
// focus the view
@@ -273,7 +248,7 @@ public class InterrogationActivityTest
// find the view again and make sure it is focused
button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertTrue(button.isFocused());
// unfocus the view
@@ -281,7 +256,7 @@ public class InterrogationActivityTest
// find the view again and make sure it is not focused
button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isFocused());
} finally {
if (DEBUG) {
@@ -296,15 +271,9 @@ public class InterrogationActivityTest
public void testPerformAccessibilityActionSelect() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not selected
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isSelected());
// select the view
@@ -312,7 +281,7 @@ public class InterrogationActivityTest
// find the view again and make sure it is selected
button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertTrue(button.isSelected());
} finally {
if (DEBUG) {
@@ -326,15 +295,9 @@ public class InterrogationActivityTest
public void testPerformAccessibilityActionClearSelection() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not selected
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isSelected());
// select the view
@@ -342,15 +305,15 @@ public class InterrogationActivityTest
// find the view again and make sure it is selected
button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertTrue(button.isSelected());
// unselect the view
assertTrue(button.performAction(ACTION_CLEAR_SELECTION));
// find the view again and make sure it is not selected
- button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ button = AccessibilityInteractionClient.getInstance()
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isSelected());
} finally {
if (DEBUG) {
@@ -365,30 +328,24 @@ public class InterrogationActivityTest
public void testAccessibilityEventGetSource() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not focused
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
assertFalse(button.isSelected());
// focus the view
assertTrue(button.performAction(ACTION_FOCUS));
- synchronized (sConnection) {
+ synchronized (this) {
try {
- sConnection.wait(500);
+ wait(TIMEOUT_ACCESSIBLITY_STATE_INITIALIZED_MILLIS);
} catch (InterruptedException ie) {
/* ignore */
}
}
// check that last event source
- AccessibilityNodeInfo source = sLastFocusAccessibilityEvent.getSource();
+ AccessibilityNodeInfo source = mLastAccessibilityEvent.getSource();
assertNotNull(source);
// bounds
@@ -430,15 +387,9 @@ public class InterrogationActivityTest
public void testObjectContract() throws Exception {
final long startTimeMillis = SystemClock.uptimeMillis();
try {
- // hook into the system first
- IAccessibilityServiceConnection connection = getConnection();
-
- // bring up the activity
- getActivity();
-
// find a view and make sure it is not focused
AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewIdInActiveWindow(connection, R.id.button5);
+ .findAccessibilityNodeInfoByViewIdInActiveWindow(sConnectionId, R.id.button5);
AccessibilityNodeInfo parent = button.getParent();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -459,24 +410,57 @@ public class InterrogationActivityTest
}
}
- @Override
- protected void scrubClass(Class<?> testCaseClass) {
- /* intentionally do not scrub */
+ private void bringUpActivityWithInitalizedAccessbility() {
+ mLastAccessibilityEvent = null;
+ // bring up the activity
+ getActivity();
+
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ while (true) {
+ if (mLastAccessibilityEvent != null) {
+ final int eventType = mLastAccessibilityEvent.getEventType();
+ if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ return;
+ }
+ }
+ final long remainingTimeMillis = TIMEOUT_ACCESSIBLITY_STATE_INITIALIZED_MILLIS
+ - (SystemClock.uptimeMillis() - startTimeMillis);
+ if (remainingTimeMillis <= 0) {
+ return;
+ }
+ synchronized (this) {
+ try {
+ wait(remainingTimeMillis);
+ } catch (InterruptedException e) {
+ /* ignore */
+ }
+ }
+ }
}
- private IAccessibilityServiceConnection getConnection() throws Exception {
- if (sConnection == null) {
+ private void ensureConnection() throws Exception {
+ if (sConnectionId == View.NO_ID) {
IEventListener listener = new IEventListener.Stub() {
- public void setConnection(IAccessibilityServiceConnection connection) {}
+ public void setConnection(IAccessibilityServiceConnection connection,
+ int connectionId) {
+ sConnectionId = connectionId;
+ if (connection != null) {
+ AccessibilityInteractionClient.getInstance().addConnection(connectionId,
+ connection);
+ } else {
+ AccessibilityInteractionClient.getInstance().removeConnection(connectionId);
+ }
+ synchronized (this) {
+ notifyAll();
+ }
+ }
public void onInterrupt() {}
public void onAccessibilityEvent(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
- sLastFocusAccessibilityEvent = AccessibilityEvent.obtain(event);
- }
- synchronized (sConnection) {
- sConnection.notifyAll();
+ mLastAccessibilityEvent = AccessibilityEvent.obtain(event);
+ synchronized (this) {
+ notifyAll();
}
}
};
@@ -485,28 +469,11 @@ public class InterrogationActivityTest
AccessibilityManager.getInstance(getInstrumentation().getContext());
synchronized (this) {
- if (!accessibilityManager.isEnabled()) {
- // Make sure we wake ourselves as the desired state is propagated.
- accessibilityManager.addAccessibilityStateChangeListener(
- new AccessibilityManager.AccessibilityStateChangeListener() {
- public void onAccessibilityStateChanged(boolean enabled) {
- synchronized (this) {
- notifyAll();
- }
- }
- });
- IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
+ IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
- sConnection = manager.registerEventListener(listener);
-
- wait(TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING);
- } else {
- IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
- ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
- sConnection = manager.registerEventListener(listener);
- }
+ manager.registerEventListener(listener);
+ wait(TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING);
}
}
- return sConnection;
}
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 30c12f9..23fa94a 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -115,8 +115,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
- private final SparseArray<IAccessibilityInteractionConnection> mWindowIdToInteractionConnectionMap =
- new SparseArray<IAccessibilityInteractionConnection>();
+ private final SparseArray<AccessibilityConnectionWrapper> mWindowIdToInteractionConnectionWrapperMap =
+ new SparseArray<AccessibilityConnectionWrapper>();
private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>();
@@ -439,16 +439,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final IWindow addedWindowToken = windowToken;
final IAccessibilityInteractionConnection addedConnection = connection;
final int windowId = sNextWindowId++;
- addedConnection.asBinder().linkToDeath(new DeathRecipient() {
- public void binderDied() {
- synchronized (mLock) {
- addedConnection.asBinder().unlinkToDeath(this, 0);
- removeAccessibilityInteractionConnection(addedWindowToken);
- }
- }
- }, 0);
+ AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(windowId,
+ connection);
+ wrapper.linkToDeath();
mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder());
- mWindowIdToInteractionConnectionMap.put(windowId, connection);
+ mWindowIdToInteractionConnectionWrapperMap.put(windowId, wrapper);
if (DEBUG) {
Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId);
}
@@ -462,18 +457,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
for (int i = 0; i < count; i++) {
if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) {
final int windowId = mWindowIdToWindowTokenMap.keyAt(i);
- mWindowIdToWindowTokenMap.remove(windowId);
- mWindowIdToInteractionConnectionMap.remove(windowId);
- if (DEBUG) {
- Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
- }
+ AccessibilityConnectionWrapper wrapper =
+ mWindowIdToInteractionConnectionWrapperMap.get(windowId);
+ wrapper.unlinkToDeath();
+ removeAccessibilityInteractionConnectionLocked(windowId);
return;
}
}
}
}
- public IAccessibilityServiceConnection registerEventListener(IEventListener listener) {
+ public void registerEventListener(IEventListener listener) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
FUNCTION_REGISTER_EVENT_LISTENER);
ComponentName componentName = new ComponentName("foo.bar",
@@ -501,7 +495,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
Service service = new Service(componentName, accessibilityServiceInfo, true);
service.onServiceConnected(componentName, listener.asBinder());
- return service;
+ }
+
+ /**
+ * Removes an AccessibilityInteractionConnection.
+ *
+ * @param windowId The id of the window to which the connection is targeted.
+ */
+ private void removeAccessibilityInteractionConnectionLocked(int windowId) {
+ mWindowIdToWindowTokenMap.remove(windowId);
+ mWindowIdToInteractionConnectionWrapperMap.remove(windowId);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
+ }
}
/**
@@ -594,6 +600,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
private void notifyEventListenerLocked(Service service, int eventType) {
IEventListener listener = service.mServiceInterface;
+
+ // If the service died/was disabled while the message for dispatching
+ // the accessibility event was propagating the listener may be null.
+ if (listener == null) {
+ return;
+ }
+
AccessibilityEvent event = service.mPendingEvents.get(eventType);
// Check for null here because there is a concurrent scenario in which this
@@ -618,7 +631,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
service.mPendingEvents.remove(eventType);
try {
if (mSecurityPolicy.canRetrieveWindowContent(service)) {
- event.setConnection(service);
+ event.setConnectionId(service.mId);
} else {
event.setSource(null);
}
@@ -666,6 +679,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mComponentNameToServiceMap.remove(service.mComponentName);
mHandler.removeMessages(service.mId);
service.unlinkToOwnDeath();
+ service.dispose();
updateInputFilterLocked();
return removed;
}
@@ -895,6 +909,33 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
sendStateToClientsLocked();
}
+ private class AccessibilityConnectionWrapper implements DeathRecipient {
+ private final int mWindowId;
+ private final IAccessibilityInteractionConnection mConnection;
+
+ public AccessibilityConnectionWrapper(int windowId,
+ IAccessibilityInteractionConnection connection) {
+ mWindowId = windowId;
+ mConnection = connection;
+ }
+
+ public void linkToDeath() throws RemoteException {
+ mConnection.asBinder().linkToDeath(this, 0);
+ }
+
+ public void unlinkToDeath() {
+ mConnection.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ unlinkToDeath();
+ synchronized (mLock) {
+ removeAccessibilityInteractionConnectionLocked(mWindowId);
+ }
+ }
+ }
+
/**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
@@ -997,7 +1038,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (!mIsAutomation) {
mContext.unbindService(this);
}
- mService = null;
return true;
}
return false;
@@ -1021,7 +1061,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mService = service;
mServiceInterface = IEventListener.Stub.asInterface(service);
try {
- mServiceInterface.setConnection(this);
+ mServiceInterface.setConnection(this, mId);
synchronized (mLock) {
tryAddServiceLocked(this);
}
@@ -1123,14 +1163,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (!permissionGranted) {
return 0;
} else {
- connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
- if (connection == null) {
+ AccessibilityConnectionWrapper wrapper =
+ mWindowIdToInteractionConnectionWrapperMap.get(accessibilityWindowId);
+ if (wrapper == null) {
if (DEBUG) {
Slog.e(LOG_TAG, "No interaction connection to window: "
+ accessibilityWindowId);
}
return 0;
}
+ connection = wrapper.mConnection;
}
}
final int interrogatingPid = Binder.getCallingPid();
@@ -1159,14 +1201,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (!permissionGranted) {
return false;
} else {
- connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
- if (connection == null) {
+ AccessibilityConnectionWrapper wrapper =
+ mWindowIdToInteractionConnectionWrapperMap.get(accessibilityWindowId);
+ if (wrapper == null) {
if (DEBUG) {
Slog.e(LOG_TAG, "No interaction connection to window: "
+ accessibilityWindowId);
}
return false;
}
+ connection = wrapper.mConnection;
}
}
final int interrogatingPid = Binder.getCallingPid();
@@ -1197,9 +1241,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mService.unlinkToDeath(this, 0);
}
+ public void dispose() {
+ try {
+ // Clear the proxy in the other process so this
+ // IAccessibilityServiceConnection can be garbage collected.
+ mServiceInterface.setConnection(null, mId);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ mService = null;
+ mServiceInterface = null;
+ }
+
public void binderDied() {
synchronized (mLock) {
- mService.unlinkToDeath(this, 0);
+ unlinkToOwnDeath();
tryRemoveServiceLocked(this);
// We no longer have an automation service, so restore
// the state based on values in the settings database.
@@ -1214,7 +1270,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (DEBUG) {
Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
}
- return mWindowIdToInteractionConnectionMap.get(windowId);
+ AccessibilityConnectionWrapper wrapper =
+ mWindowIdToInteractionConnectionWrapperMap.get(windowId);
+ return (wrapper != null) ? wrapper.mConnection : null;
}
private float getCompatibilityScale(int windowId) {