diff options
author | Svetoslav Ganov <svetoslavganov@google.com> | 2011-11-30 16:56:41 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-11-30 16:56:41 -0800 |
commit | d8ec8db5e0f227e4f63e948acb78d829f5ad30c8 (patch) | |
tree | cb598a2cc487e3298e9cbafd3e0e4d274347fa1f /core | |
parent | 88f10c6ce5a97b1990934148deb8ed6aa6ef7a48 (diff) | |
parent | d116d7c78a9c53f30a73bf273bd7618312cf3847 (diff) | |
download | frameworks_base-d8ec8db5e0f227e4f63e948acb78d829f5ad30c8.zip frameworks_base-d8ec8db5e0f227e4f63e948acb78d829f5ad30c8.tar.gz frameworks_base-d8ec8db5e0f227e4f63e948acb78d829f5ad30c8.tar.bz2 |
Merge "Fixing memory leaks in the accessiiblity layer." into ics-mr1
Diffstat (limited to 'core')
9 files changed, 331 insertions, 285 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 7bf0c83..91dcac8 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 25b980b..00a7c87 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -21,6 +21,8 @@ import android.graphics.Rect; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; +import android.util.Log; +import android.util.SparseArray; import java.util.Collections; import java.util.List; @@ -61,6 +63,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(); @@ -83,6 +91,9 @@ public final class AccessibilityInteractionClient private final Rect mTempBounds = new Rect(); + private final SparseArray<IAccessibilityServiceConnection> mConnectionCache = + new SparseArray<IAccessibilityServiceConnection>(); + /** * @return The singleton of this class. */ @@ -111,28 +122,37 @@ 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 accessibilityViewId A unique View accessibility id. * @return An {@link AccessibilityNodeInfo} if found, null otherwise. */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId( - IAccessibilityServiceConnection connection, int accessibilityWindowId, - int accessibilityViewId) { + public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId, + int accessibilityWindowId, int accessibilityViewId) { try { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId( - accessibilityWindowId, accessibilityViewId, 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, accessibilityViewId, 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; } @@ -141,25 +161,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; } @@ -169,25 +200,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> findAccessibilityNodeInfosByViewTextInActiveWindow( - IAccessibilityServiceConnection connection, String text) { + int connectionId, String text) { try { - 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, connection, windowScale); - return infos; + 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); + } } } catch (RemoteException re) { - /* ignore */ + if (DEBUG) { + Log.w(LOG_TAG, "Error while calling remote" + + " findAccessibilityNodeInfosByViewTextInActiveWindow", re); + } } return null; } @@ -198,30 +240,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 accessibilityViewId A unique View accessibility 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> findAccessibilityNodeInfosByViewText( - IAccessibilityServiceConnection connection, String text, int accessibilityWindowId, - int accessibilityViewId) { + public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(int connectionId, + String text, int accessibilityWindowId, int accessibilityViewId) { try { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final float windowScale = connection.findAccessibilityNodeInfosByViewText(text, - accessibilityWindowId, accessibilityViewId, 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, accessibilityViewId, 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(); } @@ -229,24 +280,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 accessibilityViewId A unique View accessibility id. * @param action The action to perform. * @return Whether the action was performed. */ - public boolean performAccessibilityAction(IAccessibilityServiceConnection connection, - int accessibilityWindowId, int accessibilityViewId, int action) { + public boolean performAccessibilityAction(int connectionId, int accessibilityWindowId, + int accessibilityViewId, int action) { try { - final int interactionId = mInteractionIdCounter.getAndIncrement(); - final boolean success = connection.performAccessibilityAction( - accessibilityWindowId, accessibilityViewId, 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, accessibilityViewId, 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; } @@ -406,14 +466,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); } } @@ -422,16 +482,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); } } } @@ -449,4 +509,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 fa34ee7..9b0f44a 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. /** @@ -107,9 +108,9 @@ public class AccessibilityNodeInfo implements Parcelable { private boolean mSealed; // Data. - private int mAccessibilityViewId = View.NO_ID; - private int mAccessibilityWindowId = View.NO_ID; - private int mParentAccessibilityViewId = View.NO_ID; + private int mAccessibilityViewId = UNDEFINED; + private int mAccessibilityWindowId = UNDEFINED; + private int mParentAccessibilityViewId = UNDEFINED; private int mBooleanProperties; private final Rect mBoundsInParent = new Rect(); private final Rect mBoundsInScreen = new Rect(); @@ -122,7 +123,7 @@ public class AccessibilityNodeInfo implements Parcelable { private SparseIntArray mChildAccessibilityIds = new SparseIntArray(); private int mActions; - private IAccessibilityServiceConnection mConnection; + private int mConnectionId = UNDEFINED; /** * Hide constructor from clients. @@ -181,7 +182,7 @@ public class AccessibilityNodeInfo implements Parcelable { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mAccessibilityWindowId, childAccessibilityViewId); } @@ -253,7 +254,7 @@ public class AccessibilityNodeInfo implements Parcelable { return false; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.performAccessibilityAction(mConnection, mAccessibilityWindowId, + return client.performAccessibilityAction(mConnectionId, mAccessibilityWindowId, mAccessibilityViewId, action); } @@ -277,7 +278,7 @@ public class AccessibilityNodeInfo implements Parcelable { return Collections.emptyList(); } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfosByViewText(mConnection, text, + return client.findAccessibilityNodeInfosByViewText(mConnectionId, text, mAccessibilityWindowId, mAccessibilityViewId); } @@ -297,7 +298,7 @@ public class AccessibilityNodeInfo implements Parcelable { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mAccessibilityWindowId, mParentAccessibilityViewId); } @@ -755,15 +756,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; } /** @@ -900,16 +902,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.writeInt(mAccessibilityViewId); parcel.writeInt(mAccessibilityWindowId); parcel.writeInt(mParentAccessibilityViewId); + parcel.writeInt(mConnectionId); SparseIntArray childIds = mChildAccessibilityIds; final int childIdsSize = childIds.size(); @@ -949,10 +946,10 @@ public class AccessibilityNodeInfo implements Parcelable { */ private void init(AccessibilityNodeInfo other) { mSealed = other.mSealed; - mConnection = other.mConnection; mAccessibilityViewId = other.mAccessibilityViewId; mParentAccessibilityViewId = other.mParentAccessibilityViewId; mAccessibilityWindowId = other.mAccessibilityWindowId; + mConnectionId = other.mConnectionId; mBoundsInParent.set(other.mBoundsInParent); mBoundsInScreen.set(other.mBoundsInScreen); mPackageName = other.mPackageName; @@ -970,14 +967,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); mAccessibilityViewId = parcel.readInt(); mAccessibilityWindowId = parcel.readInt(); mParentAccessibilityViewId = parcel.readInt(); + mConnectionId = parcel.readInt(); SparseIntArray childIds = mChildAccessibilityIds; final int childrenSize = parcel.readInt(); @@ -1011,10 +1005,10 @@ public class AccessibilityNodeInfo implements Parcelable { */ private void clear() { mSealed = false; - mConnection = null; - mAccessibilityViewId = View.NO_ID; - mParentAccessibilityViewId = View.NO_ID; - mAccessibilityWindowId = View.NO_ID; + mAccessibilityViewId = UNDEFINED; + mParentAccessibilityViewId = UNDEFINED; + mAccessibilityWindowId = UNDEFINED; + mConnectionId = UNDEFINED; mChildAccessibilityIds.clear(); mBoundsInParent.set(0, 0, 0, 0); mBoundsInScreen.set(0, 0, 0, 0); @@ -1048,9 +1042,8 @@ public class AccessibilityNodeInfo implements Parcelable { } private boolean canPerformRequestOverConnection(int accessibilityViewId) { - return (mAccessibilityWindowId != View.NO_ID - && accessibilityViewId != View.NO_ID - && mConnection != null); + return (mConnectionId != UNDEFINED && mAccessibilityWindowId != UNDEFINED + && accessibilityViewId != UNDEFINED); } @Override diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index a4e0688..18d0f6f 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; - int mSourceViewId = View.NO_ID; - int mSourceWindowId = View.NO_ID; + int mSourceViewId = 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. @@ -108,8 +108,8 @@ public class AccessibilityRecord { mSourceWindowId = source.getAccessibilityWindowId(); mSourceViewId = source.getAccessibilityViewId(); } else { - mSourceWindowId = View.NO_ID; - mSourceViewId = View.NO_ID; + mSourceWindowId = UNDEFINED; + mSourceViewId = UNDEFINED; } } @@ -119,33 +119,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 (mSourceWindowId == View.NO_ID || mSourceViewId == View.NO_ID || mConnection == null) { + if (mConnectionId == UNDEFINED || mSourceWindowId == UNDEFINED + || mSourceViewId == UNDEFINED) { return null; } AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, mSourceWindowId, + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId, mSourceViewId); } /** - * 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. @@ -561,6 +549,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. @@ -708,7 +709,7 @@ public class AccessibilityRecord { mText.addAll(record.mText); mSourceWindowId = record.mSourceWindowId; mSourceViewId = record.mSourceViewId; - mConnection = record.mConnection; + mConnectionId = record.mConnectionId; } /** @@ -732,8 +733,9 @@ public class AccessibilityRecord { mBeforeText = null; mParcelableData = null; mText.clear(); - mSourceViewId = View.NO_ID; - mSourceWindowId = View.NO_ID; + mSourceViewId = 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 3521296..ec12124 100644 --- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java +++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java @@ -26,6 +26,7 @@ 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; @@ -54,28 +55,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 +124,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() - .findAccessibilityNodeInfosByViewTextInActiveWindow(connection, "butto"); + List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance() + .findAccessibilityNodeInfosByViewTextInActiveWindow(sConnectionId, "butto"); assertEquals(9, buttons.size()); } finally { if (DEBUG) { @@ -143,15 +141,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() - .findAccessibilityNodeInfosByViewTextInActiveWindow(connection, + List<AccessibilityNodeInfo> buttons = AccessibilityInteractionClient.getInstance() + .findAccessibilityNodeInfosByViewTextInActiveWindow(sConnectionId, "contentDescription"); assertEquals(1, buttons.size()); } finally { @@ -167,12 +161,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 +178,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 +215,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 +239,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 +249,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 +257,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 +272,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 +282,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 +296,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 +306,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 +329,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 +388,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 +411,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 +470,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; } } |