summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Weaver <pweaver@google.com>2016-08-05 11:23:50 -0700
committergitbuildkicker <android-build@google.com>2016-08-26 10:40:50 -0700
commit5f256310187b4ff2f13a7abb9afed9126facd7bc (patch)
tree0ed70311a461913e6f787b8fe4ca22079b187565
parent2c7008421cb67f5d89f16911bdbe36f6c35311ad (diff)
downloadbase-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)
-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 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");
}