Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 26f7a81f authored by Svetoslav Ganov's avatar Svetoslav Ganov Committed by Android Git Automerger
Browse files

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

* commit 'd8ec8db5':
  Fixing memory leaks in the accessiiblity layer.
parents 2fc9467e d8ec8db5
Loading
Loading
Loading
Loading
+24 −9
Original line number Diff line number Diff line
@@ -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);
                    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);
+1 −1
Original line number Diff line number Diff line
@@ -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);

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

                o++;
+4 −32
Original line number Diff line number Diff line
@@ -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.
+167 −72
Original line number Diff line number Diff line
@@ -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,15 +122,16 @@ 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 {
            IAccessibilityServiceConnection connection = getConnection(connectionId);
            if (connection != null) {
                final int interactionId = mInteractionIdCounter.getAndIncrement();
                final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
                        accessibilityWindowId, accessibilityViewId, interactionId, this,
@@ -128,11 +140,19 @@ public final class AccessibilityInteractionClient
                if (windowScale > 0) {
                    AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
                            interactionId);
                finalizeAccessibilityNodeInfo(info, connection, windowScale);
                    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 {
            IAccessibilityServiceConnection connection = getConnection(connectionId);
            if (connection != null) {
                final int interactionId = mInteractionIdCounter.getAndIncrement();
            final float windowScale = connection.findAccessibilityNodeInfoByViewIdInActiveWindow(
                    viewId, interactionId, this, Thread.currentThread().getId());
                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);
                    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 {
            IAccessibilityServiceConnection connection = getConnection(connectionId);
            if (connection != null) {
                final int interactionId = mInteractionIdCounter.getAndIncrement();
            final float windowScale = connection.findAccessibilityNodeInfosByViewTextInActiveWindow(
                    text, interactionId, this, Thread.currentThread().getId());
                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);
                    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,17 +240,18 @@ 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 {
            IAccessibilityServiceConnection connection = getConnection(connectionId);
            if (connection != null) {
                final int interactionId = mInteractionIdCounter.getAndIncrement();
                final float windowScale = connection.findAccessibilityNodeInfosByViewText(text,
                        accessibilityWindowId, accessibilityViewId, interactionId, this,
@@ -217,11 +260,19 @@ public final class AccessibilityInteractionClient
                if (windowScale > 0) {
                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
                            interactionId);
                finalizeAccessibilityNodeInfos(infos, connection, windowScale);
                    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,15 +280,17 @@ 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 {
            IAccessibilityServiceConnection connection = getConnection(connectionId);
            if (connection != null) {
                final int interactionId = mInteractionIdCounter.getAndIncrement();
                final boolean success = connection.performAccessibilityAction(
                        accessibilityWindowId, accessibilityViewId, action, interactionId, this,
@@ -245,8 +298,15 @@ public final class AccessibilityInteractionClient
                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);
        }
    }
}
Loading