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

Commit d8ec8db5 authored by Svetoslav Ganov's avatar Svetoslav Ganov Committed by Android (Google) Code Review
Browse files

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

parents 88f10c6c d116d7c7
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