diff options
author | Phil Weaver <pweaver@google.com> | 2016-08-05 11:23:50 -0700 |
---|---|---|
committer | gitbuildkicker <android-build@google.com> | 2016-08-26 10:40:50 -0700 |
commit | 5f256310187b4ff2f13a7abb9afed9126facd7bc (patch) | |
tree | 0ed70311a461913e6f787b8fe4ca22079b187565 | |
parent | 2c7008421cb67f5d89f16911bdbe36f6c35311ad (diff) | |
download | base-5f256310187b4ff2f13a7abb9afed9126facd7bc.tar.gz |
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 a8918f23c712e97fa1dc4911f64827d64fc906e5)
(cherry picked from commit 157f416a3549420bd109dbc4931b437089e22d04)
4 files changed, 158 insertions, 28 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index ae78e21807e8..c4eaccc1b406 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -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); diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java index fc9581e7367c..d9b03faa42fa 100644 --- a/core/java/android/accessibilityservice/GestureDescription.java +++ b/core/java/android/accessibilityservice/GestureDescription.java @@ -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; } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 7a55079d3ecf..81cddbac3f53 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -88,5 +88,5 @@ interface IAccessibilityServiceConnection { void setSoftKeyboardCallbackEnabled(boolean enabled); - void sendMotionEvents(int sequence, in ParceledListSlice events); + void sendGesture(int sequence, in ParceledListSlice gestureSteps); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 60d33397368d..f039e1e4a404 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -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); - return; + 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"); } |