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
This commit is contained in:
Phil Weaver
2016-08-05 11:23:50 -07:00
parent 0cf4d47a40
commit a8918f23c7
4 changed files with 159 additions and 29 deletions

View File

@@ -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);

View File

@@ -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,39 +449,53 @@ 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);
appendMoveEventIfNeeded(motionEvents, lastTouchPoints, lastTouchPointSize,
currentTouchPoints, currentTouchPointSize, timeSinceGestureStart);
lastTouchPointSize = appendUpEvents(motionEvents, lastTouchPoints,
lastTouchPointSize, currentTouchPoints, currentTouchPointSize,
timeSinceGestureStart);
lastTouchPointSize = appendDownEvents(motionEvents, lastTouchPoints,
lastTouchPointSize, currentTouchPoints, currentTouchPointSize,
timeSinceGestureStart);
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,
step.touchPoints, currentTouchPointSize, step.timeSinceGestureStart);
lastTouchPointSize = appendUpEvents(motionEvents, lastTouchPoints,
lastTouchPointSize, step.touchPoints, currentTouchPointSize,
step.timeSinceGestureStart);
lastTouchPointSize = appendDownEvents(motionEvents, lastTouchPoints,
lastTouchPointSize, step.touchPoints, currentTouchPointSize,
step.timeSinceGestureStart);
}
return motionEvents;
}

View File

@@ -88,5 +88,5 @@ interface IAccessibilityServiceConnection {
void setSoftKeyboardCallbackEnabled(boolean enabled);
void sendMotionEvents(int sequence, in ParceledListSlice events);
void sendGesture(int sequence, in ParceledListSlice gestureSteps);
}

View File

@@ -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");
}