Add FLAG_IS_ACCESSIBILITY_EVENT to KeyEvent and MotionEvent
This flag indicates that the event was modified or generated by an accessibility service. It allows apps to tell apart real hardware events, events that are injected (device id == -1), and events coming from accessibility (has flag is_accessibility_event). Events that have gone into accessibility, and got reinjected without being modified will not be distinguishable from real hardware events. In the next release, we will make FLAG_IS_ACCESSIBILITY_EVENT public api. Until then, applications will have to hard-code its value (0x800) to use it. The value is the same for both KeyEvents and MotionEvents for convenience. Bug: 175069843 Bug: 152399927 Test: atest VerifiedMotionEventTest VerifiedKeyEventTest Test: atest AccessibilityGestureDispatchTest Test: atest inputflinger_tests libinput_tests GamepadWithAccessibilityTest Change-Id: I38ac2ab8e19e32cad927742c623f14f43ea0c588
This commit is contained in:
@@ -2724,12 +2724,12 @@ package android.view {
|
||||
public final class InputDevice implements android.os.Parcelable {
|
||||
method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void disable();
|
||||
method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void enable();
|
||||
field public static final int ACCESSIBILITY_DEVICE_ID = -2; // 0xfffffffe
|
||||
}
|
||||
|
||||
public class KeyEvent extends android.view.InputEvent implements android.os.Parcelable {
|
||||
method public static String actionToString(int);
|
||||
method public final void setDisplayId(int);
|
||||
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
|
||||
field public static final int LAST_KEYCODE = 288; // 0x120
|
||||
}
|
||||
|
||||
@@ -2747,6 +2747,7 @@ package android.view {
|
||||
method public void setActionButton(int);
|
||||
method public void setButtonState(int);
|
||||
method public void setDisplayId(int);
|
||||
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
|
||||
}
|
||||
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface RemotableViewMethod {
|
||||
|
||||
@@ -444,13 +444,6 @@ public final class InputDevice implements Parcelable {
|
||||
|
||||
private static final int VIBRATOR_ID_ALL = -1;
|
||||
|
||||
/**
|
||||
* The device id of input events generated inside accessibility service.
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final int ACCESSIBILITY_DEVICE_ID = -2;
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<InputDevice> CREATOR =
|
||||
new Parcelable.Creator<InputDevice>() {
|
||||
public InputDevice createFromParcel(Parcel in) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.os.IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -1221,6 +1222,14 @@ public class KeyEvent extends InputEvent implements Parcelable {
|
||||
*/
|
||||
public static final int FLAG_FALLBACK = 0x400;
|
||||
|
||||
/**
|
||||
* This flag indicates that this event was modified by or generated from an accessibility
|
||||
* service. Value = 0x800
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final int FLAG_IS_ACCESSIBILITY_EVENT = INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
|
||||
|
||||
/**
|
||||
* Signifies that the key is being predispatched.
|
||||
* @hide
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.os.IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
@@ -493,6 +494,14 @@ public final class MotionEvent extends InputEvent implements Parcelable {
|
||||
*/
|
||||
public static final int FLAG_NO_FOCUS_CHANGE = 0x40;
|
||||
|
||||
/**
|
||||
* This flag indicates that this event was modified by or generated from an accessibility
|
||||
* service. Value = 0x800
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final int FLAG_IS_ACCESSIBILITY_EVENT = INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
|
||||
|
||||
/**
|
||||
* Private flag that indicates when the system has detected that this motion event
|
||||
* may be inconsistent with respect to the sequence of previously delivered motion events,
|
||||
|
||||
@@ -20,6 +20,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
@@ -157,4 +158,28 @@ public abstract class VerifiedInputEvent implements Parcelable {
|
||||
throw new IllegalArgumentException("Unexpected input event type in parcel.");
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
@SuppressWarnings("unchecked")
|
||||
VerifiedInputEvent that = (VerifiedInputEvent) o;
|
||||
return mType == that.mType
|
||||
&& getDeviceId() == that.getDeviceId()
|
||||
&& getEventTimeNanos() == that.getEventTimeNanos()
|
||||
&& getSource() == that.getSource()
|
||||
&& getDisplayId() == that.getDisplayId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int _hash = 1;
|
||||
_hash = 31 * _hash + mType;
|
||||
_hash = 31 * _hash + getDeviceId();
|
||||
_hash = 31 * _hash + Long.hashCode(getEventTimeNanos());
|
||||
_hash = 31 * _hash + getSource();
|
||||
_hash = 31 * _hash + getDisplayId();
|
||||
return _hash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.view;
|
||||
|
||||
import static android.view.KeyEvent.FLAG_CANCELED;
|
||||
import static android.view.KeyEvent.FLAG_IS_ACCESSIBILITY_EVENT;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
@@ -72,6 +73,7 @@ public final class VerifiedKeyEvent extends VerifiedInputEvent implements Parcel
|
||||
*
|
||||
* @see KeyEvent#getFlags()
|
||||
* @see KeyEvent#FLAG_CANCELED
|
||||
* @see KeyEvent#FLAG_IS_ACCESSIBILITY_EVENT
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -125,6 +127,7 @@ public final class VerifiedKeyEvent extends VerifiedInputEvent implements Parcel
|
||||
// InputDispatcher only verifies a subset of the KeyEvent flags.
|
||||
// These values must be kept in sync with Input.cpp
|
||||
case FLAG_CANCELED:
|
||||
case FLAG_IS_ACCESSIBILITY_EVENT:
|
||||
return (mFlags & flag) != 0;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.view.MotionEvent.FLAG_IS_ACCESSIBILITY_EVENT;
|
||||
import static android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED;
|
||||
import static android.view.MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
|
||||
|
||||
@@ -83,6 +84,9 @@ public final class VerifiedMotionEvent extends VerifiedInputEvent implements Par
|
||||
* Returns the flags for this motion event.
|
||||
*
|
||||
* @see MotionEvent#getFlags()
|
||||
* @see MotionEvent#FLAG_IS_ACCESSIBILITY_EVENT
|
||||
* @see MotionEvent#FLAG_WINDOW_IS_OBSCURED
|
||||
* @see MotionEvent#FLAG_WINDOW_IS_PARTIALLY_OBSCURED
|
||||
* @hide
|
||||
*/
|
||||
private int mFlags;
|
||||
@@ -117,6 +121,7 @@ public final class VerifiedMotionEvent extends VerifiedInputEvent implements Par
|
||||
switch(flag) {
|
||||
// InputDispatcher only verifies a subset of the MotionEvent flags.
|
||||
// These values must be kept in sync with Input.cpp
|
||||
case FLAG_IS_ACCESSIBILITY_EVENT:
|
||||
case FLAG_WINDOW_IS_OBSCURED:
|
||||
case FLAG_WINDOW_IS_PARTIALLY_OBSCURED:
|
||||
return (mFlags & flag) != 0;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package android.view;
|
||||
|
||||
import static android.os.IInputConstants.POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY;
|
||||
import static android.os.IInputConstants.POLICY_FLAG_INPUTFILTER_TRUSTED;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.os.PowerManager;
|
||||
@@ -35,7 +34,6 @@ public interface WindowManagerPolicyConstants {
|
||||
int FLAG_WAKE = 0x00000001;
|
||||
int FLAG_VIRTUAL = 0x00000002;
|
||||
|
||||
int FLAG_INPUTFILTER_TRUSTED = POLICY_FLAG_INPUTFILTER_TRUSTED;
|
||||
int FLAG_INJECTED_FROM_ACCESSIBILITY = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY;
|
||||
int FLAG_INJECTED = 0x01000000;
|
||||
int FLAG_TRUSTED = 0x02000000;
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.view
|
||||
|
||||
import android.view.InputDevice.SOURCE_TOUCHSCREEN
|
||||
import android.view.MotionEvent.ACTION_MOVE
|
||||
import android.view.MotionEvent.FLAG_IS_ACCESSIBILITY_EVENT
|
||||
import android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED
|
||||
import android.view.MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED
|
||||
import android.view.MotionEvent.FLAG_TAINTED
|
||||
@@ -49,6 +50,7 @@ class VerifiedMotionEventTest {
|
||||
assertEquals(RAW_Y, event.rawY, 0f)
|
||||
assertEquals(ACTION_MASKED, event.actionMasked)
|
||||
assertEquals(DOWN_TIME_NANOS, event.downTimeNanos)
|
||||
assertEquals(FLAGS, event.flags)
|
||||
assertEquals(META_STATE, event.metaState)
|
||||
assertEquals(BUTTON_STATE, event.buttonState)
|
||||
}
|
||||
@@ -128,8 +130,9 @@ class VerifiedMotionEventTest {
|
||||
assertNull(motionEvent.getFlag(0))
|
||||
// Flag that was not set
|
||||
assertEquals(false, motionEvent.getFlag(FLAG_WINDOW_IS_PARTIALLY_OBSCURED))
|
||||
// Flag that was set
|
||||
// Flags that were set
|
||||
assertEquals(true, motionEvent.getFlag(FLAG_WINDOW_IS_OBSCURED))
|
||||
assertEquals(true, motionEvent.getFlag(FLAG_IS_ACCESSIBILITY_EVENT))
|
||||
// Only 1 flag at a time is accepted
|
||||
assertNull(motionEvent.getFlag(
|
||||
FLAG_WINDOW_IS_PARTIALLY_OBSCURED or FLAG_WINDOW_IS_OBSCURED))
|
||||
@@ -153,7 +156,7 @@ class VerifiedMotionEventTest {
|
||||
private const val RAW_Y = 200f
|
||||
private const val ACTION_MASKED = ACTION_MOVE
|
||||
private const val DOWN_TIME_NANOS: Long = 1000
|
||||
private const val FLAGS = FLAG_WINDOW_IS_OBSCURED
|
||||
private const val FLAGS = FLAG_WINDOW_IS_OBSCURED or FLAG_IS_ACCESSIBILITY_EVENT
|
||||
private const val META_STATE = 11
|
||||
private const val BUTTON_STATE = 22
|
||||
|
||||
@@ -178,6 +181,7 @@ class VerifiedMotionEventTest {
|
||||
assertEquals(event1.rawY, event2.rawY, 0f)
|
||||
assertEquals(event1.actionMasked, event2.actionMasked)
|
||||
assertEquals(event1.downTimeNanos, event2.downTimeNanos)
|
||||
assertEquals(event1.flags, event2.flags)
|
||||
assertEquals(event1.metaState, event2.metaState)
|
||||
assertEquals(event1.buttonState, event2.buttonState)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManagerPolicyConstants;
|
||||
|
||||
@@ -55,7 +56,6 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
|
||||
*/
|
||||
private static final int EVENT_META_STATE = 0;
|
||||
private static final int EVENT_BUTTON_STATE = 0;
|
||||
private static final int EVENT_DEVICE_ID = 0;
|
||||
private static final int EVENT_EDGE_FLAGS = 0;
|
||||
private static final int EVENT_SOURCE = InputDevice.SOURCE_TOUCHSCREEN;
|
||||
private static final int EVENT_FLAGS = 0;
|
||||
@@ -122,9 +122,6 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
|
||||
return;
|
||||
}
|
||||
cancelAnyPendingInjectedEvents();
|
||||
// The events injected from outside of system_server are not trusted. Remove the flag to
|
||||
// prevent accessibility service from impersonating a real input device.
|
||||
policyFlags &= ~WindowManagerPolicyConstants.FLAG_INPUTFILTER_TRUSTED;
|
||||
// Indicate that the input event is injected from accessibility, to let applications
|
||||
// distinguish it from events injected by other means.
|
||||
policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
|
||||
@@ -483,8 +480,8 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
|
||||
}
|
||||
return MotionEvent.obtain(downTime, eventTime, action, touchPointsSize,
|
||||
sPointerProps, sPointerCoords, EVENT_META_STATE, EVENT_BUTTON_STATE,
|
||||
EVENT_X_PRECISION, EVENT_Y_PRECISION, EVENT_DEVICE_ID, EVENT_EDGE_FLAGS,
|
||||
EVENT_SOURCE, EVENT_FLAGS);
|
||||
EVENT_X_PRECISION, EVENT_Y_PRECISION, KeyCharacterMap.VIRTUAL_KEYBOARD,
|
||||
EVENT_EDGE_FLAGS, EVENT_SOURCE, EVENT_FLAGS);
|
||||
}
|
||||
|
||||
private static int findPointByStrokeId(TouchPoint[] touchPoints, int touchPointsSize,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.accessibility;
|
||||
|
||||
import static android.view.KeyCharacterMap.VIRTUAL_KEYBOARD;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_HOVER_MOVE;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
@@ -112,6 +113,13 @@ public class MotionEventInjectorTest {
|
||||
private static final int CONTINUED_LINE_SEQUENCE_1 = 52;
|
||||
private static final int CONTINUED_LINE_SEQUENCE_2 = 53;
|
||||
|
||||
private static final float PRESSURE = 1;
|
||||
private static final float X_PRECISION = 1;
|
||||
private static final float Y_PRECISION = 1;
|
||||
private static final int EDGEFLAGS = 0;
|
||||
private static final float POINTER_SIZE = 1;
|
||||
private static final int METASTATE = 0;
|
||||
|
||||
MotionEventInjector mMotionEventInjector;
|
||||
IAccessibilityServiceClient mServiceInterface;
|
||||
List<GestureStep> mLineList = new ArrayList<>();
|
||||
@@ -152,14 +160,18 @@ public class MotionEventInjectorTest {
|
||||
CONTINUED_LINE_STROKE_ID_1, false, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_MID1,
|
||||
CONTINUED_LINE_MID2, CONTINUED_LINE_END);
|
||||
|
||||
mClickDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, CLICK_POINT.x, CLICK_POINT.y, 0);
|
||||
mClickDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, CLICK_POINT.x, CLICK_POINT.y,
|
||||
PRESSURE, POINTER_SIZE, METASTATE, X_PRECISION, Y_PRECISION, VIRTUAL_KEYBOARD,
|
||||
EDGEFLAGS);
|
||||
mClickDownEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
mClickUpEvent = MotionEvent.obtain(0, CLICK_DURATION, ACTION_UP, CLICK_POINT.x,
|
||||
CLICK_POINT.y, 0);
|
||||
CLICK_POINT.y, PRESSURE, POINTER_SIZE, METASTATE, X_PRECISION, Y_PRECISION,
|
||||
VIRTUAL_KEYBOARD, EDGEFLAGS);
|
||||
mClickUpEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
|
||||
mHoverMoveEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, CLICK_POINT.x, CLICK_POINT.y,
|
||||
0);
|
||||
PRESSURE, POINTER_SIZE, METASTATE, X_PRECISION, Y_PRECISION, VIRTUAL_KEYBOARD,
|
||||
EDGEFLAGS);
|
||||
mHoverMoveEvent.setSource(InputDevice.SOURCE_MOUSE);
|
||||
|
||||
mIsLineStart = allOf(IS_ACTION_DOWN, isAtPoint(LINE_START), hasStandardInitialization(),
|
||||
@@ -874,12 +886,14 @@ public class MotionEventInjectorTest {
|
||||
return new TypeSafeMatcher<MotionEvent>() {
|
||||
@Override
|
||||
protected boolean matchesSafely(MotionEvent event) {
|
||||
return (0 == event.getActionIndex()) && (0 == event.getDeviceId())
|
||||
&& (0 == event.getEdgeFlags()) && (0 == event.getFlags())
|
||||
&& (0 == event.getMetaState()) && (0F == event.getOrientation())
|
||||
return (0 == event.getActionIndex()) && (VIRTUAL_KEYBOARD == event.getDeviceId())
|
||||
&& (EDGEFLAGS == event.getEdgeFlags()) && (0 == event.getFlags())
|
||||
&& (METASTATE == event.getMetaState()) && (0F == event.getOrientation())
|
||||
&& (0F == event.getTouchMajor()) && (0F == event.getTouchMinor())
|
||||
&& (1F == event.getXPrecision()) && (1F == event.getYPrecision())
|
||||
&& (1 == event.getPointerCount()) && (1F == event.getPressure())
|
||||
&& (X_PRECISION == event.getXPrecision())
|
||||
&& (Y_PRECISION == event.getYPrecision())
|
||||
&& (POINTER_SIZE == event.getSize())
|
||||
&& (1 == event.getPointerCount()) && (PRESSURE == event.getPressure())
|
||||
&& (InputDevice.SOURCE_TOUCHSCREEN == event.getSource());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user