diff options
21 files changed, 1691 insertions, 87 deletions
diff --git a/api/current.txt b/api/current.txt index 826d409b0a2d..708e17b5e6e2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -18109,6 +18109,7 @@ package android.hardware.input { method public int[] getInputDeviceIds(); method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler); method public void unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener); + method @Nullable public android.view.VerifiedInputEvent verifyInputEvent(@NonNull android.view.InputEvent); field public static final String ACTION_QUERY_KEYBOARD_LAYOUTS = "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS"; field public static final String META_DATA_KEYBOARD_LAYOUTS = "android.hardware.input.metadata.KEYBOARD_LAYOUTS"; } @@ -53318,6 +53319,38 @@ package android.view { method public void recycle(); } + public abstract class VerifiedInputEvent implements android.os.Parcelable { + method public int describeContents(); + method public int getDeviceId(); + method public int getDisplayId(); + method public long getEventTimeNanos(); + method public int getSource(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.VerifiedInputEvent> CREATOR; + } + + public final class VerifiedKeyEvent extends android.view.VerifiedInputEvent implements android.os.Parcelable { + method public int getAction(); + method public long getDownTimeNanos(); + method @Nullable public Boolean getFlag(int); + method public int getKeyCode(); + method public int getMetaState(); + method public int getRepeatCount(); + method public int getScanCode(); + field @NonNull public static final android.os.Parcelable.Creator<android.view.VerifiedKeyEvent> CREATOR; + } + + public final class VerifiedMotionEvent extends android.view.VerifiedInputEvent implements android.os.Parcelable { + method public int getActionMasked(); + method public int getButtonState(); + method public long getDownTimeNanos(); + method @Nullable public Boolean getFlag(int); + method public int getMetaState(); + method public float getRawX(); + method public float getRawY(); + field @NonNull public static final android.os.Parcelable.Creator<android.view.VerifiedMotionEvent> CREATOR; + } + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { ctor public View(android.content.Context); ctor public View(android.content.Context, @Nullable android.util.AttributeSet); diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 6e1987c47d20..fb44b0b66fd8 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -27,6 +27,7 @@ import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; import android.view.PointerIcon; +import android.view.VerifiedInputEvent; /** @hide */ interface IInputManager { @@ -50,6 +51,8 @@ interface IInputManager { @UnsupportedAppUsage boolean injectInputEvent(in InputEvent ev, int mode); + VerifiedInputEvent verifyInputEvent(in InputEvent ev); + // Calibrate input device position TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation); void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation, diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 83f01a5dca35..fb27081a5512 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -18,6 +18,7 @@ package android.hardware.input; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; @@ -45,6 +46,7 @@ import android.view.InputEvent; import android.view.InputMonitor; import android.view.MotionEvent; import android.view.PointerIcon; +import android.view.VerifiedInputEvent; import com.android.internal.os.SomeArgs; @@ -907,6 +909,27 @@ public final class InputManager { } /** + * Verify the details of an {@link android.view.InputEvent} that came from the system. + * If the event did not come from the system, or its details could not be verified, then this + * will return {@code null}. Receiving {@code null} does not mean that the event did not + * originate from the system, just that we were unable to verify it. This can + * happen for a number of reasons during normal operation. + * + * @param event The {@link android.view.InputEvent} to check + * + * @return {@link android.view.VerifiedInputEvent}, which is a subset of the provided + * {@link android.view.InputEvent} + * {@code null} if the event could not be verified. + */ + public @Nullable VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) { + try { + return mIm.verifyInputEvent(event); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Changes the mouse pointer's icon shape into the specified id. * * @param iconId The id of the pointer graphic, as a value between diff --git a/core/java/android/view/VerifiedInputEvent.aidl b/core/java/android/view/VerifiedInputEvent.aidl new file mode 100644 index 000000000000..41608d8971f7 --- /dev/null +++ b/core/java/android/view/VerifiedInputEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +parcelable VerifiedInputEvent; diff --git a/core/java/android/view/VerifiedInputEvent.java b/core/java/android/view/VerifiedInputEvent.java new file mode 100644 index 000000000000..531b3ed39fc3 --- /dev/null +++ b/core/java/android/view/VerifiedInputEvent.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; + + +/** + * Base class for verified events. + * Verified events contain the subset of an InputEvent that the system can verify. + * Data contained inside VerifiedInputEvent's should be considered trusted and contain only + * the original event data that first came from the system. + * + * @see android.hardware.input.InputManager#verifyInputEvent(InputEvent) + */ +@SuppressLint("ParcelNotFinal") +public abstract class VerifiedInputEvent implements Parcelable { + private static final String TAG = "VerifiedInputEvent"; + + /** @hide */ + protected static final int VERIFIED_KEY = 1; + /** @hide */ + protected static final int VERIFIED_MOTION = 2; + + /** @hide */ + @Retention(SOURCE) + @IntDef(prefix = "VERIFIED", value = {VERIFIED_KEY, VERIFIED_MOTION}) + public @interface VerifiedInputEventType {}; + + @VerifiedInputEventType + private int mType; + + private int mDeviceId; + private long mEventTimeNanos; + private int mSource; + private int mDisplayId; + + /** @hide */ + protected VerifiedInputEvent(int type, int deviceId, long eventTimeNanos, int source, + int displayId) { + mType = type; + mDeviceId = deviceId; + mEventTimeNanos = eventTimeNanos; + mSource = source; + mDisplayId = displayId; + } + /** @hide */ + protected VerifiedInputEvent(@NonNull Parcel in, int expectedType) { + mType = in.readInt(); + if (mType != expectedType) { + throw new IllegalArgumentException("Unexpected input event type token in parcel."); + } + mDeviceId = in.readInt(); + mEventTimeNanos = in.readLong(); + mSource = in.readInt(); + mDisplayId = in.readInt(); + } + + /** + * Get the id of the device that generated this event. + * + * @see InputEvent#getDeviceId() + */ + public int getDeviceId() { + return mDeviceId; + } + + /** + * Get the time this event occurred, in the {@link android.os.SystemClock#uptimeMillis()} + * time base. + * + * @see InputEvent#getEventTime() + * @see KeyEvent#getEventTimeNano() + * @see MotionEvent#getEventTimeNano() + */ + @SuppressLint("MethodNameUnits") + public long getEventTimeNanos() { + return mEventTimeNanos; + } + + /** + * Get the source of the event. + * + * @see InputEvent#getSource() + */ + public int getSource() { + return mSource; + } + + /** + * Get the display id that is associated with this event. + * + * @see Display#getDisplayId() + */ + public int getDisplayId() { + return mDisplayId; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mDeviceId); + dest.writeLong(mEventTimeNanos); + dest.writeInt(mSource); + dest.writeInt(mDisplayId); + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + private static int peekInt(@NonNull Parcel parcel) { + final int initialDataPosition = parcel.dataPosition(); + int data = parcel.readInt(); + parcel.setDataPosition(initialDataPosition); + return data; + } + + public static final @NonNull Parcelable.Creator<VerifiedInputEvent> CREATOR = + new Parcelable.Creator<VerifiedInputEvent>() { + @Override + public VerifiedInputEvent[] newArray(int size) { + return new VerifiedInputEvent[size]; + } + + @Override + public VerifiedInputEvent createFromParcel(@NonNull Parcel in) { + final int type = peekInt(in); + if (type == VERIFIED_KEY) { + return VerifiedKeyEvent.CREATOR.createFromParcel(in); + } else if (type == VERIFIED_MOTION) { + return VerifiedMotionEvent.CREATOR.createFromParcel(in); + } + throw new IllegalArgumentException("Unexpected input event type in parcel."); + } + }; +} diff --git a/core/java/android/view/VerifiedKeyEvent.aidl b/core/java/android/view/VerifiedKeyEvent.aidl new file mode 100644 index 000000000000..2d6c3a481b37 --- /dev/null +++ b/core/java/android/view/VerifiedKeyEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +parcelable VerifiedKeyEvent; diff --git a/core/java/android/view/VerifiedKeyEvent.java b/core/java/android/view/VerifiedKeyEvent.java new file mode 100644 index 000000000000..dc5b7cc1e13a --- /dev/null +++ b/core/java/android/view/VerifiedKeyEvent.java @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static android.view.KeyEvent.FLAG_CANCELED; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.lang.annotation.Retention; + +/** + * KeyEvent that has been verified by the system. + * The data contained in this class is always a subset of a {@link KeyEvent}. Use this class to + * check which data has been confirmed by the system to be authentic. + * + * Most applications do not need to use this class. + * + * {@see android.hardware.input.InputManager#verifyInputEvent} + */ +@DataClass(genHiddenConstructor = true, genEqualsHashCode = true) +public final class VerifiedKeyEvent extends VerifiedInputEvent implements Parcelable { + private static final String TAG = "VerifiedKeyEvent"; + + /** @hide */ + @Retention(SOURCE) + @IntDef({KeyEvent.ACTION_DOWN, KeyEvent.ACTION_UP}) + public @interface KeyEventAction {}; + + /** + * The action of this key event. May be either {@link KeyEvent#ACTION_DOWN} or + * {@link KeyEvent#ACTION_UP}. + * + * @see KeyEvent#getAction() + */ + @KeyEventAction + private int mAction; + + /** + * Retrieve the time of the most recent key down event, in the + * {@link android.os.SystemClock#uptimeMillis} time base, but in nanoseconds. If this + * is a down event, this will be the same as {@link VerifiedInputEvent#getEventTimeNanos()}. + * + * @see KeyEvent#getDownTime() + */ + @SuppressLint({"MethodNameUnits"}) + private long mDownTimeNanos; + + /** + * Returns the flags for this key event. + * + * @see KeyEvent#getFlags() + * @see KeyEvent#FLAG_CANCELED + * + * @hide + */ + private int mFlags; + + /** + * Retrieve the key code of the key event. + * + * @see KeyEvent#getKeyCode() + */ + private int mKeyCode; + + /** + * Retrieve the hardware key id of this key event. These values are not reliable + * and vary from device to device. + * + * @see KeyEvent#getScanCode() + */ + private int mScanCode; + + /** + * <p>Returns the state of the meta keys.</p> + * + * @return an integer in which each bit set to 1 represents a pressed meta key + * @see KeyEvent#getMetaState() + */ + private int mMetaState; + + /** + * Retrieve the repeat count of the event. For key down events, this is the number of times + * the key has repeated with the first down starting at 0 and counting up from there. + * For key up events, this is always equal to zero. For multiple key events, + * this is the number of down/up pairs that have occurred. + */ + private int mRepeatCount; + + /** + * Get a specific flag of this key event, if possible. Return null if the flag value could + * not be checked. + * + * @param flag the flag of interest + * @return Boolean(true) if the key event has the requested flag + * Boolean(false) if the key event does not have the requested flag + * null if the flag value could not be checked + * + * @see KeyEvent#getFlags() + * @see KeyEvent#FLAG_CANCELED + */ + public @Nullable Boolean getFlag(int flag) { + switch(flag) { + // InputDispatcher only verifies a subset of the KeyEvent flags. + // These values must be kept in sync with Input.cpp + case FLAG_CANCELED: + return (mFlags & flag) != 0; + } + return null; + } + + // The codegen tool doesn't fully support subclasses, since it works on a per-file basis. + // To modify this file: + // 1. run codegen on this file + // 2. edit the constructor signature + // 3. add the "super" call for constructor that receives a Parcel + // 4. add the "super" call to the writeToParcel method + // 5. Update "equals" and "hashcode" methods to include VerifiedInputEvent fields + // 6. Edit "inputSignatures" to ensure KeyEventAction is properly qualified + + + + // Code below generated by codegen v1.0.14. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/VerifiedKeyEvent.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new VerifiedKeyEvent. + * + * @param action + * The action of this key event. May be either {@link KeyEvent#ACTION_DOWN} or + * {@link KeyEvent#ACTION_UP}. + * @param downTimeNanos + * Retrieve the time of the most recent key down event, in the + * {@link android.os.SystemClock#uptimeMillis} time base, but in nanoseconds. If this + * is a down event, this will be the same as {@link VerifiedInputEvent#getEventTimeNanos()}. + * @param flags + * Returns the flags for this key event. + * @param keyCode + * Retrieve the key code of the key event. + * @param scanCode + * Retrieve the hardware key id of this key event. These values are not reliable + * and vary from device to device. + * @param metaState + * <p>Returns the state of the meta keys.</p> + * @param repeatCount + * Retrieve the repeat count of the event. For key down events, this is the number of times + * the key has repeated with the first down starting at 0 and counting up from there. + * For key up events, this is always equal to zero. For multiple key events, + * this is the number of down/up pairs that have occurred. + * @hide + */ + @DataClass.Generated.Member + public VerifiedKeyEvent( + int deviceId, + long eventTimeNanos, + int source, + int displayId, + @KeyEventAction int action, + @SuppressLint({"MethodNameUnits"}) long downTimeNanos, + int flags, + int keyCode, + int scanCode, + int metaState, + int repeatCount) { + super(VERIFIED_KEY, deviceId, eventTimeNanos, source, displayId); + this.mAction = action; + com.android.internal.util.AnnotationValidations.validate( + KeyEventAction.class, null, mAction); + this.mDownTimeNanos = downTimeNanos; + com.android.internal.util.AnnotationValidations.validate( + SuppressLint.class, null, mDownTimeNanos, + "value", "MethodNameUnits"); + this.mFlags = flags; + this.mKeyCode = keyCode; + this.mScanCode = scanCode; + this.mMetaState = metaState; + this.mRepeatCount = repeatCount; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The action of this key event. May be either {@link KeyEvent#ACTION_DOWN} or + * {@link KeyEvent#ACTION_UP}. + * + * @see KeyEvent#getAction() + */ + @DataClass.Generated.Member + public @KeyEventAction int getAction() { + return mAction; + } + + /** + * Retrieve the time of the most recent key down event, in the + * {@link android.os.SystemClock#uptimeMillis} time base, but in nanoseconds. If this + * is a down event, this will be the same as {@link VerifiedInputEvent#getEventTimeNanos()}. + * + * @see KeyEvent#getDownTime() + */ + @DataClass.Generated.Member + public @SuppressLint({"MethodNameUnits"}) long getDownTimeNanos() { + return mDownTimeNanos; + } + + /** + * Returns the flags for this key event. + * + * @see KeyEvent#getFlags() + * @see KeyEvent#FLAG_CANCELED + * @hide + */ + @DataClass.Generated.Member + public int getFlags() { + return mFlags; + } + + /** + * Retrieve the key code of the key event. + * + * @see KeyEvent#getKeyCode() + */ + @DataClass.Generated.Member + public int getKeyCode() { + return mKeyCode; + } + + /** + * Retrieve the hardware key id of this key event. These values are not reliable + * and vary from device to device. + * + * @see KeyEvent#getScanCode() + */ + @DataClass.Generated.Member + public int getScanCode() { + return mScanCode; + } + + /** + * <p>Returns the state of the meta keys.</p> + * + * @return an integer in which each bit set to 1 represents a pressed meta key + * @see KeyEvent#getMetaState() + */ + @DataClass.Generated.Member + public int getMetaState() { + return mMetaState; + } + + /** + * Retrieve the repeat count of the event. For key down events, this is the number of times + * the key has repeated with the first down starting at 0 and counting up from there. + * For key up events, this is always equal to zero. For multiple key events, + * this is the number of down/up pairs that have occurred. + */ + @DataClass.Generated.Member + public int getRepeatCount() { + return mRepeatCount; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(VerifiedKeyEvent other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + VerifiedKeyEvent that = (VerifiedKeyEvent) o; + //noinspection PointlessBooleanExpression + return true + && getDeviceId() == that.getDeviceId() + && getEventTimeNanos() == that.getEventTimeNanos() + && getSource() == that.getSource() + && getDisplayId() == that.getDisplayId() + && mAction == that.mAction + && mDownTimeNanos == that.mDownTimeNanos + && mFlags == that.mFlags + && mKeyCode == that.mKeyCode + && mScanCode == that.mScanCode + && mMetaState == that.mMetaState + && mRepeatCount == that.mRepeatCount; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + getDeviceId(); + _hash = 31 * _hash + Long.hashCode(getEventTimeNanos()); + _hash = 31 * _hash + getSource(); + _hash = 31 * _hash + getDisplayId(); + _hash = 31 * _hash + mAction; + _hash = 31 * _hash + Long.hashCode(mDownTimeNanos); + _hash = 31 * _hash + mFlags; + _hash = 31 * _hash + mKeyCode; + _hash = 31 * _hash + mScanCode; + _hash = 31 * _hash + mMetaState; + _hash = 31 * _hash + mRepeatCount; + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + super.writeToParcel(dest, flags); + dest.writeInt(mAction); + dest.writeLong(mDownTimeNanos); + dest.writeInt(mFlags); + dest.writeInt(mKeyCode); + dest.writeInt(mScanCode); + dest.writeInt(mMetaState); + dest.writeInt(mRepeatCount); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ VerifiedKeyEvent(@android.annotation.NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + super(in, VERIFIED_KEY); + int action = in.readInt(); + long downTimeNanos = in.readLong(); + int flags = in.readInt(); + int keyCode = in.readInt(); + int scanCode = in.readInt(); + int metaState = in.readInt(); + int repeatCount = in.readInt(); + + this.mAction = action; + com.android.internal.util.AnnotationValidations.validate( + KeyEventAction.class, null, mAction); + this.mDownTimeNanos = downTimeNanos; + com.android.internal.util.AnnotationValidations.validate( + SuppressLint.class, null, mDownTimeNanos, + "value", "MethodNameUnits"); + this.mFlags = flags; + this.mKeyCode = keyCode; + this.mScanCode = scanCode; + this.mMetaState = metaState; + this.mRepeatCount = repeatCount; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @android.annotation.NonNull Parcelable.Creator<VerifiedKeyEvent> CREATOR + = new Parcelable.Creator<VerifiedKeyEvent>() { + @Override + public VerifiedKeyEvent[] newArray(int size) { + return new VerifiedKeyEvent[size]; + } + + @Override + public VerifiedKeyEvent createFromParcel(@android.annotation.NonNull Parcel in) { + return new VerifiedKeyEvent(in); + } + }; + + @DataClass.Generated( + time = 1581107066890L, + codegenVersion = "1.0.14", + sourceFile = "frameworks/base/core/java/android/view/VerifiedKeyEvent.java", + inputSignatures = "private static final java.lang.String TAG\nprivate @android.view.VerifiedKeyEvent.KeyEventAction int mAction\nprivate @android.annotation.SuppressLint({\"MethodNameUnits\"}) long mDownTimeNanos\nprivate int mFlags\nprivate int mKeyCode\nprivate int mScanCode\nprivate int mMetaState\nprivate int mRepeatCount\npublic @android.annotation.Nullable java.lang.Boolean getFlag(int)\nclass VerifiedKeyEvent extends android.view.VerifiedInputEvent implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/VerifiedMotionEvent.aidl b/core/java/android/view/VerifiedMotionEvent.aidl new file mode 100644 index 000000000000..3546c631d884 --- /dev/null +++ b/core/java/android/view/VerifiedMotionEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +parcelable VerifiedMotionEvent; diff --git a/core/java/android/view/VerifiedMotionEvent.java b/core/java/android/view/VerifiedMotionEvent.java new file mode 100644 index 000000000000..b4c5d24775ea --- /dev/null +++ b/core/java/android/view/VerifiedMotionEvent.java @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED; +import static android.view.MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.lang.annotation.Retention; + +/** + * MotionEvent that has been verified by the system. + * The data contained in this class is always a subset of a {@link MotionEvent}. Use this class to + * check which data has been confirmed by the system to be authentic. + * + * Most applications do not need to use this class. + * + * {@see android.hardware.input.InputManager#verifyInputEvent} + */ +@DataClass(genHiddenConstructor = true, genEqualsHashCode = true) +public final class VerifiedMotionEvent extends VerifiedInputEvent implements Parcelable { + private static final String TAG = "VerifiedMotionEvent"; + + /** + * The raw X coordinate of the primary pointer. + * @see MotionEvent#getRawX() + */ + private float mRawX; + + /** + * The raw Y coordinate of the primary pointer. + * @see MotionEvent#getRawY() + */ + private float mRawY; + + /** @hide */ + @Retention(SOURCE) + @IntDef({MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_CANCEL, + MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_UP}) + public @interface MotionEventAction {}; + + /** + * The masked action being performed, without pointer index information. + * + * @see MotionEvent#getActionMasked() + */ + @MotionEventAction + private int mActionMasked; + + /** + * The time that the gesture started, in nanoseconds. + * Uses the same time base as {@link android.os.SystemClock#uptimeMillis()} + * + * @see MotionEvent#getDownTime() + */ + @SuppressLint({"MethodNameUnits"}) + private long mDownTimeNanos; + + /** + * Returns the flags for this motion event. + * + * @see MotionEvent#getFlags() + * @hide + */ + private int mFlags; + + /** + * The state of any meta / modifier keys that were in effect when the event was generated. + * + * @see MotionEvent#getMetaState() + */ + private int mMetaState; + + /** + * The state of all buttons that are pressed such as a mouse or stylus button. + * + * @see MotionEvent#getButtonState() + */ + private int mButtonState; + + /** + * Get a specific flag of this motion event, if possible. Return null if the flag value could + * not be checked. + * + * @param flag the flag of interest + * @return Boolean(true) if the motion event has the requested flag + * Boolean(false) if the motion event does not have the requested flag + * null if the flag value could not be checked + * + * @see MotionEvent#FLAG_WINDOW_IS_OBSCURED + * @see MotionEvent#FLAG_WINDOW_IS_PARTIALLY_OBSCURED + */ + public @Nullable Boolean getFlag(int flag) { + switch(flag) { + // InputDispatcher only verifies a subset of the MotionEvent flags. + // These values must be kept in sync with Input.cpp + case FLAG_WINDOW_IS_OBSCURED: + case FLAG_WINDOW_IS_PARTIALLY_OBSCURED: + return (mFlags & flag) != 0; + } + return null; + } + + // The codegen tool doesn't fully support subclasses, since it works on a per-file basis. + // To modify this file: + // 1. run codegen on this file + // 2. edit the constructor signature + // 3. add the "super" call for constructor that receives a Parcel + // 4. add the "super" call to the writeToParcel method + // 5. Update "equals" and "hashcode" methods to include VerifiedInputEvent fields + // 6. Edit "inputSignatures" to ensure MotionEventAction is properly qualified + + + + // Code below generated by codegen v1.0.14. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/VerifiedMotionEvent.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new VerifiedMotionEvent. + * + * @param rawX + * The raw X coordinate of the primary pointer. + * @param rawY + * The raw Y coordinate of the primary pointer. + * @param actionMasked + * The masked action being performed, without pointer index information. + * @param downTimeNanos + * The time that the gesture started, in nanoseconds. + * Uses the same time base as {@link android.os.SystemClock#uptimeMillis()} + * @param flags + * Returns the flags for this motion event. + * @param metaState + * The state of any meta / modifier keys that were in effect when the event was generated. + * @param buttonState + * The state of all buttons that are pressed such as a mouse or stylus button. + * @hide + */ + @DataClass.Generated.Member + public VerifiedMotionEvent( + int deviceId, long eventTimeNanos, int source, int displayId, + float rawX, + float rawY, + @MotionEventAction int actionMasked, + @SuppressLint({"MethodNameUnits"}) long downTimeNanos, + int flags, + int metaState, + int buttonState) { + super(VERIFIED_MOTION, deviceId, eventTimeNanos, source, displayId); + this.mRawX = rawX; + this.mRawY = rawY; + this.mActionMasked = actionMasked; + com.android.internal.util.AnnotationValidations.validate( + MotionEventAction.class, null, mActionMasked); + this.mDownTimeNanos = downTimeNanos; + com.android.internal.util.AnnotationValidations.validate( + SuppressLint.class, null, mDownTimeNanos, + "value", "MethodNameUnits"); + this.mFlags = flags; + this.mMetaState = metaState; + this.mButtonState = buttonState; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The raw X coordinate of the primary pointer. + * + * @see MotionEvent#getRawX() + */ + @DataClass.Generated.Member + public float getRawX() { + return mRawX; + } + + /** + * The raw Y coordinate of the primary pointer. + * + * @see MotionEvent#getRawY() + */ + @DataClass.Generated.Member + public float getRawY() { + return mRawY; + } + + /** + * The masked action being performed, without pointer index information. + * + * @see MotionEvent#getActionMasked() + */ + @DataClass.Generated.Member + public @MotionEventAction int getActionMasked() { + return mActionMasked; + } + + /** + * The time that the gesture started, in nanoseconds. + * Uses the same time base as {@link android.os.SystemClock#uptimeMillis()} + * + * @see MotionEvent#getDownTime() + */ + @DataClass.Generated.Member + public @SuppressLint({"MethodNameUnits"}) long getDownTimeNanos() { + return mDownTimeNanos; + } + + /** + * Returns the flags for this motion event. + * + * @see MotionEvent#getFlags() + * @hide + */ + @DataClass.Generated.Member + public int getFlags() { + return mFlags; + } + + /** + * The state of any meta / modifier keys that were in effect when the event was generated. + * + * @see MotionEvent#getMetaState() + */ + @DataClass.Generated.Member + public int getMetaState() { + return mMetaState; + } + + /** + * The state of all buttons that are pressed such as a mouse or stylus button. + * + * @see MotionEvent#getButtonState() + */ + @DataClass.Generated.Member + public int getButtonState() { + return mButtonState; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(VerifiedMotionEvent other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + VerifiedMotionEvent that = (VerifiedMotionEvent) o; + //noinspection PointlessBooleanExpression + return true + && getDeviceId() == that.getDeviceId() + && getEventTimeNanos() == that.getEventTimeNanos() + && getSource() == that.getSource() + && getDisplayId() == that.getDisplayId() + && mRawX == that.mRawX + && mRawY == that.mRawY + && mActionMasked == that.mActionMasked + && mDownTimeNanos == that.mDownTimeNanos + && mFlags == that.mFlags + && mMetaState == that.mMetaState + && mButtonState == that.mButtonState; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + getDeviceId(); + _hash = 31 * _hash + Long.hashCode(getEventTimeNanos()); + _hash = 31 * _hash + getSource(); + _hash = 31 * _hash + getDisplayId(); + _hash = 31 * _hash + Float.hashCode(mRawX); + _hash = 31 * _hash + Float.hashCode(mRawY); + _hash = 31 * _hash + mActionMasked; + _hash = 31 * _hash + Long.hashCode(mDownTimeNanos); + _hash = 31 * _hash + mFlags; + _hash = 31 * _hash + mMetaState; + _hash = 31 * _hash + mButtonState; + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + super.writeToParcel(dest, flags); + dest.writeFloat(mRawX); + dest.writeFloat(mRawY); + dest.writeInt(mActionMasked); + dest.writeLong(mDownTimeNanos); + dest.writeInt(mFlags); + dest.writeInt(mMetaState); + dest.writeInt(mButtonState); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ VerifiedMotionEvent(@android.annotation.NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + super(in, VERIFIED_MOTION); + float rawX = in.readFloat(); + float rawY = in.readFloat(); + int actionMasked = in.readInt(); + long downTimeNanos = in.readLong(); + int flags = in.readInt(); + int metaState = in.readInt(); + int buttonState = in.readInt(); + + this.mRawX = rawX; + this.mRawY = rawY; + this.mActionMasked = actionMasked; + com.android.internal.util.AnnotationValidations.validate( + MotionEventAction.class, null, mActionMasked); + this.mDownTimeNanos = downTimeNanos; + com.android.internal.util.AnnotationValidations.validate( + SuppressLint.class, null, mDownTimeNanos, + "value", "MethodNameUnits"); + this.mFlags = flags; + this.mMetaState = metaState; + this.mButtonState = buttonState; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @android.annotation.NonNull Parcelable.Creator<VerifiedMotionEvent> CREATOR + = new Parcelable.Creator<VerifiedMotionEvent>() { + @Override + public VerifiedMotionEvent[] newArray(int size) { + return new VerifiedMotionEvent[size]; + } + + @Override + public VerifiedMotionEvent createFromParcel(@android.annotation.NonNull Parcel in) { + return new VerifiedMotionEvent(in); + } + }; + + @DataClass.Generated( + time = 1581107073238L, + codegenVersion = "1.0.14", + sourceFile = "frameworks/base/core/java/android/view/VerifiedMotionEvent.java", + inputSignatures = "private static final java.lang.String TAG\nprivate float mRawX\nprivate float mRawY\nprivate @android.view.VerifiedMotionEvent.MotionEventAction int mActionMasked\nprivate @android.annotation.SuppressLint({\"MethodNameUnits\"}) long mDownTimeNanos\nprivate int mFlags\nprivate int mMetaState\nprivate int mButtonState\npublic @android.annotation.Nullable java.lang.Boolean getFlag(int)\nclass VerifiedMotionEvent extends android.view.VerifiedInputEvent implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 8959d6fb845e..6ca0b0e7a9e6 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -125,6 +125,8 @@ cc_library_shared { "android_view_SurfaceSession.cpp", "android_view_TextureView.cpp", "android_view_VelocityTracker.cpp", + "android_view_VerifiedKeyEvent.cpp", + "android_view_VerifiedMotionEvent.cpp", "android_text_Hyphenator.cpp", "android_os_Debug.cpp", "android_os_GraphicsEnvironment.cpp", diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp index 57979bd9b546..bbe563e12a4c 100644 --- a/core/jni/android_view_KeyEvent.cpp +++ b/core/jni/android_view_KeyEvent.cpp @@ -102,7 +102,7 @@ jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) { event->getRepeatCount(), event->getMetaState(), event->getDeviceId(), event->getScanCode(), event->getFlags(), event->getSource(), - event->getDisplayId(), hmac.get(), NULL); + event->getDisplayId(), hmac.get(), nullptr); if (env->ExceptionCheck()) { ALOGE("An exception occurred while obtaining a key event."); LOGE_EX(env); diff --git a/core/jni/android_view_VerifiedKeyEvent.cpp b/core/jni/android_view_VerifiedKeyEvent.cpp new file mode 100644 index 000000000000..8fc301ccc735 --- /dev/null +++ b/core/jni/android_view_VerifiedKeyEvent.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MotionEvent-JNI" + +#include "android_view_VerifiedKeyEvent.h" +#include <input/Input.h> +#include "core_jni_helpers.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +jobject android_view_VerifiedKeyEvent(JNIEnv* env, const VerifiedKeyEvent& event) { + static jclass clazz = FindClassOrDie(env, "android/view/VerifiedKeyEvent"); + + static jmethodID constructor = GetMethodIDOrDie(env, clazz, "<init>", "(IJIIIJIIIII)V"); + + jobject object = + env->NewObject(clazz, constructor, event.deviceId, event.eventTimeNanos, event.source, + event.displayId, event.action, event.downTimeNanos, event.flags, + event.keyCode, event.scanCode, event.metaState, event.repeatCount); + return object; +} + +} // namespace android diff --git a/core/jni/android_view_VerifiedKeyEvent.h b/core/jni/android_view_VerifiedKeyEvent.h new file mode 100644 index 000000000000..032291b3c39f --- /dev/null +++ b/core/jni/android_view_VerifiedKeyEvent.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_VIEW_VERIFIEDKEYEVENT_H +#define _ANDROID_VIEW_VERIFIEDKEYEVENT_H + +#include "jni.h" + +namespace android { + +class VerifiedKeyEvent; + +/* Create an instance of a DVM VerifiedMotionEvent object + * Return nullptr on error. */ +extern jobject android_view_VerifiedKeyEvent(JNIEnv* env, const VerifiedKeyEvent& event); + +} // namespace android + +#endif // _ANDROID_VIEW_VERIFIEDKEYEVENT_H diff --git a/core/jni/android_view_VerifiedMotionEvent.cpp b/core/jni/android_view_VerifiedMotionEvent.cpp new file mode 100644 index 000000000000..7a5c71aa6602 --- /dev/null +++ b/core/jni/android_view_VerifiedMotionEvent.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MotionEvent-JNI" + +#include "android_view_VerifiedMotionEvent.h" +#include <input/Input.h> +#include "core_jni_helpers.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +jobject android_view_VerifiedMotionEvent(JNIEnv* env, const VerifiedMotionEvent& event) { + static jclass clazz = FindClassOrDie(env, "android/view/VerifiedMotionEvent"); + + static jmethodID constructor = GetMethodIDOrDie(env, clazz, "<init>", "(IJIIFFIJIII)V"); + + jobject object = + env->NewObject(clazz, constructor, event.deviceId, event.eventTimeNanos, event.source, + event.displayId, event.rawX, event.rawY, event.actionMasked, + event.downTimeNanos, event.flags, event.metaState, event.buttonState); + return object; +} + +} // namespace android diff --git a/core/jni/android_view_VerifiedMotionEvent.h b/core/jni/android_view_VerifiedMotionEvent.h new file mode 100644 index 000000000000..deda1b7255da --- /dev/null +++ b/core/jni/android_view_VerifiedMotionEvent.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_VIEW_VERIFIEDMOTIONEVENT_H +#define _ANDROID_VIEW_VERIFIEDMOTIONEVENT_H + +#include "jni.h" + +namespace android { + +class VerifiedMotionEvent; + +/* Create an instance of a DVM VerifiedMotionEvent object + * Return nullptr on error. */ +extern jobject android_view_VerifiedMotionEvent(JNIEnv* env, const VerifiedMotionEvent& event); + +} // namespace android + +#endif // _ANDROID_VIEW_VERIFIEDMOTIONEVENT_H diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index e04d3de622d8..18778b93fb57 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -3,6 +3,7 @@ android_test { srcs: [ "src/**/*.java", + "src/**/*.kt", "src/**/I*.aidl", "DisabledTestApp/src/**/*.java", "EnabledTestApp/src/**/*.java", diff --git a/core/tests/coretests/src/android/view/VerifiedKeyEventTest.kt b/core/tests/coretests/src/android/view/VerifiedKeyEventTest.kt new file mode 100644 index 000000000000..531d55d7738a --- /dev/null +++ b/core/tests/coretests/src/android/view/VerifiedKeyEventTest.kt @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view + +import android.view.InputDevice.SOURCE_KEYBOARD +import android.view.KeyEvent.ACTION_DOWN +import android.os.Parcel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.runner.RunWith +import org.junit.Test + +@RunWith(AndroidJUnit4::class) +@SmallTest +class VerifiedKeyEventTest { + + @Test + fun testConstructor() { + val event = createVerifiedKeyEvent() + + assertEquals(DEVICE_ID, event.deviceId) + assertEquals(EVENT_TIME_NANOS, event.eventTimeNanos) + assertEquals(SOURCE, event.source) + assertEquals(DISPLAY_ID, event.displayId) + + assertEquals(ACTION, event.action) + assertEquals(DOWN_TIME_NANOS, event.downTimeNanos) + assertEquals(FLAGS, event.flags) + assertEquals(KEY_CODE, event.keyCode) + assertEquals(SCAN_CODE, event.scanCode) + assertEquals(META_STATE, event.metaState) + assertEquals(REPEAT_COUNT, event.repeatCount) + } + + /** + * Write to parcel as a KeyEvent, read back as a KeyEvent + */ + @Test + fun testParcelUnparcel() { + val keyEvent = createVerifiedKeyEvent() + val parcel = Parcel.obtain() + keyEvent.writeToParcel(parcel, 0 /*flags*/) + parcel.setDataPosition(0) + + val unparceledKeyEvent = VerifiedKeyEvent.CREATOR.createFromParcel(parcel) + parcel.recycle() + + compareVerifiedKeyEvents(keyEvent, unparceledKeyEvent) + } + + /** + * Write to parcel as an InputEvent, read back as an InputEvent + */ + @Test + fun testParcelInputEvent() { + val keyEvent = createVerifiedKeyEvent() + val inputEvent: VerifiedInputEvent = keyEvent + val parcel = Parcel.obtain() + inputEvent.writeToParcel(parcel, 0 /*flags*/) + parcel.setDataPosition(0) + + val unparceledEvent = VerifiedInputEvent.CREATOR.createFromParcel(parcel) + parcel.recycle() + + assertTrue(unparceledEvent is VerifiedKeyEvent) + compareVerifiedKeyEvents(keyEvent, unparceledEvent as VerifiedKeyEvent) + } + + /** + * Write to parcel as a KeyEvent, read back as an InputEvent + */ + @Test + fun testParcelKeyEvent() { + val keyEvent = createVerifiedKeyEvent() + val parcel = Parcel.obtain() + keyEvent.writeToParcel(parcel, 0 /*flags*/) + parcel.setDataPosition(0) + + val unparceledEvent = VerifiedInputEvent.CREATOR.createFromParcel(parcel) + parcel.recycle() + + assertTrue(unparceledEvent is VerifiedKeyEvent) + compareVerifiedKeyEvents(keyEvent, unparceledEvent as VerifiedKeyEvent) + } + + /** + * Write to parcel as an InputEvent, read back as a KeyEvent + */ + @Test + fun testParcelInputToKeyEvent() { + val keyEvent = createVerifiedKeyEvent() + val inputEvent: VerifiedInputEvent = keyEvent + val parcel = Parcel.obtain() + inputEvent.writeToParcel(parcel, 0 /*flags*/) + parcel.setDataPosition(0) + + val unparceledEvent = VerifiedKeyEvent.CREATOR.createFromParcel(parcel) + parcel.recycle() + + compareVerifiedKeyEvents(keyEvent, unparceledEvent) + } + + @Test + fun testEqualsHashcode() { + val keyEvent1 = createVerifiedKeyEvent() + val keyEvent2 = createVerifiedKeyEvent() + compareVerifiedKeyEvents(keyEvent1, keyEvent2) + } + + companion object { + private const val DEVICE_ID = 0 + private const val EVENT_TIME_NANOS: Long = 2000 + private const val SOURCE = SOURCE_KEYBOARD + private const val DISPLAY_ID = 2 + + private const val ACTION = ACTION_DOWN + private const val DOWN_TIME_NANOS: Long = 1000 + private const val FLAGS = 3 + private const val KEY_CODE = 4 + private const val SCAN_CODE = 5 + private const val META_STATE = 11 + private const val REPEAT_COUNT = 22 + + private fun createVerifiedKeyEvent(): VerifiedKeyEvent { + return VerifiedKeyEvent(DEVICE_ID, EVENT_TIME_NANOS, SOURCE, DISPLAY_ID, ACTION, + DOWN_TIME_NANOS, FLAGS, KEY_CODE, SCAN_CODE, META_STATE, REPEAT_COUNT) + } + + private fun compareVerifiedKeyEvents(event1: VerifiedKeyEvent, event2: VerifiedKeyEvent) { + assertEquals(event1, event2) + assertEquals(event1.hashCode(), event2.hashCode()) + + assertEquals(event1.deviceId, event2.deviceId) + assertEquals(event1.eventTimeNanos, event2.eventTimeNanos) + assertEquals(event1.source, event2.source) + assertEquals(event1.displayId, event2.displayId) + + assertEquals(event1.action, event2.action) + assertEquals(event1.downTimeNanos, event2.downTimeNanos) + assertEquals(event1.flags, event2.flags) + assertEquals(event1.keyCode, event2.keyCode) + assertEquals(event1.scanCode, event2.scanCode) + assertEquals(event1.metaState, event2.metaState) + assertEquals(event1.repeatCount, event2.repeatCount) + } + } +} diff --git a/core/tests/coretests/src/android/view/VerifiedMotionEventTest.kt b/core/tests/coretests/src/android/view/VerifiedMotionEventTest.kt new file mode 100644 index 000000000000..2c4851f0ab4e --- /dev/null +++ b/core/tests/coretests/src/android/view/VerifiedMotionEventTest.kt @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view + +import android.view.InputDevice.SOURCE_TOUCHSCREEN +import android.view.MotionEvent.ACTION_MOVE +import android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED +import android.view.MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED +import android.view.MotionEvent.FLAG_TAINTED +import android.os.Parcel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.runner.RunWith +import org.junit.Test + +@RunWith(AndroidJUnit4::class) +@SmallTest +class VerifiedMotionEventTest { + + @Test + fun testConstructor() { + val event = createVerifiedMotionEvent() + + assertEquals(DEVICE_ID, event.deviceId) + assertEquals(EVENT_TIME_NANOS, event.eventTimeNanos) + assertEquals(SOURCE, event.source) + assertEquals(DISPLAY_ID, event.displayId) + + assertEquals(RAW_X, event.rawX, 0f) + assertEquals(RAW_Y, event.rawY, 0f) + assertEquals(ACTION_MASKED, event.actionMasked) + assertEquals(DOWN_TIME_NANOS, event.downTimeNanos) + assertEquals(META_STATE, event.metaState) + assertEquals(BUTTON_STATE, event.buttonState) + } + + /** + * Write to parcel as a MotionEvent, read back as a MotionEvent + */ + @Test + fun testParcelUnparcel() { + val motionEvent = createVerifiedMotionEvent() + val parcel = Parcel.obtain() + motionEvent.writeToParcel(parcel, 0 /*flags*/) + parcel.setDataPosition(0) + + val unparceledMotionEvent = VerifiedMotionEvent.CREATOR.createFromParcel(parcel) + parcel.recycle() + + compareVerifiedMotionEvents(motionEvent, unparceledMotionEvent) + } + + /** + * Write to parcel as an InputEvent, read back as an InputEvent + */ + @Test + fun testParcelInputEvent() { + val motionEvent = createVerifiedMotionEvent() + val inputEvent: VerifiedInputEvent = motionEvent + val parcel = Parcel.obtain() + inputEvent.writeToParcel(parcel, 0 /*flags*/) + parcel.setDataPosition(0) + + val unparceledEvent = VerifiedInputEvent.CREATOR.createFromParcel(parcel) + parcel.recycle() + + assertTrue(unparceledEvent is VerifiedMotionEvent) + compareVerifiedMotionEvents(motionEvent, unparceledEvent as VerifiedMotionEvent) + } + + /** + * Write to parcel as a MotionEvent, read back as an InputEvent + */ + @Test + fun testParcelMotionEvent() { + val motionEvent = createVerifiedMotionEvent() + val parcel = Parcel.obtain() + motionEvent.writeToParcel(parcel, 0 /*flags*/) + parcel.setDataPosition(0) + + val unparceledEvent = VerifiedInputEvent.CREATOR.createFromParcel(parcel) + parcel.recycle() + + assertTrue(unparceledEvent is VerifiedMotionEvent) + compareVerifiedMotionEvents(motionEvent, unparceledEvent as VerifiedMotionEvent) + } + + /** + * Write to parcel as an InputEvent, read back as a MotionEvent + */ + @Test + fun testParcelInputToMotionEvent() { + val motionEvent = createVerifiedMotionEvent() + val inputEvent: VerifiedInputEvent = motionEvent + val parcel = Parcel.obtain() + inputEvent.writeToParcel(parcel, 0 /*flags*/) + parcel.setDataPosition(0) + + val unparceledEvent = VerifiedMotionEvent.CREATOR.createFromParcel(parcel) + parcel.recycle() + + compareVerifiedMotionEvents(motionEvent, unparceledEvent) + } + + @Test + fun testGetFlag() { + val motionEvent = createVerifiedMotionEvent() + // Invalid value of a flag + assertNull(motionEvent.getFlag(0)) + // Flag that was not set + assertEquals(false, motionEvent.getFlag(FLAG_WINDOW_IS_PARTIALLY_OBSCURED)) + // Flag that was set + assertEquals(true, motionEvent.getFlag(FLAG_WINDOW_IS_OBSCURED)) + // Only 1 flag at a time is accepted + assertNull(motionEvent.getFlag( + FLAG_WINDOW_IS_PARTIALLY_OBSCURED or FLAG_WINDOW_IS_OBSCURED)) + // Flag that is not verified returns null + assertNull(motionEvent.getFlag(FLAG_TAINTED)) + } + + @Test + fun testEqualsHashcode() { + val motionEvent1 = createVerifiedMotionEvent() + val motionEvent2 = createVerifiedMotionEvent() + compareVerifiedMotionEvents(motionEvent1, motionEvent2) + } + + companion object { + private const val DEVICE_ID = 0 + private const val EVENT_TIME_NANOS: Long = 2000 + private const val SOURCE = SOURCE_TOUCHSCREEN + private const val DISPLAY_ID = 2 + private const val RAW_X = 100f + 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 META_STATE = 11 + private const val BUTTON_STATE = 22 + + private fun createVerifiedMotionEvent(): VerifiedMotionEvent { + return VerifiedMotionEvent(DEVICE_ID, EVENT_TIME_NANOS, SOURCE, DISPLAY_ID, + RAW_X, RAW_Y, ACTION_MASKED, DOWN_TIME_NANOS, FLAGS, META_STATE, BUTTON_STATE) + } + + private fun compareVerifiedMotionEvents( + event1: VerifiedMotionEvent, + event2: VerifiedMotionEvent + ) { + assertEquals(event1, event2) + assertEquals(event1.hashCode(), event2.hashCode()) + + assertEquals(event1.deviceId, event2.deviceId) + assertEquals(event1.eventTimeNanos, event2.eventTimeNanos) + assertEquals(event1.source, event2.source) + assertEquals(event1.displayId, event2.displayId) + + assertEquals(event1.rawX, event2.rawX, 0f) + assertEquals(event1.rawY, event2.rawY, 0f) + assertEquals(event1.actionMasked, event2.actionMasked) + assertEquals(event1.downTimeNanos, event2.downTimeNanos) + assertEquals(event1.metaState, event2.metaState) + assertEquals(event1.buttonState, event2.buttonState) + } + } +} diff --git a/native/android/Android.bp b/native/android/Android.bp index 0c6f507787d9..257ae7332cc1 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -25,6 +25,7 @@ cc_defaults { cflags: [ "-Wall", "-Werror", + "-Wextra", "-Wunused", "-Wunreachable-code", ], diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index a8f706cdd629..ca06b47a6e80 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -79,6 +79,7 @@ import android.view.InputWindowHandle; import android.view.KeyEvent; import android.view.PointerIcon; import android.view.Surface; +import android.view.VerifiedInputEvent; import android.view.ViewConfiguration; import android.widget.Toast; @@ -214,6 +215,7 @@ public class InputManagerService extends IInputManager.Stub private static native int nativeInjectInputEvent(long ptr, InputEvent event, int injectorPid, int injectorUid, int syncMode, int timeoutMillis, int policyFlags); + private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event); private static native void nativeToggleCapsLock(long ptr, int deviceId); private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles, int displayId); @@ -672,6 +674,11 @@ public class InputManagerService extends IInputManager.Stub } } + @Override // Binder call + public VerifiedInputEvent verifyInputEvent(InputEvent event) { + return nativeVerifyInputEvent(mPtr, event); + } + /** * Gets information about the input device with the specified id. * @param deviceId The device id. diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 49db3d5b2b64..8772edd73845 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -50,13 +50,15 @@ #include <inputflinger/InputManager.h> +#include <android/graphics/GraphicsJNI.h> #include <android_os_MessageQueue.h> +#include <android_view_InputChannel.h> #include <android_view_InputDevice.h> #include <android_view_KeyEvent.h> #include <android_view_MotionEvent.h> -#include <android_view_InputChannel.h> #include <android_view_PointerIcon.h> -#include <android/graphics/GraphicsJNI.h> +#include <android_view_VerifiedKeyEvent.h> +#include <android_view_VerifiedMotionEvent.h> #include <nativehelper/ScopedLocalFrame.h> #include <nativehelper/ScopedLocalRef.h> @@ -1513,6 +1515,49 @@ static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */, } } +static jobject nativeVerifyInputEvent(JNIEnv* env, jclass /* clazz */, jlong ptr, + jobject inputEventObj) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) { + KeyEvent keyEvent; + status_t status = android_view_KeyEvent_toNative(env, inputEventObj, &keyEvent); + if (status) { + jniThrowRuntimeException(env, "Could not read contents of KeyEvent object."); + return nullptr; + } + + std::unique_ptr<VerifiedInputEvent> verifiedEvent = + im->getInputManager()->getDispatcher()->verifyInputEvent(keyEvent); + if (verifiedEvent == nullptr) { + return nullptr; + } + + return android_view_VerifiedKeyEvent(env, + static_cast<const VerifiedKeyEvent&>(*verifiedEvent)); + } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) { + const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj); + if (!motionEvent) { + jniThrowRuntimeException(env, "Could not read contents of MotionEvent object."); + return nullptr; + } + + std::unique_ptr<VerifiedInputEvent> verifiedEvent = + im->getInputManager()->getDispatcher()->verifyInputEvent(*motionEvent); + + if (verifiedEvent == nullptr) { + return nullptr; + } + + return android_view_VerifiedMotionEvent(env, + static_cast<const VerifiedMotionEvent&>( + *verifiedEvent)); + } else { + jniThrowRuntimeException(env, "Invalid input event type."); + return nullptr; + } +} + static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -1735,90 +1780,61 @@ static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */, // ---------------------------------------------------------------------------- static const JNINativeMethod gInputManagerMethods[] = { - /* name, signature, funcPtr */ - { "nativeInit", - "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J", - (void*) nativeInit }, - { "nativeStart", "(J)V", - (void*) nativeStart }, - { "nativeSetDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V", - (void*) nativeSetDisplayViewports }, - { "nativeGetScanCodeState", "(JIII)I", - (void*) nativeGetScanCodeState }, - { "nativeGetKeyCodeState", "(JIII)I", - (void*) nativeGetKeyCodeState }, - { "nativeGetSwitchState", "(JIII)I", - (void*) nativeGetSwitchState }, - { "nativeHasKeys", "(JII[I[Z)Z", - (void*) nativeHasKeys }, - { "nativeRegisterInputChannel", - "(JLandroid/view/InputChannel;)V", - (void*) nativeRegisterInputChannel }, - { "nativeRegisterInputMonitor", - "(JLandroid/view/InputChannel;IZ)V", - (void*) nativeRegisterInputMonitor}, - { "nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V", - (void*) nativeUnregisterInputChannel }, - { "nativePilferPointers", "(JLandroid/os/IBinder;)V", - (void*) nativePilferPointers }, - { "nativeSetInputFilterEnabled", "(JZ)V", - (void*) nativeSetInputFilterEnabled }, - { "nativeSetInTouchMode", "(JZ)V", - (void*) nativeSetInTouchMode }, - { "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I", - (void*) nativeInjectInputEvent }, - { "nativeToggleCapsLock", "(JI)V", - (void*) nativeToggleCapsLock }, - { "nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V", - (void*) nativeSetInputWindows }, - { "nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V", - (void*) nativeSetFocusedApplication }, - { "nativeSetFocusedDisplay", "(JI)V", - (void*) nativeSetFocusedDisplay }, - { "nativeSetPointerCapture", "(JZ)V", - (void*) nativeSetPointerCapture }, - { "nativeSetInputDispatchMode", "(JZZ)V", - (void*) nativeSetInputDispatchMode }, - { "nativeSetSystemUiVisibility", "(JI)V", - (void*) nativeSetSystemUiVisibility }, - { "nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z", - (void*) nativeTransferTouchFocus }, - { "nativeSetPointerSpeed", "(JI)V", - (void*) nativeSetPointerSpeed }, - { "nativeSetShowTouches", "(JZ)V", - (void*) nativeSetShowTouches }, - { "nativeSetInteractive", "(JZ)V", - (void*) nativeSetInteractive }, - { "nativeReloadCalibration", "(J)V", - (void*) nativeReloadCalibration }, - { "nativeVibrate", "(JI[JII)V", - (void*) nativeVibrate }, - { "nativeCancelVibrate", "(JII)V", - (void*) nativeCancelVibrate }, - { "nativeReloadKeyboardLayouts", "(J)V", - (void*) nativeReloadKeyboardLayouts }, - { "nativeReloadDeviceAliases", "(J)V", - (void*) nativeReloadDeviceAliases }, - { "nativeDump", "(J)Ljava/lang/String;", - (void*) nativeDump }, - { "nativeMonitor", "(J)V", - (void*) nativeMonitor }, - { "nativeIsInputDeviceEnabled", "(JI)Z", - (void*) nativeIsInputDeviceEnabled }, - { "nativeEnableInputDevice", "(JI)V", - (void*) nativeEnableInputDevice }, - { "nativeDisableInputDevice", "(JI)V", - (void*) nativeDisableInputDevice }, - { "nativeSetPointerIconType", "(JI)V", - (void*) nativeSetPointerIconType }, - { "nativeReloadPointerIcons", "(J)V", - (void*) nativeReloadPointerIcons }, - { "nativeSetCustomPointerIcon", "(JLandroid/view/PointerIcon;)V", - (void*) nativeSetCustomPointerIcon }, - { "nativeCanDispatchToDisplay", "(JII)Z", - (void*) nativeCanDispatchToDisplay }, - { "nativeNotifyPortAssociationsChanged", "(J)V", - (void*) nativeNotifyPortAssociationsChanged }, + /* name, signature, funcPtr */ + {"nativeInit", + "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/" + "MessageQueue;)J", + (void*)nativeInit}, + {"nativeStart", "(J)V", (void*)nativeStart}, + {"nativeSetDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V", + (void*)nativeSetDisplayViewports}, + {"nativeGetScanCodeState", "(JIII)I", (void*)nativeGetScanCodeState}, + {"nativeGetKeyCodeState", "(JIII)I", (void*)nativeGetKeyCodeState}, + {"nativeGetSwitchState", "(JIII)I", (void*)nativeGetSwitchState}, + {"nativeHasKeys", "(JII[I[Z)Z", (void*)nativeHasKeys}, + {"nativeRegisterInputChannel", "(JLandroid/view/InputChannel;)V", + (void*)nativeRegisterInputChannel}, + {"nativeRegisterInputMonitor", "(JLandroid/view/InputChannel;IZ)V", + (void*)nativeRegisterInputMonitor}, + {"nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V", + (void*)nativeUnregisterInputChannel}, + {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers}, + {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled}, + {"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode}, + {"nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I", + (void*)nativeInjectInputEvent}, + {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", + (void*)nativeVerifyInputEvent}, + {"nativeToggleCapsLock", "(JI)V", (void*)nativeToggleCapsLock}, + {"nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V", + (void*)nativeSetInputWindows}, + {"nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V", + (void*)nativeSetFocusedApplication}, + {"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay}, + {"nativeSetPointerCapture", "(JZ)V", (void*)nativeSetPointerCapture}, + {"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode}, + {"nativeSetSystemUiVisibility", "(JI)V", (void*)nativeSetSystemUiVisibility}, + {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z", + (void*)nativeTransferTouchFocus}, + {"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed}, + {"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches}, + {"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive}, + {"nativeReloadCalibration", "(J)V", (void*)nativeReloadCalibration}, + {"nativeVibrate", "(JI[JII)V", (void*)nativeVibrate}, + {"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate}, + {"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts}, + {"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases}, + {"nativeDump", "(J)Ljava/lang/String;", (void*)nativeDump}, + {"nativeMonitor", "(J)V", (void*)nativeMonitor}, + {"nativeIsInputDeviceEnabled", "(JI)Z", (void*)nativeIsInputDeviceEnabled}, + {"nativeEnableInputDevice", "(JI)V", (void*)nativeEnableInputDevice}, + {"nativeDisableInputDevice", "(JI)V", (void*)nativeDisableInputDevice}, + {"nativeSetPointerIconType", "(JI)V", (void*)nativeSetPointerIconType}, + {"nativeReloadPointerIcons", "(J)V", (void*)nativeReloadPointerIcons}, + {"nativeSetCustomPointerIcon", "(JLandroid/view/PointerIcon;)V", + (void*)nativeSetCustomPointerIcon}, + {"nativeCanDispatchToDisplay", "(JII)Z", (void*)nativeCanDispatchToDisplay}, + {"nativeNotifyPortAssociationsChanged", "(J)V", (void*)nativeNotifyPortAssociationsChanged}, }; #define FIND_CLASS(var, className) \ |