am d8ec8db5: Merge "Fixing memory leaks in the accessiiblity layer." into ics-mr1

* commit 'd8ec8db5e0f227e4f63e948acb78d829f5ad30c8':
  Fixing memory leaks in the accessiiblity layer.
This commit is contained in:
Svetoslav Ganov
2011-11-30 16:59:46 -08:00
committed by Android Git Automerger
10 changed files with 416 additions and 312 deletions

View File

@ -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);

View File

@ -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);

View File

@ -134,6 +134,7 @@ public class SparseArray<E> implements Cloneable {
if (i != o) {
keys[o] = keys[i];
values[o] = val;
values[i] = null;
}
o++;

View File

@ -16,7 +16,6 @@
package android.view.accessibility;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@ -589,24 +588,6 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
mPackageName = event.mPackageName;
}
/**
* 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.
*
@ -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.

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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,32 +119,20 @@ 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.
*
@ -560,6 +548,19 @@ public class AccessibilityRecord {
mParcelableData = parcelableData;
}
/**
* 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.
*
@ -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

View File

@ -49,5 +49,5 @@ interface IAccessibilityManager {
void removeAccessibilityInteractionConnection(IWindow windowToken);
IAccessibilityServiceConnection registerEventListener(IEventListener client);
void registerEventListener(IEventListener client);
}

View File

@ -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;
}
}

View File

@ -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) {