diff options
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) { |