summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Phil Weaver <pweaver@google.com> 2016-08-05 11:23:50 -0700
committer Phil Weaver <pweaver@google.com> 2016-08-08 10:23:17 -0700
commita8918f23c712e97fa1dc4911f64827d64fc906e5 (patch)
tree48e1ef71ad750f87c0e28867d77abadf0c9eeea1
parent0cf4d47a4094a43984a5215ab1a283b0fdcf87a8 (diff)
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
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java8
-rw-r--r--core/java/android/accessibilityservice/GestureDescription.java160
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java16
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 7ebc150bb30b..da89a7cec702 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;
@@ -2747,7 +2748,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 =
@@ -2761,9 +2762,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");
}