Add verifyInputEvent api to InputDispatcher
Now InputDispatcher will be able to check whether a certain InputEvent
is legitimate.
Use the 'verifyInputEvent' api to determine if a given 'InputEvent'
actually came from InputDispatcher.
Bug: 134977432
Test: atest VerifiedKeyEventTest VerifiedMotionEventTest libinput_tests
inputflinger_tests
Change-Id: I8e7fa9bfa3c14b0b0d949fb5e28b43ff7583398f
diff --git a/include/input/Input.h b/include/input/Input.h
index cf0814c..14a7288 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -73,6 +73,19 @@
AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
};
+/**
+ * Allowed VerifiedKeyEvent flags. All other flags from KeyEvent do not get verified.
+ * These values must be kept in sync with VerifiedKeyEvent.java
+ */
+constexpr int32_t VERIFIED_KEY_EVENT_FLAGS = AKEY_EVENT_FLAG_CANCELED;
+
+/**
+ * Allowed VerifiedMotionEventFlags. All other flags from MotionEvent do not get verified.
+ * These values must be kept in sync with VerifiedMotionEvent.java
+ */
+constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
enum {
/* Used when a motion event is not associated with any display.
* Typically used for non-pointer events. */
@@ -718,6 +731,55 @@
bool mInTouchMode;
};
+/**
+ * Base class for verified events.
+ * Do not create a VerifiedInputEvent explicitly.
+ * Use helper functions to create them from InputEvents.
+ */
+struct __attribute__((__packed__)) VerifiedInputEvent {
+ enum class Type : int32_t {
+ KEY = AINPUT_EVENT_TYPE_KEY,
+ MOTION = AINPUT_EVENT_TYPE_MOTION,
+ };
+
+ Type type;
+ int32_t deviceId;
+ nsecs_t eventTimeNanos;
+ uint32_t source;
+ int32_t displayId;
+};
+
+/**
+ * Same as KeyEvent, but only contains the data that can be verified.
+ * If you update this class, you must also update VerifiedKeyEvent.java
+ */
+struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent {
+ int32_t action;
+ nsecs_t downTimeNanos;
+ int32_t flags;
+ int32_t keyCode;
+ int32_t scanCode;
+ int32_t metaState;
+ int32_t repeatCount;
+};
+
+/**
+ * Same as MotionEvent, but only contains the data that can be verified.
+ * If you update this class, you must also update VerifiedMotionEvent.java
+ */
+struct __attribute__((__packed__)) VerifiedMotionEvent : public VerifiedInputEvent {
+ float rawX;
+ float rawY;
+ int32_t actionMasked;
+ nsecs_t downTimeNanos;
+ int32_t flags;
+ int32_t metaState;
+ int32_t buttonState;
+};
+
+VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event);
+VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event);
+
/*
* Input event factory.
*/
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 85b0fd0..2a73dc0 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -57,6 +57,30 @@
return "UNKNOWN";
}
+VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
+ return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
+ event.getSource(), event.getDisplayId()},
+ event.getAction(),
+ event.getDownTime(),
+ event.getFlags() & VERIFIED_KEY_EVENT_FLAGS,
+ event.getKeyCode(),
+ event.getScanCode(),
+ event.getMetaState(),
+ event.getRepeatCount()};
+}
+
+VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) {
+ return {{VerifiedInputEvent::Type::MOTION, event.getDeviceId(), event.getEventTime(),
+ event.getSource(), event.getDisplayId()},
+ event.getRawX(0),
+ event.getRawY(0),
+ event.getActionMasked(),
+ event.getDownTime(),
+ event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS,
+ event.getMetaState(),
+ event.getButtonState()};
+}
+
void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac) {
mDeviceId = deviceId;
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index c1c35e1..fb21d5e 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -10,12 +10,12 @@
"LatencyStatistics_test.cpp",
"TouchVideoFrame_test.cpp",
"VelocityTracker_test.cpp",
+ "VerifiedInputEvent_test.cpp",
],
cflags: [
"-Wall",
"-Wextra",
"-Werror",
- "-Wno-unused-variable",
],
shared_libs: [
"libinput",
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index dce1f29..d0f7618 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -46,7 +46,6 @@
}
TEST_F(PointerCoordsTest, AxisValues) {
- float* valuePtr;
PointerCoords coords;
coords.clear();
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index d4bbf6c..885196f 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -38,6 +38,7 @@
virtual void SetUp() {
status_t result = InputChannel::openInputChannelPair("channel name",
serverChannel, clientChannel);
+ ASSERT_EQ(OK, result);
mPublisher = new InputPublisher(serverChannel);
mConsumer = new InputConsumer(clientChannel);
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index aa8a2d4..dd127fc 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -98,4 +98,34 @@
static_assert(sizeof(InputMessage::Body::Focus) == 8);
}
+// --- VerifiedInputEvent ---
+// Ensure that VerifiedInputEvent, VerifiedKeyEvent, VerifiedMotionEvent are packed.
+// We will treat them as byte collections when signing them. There should not be any uninitialized
+// data in-between fields. Otherwise, the padded data will affect the hmac value and verifications
+// will fail.
+
+void TestVerifiedEventSize() {
+ // VerifiedInputEvent
+ constexpr size_t VERIFIED_INPUT_EVENT_SIZE = sizeof(VerifiedInputEvent::type) +
+ sizeof(VerifiedInputEvent::deviceId) + sizeof(VerifiedInputEvent::eventTimeNanos) +
+ sizeof(VerifiedInputEvent::source) + sizeof(VerifiedInputEvent::displayId);
+ static_assert(sizeof(VerifiedInputEvent) == VERIFIED_INPUT_EVENT_SIZE);
+
+ // VerifiedKeyEvent
+ constexpr size_t VERIFIED_KEY_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE +
+ sizeof(VerifiedKeyEvent::action) + sizeof(VerifiedKeyEvent::downTimeNanos) +
+ sizeof(VerifiedKeyEvent::flags) + sizeof(VerifiedKeyEvent::keyCode) +
+ sizeof(VerifiedKeyEvent::scanCode) + sizeof(VerifiedKeyEvent::metaState) +
+ sizeof(VerifiedKeyEvent::repeatCount);
+ static_assert(sizeof(VerifiedKeyEvent) == VERIFIED_KEY_EVENT_SIZE);
+
+ // VerifiedMotionEvent
+ constexpr size_t VERIFIED_MOTION_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE +
+ sizeof(VerifiedMotionEvent::rawX) + sizeof(VerifiedMotionEvent::rawY) +
+ sizeof(VerifiedMotionEvent::actionMasked) + sizeof(VerifiedMotionEvent::downTimeNanos) +
+ sizeof(VerifiedMotionEvent::flags) + sizeof(VerifiedMotionEvent::metaState) +
+ sizeof(VerifiedMotionEvent::buttonState);
+ static_assert(sizeof(VerifiedMotionEvent) == VERIFIED_MOTION_EVENT_SIZE);
+}
+
} // namespace android
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
new file mode 100644
index 0000000..a59dbe5
--- /dev/null
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <input/Input.h>
+
+namespace android {
+
+static KeyEvent getKeyEventWithFlags(int32_t flags) {
+ KeyEvent event;
+ event.initialize(2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
+ AKEY_EVENT_ACTION_DOWN, flags, AKEYCODE_BUTTON_X, 121 /*scanCode*/,
+ AMETA_ALT_ON, 1 /*repeatCount*/, 1000 /*downTime*/, 2000 /*eventTime*/);
+ return event;
+}
+
+static MotionEvent getMotionEventWithFlags(int32_t flags) {
+ MotionEvent event;
+ constexpr size_t pointerCount = 1;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerCoords[i].clear();
+ }
+
+ event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
+ AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
+ AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/,
+ 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/,
+ 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount,
+ pointerProperties, pointerCoords);
+ return event;
+}
+
+TEST(VerifiedKeyEventTest, ConvertKeyEventToVerifiedKeyEvent) {
+ KeyEvent event = getKeyEventWithFlags(0);
+ VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+
+ ASSERT_EQ(VerifiedInputEvent::Type::KEY, verified.type);
+
+ ASSERT_EQ(event.getDeviceId(), verified.deviceId);
+ ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos);
+ ASSERT_EQ(event.getSource(), verified.source);
+ ASSERT_EQ(event.getDisplayId(), verified.displayId);
+
+ ASSERT_EQ(event.getAction(), verified.action);
+ ASSERT_EQ(event.getDownTime(), verified.downTimeNanos);
+ ASSERT_EQ(event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, verified.flags);
+ ASSERT_EQ(event.getKeyCode(), verified.keyCode);
+ ASSERT_EQ(event.getScanCode(), verified.scanCode);
+ ASSERT_EQ(event.getMetaState(), verified.metaState);
+ ASSERT_EQ(event.getRepeatCount(), verified.repeatCount);
+}
+
+TEST(VerifiedKeyEventTest, VerifiedKeyEventContainsOnlyVerifiedFlags) {
+ KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_FALLBACK);
+ VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+ ASSERT_EQ(AKEY_EVENT_FLAG_CANCELED, verified.flags);
+}
+
+TEST(VerifiedKeyEventTest, VerifiedKeyEventDoesNotContainUnverifiedFlags) {
+ KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_EDITOR_ACTION);
+ VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+ ASSERT_EQ(0, verified.flags);
+}
+
+TEST(VerifiedMotionEventTest, ConvertMotionEventToVerifiedMotionEvent) {
+ MotionEvent event = getMotionEventWithFlags(0);
+ VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+
+ ASSERT_EQ(VerifiedInputEvent::Type::MOTION, verified.type);
+
+ ASSERT_EQ(event.getDeviceId(), verified.deviceId);
+ ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos);
+ ASSERT_EQ(event.getSource(), verified.source);
+ ASSERT_EQ(event.getDisplayId(), verified.displayId);
+
+ ASSERT_EQ(event.getRawX(0), verified.rawX);
+ ASSERT_EQ(event.getRawY(0), verified.rawY);
+ ASSERT_EQ(event.getAction(), verified.actionMasked);
+ ASSERT_EQ(event.getDownTime(), verified.downTimeNanos);
+ ASSERT_EQ(event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, verified.flags);
+ ASSERT_EQ(event.getMetaState(), verified.metaState);
+ ASSERT_EQ(event.getButtonState(), verified.buttonState);
+}
+
+TEST(VerifiedMotionEventTest, VerifiedMotionEventContainsOnlyVerifiedFlags) {
+ MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE);
+ VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+ ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, verified.flags);
+}
+
+TEST(VerifiedMotionEventTest, VerifiedMotionEventDoesNotContainUnverifiedFlags) {
+ MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_TAINTED);
+ VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+ ASSERT_EQ(0, verified.flags);
+}
+
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f2b95e7..a8158ba 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3340,6 +3340,10 @@
return injectionResult;
}
+std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const InputEvent& event) {
+ return nullptr;
+}
+
bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
return injectorUid == 0 ||
mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index d2aea80..72511e9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -96,6 +96,8 @@
int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
uint32_t policyFlags) override;
+ virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
+
virtual void setInputWindows(
const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId,
const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3424f4c..6e98676 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -92,6 +92,13 @@
int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
uint32_t policyFlags) = 0;
+ /*
+ * Check whether InputEvent actually happened by checking the signature of the event.
+ *
+ * Return nullptr if the event cannot be verified.
+ */
+ virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) = 0;
+
/* Sets the list of input windows.
*
* This method may be called on any thread (usually by the input manager).