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

Commit 157f416a authored by Phil Weaver's avatar Phil Weaver
Browse files

Limit capabilities of a11y gesture dispatch.

Changing the service side to accept descriptions of
motion events, not motion events themselves, so we can
control their creation.

Bug: 30647115
Change-Id: Ia6772a1fc05df91818e3f88959d1e2b4a35fe0cc
(cherry picked from commit a8918f23)
parent abfa7e2c
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -628,8 +628,8 @@ public abstract class AccessibilityService extends Service {
        if (connection == null) {
            return false;
        }
        List<MotionEvent> events = MotionEventGenerator.getMotionEventsFromGestureDescription(
                gesture, 100);
        List<GestureDescription.GestureStep> steps =
                MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100);
        try {
            synchronized (mLock) {
                mGestureStatusCallbackSequence++;
@@ -641,8 +641,8 @@ public abstract class AccessibilityService extends Service {
                            callback, handler);
                    mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
                }
                connection.sendMotionEvents(mGestureStatusCallbackSequence,
                        new ParceledListSlice<>(events));
                connection.sendGesture(mGestureStatusCallbackSequence,
                        new ParceledListSlice<>(steps));
            }
        } catch (RemoteException re) {
            throw new RuntimeException(re);
+141 −19
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import android.annotation.NonNull;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
@@ -303,13 +305,37 @@ public final class GestureDescription {
        }
    }

    private static class TouchPoint {
    /**
     * The location of a finger for gesture dispatch
     *
     * @hide
     */
    public static class TouchPoint implements Parcelable {
        private static final int FLAG_IS_START_OF_PATH = 0x01;
        private static final int FLAG_IS_END_OF_PATH = 0x02;

        int mPathIndex;
        boolean mIsStartOfPath;
        boolean mIsEndOfPath;
        float mX;
        float mY;

        public TouchPoint() {
        }

        public TouchPoint(TouchPoint pointToCopy) {
            copyFrom(pointToCopy);
        }

        public TouchPoint(Parcel parcel) {
            mPathIndex = parcel.readInt();
            int startEnd = parcel.readInt();
            mIsStartOfPath = (startEnd & FLAG_IS_START_OF_PATH) != 0;
            mIsEndOfPath = (startEnd & FLAG_IS_END_OF_PATH) != 0;
            mX = parcel.readFloat();
            mY = parcel.readFloat();
        }

        void copyFrom(TouchPoint other) {
            mPathIndex = other.mPathIndex;
            mIsStartOfPath = other.mIsStartOfPath;
@@ -317,12 +343,94 @@ public final class GestureDescription {
            mX = other.mX;
            mY = other.mY;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mPathIndex);
            int startEnd = mIsStartOfPath ? FLAG_IS_START_OF_PATH : 0;
            startEnd |= mIsEndOfPath ? FLAG_IS_END_OF_PATH : 0;
            dest.writeInt(startEnd);
            dest.writeFloat(mX);
            dest.writeFloat(mY);
        }

        public static final Parcelable.Creator<TouchPoint> CREATOR
                = new Parcelable.Creator<TouchPoint>() {
            public TouchPoint createFromParcel(Parcel in) {
                return new TouchPoint(in);
            }

            public TouchPoint[] newArray(int size) {
                return new TouchPoint[size];
            }
        };
    }

    /**
     * A step along a gesture. Contains all of the touch points at a particular time
     *
     * @hide
     */
    public static class GestureStep implements Parcelable {
        public long timeSinceGestureStart;
        public int numTouchPoints;
        public TouchPoint[] touchPoints;

        public GestureStep(long timeSinceGestureStart, int numTouchPoints,
                TouchPoint[] touchPointsToCopy) {
            this.timeSinceGestureStart = timeSinceGestureStart;
            this.numTouchPoints = numTouchPoints;
            this.touchPoints = new TouchPoint[numTouchPoints];
            for (int i = 0; i < numTouchPoints; i++) {
                this.touchPoints[i] = new TouchPoint(touchPointsToCopy[i]);
            }
        }

        public GestureStep(Parcel parcel) {
            timeSinceGestureStart = parcel.readLong();
            Parcelable[] parcelables =
                    parcel.readParcelableArray(TouchPoint.class.getClassLoader());
            numTouchPoints = (parcelables == null) ? 0 : parcelables.length;
            touchPoints = new TouchPoint[numTouchPoints];
            for (int i = 0; i < numTouchPoints; i++) {
                touchPoints[i] = (TouchPoint) parcelables[i];
            }
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeLong(timeSinceGestureStart);
            dest.writeParcelableArray(touchPoints, flags);
        }

        public static final Parcelable.Creator<GestureStep> CREATOR
                = new Parcelable.Creator<GestureStep>() {
            public GestureStep createFromParcel(Parcel in) {
                return new GestureStep(in);
            }

            public GestureStep[] newArray(int size) {
                return new GestureStep[size];
            }
        };
    }

    /**
     * Class to convert a GestureDescription to a series of MotionEvents.
     *
     * @hide
     */
    static class MotionEventGenerator {
    public static class MotionEventGenerator {
        /**
         * Constants used to initialize all MotionEvents
         */
@@ -341,38 +449,52 @@ public final class GestureDescription {
        private static PointerCoords[] sPointerCoords;
        private static PointerProperties[] sPointerProps;

        static List<MotionEvent> getMotionEventsFromGestureDescription(
        static List<GestureStep> getGestureStepsFromGestureDescription(
                GestureDescription description, int sampleTimeMs) {
            final List<MotionEvent> motionEvents = new ArrayList<>();
            final List<GestureStep> gestureSteps = new ArrayList<>();

            // Point data at each time we generate an event for
            final TouchPoint[] currentTouchPoints =
                    getCurrentTouchPoints(description.getStrokeCount());
            // Point data sent in last touch event
            int lastTouchPointSize = 0;
            final TouchPoint[] lastTouchPoints =
                    getLastTouchPoints(description.getStrokeCount());

            int currentTouchPointSize = 0;
            /* Loop through each time slice where there are touch points */
            long timeSinceGestureStart = 0;
            long nextKeyPointTime = description.getNextKeyPointAtLeast(timeSinceGestureStart);
            while (nextKeyPointTime >= 0) {
                timeSinceGestureStart = (lastTouchPointSize == 0) ? nextKeyPointTime
                timeSinceGestureStart = (currentTouchPointSize == 0) ? nextKeyPointTime
                        : Math.min(nextKeyPointTime, timeSinceGestureStart + sampleTimeMs);
                int currentTouchPointSize = description.getPointsForTime(timeSinceGestureStart,
                currentTouchPointSize = description.getPointsForTime(timeSinceGestureStart,
                        currentTouchPoints);
                gestureSteps.add(new GestureStep(timeSinceGestureStart, currentTouchPointSize,
                        currentTouchPoints));

                /* Move to next time slice */
                nextKeyPointTime = description.getNextKeyPointAtLeast(timeSinceGestureStart + 1);
            }
            return gestureSteps;
        }

        public static List<MotionEvent> getMotionEventsFromGestureSteps(List<GestureStep> steps) {
            final List<MotionEvent> motionEvents = new ArrayList<>();

            // Number of points in last touch event
            int lastTouchPointSize = 0;
            TouchPoint[] lastTouchPoints;

            for (int i = 0; i < steps.size(); i++) {
                GestureStep step = steps.get(i);
                int currentTouchPointSize = step.numTouchPoints;
                lastTouchPoints = getLastTouchPoints(
                        Math.max(lastTouchPointSize, currentTouchPointSize));

                appendMoveEventIfNeeded(motionEvents, lastTouchPoints, lastTouchPointSize,
                        currentTouchPoints, currentTouchPointSize, timeSinceGestureStart);
                        step.touchPoints, currentTouchPointSize, step.timeSinceGestureStart);
                lastTouchPointSize = appendUpEvents(motionEvents, lastTouchPoints,
                        lastTouchPointSize, currentTouchPoints, currentTouchPointSize,
                        timeSinceGestureStart);
                        lastTouchPointSize, step.touchPoints, currentTouchPointSize,
                        step.timeSinceGestureStart);
                lastTouchPointSize = appendDownEvents(motionEvents, lastTouchPoints,
                        lastTouchPointSize, currentTouchPoints, currentTouchPointSize,
                        timeSinceGestureStart);

                /* Move to next time slice */
                nextKeyPointTime = description.getNextKeyPointAtLeast(timeSinceGestureStart + 1);
                        lastTouchPointSize, step.touchPoints, currentTouchPointSize,
                        step.timeSinceGestureStart);
            }
            return motionEvents;
        }
+1 −1
Original line number Diff line number Diff line
@@ -88,5 +88,5 @@ interface IAccessibilityServiceConnection {

    void setSoftKeyboardCallbackEnabled(boolean enabled);

    void sendMotionEvents(int sequence, in ParceledListSlice events);
    void sendGesture(int sequence, in ParceledListSlice gestureSteps);
}
+12 −4
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
@@ -2755,7 +2756,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        }

        @Override
        public void sendMotionEvents(int sequence, ParceledListSlice events) {
        public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
            synchronized (mLock) {
                if (mSecurityPolicy.canPerformGestures(this)) {
                    final long endMillis =
@@ -2769,9 +2770,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                        }
                    }
                    if (mMotionEventInjector != null) {
                        mMotionEventInjector.injectEvents((List<MotionEvent>) events.getList(),
                                mServiceInterface, sequence);
                        List<GestureDescription.GestureStep> steps = gestureSteps.getList();
                        List<MotionEvent> events = GestureDescription.MotionEventGenerator
                                .getMotionEventsFromGestureSteps(steps);
                        // Confirm that the motion events end with an UP event.
                        if (events.get(events.size() - 1).getAction() == MotionEvent.ACTION_UP) {
                            mMotionEventInjector.injectEvents(events, mServiceInterface, sequence);
                            return;
                        } else {
                            Slog.e(LOG_TAG, "Gesture is not well-formed");
                        }
                    } else {
                        Slog.e(LOG_TAG, "MotionEventInjector installation timed out");
                    }