From 00f511d329924824b1961e9472c3a06683fc2216 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Wed, 12 Jun 2019 16:55:40 -0700 Subject: Dispatch mouse events to window under the cursor. This CL adds cursor positions to NotifyMotionArgs, MotionEntry, InputMessage motion body and MotionEvent. Bug: 134788085 Test: The window under the cursor always responds to the gesture. Test: atest inputflinger_tests Test: atest libinput_tests Change-Id: I8ea460ed8738ffc3a5e997215685889cc1e1f2fe --- libs/input/Input.cpp | 55 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 20 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 9fd25f9cb7..3266b0740d 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -235,26 +235,14 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { +void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, + int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + size_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; @@ -267,6 +255,8 @@ void MotionEvent::initialize( mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; + mXCursorPosition = xCursorPosition; + mYCursorPosition = yCursorPosition; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -288,6 +278,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; + mXCursorPosition = other->mXCursorPosition; + mYCursorPosition = other->mYCursorPosition; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -312,6 +304,16 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +float MotionEvent::getXCursorPosition() const { + const float rawX = getRawXCursorPosition(); + return rawX + mXOffset; +} + +float MotionEvent::getYCursorPosition() const { + const float rawY = getRawYCursorPosition(); + return rawY + mYOffset; +} + const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; } @@ -431,6 +433,15 @@ void MotionEvent::transform(const float matrix[9]) { float originX, originY; transformPoint(matrix, 0, 0, &originX, &originY); + // Apply the transformation to cursor position. + if (!isnan(mXCursorPosition) && !isnan(mYCursorPosition)) { + float x = mXCursorPosition + oldXOffset; + float y = mYCursorPosition + oldYOffset; + transformPoint(matrix, x, y, &x, &y); + mXCursorPosition = x - mXOffset; + mYCursorPosition = y - mYOffset; + } + // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { @@ -470,6 +481,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); + mXCursorPosition = parcel->readFloat(); + mYCursorPosition = parcel->readFloat(); mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -521,6 +534,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); + parcel->writeFloat(mXCursorPosition); + parcel->writeFloat(mYCursorPosition); parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { -- cgit v1.2.3-59-g8ed1b From ab0ab9c57c8fa9d8c9648734fea74ee010e28e8c Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Wed, 10 Jul 2019 18:58:28 -0700 Subject: Address comments from a previous change. The original change is 00f511d329924824b1961e9472c3a06683fc2216. Bug: 134788085 Test: atest libinput_tests Change-Id: I1f3326067f94fe6a09850f4389483e60fa57a8d4 --- include/input/Input.h | 3 +++ libs/input/Input.cpp | 3 +-- libs/input/tests/InputEvent_test.cpp | 10 +++++++--- services/inputflinger/InputDispatcher.cpp | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index a97624658c..ad8c233577 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -476,6 +477,8 @@ public: float getYCursorPosition() const; + static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } + inline nsecs_t getDownTime() const { return mDownTime; } inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 3266b0740d..dc4978b836 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 -#include #include #include @@ -434,7 +433,7 @@ void MotionEvent::transform(const float matrix[9]) { transformPoint(matrix, 0, 0, &originX, &originY); // Apply the transformation to cursor position. - if (!isnan(mXCursorPosition) && !isnan(mYCursorPosition)) { + if (isValidCursorPosition(mXCursorPosition, mYCursorPosition)) { float x = mXCursorPosition + oldXOffset; float y = mYCursorPosition + oldYOffset; transformPoint(matrix, x, y, &x, &y); diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index ec34f3e652..b879de6a74 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -603,9 +603,13 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); } - // Check cursor positions. - ASSERT_NEAR(sinf(PI_180 * (90 + ROTATION)) * RADIUS, event.getXCursorPosition(), 0.001); - ASSERT_NEAR(-cosf(PI_180 * (90 + ROTATION)) * RADIUS, event.getYCursorPosition(), 0.001); + // Check cursor positions. The original cursor position is at (3 + RADIUS, 2), where the center + // of the circle is (3, 2), so the cursor position is to the right of the center of the circle. + // The choice of triangular functions in this test defines the angle of rotation clockwise + // relative to the y-axis. Therefore the cursor position's angle is 90 degrees. Here we swap the + // triangular function so that we don't have to add the 90 degrees. + ASSERT_NEAR(cosf(PI_180 * ROTATION) * RADIUS, event.getXCursorPosition(), 0.001); + ASSERT_NEAR(sinf(PI_180 * ROTATION) * RADIUS, event.getYCursorPosition(), 0.001); // Applying the transformation should preserve the raw X and Y of the first point. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index be1370747c..1d8c365702 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -2766,7 +2766,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { ", policyFlags=0x%x, " "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " - "mYCursorPosition=%f, downTime=%" PRId64, + "yCursorPosition=%f, downTime=%" PRId64, args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, arg->xCursorPosition, -- cgit v1.2.3-59-g8ed1b From 937bb83a143631fcb25f0962aa95c1f850fdf023 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 25 Jul 2019 17:48:31 -0700 Subject: Add setCursorPosition. When source is updated we need to update their values. Also renamed mX/YCursorPosition to mRawX/YCursorPosition to indicate it stores raw values (w/o applying offset). Bug: 134788085 Test: atest libinput_tests Change-Id: I1533d79a7542291974ff572d3aeaf9924e3f0751 --- include/input/Input.h | 14 ++++++++------ libs/input/Input.cpp | 35 ++++++++++++++++++++--------------- libs/input/tests/InputEvent_test.cpp | 10 ++++++++++ 3 files changed, 38 insertions(+), 21 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index ad8c233577..cbd1a412bf 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -469,14 +469,16 @@ public: inline float getYPrecision() const { return mYPrecision; } - inline float getRawXCursorPosition() const { return mXCursorPosition; } + inline float getRawXCursorPosition() const { return mRawXCursorPosition; } float getXCursorPosition() const; - inline float getRawYCursorPosition() const { return mYCursorPosition; } + inline float getRawYCursorPosition() const { return mRawYCursorPosition; } float getYCursorPosition() const; + void setCursorPosition(float x, float y); + static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } inline nsecs_t getDownTime() const { return mDownTime; } @@ -623,8 +625,8 @@ public: void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xOffset, - float yOffset, float xPrecision, float yPrecision, float mXCursorPosition, - float mYCursorPosition, nsecs_t downTime, nsecs_t eventTime, + float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition, + float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); @@ -676,8 +678,8 @@ protected: float mYOffset; float mXPrecision; float mYPrecision; - float mXCursorPosition; - float mYCursorPosition; + float mRawXCursorPosition; + float mRawYCursorPosition; nsecs_t mDownTime; Vector mPointerProperties; Vector mSampleEventTimes; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index dc4978b836..34b305e548 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -238,8 +238,8 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, - float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + float xPrecision, float yPrecision, float rawXCursorPosition, + float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(deviceId, source, displayId); @@ -254,8 +254,8 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; - mXCursorPosition = xCursorPosition; - mYCursorPosition = yCursorPosition; + mRawXCursorPosition = rawXCursorPosition; + mRawYCursorPosition = rawYCursorPosition; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -277,8 +277,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; - mXCursorPosition = other->mXCursorPosition; - mYCursorPosition = other->mYCursorPosition; + mRawXCursorPosition = other->mRawXCursorPosition; + mRawYCursorPosition = other->mRawYCursorPosition; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -313,6 +313,11 @@ float MotionEvent::getYCursorPosition() const { return rawY + mYOffset; } +void MotionEvent::setCursorPosition(float x, float y) { + mRawXCursorPosition = x - mXOffset; + mRawYCursorPosition = y - mYOffset; +} + const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; } @@ -433,12 +438,12 @@ void MotionEvent::transform(const float matrix[9]) { transformPoint(matrix, 0, 0, &originX, &originY); // Apply the transformation to cursor position. - if (isValidCursorPosition(mXCursorPosition, mYCursorPosition)) { - float x = mXCursorPosition + oldXOffset; - float y = mYCursorPosition + oldYOffset; + if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { + float x = mRawXCursorPosition + oldXOffset; + float y = mRawYCursorPosition + oldYOffset; transformPoint(matrix, x, y, &x, &y); - mXCursorPosition = x - mXOffset; - mYCursorPosition = y - mYOffset; + mRawXCursorPosition = x - mXOffset; + mRawYCursorPosition = y - mYOffset; } // Apply the transformation to all samples. @@ -480,8 +485,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); - mXCursorPosition = parcel->readFloat(); - mYCursorPosition = parcel->readFloat(); + mRawXCursorPosition = parcel->readFloat(); + mRawYCursorPosition = parcel->readFloat(); mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -533,8 +538,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); - parcel->writeFloat(mXCursorPosition); - parcel->writeFloat(mYCursorPosition); + parcel->writeFloat(mRawXCursorPosition); + parcel->writeFloat(mRawYCursorPosition); parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index b879de6a74..b90857c99c 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -665,4 +665,14 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { ASSERT_EQ(600, event.getYCursorPosition()); } +TEST_F(MotionEventTest, SetCursorPosition) { + MotionEvent event; + initializeEventWithHistory(&event); + event.setSource(AINPUT_SOURCE_MOUSE); + + event.setCursorPosition(3, 4); + ASSERT_EQ(3, event.getXCursorPosition()); + ASSERT_EQ(4, event.getYCursorPosition()); +} + } // namespace android -- cgit v1.2.3-59-g8ed1b From 727a44e37ee5ac778fc3abf0fcd849847f5d7c29 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Sat, 23 Nov 2019 12:59:16 -0800 Subject: Use queue and unique_ptr for pooled events Currently, we are doing some manual memory management for pooled events. Refactor to use queue and unique_ptr Bug: 70668286 Test: presubmit Change-Id: Ia3f39a841a84b76243e4a68cb2cf8005e0a48ce3 --- include/input/Input.h | 14 +++++++------- libs/input/Input.cpp | 30 ++++++++++++------------------ 2 files changed, 19 insertions(+), 25 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index cbd1a412bf..a7e706ed18 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -31,8 +31,8 @@ #include #include #include - #include +#include /* * Additional private constants not defined in ndk/ui/input.h. @@ -709,8 +709,8 @@ public: PreallocatedInputEventFactory() { } virtual ~PreallocatedInputEventFactory() { } - virtual KeyEvent* createKeyEvent() { return & mKeyEvent; } - virtual MotionEvent* createMotionEvent() { return & mMotionEvent; } + virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; } + virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; } private: KeyEvent mKeyEvent; @@ -725,16 +725,16 @@ public: explicit PooledInputEventFactory(size_t maxPoolSize = 20); virtual ~PooledInputEventFactory(); - virtual KeyEvent* createKeyEvent(); - virtual MotionEvent* createMotionEvent(); + virtual KeyEvent* createKeyEvent() override; + virtual MotionEvent* createMotionEvent() override; void recycle(InputEvent* event); private: const size_t mMaxPoolSize; - Vector mKeyEventPool; - Vector mMotionEventPool; + std::queue> mKeyEventPool; + std::queue> mMotionEventPool; }; } // namespace android diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 34b305e548..c7303efd13 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -595,43 +595,37 @@ PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : } PooledInputEventFactory::~PooledInputEventFactory() { - for (size_t i = 0; i < mKeyEventPool.size(); i++) { - delete mKeyEventPool.itemAt(i); - } - for (size_t i = 0; i < mMotionEventPool.size(); i++) { - delete mMotionEventPool.itemAt(i); - } } KeyEvent* PooledInputEventFactory::createKeyEvent() { - if (!mKeyEventPool.isEmpty()) { - KeyEvent* event = mKeyEventPool.top(); - mKeyEventPool.pop(); - return event; + if (mKeyEventPool.empty()) { + return new KeyEvent(); } - return new KeyEvent(); + KeyEvent* event = mKeyEventPool.front().release(); + mKeyEventPool.pop(); + return event; } MotionEvent* PooledInputEventFactory::createMotionEvent() { - if (!mMotionEventPool.isEmpty()) { - MotionEvent* event = mMotionEventPool.top(); - mMotionEventPool.pop(); - return event; + if (mMotionEventPool.empty()) { + return new MotionEvent(); } - return new MotionEvent(); + MotionEvent* event = mMotionEventPool.front().release(); + mMotionEventPool.pop(); + return event; } void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: if (mKeyEventPool.size() < mMaxPoolSize) { - mKeyEventPool.push(static_cast(event)); + mKeyEventPool.push(std::unique_ptr(static_cast(event))); return; } break; case AINPUT_EVENT_TYPE_MOTION: if (mMotionEventPool.size() < mMaxPoolSize) { - mMotionEventPool.push(static_cast(event)); + mMotionEventPool.push(std::unique_ptr(static_cast(event))); return; } break; -- cgit v1.2.3-59-g8ed1b From 7feb2eaf82d88bd4d903f43776860015806274ce Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 25 Nov 2019 15:11:23 -0800 Subject: Add FocusEvent and InputMessage::Type::FOCUS FocusEvents will be consumed by InputConsumer on the app side. They will be used to notify app that focus has been gained / lost. They will also carry information about the current state of touch mode. Also add a new type of InputMessage with type FOCUS. This new data structure will be used to pass focus events to the apps from input across the socket. Bug: 70668286 Test: presubmit Change-Id: I88582c64ee41ecb49623b9b7f5c149eafa694788 --- include/android/input.h | 5 +- include/input/Input.h | 29 ++++++++++ include/input/InputTransport.h | 34 ++++++++--- libs/input/Input.cpp | 45 +++++++++++++++ libs/input/InputTransport.cpp | 67 +++++++++++++++++----- .../input/tests/InputPublisherAndConsumer_test.cpp | 43 ++++++++++++++ libs/input/tests/StructLayout_test.cpp | 5 ++ .../inputflinger/dispatcher/InputDispatcher.cpp | 2 +- .../inputflinger/tests/InputDispatcher_test.cpp | 4 +- 9 files changed, 209 insertions(+), 25 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/include/android/input.h b/include/android/input.h index ce439c6d75..f51cd79504 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -158,7 +158,10 @@ enum { AINPUT_EVENT_TYPE_KEY = 1, /** Indicates that the input event is a motion event. */ - AINPUT_EVENT_TYPE_MOTION = 2 + AINPUT_EVENT_TYPE_MOTION = 2, + + /** Focus event */ + AINPUT_EVENT_TYPE_FOCUS = 3, }; /** diff --git a/include/input/Input.h b/include/input/Input.h index a7e706ed18..f8718479f9 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -167,6 +167,8 @@ namespace android { class Parcel; #endif +const char* inputEventTypeToString(int32_t type); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. @@ -686,6 +688,28 @@ protected: Vector mSamplePointerCoords; }; +/* + * Focus events. + */ +class FocusEvent : public InputEvent { +public: + virtual ~FocusEvent() {} + + virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_FOCUS; } + + inline bool getHasFocus() const { return mHasFocus; } + + inline bool getInTouchMode() const { return mInTouchMode; } + + void initialize(bool hasFocus, bool inTouchMode); + + void initialize(const FocusEvent& from); + +protected: + bool mHasFocus; + bool mInTouchMode; +}; + /* * Input event factory. */ @@ -698,6 +722,7 @@ public: virtual KeyEvent* createKeyEvent() = 0; virtual MotionEvent* createMotionEvent() = 0; + virtual FocusEvent* createFocusEvent() = 0; }; /* @@ -711,10 +736,12 @@ public: virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; } virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; } + virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; } private: KeyEvent mKeyEvent; MotionEvent mMotionEvent; + FocusEvent mFocusEvent; }; /* @@ -727,6 +754,7 @@ public: virtual KeyEvent* createKeyEvent() override; virtual MotionEvent* createMotionEvent() override; + virtual FocusEvent* createFocusEvent() override; void recycle(InputEvent* event); @@ -735,6 +763,7 @@ private: std::queue> mKeyEventPool; std::queue> mMotionEventPool; + std::queue> mFocusEventPool; }; } // namespace android diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index d39ee250a5..ae47438ac8 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -64,6 +64,7 @@ struct InputMessage { KEY, MOTION, FINISHED, + FOCUS, }; struct Header { @@ -92,9 +93,7 @@ struct InputMessage { uint32_t empty2; nsecs_t downTime __attribute__((aligned(8))); - inline size_t size() const { - return sizeof(Key); - } + inline size_t size() const { return sizeof(Key); } } key; struct Motion { @@ -110,7 +109,7 @@ struct InputMessage { int32_t metaState; int32_t buttonState; MotionClassification classification; // base type: uint8_t - uint8_t empty2[3]; + uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); float xOffset; @@ -148,10 +147,17 @@ struct InputMessage { uint32_t seq; uint32_t handled; // actually a bool, but we must maintain 8-byte alignment - inline size_t size() const { - return sizeof(Finished); - } + inline size_t size() const { return sizeof(Finished); } } finished; + + struct Focus { + uint32_t seq; + // The following two fields take up 4 bytes total + uint16_t hasFocus; // actually a bool + uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment + + inline size_t size() const { return sizeof(Focus); } + } focus; } __attribute__((aligned(8))) body; bool isValid(size_t actualSize) const; @@ -294,6 +300,15 @@ public: uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); + /* Publishes a focus event to the input channel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode); + /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, * and whether the consumer handled the message. @@ -349,8 +364,8 @@ public: * Returns NO_MEMORY if the event could not be created. * Other errors probably indicate that the channel is broken. */ - status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, + uint32_t* outSeq, InputEvent** outEvent); /* Sends a finished signal to the publisher to inform it that the message * with the specified sequence number has finished being process and whether @@ -521,6 +536,7 @@ private: static void rewriteMessage(TouchState& state, InputMessage& msg); static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); + static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg); static void addSample(MotionEvent* event, const InputMessage* msg); static bool canAddSample(const Batch& batch, const InputMessage* msg); static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index c7303efd13..8ccbc7f650 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #ifdef __ANDROID__ @@ -41,6 +42,21 @@ const char* motionClassificationToString(MotionClassification classification) { // --- InputEvent --- +const char* inputEventTypeToString(int32_t type) { + switch (type) { + case AINPUT_EVENT_TYPE_KEY: { + return "KEY"; + } + case AINPUT_EVENT_TYPE_MOTION: { + return "MOTION"; + } + case AINPUT_EVENT_TYPE_FOCUS: { + return "FOCUS"; + } + } + return "UNKNOWN"; +} + void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { mDeviceId = deviceId; mSource = source; @@ -587,6 +603,20 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { return getAxisByLabel(label); } +// --- FocusEvent --- + +void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { + InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, + ADISPLAY_ID_NONE); + mHasFocus = hasFocus; + mInTouchMode = inTouchMode; +} + +void FocusEvent::initialize(const FocusEvent& from) { + InputEvent::initialize(from); + mHasFocus = from.mHasFocus; + mInTouchMode = from.mInTouchMode; +} // --- PooledInputEventFactory --- @@ -615,6 +645,15 @@ MotionEvent* PooledInputEventFactory::createMotionEvent() { return event; } +FocusEvent* PooledInputEventFactory::createFocusEvent() { + if (mFocusEventPool.empty()) { + return new FocusEvent(); + } + FocusEvent* event = mFocusEventPool.front().release(); + mFocusEventPool.pop(); + return event; +} + void PooledInputEventFactory::recycle(InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: @@ -629,6 +668,12 @@ void PooledInputEventFactory::recycle(InputEvent* event) { return; } break; + case AINPUT_EVENT_TYPE_FOCUS: + if (mFocusEventPool.size() < mMaxPoolSize) { + mFocusEventPool.push(std::unique_ptr(static_cast(event))); + return; + } + break; } delete event; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index b7937dc95e..200e1f39d9 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -103,6 +103,8 @@ bool InputMessage::isValid(size_t actualSize) const { return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS; case Type::FINISHED: return true; + case Type::FOCUS: + return true; } } return false; @@ -116,6 +118,8 @@ size_t InputMessage::size() const { return sizeof(Header) + body.motion.size(); case Type::FINISHED: return sizeof(Header) + body.finished.size(); + case Type::FOCUS: + return sizeof(Header) + body.focus.size(); } return sizeof(Header); } @@ -220,6 +224,12 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.finished.handled = body.finished.handled; break; } + case InputMessage::Type::FOCUS: { + msg->body.focus.seq = body.focus.seq; + msg->body.focus.hasFocus = body.focus.hasFocus; + msg->body.focus.inTouchMode = body.focus.inTouchMode; + break; + } } } @@ -529,6 +539,23 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } +status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) { + if (ATRACE_ENABLED()) { + std::string message = + StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)", + mChannel->getName().c_str(), toString(hasFocus), + toString(inTouchMode)); + ATRACE_NAME(message.c_str()); + } + + InputMessage msg; + msg.header.type = InputMessage::Type::FOCUS; + msg.body.focus.seq = seq; + msg.body.focus.hasFocus = hasFocus ? 1 : 0; + msg.body.focus.inTouchMode = inTouchMode ? 1 : 0; + return mChannel->sendMessage(&msg); +} + status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str()); @@ -565,8 +592,8 @@ bool InputConsumer::isTouchResamplingEnabled() { return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); } -status_t InputConsumer::consume(InputEventFactoryInterface* factory, - bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { +status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, mChannel->getName().c_str(), toString(consumeBatches), frameTime); @@ -669,19 +696,19 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, break; } - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; + MotionEvent* motionEvent = factory->createMotionEvent(); + if (!motionEvent) return NO_MEMORY; - updateTouchState(mMsg); - initializeMotionEvent(motionEvent, &mMsg); - *outSeq = mMsg.body.motion.seq; - *outEvent = motionEvent; + updateTouchState(mMsg); + initializeMotionEvent(motionEvent, &mMsg); + *outSeq = mMsg.body.motion.seq; + *outEvent = motionEvent; - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", - mChannel->getName().c_str(), *outSeq); - } - break; + if (DEBUG_TRANSPORT_ACTIONS) { + ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().c_str(), *outSeq); + } + break; } case InputMessage::Type::FINISHED: { @@ -689,6 +716,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, "InputConsumer!"); break; } + + case InputMessage::Type::FOCUS: { + FocusEvent* focusEvent = factory->createFocusEvent(); + if (!focusEvent) return NO_MEMORY; + + initializeFocusEvent(focusEvent, &mMsg); + *outSeq = mMsg.body.focus.seq; + *outEvent = focusEvent; + break; + } } } return OK; @@ -1113,6 +1150,10 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) msg->body.key.eventTime); } +void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { + event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1); +} + void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { uint32_t pointerCount = msg->body.motion.pointerCount; PointerProperties pointerProperties[pointerCount]; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index a362f3281d..2fc77e97a0 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -60,6 +60,7 @@ protected: void PublishAndConsumeKeyEvent(); void PublishAndConsumeMotionEvent(); + void PublishAndConsumeFocusEvent(); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -256,6 +257,43 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { << "publisher receiveFinishedSignal should have set handled to consumer's reply"; } +void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { + status_t status; + + constexpr uint32_t seq = 15; + constexpr bool hasFocus = true; + constexpr bool inTouchMode = true; + + status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode); + ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + + uint32_t consumeSeq; + InputEvent* event; + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; + + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; + ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType()) + << "consumer should have returned a focus event"; + + FocusEvent* focusEvent = static_cast(event); + EXPECT_EQ(seq, consumeSeq); + EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); + EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); + + status = mConsumer->sendFinishedSignal(seq, true); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + + uint32_t finishedSeq = 0; + bool handled = false; + status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_EQ(seq, finishedSeq) + << "publisher receiveFinishedSignal should have returned the original sequence number"; + ASSERT_TRUE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; +} + TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } @@ -264,6 +302,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); } +TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); +} + TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) { status_t status; const size_t pointerCount = 1; @@ -322,6 +364,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 0fb6cfc204..9ab0dba08d 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -69,6 +69,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); + CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); + CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); + CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6); + CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } @@ -87,6 +91,7 @@ void TestBodySize() { offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); static_assert(sizeof(InputMessage::Body::Finished) == 8); + static_assert(sizeof(InputMessage::Body::Focus) == 8); } } // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 746908ba40..9c0e08e297 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3036,7 +3036,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec } default: - ALOGW("Cannot inject event of type %d", event->getType()); + ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType())); return INPUT_EVENT_INJECTION_FAILED; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index ebce47f1e7..4f282611d5 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -410,7 +411,8 @@ public: ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(expectedEventType, event->getType()) - << mName.c_str() << ": event type should match."; + << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType) + << " event, got " << inputEventTypeToString(event->getType()) << " event"; EXPECT_EQ(expectedDisplayId, event->getDisplayId()); -- cgit v1.2.3-59-g8ed1b From 9c858ac6573d32a98d24cc0da0722592873ae3ec Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 23 Jan 2020 14:20:11 -0600 Subject: Add hmac to InputEvent and scale to MotionEvent We need to pass some additional data to the app to be able to construct the MotionEvent. For proper support of getRawX/getRawY, add the xScale and yScale to MotionEvent. For the verification of input events, add hmac to InputEvent. Bug: 134977432 Bug: 140786233 Test: atest libinput_tests inputflinger_tests Change-Id: Ia3400ebbd9698549aad4d97a3b789ab7e10f6b65 --- include/input/Input.h | 40 ++++---- include/input/InputTransport.h | 31 +++--- libs/input/Input.cpp | 60 +++++++----- libs/input/InputTransport.cpp | 76 +++++++-------- libs/input/KeyCharacterMap.cpp | 6 +- libs/input/tests/InputEvent_test.cpp | 69 ++++++++------ .../input/tests/InputPublisherAndConsumer_test.cpp | 64 ++++++++----- libs/input/tests/StructLayout_test.cpp | 52 +++++----- libs/input/tests/VelocityTracker_test.cpp | 10 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 3 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 26 ++--- .../inputflinger/tests/InputDispatcher_test.cpp | 106 +++++++++++---------- 12 files changed, 300 insertions(+), 243 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index f8718479f9..1cf58efcaf 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -258,6 +259,11 @@ const char* motionClassificationToString(MotionClassification classification); */ constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits::quiet_NaN(); +/** + * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified. + */ +constexpr std::array INVALID_HMAC = {0}; + /* * Pointer coordinate data. */ @@ -356,14 +362,17 @@ public: inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; } + inline std::array getHmac() const { return mHmac; } protected: - void initialize(int32_t deviceId, int32_t source, int32_t displayId); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac); void initialize(const InputEvent& from); int32_t mDeviceId; int32_t mSource; int32_t mDisplayId; + std::array mHmac; }; /* @@ -396,18 +405,10 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - void initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, + nsecs_t eventTime); void initialize(const KeyEvent& from); protected: @@ -463,6 +464,10 @@ public: inline void setActionButton(int32_t button) { mActionButton = button; } + inline float getXScale() const { return mXScale; } + + inline float getYScale() const { return mYScale; } + inline float getXOffset() const { return mXOffset; } inline float getYOffset() const { return mYOffset; } @@ -624,9 +629,10 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xOffset, + void initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xScale, float yScale, float xOffset, float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, @@ -676,6 +682,8 @@ protected: int32_t mMetaState; int32_t mButtonState; MotionClassification mClassification; + float mXScale; + float mYScale; float mXOffset; float mYOffset; float mXPrecision; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index ae47438ac8..06fd3bb364 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -76,6 +76,9 @@ struct InputMessage { } header; // Body *must* be 8 byte aligned. + // For keys and motions, rely on the fact that std::array takes up exactly as much space + // as the underlying data. This is not guaranteed by C++, but it simplifies the conversions. + static_assert(sizeof(std::array) == 32); union Body { struct Key { uint32_t seq; @@ -84,6 +87,7 @@ struct InputMessage { int32_t deviceId; int32_t source; int32_t displayId; + std::array hmac; int32_t action; int32_t flags; int32_t keyCode; @@ -103,6 +107,7 @@ struct InputMessage { int32_t deviceId; int32_t source; int32_t displayId; + std::array hmac; int32_t action; int32_t actionButton; int32_t flags; @@ -112,6 +117,8 @@ struct InputMessage { uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); + float xScale; + float yScale; float xOffset; float yOffset; float xPrecision; @@ -269,19 +276,10 @@ public: * Returns BAD_VALUE if seq is 0. * Other errors probably indicate that the channel is broken. */ - status_t publishKeyEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime); + status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); /* Publishes a motion event to the input channel. * @@ -292,9 +290,10 @@ public: * Other errors probably indicate that the channel is broken. */ status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, - int32_t action, int32_t actionButton, int32_t flags, - int32_t edgeFlags, int32_t metaState, int32_t buttonState, - MotionClassification classification, float xOffset, float yOffset, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, + float xScale, float yScale, float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8ccbc7f650..bff1b97769 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -57,16 +57,19 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } -void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { +void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac) { mDeviceId = deviceId; mSource = source; mDisplayId = displayId; + mHmac = hmac; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; mDisplayId = from.mDisplayId; + mHmac = from.mHmac; } // --- KeyEvent --- @@ -79,19 +82,11 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } -void KeyEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime) { - InputEvent::initialize(deviceId, source, displayId); +void KeyEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime, nsecs_t eventTime) { + InputEvent::initialize(deviceId, source, displayId, hmac); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -250,15 +245,16 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, - int32_t metaState, int32_t buttonState, - MotionClassification classification, float xOffset, float yOffset, - float xPrecision, float yPrecision, float rawXCursorPosition, - float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, - size_t pointerCount, const PointerProperties* pointerProperties, +void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, + int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xScale, + float yScale, float xOffset, float yOffset, float xPrecision, + float yPrecision, float rawXCursorPosition, float rawYCursorPosition, + nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source, displayId); + InputEvent::initialize(deviceId, source, displayId, hmac); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -266,6 +262,8 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId mMetaState = metaState; mButtonState = buttonState; mClassification = classification; + mXScale = xScale; + mYScale = yScale; mXOffset = xOffset; mYOffset = yOffset; mXPrecision = xPrecision; @@ -281,7 +279,7 @@ void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId); + InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; @@ -289,6 +287,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mMetaState = other->mMetaState; mButtonState = other->mButtonState; mClassification = other->mClassification; + mXScale = other->mXScale; + mYScale = other->mYScale; mXOffset = other->mXOffset; mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; @@ -490,6 +490,12 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mDeviceId = parcel->readInt32(); mSource = parcel->readInt32(); mDisplayId = parcel->readInt32(); + std::vector hmac; + status_t result = parcel->readByteVector(&hmac); + if (result != OK || hmac.size() != 32) { + return BAD_VALUE; + } + std::move(hmac.begin(), hmac.begin() + hmac.size(), mHmac.begin()); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); @@ -497,6 +503,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mMetaState = parcel->readInt32(); mButtonState = parcel->readInt32(); mClassification = static_cast(parcel->readByte()); + mXScale = parcel->readFloat(); + mYScale = parcel->readFloat(); mXOffset = parcel->readFloat(); mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); @@ -543,6 +551,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mDeviceId); parcel->writeInt32(mSource); parcel->writeInt32(mDisplayId); + std::vector hmac(mHmac.begin(), mHmac.end()); + parcel->writeByteVector(hmac); parcel->writeInt32(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); @@ -550,6 +560,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mMetaState); parcel->writeInt32(mButtonState); parcel->writeByte(static_cast(mClassification)); + parcel->writeFloat(mXScale); + parcel->writeFloat(mYScale); parcel->writeFloat(mXOffset); parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); @@ -607,7 +619,7 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, - ADISPLAY_ID_NONE); + ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; mInTouchMode = inTouchMode; } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d53a557b28..d25a5cc0ef 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -147,6 +147,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.key.source = body.key.source; // int32_t displayId msg->body.key.displayId = body.key.displayId; + // std::array hmac + msg->body.key.hmac = body.key.hmac; // int32_t action msg->body.key.action = body.key.action; // int32_t flags @@ -174,6 +176,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.source = body.motion.source; // int32_t displayId msg->body.motion.displayId = body.motion.displayId; + // std::array hmac + msg->body.motion.hmac = body.motion.hmac; // int32_t action msg->body.motion.action = body.motion.action; // int32_t actionButton @@ -190,6 +194,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.edgeFlags = body.motion.edgeFlags; // nsecs_t downTime msg->body.motion.downTime = body.motion.downTime; + // float xScale + msg->body.motion.xScale = body.motion.xScale; + // float yScale + msg->body.motion.yScale = body.motion.yScale; // float xOffset msg->body.motion.xOffset = body.motion.xOffset; // float yOffset @@ -424,19 +432,11 @@ InputPublisher::InputPublisher(const sp& channel) : InputPublisher::~InputPublisher() { } -status_t InputPublisher::publishKeyEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t flags, - int32_t keyCode, - int32_t scanCode, - int32_t metaState, - int32_t repeatCount, - nsecs_t downTime, - nsecs_t eventTime) { +status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, std::array hmac, + int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime, nsecs_t eventTime) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")", mChannel->getName().c_str(), keyCode); @@ -461,6 +461,7 @@ status_t InputPublisher::publishKeyEvent( msg.body.key.deviceId = deviceId; msg.body.key.source = source; msg.body.key.displayId = displayId; + msg.body.key.hmac = hmac; msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; @@ -473,11 +474,12 @@ status_t InputPublisher::publishKeyEvent( } status_t InputPublisher::publishMotionEvent( - uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, - int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, - float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, - nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + std::array hmac, int32_t action, int32_t actionButton, int32_t flags, + int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xScale, float yScale, float xOffset, + float yOffset, float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( @@ -489,13 +491,14 @@ status_t InputPublisher::publishMotionEvent( ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " - "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, " + "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, " + "xOffset=%.1f, yOffset=%.1f, " "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32, mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, buttonState, - motionClassificationToString(classification), xOffset, yOffset, xPrecision, - yPrecision, downTime, eventTime, pointerCount); + motionClassificationToString(classification), xScale, yScale, xOffset, yOffset, + xPrecision, yPrecision, downTime, eventTime, pointerCount); } if (!seq) { @@ -515,6 +518,7 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; msg.body.motion.displayId = displayId; + msg.body.motion.hmac = hmac; msg.body.motion.action = action; msg.body.motion.actionButton = actionButton; msg.body.motion.flags = flags; @@ -522,6 +526,8 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.classification = classification; + msg.body.motion.xScale = xScale; + msg.body.motion.yScale = yScale; msg.body.motion.xOffset = xOffset; msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; @@ -1136,18 +1142,10 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize( - msg->body.key.deviceId, - msg->body.key.source, - msg->body.key.displayId, - msg->body.key.action, - msg->body.key.flags, - msg->body.key.keyCode, - msg->body.key.scanCode, - msg->body.key.metaState, - msg->body.key.repeatCount, - msg->body.key.downTime, - msg->body.key.eventTime); + event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, + msg->body.key.hmac, msg->body.key.action, msg->body.key.flags, + msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, + msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime); } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { @@ -1164,15 +1162,15 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage } event->initialize(msg->body.motion.deviceId, msg->body.motion.source, - msg->body.motion.displayId, msg->body.motion.action, + msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, msg->body.motion.buttonState, msg->body.motion.classification, - msg->body.motion.xOffset, msg->body.motion.yOffset, - msg->body.motion.xPrecision, msg->body.motion.yPrecision, - msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, - pointerProperties, pointerCoords); + msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, + msg->body.motion.yOffset, msg->body.motion.xPrecision, + msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, + msg->body.motion.yCursorPosition, msg->body.motion.downTime, + msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index e189d20e28..6f9b162986 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -487,9 +487,9 @@ void KeyCharacterMap::addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - 0, keyCode, 0, metaState, 0, time, time); + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, + 0, time, time); } void KeyCharacterMap::addMetaKeys(Vector& outEvents, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index b90857c99c..8c6890f542 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -26,7 +26,12 @@ namespace android { // Default display id. static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; -class BaseTest : public testing::Test { }; +class BaseTest : public testing::Test { +protected: + static constexpr std::array HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; +}; // --- PointerCoordsTest --- @@ -176,16 +181,17 @@ TEST_F(KeyEventTest, Properties) { KeyEvent event; // Initialize and get properties. - const nsecs_t ARBITRARY_DOWN_TIME = 1; - const nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, - AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; + constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; + event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(static_cast(AINPUT_SOURCE_GAMEPAD), event.getSource()); ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); + EXPECT_EQ(HMAC, event.getHmac()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); @@ -210,19 +216,17 @@ TEST_F(KeyEventTest, Properties) { class MotionEventTest : public BaseTest { protected: - static const nsecs_t ARBITRARY_DOWN_TIME; - static const nsecs_t ARBITRARY_EVENT_TIME; - static const float X_OFFSET; - static const float Y_OFFSET; + static constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; + static constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; + static constexpr float X_SCALE = 2.0; + static constexpr float Y_SCALE = 3.0; + static constexpr float X_OFFSET = 1; + static constexpr float Y_OFFSET = 1.1; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; -const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1; -const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2; -const float MotionEventTest::X_OFFSET = 1.0f; -const float MotionEventTest::Y_OFFSET = 1.1f; void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { PointerProperties pointerProperties[2]; @@ -254,12 +258,13 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, + event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, - X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, - ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); + X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, + pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -308,12 +313,15 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(static_cast(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); + EXPECT_EQ(HMAC, event->getHmac()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); ASSERT_EQ(MotionClassification::NONE, event->getClassification()); + EXPECT_EQ(X_SCALE, event->getXScale()); + EXPECT_EQ(Y_SCALE, event->getYScale()); ASSERT_EQ(X_OFFSET, event->getXOffset()); ASSERT_EQ(Y_OFFSET, event->getYOffset()); ASSERT_EQ(2.0f, event->getXPrecision()); @@ -570,12 +578,13 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/, - 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, - 2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, - pointerProperties, pointerCoords); + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/, + 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -634,9 +643,10 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { } for (MotionClassification classification : classifications) { - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); @@ -654,9 +664,10 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, - 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0, - 0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 2fc77e97a0..1e51ea8b2f 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -75,6 +75,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr int32_t deviceId = 1; constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr std::array hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; constexpr int32_t action = AKEY_EVENT_ACTION_DOWN; constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; constexpr int32_t keyCode = AKEYCODE_ENTER; @@ -84,8 +87,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr nsecs_t downTime = 3; constexpr nsecs_t eventTime = 4; - status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags, - keyCode, scanCode, metaState, repeatCount, downTime, eventTime); + status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags, + keyCode, scanCode, metaState, repeatCount, downTime, + eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; @@ -105,6 +109,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); EXPECT_EQ(displayId, keyEvent->getDisplayId()); + EXPECT_EQ(hmac, keyEvent->getHmac()); EXPECT_EQ(action, keyEvent->getAction()); EXPECT_EQ(flags, keyEvent->getFlags()); EXPECT_EQ(keyCode, keyEvent->getKeyCode()); @@ -136,6 +141,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr int32_t deviceId = 1; constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr std::array hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE; constexpr int32_t actionButton = 0; constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; @@ -143,6 +151,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; + constexpr float xScale = 2; + constexpr float yScale = 3; constexpr float xOffset = -10; constexpr float yOffset = -20; constexpr float xPrecision = 0.25; @@ -171,12 +181,12 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = - mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, - flags, edgeFlags, metaState, buttonState, classification, - xOffset, yOffset, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, downTime, eventTime, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action, + actionButton, flags, edgeFlags, metaState, buttonState, + classification, xScale, yScale, xOffset, yOffset, + xPrecision, yPrecision, xCursorPosition, + yCursorPosition, downTime, eventTime, pointerCount, + pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -196,12 +206,17 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); EXPECT_EQ(displayId, motionEvent->getDisplayId()); + EXPECT_EQ(hmac, motionEvent->getHmac()); EXPECT_EQ(action, motionEvent->getAction()); EXPECT_EQ(flags, motionEvent->getFlags()); EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); EXPECT_EQ(metaState, motionEvent->getMetaState()); EXPECT_EQ(buttonState, motionEvent->getButtonState()); EXPECT_EQ(classification, motionEvent->getClassification()); + EXPECT_EQ(xScale, motionEvent->getXScale()); + EXPECT_EQ(yScale, motionEvent->getYScale()); + EXPECT_EQ(xOffset, motionEvent->getXOffset()); + EXPECT_EQ(yOffset, motionEvent->getYOffset()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); @@ -316,11 +331,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } - status = - mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -331,11 +347,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = - mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -351,11 +368,12 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } - status = - mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 9ab0dba08d..aa8a2d488f 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -39,35 +39,39 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Key, source, 20); CHECK_OFFSET(InputMessage::Body::Key, displayId, 24); - CHECK_OFFSET(InputMessage::Body::Key, action, 28); - CHECK_OFFSET(InputMessage::Body::Key, flags, 32); - CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36); - CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40); - CHECK_OFFSET(InputMessage::Body::Key, metaState, 44); - CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48); - CHECK_OFFSET(InputMessage::Body::Key, downTime, 56); + CHECK_OFFSET(InputMessage::Body::Key, hmac, 28); + CHECK_OFFSET(InputMessage::Body::Key, action, 60); + CHECK_OFFSET(InputMessage::Body::Key, flags, 64); + CHECK_OFFSET(InputMessage::Body::Key, keyCode, 68); + CHECK_OFFSET(InputMessage::Body::Key, scanCode, 72); + CHECK_OFFSET(InputMessage::Body::Key, metaState, 76); + CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80); + CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); CHECK_OFFSET(InputMessage::Body::Motion, seq, 0); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24); - CHECK_OFFSET(InputMessage::Body::Motion, action, 28); - CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32); - CHECK_OFFSET(InputMessage::Body::Motion, flags, 36); - CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40); - CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44); - CHECK_OFFSET(InputMessage::Body::Motion, classification, 48); - CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52); - CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56); - CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64); - CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68); - CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72); - CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); - CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80); - CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); + CHECK_OFFSET(InputMessage::Body::Motion, hmac, 28); + CHECK_OFFSET(InputMessage::Body::Motion, action, 60); + CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 64); + CHECK_OFFSET(InputMessage::Body::Motion, flags, 68); + CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72); + CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76); + CHECK_OFFSET(InputMessage::Body::Motion, classification, 80); + CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84); + CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88); + CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96); + CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100); + CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104); + CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108); + CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112); + CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116); + CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120); + CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136); CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); @@ -86,7 +90,7 @@ void TestHeaderSize() { * the Motion type, where "pointerCount" variable affects the size and can change at runtime. */ void TestBodySize() { - static_assert(sizeof(InputMessage::Body::Key) == 64); + static_assert(sizeof(InputMessage::Body::Key) == 96); static_assert(sizeof(InputMessage::Body::Motion) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 968e2fa6bc..731eb6a000 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,11 +176,11 @@ static std::vector createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, - 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, + action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, + 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 246e735330..9a6ef21724 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -203,9 +203,10 @@ static MotionEvent generateMotionEvent() { const nsecs_t currentTime = now(); MotionEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + 1 /* xScale */, 1 /* yScale */, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime, diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 30fdf906fc..d8e91cc6ad 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2410,7 +2410,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, status = connection->inputPublisher .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, keyEntry->source, keyEntry->displayId, - dispatchEntry->resolvedAction, + INVALID_HMAC, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, keyEntry->keyCode, keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, @@ -2457,12 +2457,14 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, status = connection->inputPublisher .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, motionEntry->source, motionEntry->displayId, - dispatchEntry->resolvedAction, + INVALID_HMAC, dispatchEntry->resolvedAction, motionEntry->actionButton, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, - motionEntry->classification, xOffset, yOffset, + motionEntry->classification, + dispatchEntry->windowXScale, + dispatchEntry->windowYScale, xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->xCursorPosition, @@ -2930,8 +2932,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode, - args->scanCode, metaState, repeatCount, args->downTime, args->eventTime); + event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, + flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, + args->eventTime); android::base::Timer t; mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); @@ -3024,9 +3027,10 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, - args->actionButton, args->flags, args->edgeFlags, args->metaState, - args->buttonState, args->classification, 0, 0, args->xPrecision, + event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, + args->action, args->actionButton, args->flags, args->edgeFlags, + args->metaState, args->buttonState, args->classification, 1 /*xScale*/, + 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime, args->eventTime, args->pointerCount, args->pointerProperties, args->pointerCoords); @@ -3126,7 +3130,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec accelerateMetaShortcuts(keyEvent.getDeviceId(), action, /*byref*/ keyCode, /*byref*/ metaState); keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), - keyEvent.getDisplayId(), action, flags, keyCode, + keyEvent.getDisplayId(), INVALID_HMAC, action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), keyEvent.getDownTime(), keyEvent.getEventTime()); @@ -4682,8 +4686,8 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { KeyEvent event; - event.initialize(entry.deviceId, entry.source, entry.displayId, entry.action, entry.flags, - entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, + event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action, + entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, entry.downTime, entry.eventTime); return event; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 98ebf50819..094452a0c0 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -225,18 +225,18 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { KeyEvent event; // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, - /*action*/ -1, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, + ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject key events with undefined action."; // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, - AKEY_EVENT_ACTION_MULTIPLE, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, + ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -260,10 +260,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { constexpr MotionClassification classification = MotionClassification::NONE; // Rejects undefined motion actions. - event.initialize(DEVICE_ID, source, DISPLAY_ID, - /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, + 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -271,24 +271,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -296,24 +296,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -321,20 +321,20 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -343,10 +343,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -354,10 +354,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -367,10 +367,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, - metaState, 0, classification, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -645,9 +645,9 @@ static int32_t injectKeyDown(const sp& dispatcher, nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, - AKEY_EVENT_ACTION_DOWN, /* flags */ 0, - AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime); + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC, + AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + /* repeatCount */ 0, currentTime, currentTime); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -674,10 +674,12 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0, + event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, + /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, - /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime, + /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, + /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition, + currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. -- cgit v1.2.3-59-g8ed1b From 3826d47fa79b505683c20317fa1b8dafb0c8bd39 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 27 Jan 2020 10:44:40 -0600 Subject: Use uint32_t for input source The input source type originates from EventHub as an unsigned integer. But later, it somehow becomes a signed value. Even the InputListeners specify the source as uint32_t. To maintain the consistency prior to adding new API that would again use source, convert the source to uint32_t. This also helps remove some static_casts, as well as ensures that the order of input arguments is harder to confuse. Bug: 134977432 Test: atest libinput_tests inputflinger_tests inputflinger_benchmarks Change-Id: Iba10da08ee3a1004d50827785f570dab72a919d1 --- include/input/Input.h | 14 +++++++------- libs/input/Input.cpp | 12 ++++++------ libs/input/tests/InputEvent_test.cpp | 8 ++++---- libs/input/tests/InputPublisherAndConsumer_test.cpp | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index 1cf58efcaf..cf0814cf2f 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -354,9 +354,9 @@ public: inline int32_t getDeviceId() const { return mDeviceId; } - inline int32_t getSource() const { return mSource; } + inline uint32_t getSource() const { return mSource; } - inline void setSource(int32_t source) { mSource = source; } + inline void setSource(uint32_t source) { mSource = source; } inline int32_t getDisplayId() const { return mDisplayId; } @@ -365,12 +365,12 @@ public: inline std::array getHmac() const { return mHmac; } protected: - void initialize(int32_t deviceId, int32_t source, int32_t displayId, + void initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac); void initialize(const InputEvent& from); int32_t mDeviceId; - int32_t mSource; + uint32_t mSource; int32_t mDisplayId; std::array mHmac; }; @@ -405,7 +405,7 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - void initialize(int32_t deviceId, int32_t source, int32_t displayId, + void initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); @@ -629,7 +629,7 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t deviceId, int32_t source, int32_t displayId, + void initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, float yScale, float xOffset, @@ -657,7 +657,7 @@ public: status_t writeToParcel(Parcel* parcel) const; #endif - static bool isTouchEvent(int32_t source, int32_t action); + static bool isTouchEvent(uint32_t source, int32_t action); inline bool isTouchEvent() const { return isTouchEvent(mSource, mAction); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index bff1b97769..3a5fc7fe6e 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -57,7 +57,7 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } -void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, +void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac) { mDeviceId = deviceId; mSource = source; @@ -82,7 +82,7 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } -void KeyEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, +void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { @@ -245,7 +245,7 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, +void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, @@ -488,7 +488,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { } mDeviceId = parcel->readInt32(); - mSource = parcel->readInt32(); + mSource = parcel->readUint32(); mDisplayId = parcel->readInt32(); std::vector hmac; status_t result = parcel->readByteVector(&hmac); @@ -549,7 +549,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(sampleCount); parcel->writeInt32(mDeviceId); - parcel->writeInt32(mSource); + parcel->writeUint32(mSource); parcel->writeInt32(mDisplayId); std::vector hmac(mHmac.begin(), mHmac.end()); parcel->writeByteVector(hmac); @@ -590,7 +590,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { } #endif -bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { +bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) { if (source & AINPUT_SOURCE_CLASS_POINTER) { // Specifically excludes HOVER_MOVE and SCROLL. switch (action & AMOTION_EVENT_ACTION_MASK) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 8c6890f542..3d7c34e542 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -189,7 +189,7 @@ TEST_F(KeyEventTest, Properties) { ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); - ASSERT_EQ(static_cast(AINPUT_SOURCE_GAMEPAD), event.getSource()); + ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); EXPECT_EQ(HMAC, event.getHmac()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); @@ -203,7 +203,7 @@ TEST_F(KeyEventTest, Properties) { // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); - ASSERT_EQ(static_cast(AINPUT_SOURCE_JOYSTICK), event.getSource()); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); // Set display id. constexpr int32_t newDisplayId = 2; @@ -311,7 +311,7 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { // Check properties. ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); - ASSERT_EQ(static_cast(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); EXPECT_EQ(HMAC, event->getHmac()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); @@ -448,7 +448,7 @@ TEST_F(MotionEventTest, Properties) { // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); - ASSERT_EQ(static_cast(AINPUT_SOURCE_JOYSTICK), event.getSource()); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); // Set displayId. constexpr int32_t newDisplayId = 2; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 1e51ea8b2f..b002893c43 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -73,7 +73,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr uint32_t seq = 15; constexpr int32_t deviceId = 1; - constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; + constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; constexpr std::array hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, @@ -139,7 +139,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr uint32_t seq = 15; constexpr int32_t deviceId = 1; - constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; + constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; constexpr std::array hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -- cgit v1.2.3-59-g8ed1b From 8235709159abca9504968f08825840d3e1f5d61a Mon Sep 17 00:00:00 2001 From: chaviw Date: Tue, 28 Jan 2020 13:13:06 -0800 Subject: Send raw coordinates to client but scale when getting relative The current code scales the coordinates before sending to the client. This changes the coordinates so when the client requests raw coordinates, they are actually getting the scaled ones. Instead, don't scale the coordinates in InputDispatcher and instead send the scale factors to the client. When the client requests raw coordinates, they will get the unscaled ones. When they request relative coordinates, they will get the coordinates with the scale and offset applied. Fixes: 140786233 Test: atest libinput_tests Change-Id: I99b9ce7236511f595a8780506bf5aea8c75ed577 --- libs/input/Input.cpp | 42 +++++++++++----------- libs/input/tests/InputEvent_test.cpp | 30 ++++++++-------- .../input/tests/InputPublisherAndConsumer_test.cpp | 12 +++---- .../inputflinger/dispatcher/InputDispatcher.cpp | 30 ++++++++-------- 4 files changed, 57 insertions(+), 57 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index bff1b97769..975483bf04 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -321,17 +321,17 @@ void MotionEvent::addSample( float MotionEvent::getXCursorPosition() const { const float rawX = getRawXCursorPosition(); - return rawX + mXOffset; + return rawX * mXScale + mXOffset; } float MotionEvent::getYCursorPosition() const { const float rawY = getRawYCursorPosition(); - return rawY + mYOffset; + return rawY * mYScale + mYOffset; } void MotionEvent::setCursorPosition(float x, float y) { - mRawXCursorPosition = x - mXOffset; - mRawYCursorPosition = y - mYOffset; + mRawXCursorPosition = (x - mXOffset) / mXScale; + mRawYCursorPosition = (y - mYOffset) / mYScale; } const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { @@ -346,9 +346,9 @@ float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: - return value + mXOffset; + return value * mXScale + mXOffset; case AMOTION_EVENT_AXIS_Y: - return value + mYOffset; + return value * mYScale + mYOffset; } return value; } @@ -368,9 +368,9 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: - return value + mXOffset; + return value * mXScale + mXOffset; case AMOTION_EVENT_AXIS_Y: - return value + mYOffset; + return value * mYScale + mYOffset; } return value; } @@ -442,11 +442,11 @@ void MotionEvent::transform(const float matrix[9]) { float oldXOffset = mXOffset; float oldYOffset = mYOffset; float newX, newY; - float rawX = getRawX(0); - float rawY = getRawY(0); - transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY); - mXOffset = newX - rawX; - mYOffset = newY - rawY; + float scaledRawX = getRawX(0) * mXScale; + float scaledRawY = getRawY(0) * mYScale; + transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY); + mXOffset = newX - scaledRawX; + mYOffset = newY - scaledRawY; // Determine how the origin is transformed by the matrix so that we // can transform orientation vectors. @@ -455,22 +455,22 @@ void MotionEvent::transform(const float matrix[9]) { // Apply the transformation to cursor position. if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { - float x = mRawXCursorPosition + oldXOffset; - float y = mRawYCursorPosition + oldYOffset; + float x = mRawXCursorPosition * mXScale + oldXOffset; + float y = mRawYCursorPosition * mYScale + oldYOffset; transformPoint(matrix, x, y, &x, &y); - mRawXCursorPosition = x - mXOffset; - mRawYCursorPosition = y - mYOffset; + mRawXCursorPosition = (x - mXOffset) / mXScale; + mRawYCursorPosition = (y - mYOffset) / mYScale; } // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { PointerCoords& c = mSamplePointerCoords.editItemAt(i); - float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset; - float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset; + float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset; + float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset; transformPoint(matrix, x, y, &x, &y); - c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset); - c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset); + c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale); + c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale); float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 8c6890f542..23043b50d6 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -375,19 +375,19 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(211, event->getRawY(0)); ASSERT_EQ(221, event->getRawY(1)); - ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0)); - ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0)); - ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1)); - ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1)); - ASSERT_EQ(X_OFFSET + 210, event->getX(0)); - ASSERT_EQ(X_OFFSET + 220, event->getX(1)); - - ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0)); - ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0)); - ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1)); - ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1)); - ASSERT_EQ(Y_OFFSET + 211, event->getY(0)); - ASSERT_EQ(Y_OFFSET + 221, event->getY(1)); + ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0)); + ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0)); + ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1)); + ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1)); + ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0)); + ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1)); + + ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0)); + ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0)); + ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1)); + ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1)); + ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0)); + ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1)); ASSERT_EQ(12, event->getHistoricalPressure(0, 0)); ASSERT_EQ(22, event->getHistoricalPressure(1, 0)); @@ -513,8 +513,8 @@ TEST_F(MotionEventTest, Scale) { ASSERT_EQ(210 * 2, event.getRawX(0)); ASSERT_EQ(211 * 2, event.getRawY(0)); - ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0)); - ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0)); + ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0)); + ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0)); ASSERT_EQ(212, event.getPressure(0)); ASSERT_EQ(213, event.getSize(0)); ASSERT_EQ(214 * 2, event.getTouchMajor(0)); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 1e51ea8b2f..cec9cba7d7 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -221,8 +221,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); - EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition()); - EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition()); + EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition()); + EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); @@ -237,10 +237,10 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { motionEvent->getRawX(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), motionEvent->getRawY(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, - motionEvent->getX(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, - motionEvent->getY(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset, + motionEvent->getX(i)); + EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset, + motionEvent->getY(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5ae24199d8..3635f3b00a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2424,26 +2424,28 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, PointerCoords scaledCoords[MAX_POINTERS]; const PointerCoords* usingCoords = motionEntry->pointerCoords; - // Set the X and Y offset depending on the input source. - float xOffset, yOffset; + // Set the X and Y offset and X and Y scale depending on the input source. + float xOffset = 0.0f, yOffset = 0.0f; + float xScale = 1.0f, yScale = 1.0f; if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { float globalScaleFactor = dispatchEntry->globalScaleFactor; - float wxs = dispatchEntry->windowXScale; - float wys = dispatchEntry->windowYScale; - xOffset = dispatchEntry->xOffset * wxs; - yOffset = dispatchEntry->yOffset * wys; - if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) { + xScale = dispatchEntry->windowXScale; + yScale = dispatchEntry->windowYScale; + xOffset = dispatchEntry->xOffset * xScale; + yOffset = dispatchEntry->yOffset * yScale; + if (globalScaleFactor != 1.0f) { for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { scaledCoords[i] = motionEntry->pointerCoords[i]; - scaledCoords[i].scale(globalScaleFactor, wxs, wys); + // Don't apply window scale here since we don't want scale to affect raw + // coordinates. The scale will be sent back to the client and applied + // later when requesting relative coordinates. + scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */, + 1 /* windowYScale */); } usingCoords = scaledCoords; } } else { - xOffset = 0.0f; - yOffset = 0.0f; - // We don't want the dispatch target to know. if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { @@ -2462,10 +2464,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, - motionEntry->classification, - dispatchEntry->windowXScale, - dispatchEntry->windowYScale, xOffset, yOffset, - motionEntry->xPrecision, + motionEntry->classification, xScale, yScale, + xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->xCursorPosition, motionEntry->yCursorPosition, -- cgit v1.2.3-59-g8ed1b From 54d3e189009e4138ec5074b0ac79f56ce322daed Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 15 Jan 2020 17:38:38 -0800 Subject: 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 --- include/input/Input.h | 62 +++++++++++ libs/input/Input.cpp | 24 +++++ libs/input/tests/Android.bp | 2 +- libs/input/tests/InputEvent_test.cpp | 1 - .../input/tests/InputPublisherAndConsumer_test.cpp | 1 + libs/input/tests/StructLayout_test.cpp | 30 ++++++ libs/input/tests/VerifiedInputEvent_test.cpp | 116 +++++++++++++++++++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 4 + services/inputflinger/dispatcher/InputDispatcher.h | 2 + .../dispatcher/include/InputDispatcherInterface.h | 7 ++ 10 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 libs/input/tests/VerifiedInputEvent_test.cpp (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index cf0814cf2f..14a7288d19 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -73,6 +73,19 @@ enum { 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 @@ protected: 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 85b0fd0ec7..2a73dc0149 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -57,6 +57,30 @@ const char* inputEventTypeToString(int32_t type) { 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 hmac) { mDeviceId = deviceId; diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index c1c35e1b89..fb21d5e3b1 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -10,12 +10,12 @@ cc_test { "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 dce1f29124..d0f761887a 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -46,7 +46,6 @@ TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { } 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 d4bbf6c6ac..885196f3f3 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -38,6 +38,7 @@ protected: 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 aa8a2d488f..dd127fcabd 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -98,4 +98,34 @@ void TestBodySize() { 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 0000000000..a59dbe5987 --- /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 +#include + +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 f2b95e7f7a..a8158ba3cf 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3340,6 +3340,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec return injectionResult; } +std::unique_ptr 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 d2aea80069..72511e9bf2 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -96,6 +96,8 @@ public: int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags) override; + virtual std::unique_ptr verifyInputEvent(const InputEvent& event) override; + virtual void setInputWindows( const std::vector>& inputWindowHandles, int32_t displayId, const sp& setInputWindowsListener = nullptr) override; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 3424f4ce84..6e986768fc 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -92,6 +92,13 @@ public: 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 verifyInputEvent(const InputEvent& event) = 0; + /* Sets the list of input windows. * * This method may be called on any thread (usually by the input manager). -- cgit v1.2.3-59-g8ed1b From bd25f1cad839bbe84f1baa0eb1aa3ddd84fcbde5 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 23 Jan 2020 10:49:05 -0800 Subject: Add event ID generator. Bug: 144889238 Test: atest libinput_test Change-Id: I0fd192b3c5a08326aa6a6598025c7b8cdc64cce4 Merged-In: I0fd192b3c5a08326aa6a6598025c7b8cdc64cce4 (cherry picked from commit 84b087ec591626e8dd7d3d2c877afb74f0de1b09) --- include/input/Input.h | 32 +++++++++++++++++++++ libs/input/Input.cpp | 29 +++++++++++++++++++ libs/input/tests/Android.bp | 1 + libs/input/tests/IdGenerator_test.cpp | 52 +++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 libs/input/tests/IdGenerator_test.cpp (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index 14a7288d19..b86d761e48 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -265,6 +265,38 @@ enum class MotionClassification : uint8_t { */ const char* motionClassificationToString(MotionClassification classification); +/** + * Generator of unique numbers used to identify input events. + * + * Layout of ID: + * |--------------------------|---------------------------| + * | 2 bits for source | 30 bits for random number | + * |--------------------------|---------------------------| + */ +class IdGenerator { +private: + static constexpr uint32_t SOURCE_SHIFT = 30; + +public: + // Used to divide integer space to ensure no conflict among these sources./ + enum class Source : int32_t { + INPUT_READER = 0x0 << SOURCE_SHIFT, + INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT, + OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events + }; + IdGenerator(Source source); + + int32_t nextId() const; + + // Extract source from given id. + static inline Source getSource(int32_t id) { return static_cast(SOURCE_MASK & id); } + +private: + const Source mSource; + + static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT; +}; + /** * Invalid value for cursor position. Used for non-mouse events, tests and injected events. Don't * use it for direct comparison with any other value, because NaN isn't equal to itself according to diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 2a73dc0149..abd031ae26 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -17,7 +17,9 @@ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 +#include #include +#include #include #include @@ -25,6 +27,7 @@ #ifdef __ANDROID__ #include +#include #endif namespace android { @@ -40,6 +43,32 @@ const char* motionClassificationToString(MotionClassification classification) { } } +// --- IdGenerator --- +IdGenerator::IdGenerator(Source source) : mSource(source) {} + +int32_t IdGenerator::nextId() const { + constexpr uint32_t SEQUENCE_NUMBER_MASK = ~SOURCE_MASK; + int32_t id = 0; + +// Avoid building against syscall getrandom(2) on host, which will fail build on Mac. Host doesn't +// use sequence number so just always return mSource. +#ifdef __ANDROID__ + constexpr size_t BUF_LEN = sizeof(id); + size_t totalBytes = 0; + while (totalBytes < BUF_LEN) { + ssize_t bytes = TEMP_FAILURE_RETRY(getrandom(&id, BUF_LEN, GRND_NONBLOCK)); + if (CC_UNLIKELY(bytes < 0)) { + ALOGW("Failed to fill in random number for sequence number: %s.", strerror(errno)); + id = 0; + break; + } + totalBytes += bytes; + } +#endif // __ANDROID__ + + return (id & SEQUENCE_NUMBER_MASK) | static_cast(mSource); +} + // --- InputEvent --- const char* inputEventTypeToString(int32_t type) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index fb21d5e3b1..3b57146461 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -2,6 +2,7 @@ cc_test { name: "libinput_tests", srcs: [ + "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", diff --git a/libs/input/tests/IdGenerator_test.cpp b/libs/input/tests/IdGenerator_test.cpp new file mode 100644 index 0000000000..f7fc3c0ef2 --- /dev/null +++ b/libs/input/tests/IdGenerator_test.cpp @@ -0,0 +1,52 @@ +/* + * 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 +#include +#include +#include +#include + +namespace android::test { + +class IdGeneratorTest : public testing::TestWithParam { +protected: + void SetUp() override { mGenerator.reset(new IdGenerator(GetParam())); } + + std::unique_ptr mGenerator; +}; + +TEST_P(IdGeneratorTest, GenerateRandomNumber) { + for (int i = 0; i < 500; ++i) { + mGenerator->nextId(); + } +} + +TEST_P(IdGeneratorTest, GenerateRandomNumberWithProperFlag) { + for (int i = 0; i < 500; ++i) { + int32_t id = mGenerator->nextId(); + IdGenerator::Source source = IdGenerator::getSource(id); + EXPECT_EQ(source, GetParam()) + << std::hex << "Generator generated a value with wrong source. Value: 0x" << id + << " Source: 0x" << static_cast(source); + } +} + +INSTANTIATE_TEST_SUITE_P(SourceInstantiation, IdGeneratorTest, + testing::Values(IdGenerator::Source::INPUT_READER, + IdGenerator::Source::INPUT_DISPATCHER, + IdGenerator::Source::OTHER)); +} // namespace android::test -- cgit v1.2.3-59-g8ed1b From fbe732ebbb25870086610e9a32acdb37df402612 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Fri, 24 Jan 2020 11:26:14 -0800 Subject: Add ID to native events. To separate this big work into pieces I introduced a placeholder in InputTransport used to initialize native input events received from InputFlinger until InputFlinger can generate random sequence numbers. The work in InputDispatcher wires up ID between events and notify args as well. Bug: 144889238 Test: Builds and some smoke tests. Test: atest libinput_tests Change-Id: I1ef7f243cc89a8b6e07ba9ba30a43c21eedb16ce Merged-In: I1ef7f243cc89a8b6e07ba9ba30a43c21eedb16ce (cherry picked from commit 4cc839fe01cd5bbb16650e85913070386f637e75) --- include/input/Input.h | 14 ++-- libs/input/Input.cpp | 26 +++++--- libs/input/InputTransport.cpp | 37 ++++++----- libs/input/KeyCharacterMap.cpp | 6 +- libs/input/tests/InputEvent_test.cpp | 37 ++++++----- libs/input/tests/VelocityTracker_test.cpp | 11 ++-- libs/input/tests/VerifiedInputEvent_test.cpp | 11 ++-- .../benchmarks/InputDispatcher_benchmarks.cpp | 5 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 55 ++++++++-------- .../inputflinger/tests/InputDispatcher_test.cpp | 76 ++++++++++++---------- 10 files changed, 160 insertions(+), 118 deletions(-) (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index b86d761e48..9e47318203 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -397,6 +397,8 @@ public: virtual int32_t getType() const = 0; + inline int32_t getId() const { return mId; } + inline int32_t getDeviceId() const { return mDeviceId; } inline uint32_t getSource() const { return mSource; } @@ -409,11 +411,15 @@ public: inline std::array getHmac() const { return mHmac; } + static int32_t nextId(); + protected: - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac); + void initialize(const InputEvent& from); + int32_t mId; int32_t mDeviceId; uint32_t mSource; int32_t mDisplayId; @@ -450,7 +456,7 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); @@ -674,7 +680,7 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize(int32_t deviceId, uint32_t source, int32_t displayId, + void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, float yScale, float xOffset, @@ -754,7 +760,7 @@ public: inline bool getInTouchMode() const { return mInTouchMode; } - void initialize(bool hasFocus, bool inTouchMode); + void initialize(int32_t id, bool hasFocus, bool inTouchMode); void initialize(const FocusEvent& from); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index abd031ae26..c2437673df 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -110,8 +110,9 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) event.getButtonState()}; } -void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void InputEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac) { + mId = id; mDeviceId = deviceId; mSource = source; mDisplayId = displayId; @@ -119,12 +120,18 @@ void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId } void InputEvent::initialize(const InputEvent& from) { + mId = from.mId; mDeviceId = from.mDeviceId; mSource = from.mSource; mDisplayId = from.mDisplayId; mHmac = from.mHmac; } +int32_t InputEvent::nextId() { + static IdGenerator idGen(IdGenerator::Source::OTHER); + return idGen.nextId(); +} + // --- KeyEvent --- const char* KeyEvent::getLabel(int32_t keyCode) { @@ -135,11 +142,11 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { return getKeyCodeByLabel(label); } -void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { - InputEvent::initialize(deviceId, source, displayId, hmac); + InputEvent::initialize(id, deviceId, source, displayId, hmac); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -298,7 +305,7 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId, +void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, float xScale, @@ -307,7 +314,7 @@ void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayI nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source, displayId, hmac); + InputEvent::initialize(id, deviceId, source, displayId, hmac); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -332,7 +339,8 @@ void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayI } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac); + InputEvent::initialize(other->mId, other->mDeviceId, other->mSource, other->mDisplayId, + other->mHmac); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; @@ -540,6 +548,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { return BAD_VALUE; } + mId = parcel->readInt32(); mDeviceId = parcel->readInt32(); mSource = parcel->readUint32(); mDisplayId = parcel->readInt32(); @@ -601,6 +610,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(pointerCount); parcel->writeInt32(sampleCount); + parcel->writeInt32(mId); parcel->writeInt32(mDeviceId); parcel->writeUint32(mSource); parcel->writeInt32(mDisplayId); @@ -670,8 +680,8 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { // --- FocusEvent --- -void FocusEvent::initialize(bool hasFocus, bool inTouchMode) { - InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, +void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { + InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; mInTouchMode = inTouchMode; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d25a5cc0ef..eda01c5032 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -63,6 +63,10 @@ static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; // far into the future. This time is further bounded by 50% of the last time delta. static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; +// A placeholder sequence number used to initialize native input events before InputFlinger is +// migrated to new sequence number system. +static constexpr int32_t INPUT_FLINGER_SEQUENCE_NUM = 0; + /** * System property for enabling / disabling touch resampling. * Resampling extrapolates / interpolates the reported touch event coordinates to better @@ -1142,14 +1146,16 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { } void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { - event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId, - msg->body.key.hmac, msg->body.key.action, msg->body.key.flags, - msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState, - msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.key.deviceId, msg->body.key.source, + msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action, + msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode, + msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime, + msg->body.key.eventTime); } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.focus.hasFocus == 1, + msg->body.focus.inTouchMode == 1); } void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { @@ -1161,16 +1167,17 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } - event->initialize(msg->body.motion.deviceId, msg->body.motion.source, - msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, - msg->body.motion.actionButton, msg->body.motion.flags, - msg->body.motion.edgeFlags, msg->body.motion.metaState, - msg->body.motion.buttonState, msg->body.motion.classification, - msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, - msg->body.motion.yOffset, msg->body.motion.xPrecision, - msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, - msg->body.motion.yCursorPosition, msg->body.motion.downTime, - msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); + event->initialize(INPUT_FLINGER_SEQUENCE_NUM, msg->body.motion.deviceId, + msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, + msg->body.motion.action, msg->body.motion.actionButton, + msg->body.motion.flags, msg->body.motion.edgeFlags, + msg->body.motion.metaState, msg->body.motion.buttonState, + msg->body.motion.classification, msg->body.motion.xScale, + msg->body.motion.yScale, msg->body.motion.xOffset, msg->body.motion.yOffset, + msg->body.motion.xPrecision, msg->body.motion.yPrecision, + msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, + pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 6f9b162986..cb68165433 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -487,9 +487,9 @@ void KeyCharacterMap::addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, - 0, time, time); + event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, + 0, metaState, 0, time, time); } void KeyCharacterMap::addMetaKeys(Vector& outEvents, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index d0f761887a..553dc4c068 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -182,10 +182,12 @@ TEST_F(KeyEventTest, Properties) { // Initialize and get properties. constexpr nsecs_t ARBITRARY_DOWN_TIME = 1; constexpr nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, + const int32_t id = InputEvent::nextId(); + event.initialize(id, 2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + ASSERT_EQ(id, event.getId()); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); @@ -222,12 +224,16 @@ protected: static constexpr float X_OFFSET = 1; static constexpr float Y_OFFSET = 1.1; + int32_t mId; + void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { + mId = InputEvent::nextId(); + PointerProperties pointerProperties[2]; pointerProperties[0].clear(); pointerProperties[0].id = 1; @@ -257,10 +263,10 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, - AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, - X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, + AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, + MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); @@ -308,6 +314,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { // Check properties. + ASSERT_EQ(mId, event->getId()); ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); @@ -577,8 +584,8 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, + INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, @@ -642,10 +649,10 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { } for (MotionClassification classification : classifications) { - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, - 0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, + DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/, + 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); @@ -663,10 +670,10 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, - MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, + INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, + AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, + 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 731eb6a000..bf452c07a3 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,11 +176,12 @@ static std::vector createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, - action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, - AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/, - 1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, - 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, + event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, + DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index a59dbe5987..4e8e840d1c 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -21,9 +21,10 @@ 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*/); + event.initialize(InputEvent::nextId(), 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; } @@ -38,8 +39,8 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { pointerCoords[i].clear(); } - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, + event.initialize(InputEvent::nextId(), 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*/, diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 9a6ef21724..238cc39646 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -203,8 +203,9 @@ static MotionEvent generateMotionEvent() { const nsecs_t currentTime = now(); MotionEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC, - AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, + /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, 1 /* xScale */, 1 /* yScale */, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f9a86dd6bb..70cee18330 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3030,9 +3030,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, - flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, - args->eventTime); + event.initialize(args->sequenceNum, args->deviceId, args->source, args->displayId, INVALID_HMAC, + args->action, flags, keyCode, args->scanCode, metaState, repeatCount, + args->downTime, args->eventTime); android::base::Timer t; mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); @@ -3125,13 +3125,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, - args->action, args->actionButton, args->flags, args->edgeFlags, - args->metaState, args->buttonState, args->classification, 1 /*xScale*/, - 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision, - args->yPrecision, args->xCursorPosition, args->yCursorPosition, - args->downTime, args->eventTime, args->pointerCount, - args->pointerProperties, args->pointerCoords); + event.initialize(args->sequenceNum, args->deviceId, args->source, args->displayId, + INVALID_HMAC, args->action, args->actionButton, args->flags, + args->edgeFlags, args->metaState, args->buttonState, + args->classification, 1 /*xScale*/, 1 /*yScale*/, 0 /* xOffset */, + 0 /* yOffset */, args->xPrecision, args->yPrecision, + args->xCursorPosition, args->yCursorPosition, args->downTime, + args->eventTime, args->pointerCount, args->pointerProperties, + args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -3227,7 +3228,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action, /*byref*/ keyCode, /*byref*/ metaState); KeyEvent keyEvent; - keyEvent.initialize(VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), + keyEvent.initialize(incomingKey.getId(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode, incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), incomingKey.getDownTime(), incomingKey.getEventTime()); @@ -3247,11 +3248,12 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec mLock.lock(); KeyEntry* injectedEntry = - new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), - VIRTUAL_KEYBOARD_ID, keyEvent.getSource(), keyEvent.getDisplayId(), - policyFlags, action, flags, keyEvent.getKeyCode(), - keyEvent.getScanCode(), keyEvent.getMetaState(), - keyEvent.getRepeatCount(), keyEvent.getDownTime()); + new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(), + VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), + incomingKey.getDisplayId(), policyFlags, action, flags, + incomingKey.getKeyCode(), incomingKey.getScanCode(), + incomingKey.getMetaState(), incomingKey.getRepeatCount(), + incomingKey.getDownTime()); injectedEntries.push(injectedEntry); break; } @@ -3281,13 +3283,12 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); MotionEntry* injectedEntry = - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, action, actionButton, - motionEvent->getFlags(), motionEvent->getMetaState(), - motionEvent->getButtonState(), motionEvent->getClassification(), - motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), - motionEvent->getYPrecision(), + new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID, + motionEvent->getSource(), motionEvent->getDisplayId(), + policyFlags, action, actionButton, motionEvent->getFlags(), + motionEvent->getMetaState(), motionEvent->getButtonState(), + motionEvent->getClassification(), motionEvent->getEdgeFlags(), + motionEvent->getXPrecision(), motionEvent->getYPrecision(), motionEvent->getRawXCursorPosition(), motionEvent->getRawYCursorPosition(), motionEvent->getDownTime(), uint32_t(pointerCount), @@ -3298,7 +3299,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec sampleEventTimes += 1; samplePointerCoords += pointerCount; MotionEntry* nextInjectedEntry = - new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), motionEvent->getDisplayId(), policyFlags, action, actionButton, motionEvent->getFlags(), @@ -4817,9 +4818,9 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) { KeyEvent event; - event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action, - entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, - entry.downTime, entry.eventTime); + event.initialize(entry.sequenceNum, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, + entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState, + entry.repeatCount, entry.downTime, entry.eventTime); return event; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 2fb1b65d93..f05f7e5a79 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -49,9 +49,9 @@ struct PointF { static KeyEvent getTestKeyEvent() { KeyEvent event; - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, - AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, - ARBITRARY_TIME); + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, + ARBITRARY_TIME, ARBITRARY_TIME); return event; } @@ -300,7 +300,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { KeyEvent event; // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( @@ -309,8 +310,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { << "Should reject key events with undefined action."; // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, - AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, + INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -335,7 +336,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { constexpr MotionClassification classification = MotionClassification::NONE; // Rejects undefined motion actions. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, @@ -346,7 +347,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -358,7 +359,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -371,7 +372,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -383,7 +384,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, @@ -396,20 +397,22 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -418,10 +421,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -429,10 +433,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -442,10 +447,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, + AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, + 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, @@ -748,8 +754,9 @@ static int32_t injectKeyDown(const sp& dispatcher, nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC, - AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, + INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, + AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime); // Inject event until dispatch out. @@ -777,7 +784,8 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, + event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action, + /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, -- cgit v1.2.3-59-g8ed1b From e4623041f91c3fac633fc0f1803bc3a7c08c6eb9 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 25 Mar 2020 16:16:40 -0700 Subject: Report ANR when waited for longer than the timeout Previously, when the touched window was unresponsive, we skipped adding the gesture monitors to the touch targets. That means, when an app has ANR, gesture nav stops working, and the phone feels frozen, until the ANR dialog comes up. Another failure mode was a stuck pending event. If there is no focused window, and we have a pending key event (or any focused event), we will be waiting for a focused window to appear. That would still prevent gesture monitors from receiving further touch events. In this solution, we do not add unresponsive windows to the list of targets, but we still proceed with handling the event, meaning that the app won't get a new DOWN when it is unresponsive, but the event would still go to the gesture monitors. That change, in isolation, would also break ANR for the case when the app loses focus. To maintain the ANR functionality, we extend the ANR detection mechanism to all connections. Now, every connection is eligible to receive an ANR, even if it's a gesture monitor. We expect everything in the system to be responsive within reasonable timeouts. We also change the handling of extended ANR timeouts coming from policy. Today, the behaviour is as follows: 1. If the policy says "wait longer", then we do nothing and just keep waiting 2. If the policy says "abort", then we send the cancel events and remove the window from the window list. The "abort" approach seems incorrect, because the policy will probably not register the existing inputchannel/connection with a new window. If the user does click "close app" when the ANR dialog appears, we will anyways receive "window removed" event via setInputWindows, and will clean up the connection that way. So we don't really need to do anything other than sending "cancel events" to the window. The policy now for sending events to unresponsive windows becomes: 1. If the unresponsive window is touched, the new touch stream does not go to the window. It will go to all other places, though. 2. If the unresponsive window receives a focused event, the event still gets queued for the unresponsive window to handle. For improved ANR performance, the ANR detection is now done by introducing a helper data structure, a multiset of the timeout times. Whenever we send an event to a connection, we will calculate the time that this event will cause a timeout. We will then add this time to the multiset of times. When we check for ANR inside dispatchOnce, we will only access the smallest (soonest) timeout inside the multiset. If the current time is before this smallest timeout time, then everything is normal, and we move on. This would cost O(1). If the time is past the timeout time, it means a connection is unresponsive. In this case, we take the closest in time unresponsive entry. That entry already has the connection token, for convenience. We then raise an ANR on that connection. The entries are removed from the multiset in several cases: 1. When we receive a 'finished' signal on an entry for a specific connection 2. When the connection becomes unresponsive and we raise ANR. In that case, no need to keep checking on the same connection. Once case 1. applies to that connection, entries from that connection will again become eligible for being added to the multiset. 3. When we reset and drop everything. 4. When we cannot find a connection for an entry in the multiset, we will drop all entries from that connection. If we report ANR for an app, we do not report the second ANR until the waitQueue becomes healthy first and then becomes clogged again. If we have a focused application, but no window has focus, then nothing will happen for pointer events. They will keep going to the touched window as normal. When we receive the first focused event, however, we will start a timer. If there is no focused window added by that time, we will send an ANR for that application. This logic should be moved into WM later, because from the input perspective, it is legitimate to have an application without a focused window. This would also allow WM to remove the "setFocusedApplication" call. Bug: 143459140 Test: use the test app from the bug. The app sleeps for 10 seconds when the button is clicked. Click the button, and try to use gesture nav several times. Observe that gesture nav continues to work even after app has ANR. Observe that ANR is reported even after interacting with gesture nav. Test: Click on the app multiple times (to clog up the queue), and then wait for a long time, even after the ANR dialog shows up. Then click "wait" to not close the app. Then click again on the app. Observe that the anr dialog appears after a while. This indicates that at some point, the app processed all events, and then became eligible for anr again. Test: adb shell -t /data/nativetest64/inputflinger_tests/inputflinger_tests Test: create an app that sets "FLAG_NOT_FOCUSABLE" on its only window. Launch the app and interact with it by touch. Notice that the app does not ANR. Then, send the back key to the app (using the back gesture). Notice that in 5 seconds, we receive an ANR for this app. While the BACK key is queued up for this app, the gesture nav continues to work and the notification shade can still be pulled down. Change-Id: I2c0fd1957cda833f5fbe26368cfcaa6fea6eddaf Merged-In: I2c0fd1957cda833f5fbe26368cfcaa6fea6eddaf --- include/input/Input.h | 4 + libs/input/Input.cpp | 31 + services/inputflinger/dispatcher/Android.bp | 1 + services/inputflinger/dispatcher/AnrTracker.cpp | 73 ++ services/inputflinger/dispatcher/AnrTracker.h | 60 ++ services/inputflinger/dispatcher/Connection.cpp | 3 +- services/inputflinger/dispatcher/Connection.h | 7 +- services/inputflinger/dispatcher/Entry.cpp | 36 +- services/inputflinger/dispatcher/Entry.h | 4 + .../inputflinger/dispatcher/InputDispatcher.cpp | 665 ++++++++++-------- services/inputflinger/dispatcher/InputDispatcher.h | 86 ++- services/inputflinger/tests/Android.bp | 1 + services/inputflinger/tests/AnrTracker_test.cpp | 167 +++++ .../inputflinger/tests/InputDispatcher_test.cpp | 750 +++++++++++++++++++-- 14 files changed, 1499 insertions(+), 389 deletions(-) create mode 100644 services/inputflinger/dispatcher/AnrTracker.cpp create mode 100644 services/inputflinger/dispatcher/AnrTracker.h create mode 100644 services/inputflinger/tests/AnrTracker_test.cpp (limited to 'libs/input/Input.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index 9e47318203..54b4e5a737 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -462,6 +462,8 @@ public: nsecs_t eventTime); void initialize(const KeyEvent& from); + static const char* actionToString(int32_t action); + protected: int32_t mAction; int32_t mFlags; @@ -725,6 +727,8 @@ public: static const char* getLabel(int32_t axis); static int32_t getAxisFromLabel(const char* label); + static const char* actionToString(int32_t action); + protected: int32_t mAction; int32_t mActionButton; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index c2437673df..31aa685391 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -169,6 +169,18 @@ void KeyEvent::initialize(const KeyEvent& from) { mEventTime = from.mEventTime; } +const char* KeyEvent::actionToString(int32_t action) { + // Convert KeyEvent action to string + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + return "DOWN"; + case AKEY_EVENT_ACTION_UP: + return "UP"; + case AKEY_EVENT_ACTION_MULTIPLE: + return "MULTIPLE"; + } + return "UNKNOWN"; +} // --- PointerCoords --- @@ -678,6 +690,25 @@ int32_t MotionEvent::getAxisFromLabel(const char* label) { return getAxisByLabel(label); } +const char* MotionEvent::actionToString(int32_t action) { + // Convert MotionEvent action to string + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + return "DOWN"; + case AMOTION_EVENT_ACTION_MOVE: + return "MOVE"; + case AMOTION_EVENT_ACTION_UP: + return "UP"; + case AMOTION_EVENT_ACTION_CANCEL: + return "CANCEL"; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + return "POINTER_DOWN"; + case AMOTION_EVENT_ACTION_POINTER_UP: + return "POINTER_UP"; + } + return "UNKNOWN"; +} + // --- FocusEvent --- void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index b242eec465..d29d8dfda3 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -22,6 +22,7 @@ cc_library_headers { filegroup { name: "libinputdispatcher_sources", srcs: [ + "AnrTracker.cpp", "Connection.cpp", "Entry.cpp", "InjectionState.cpp", diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp new file mode 100644 index 0000000000..c3f611e7db --- /dev/null +++ b/services/inputflinger/dispatcher/AnrTracker.cpp @@ -0,0 +1,73 @@ +/* + * 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 "AnrTracker.h" + +namespace android::inputdispatcher { + +template +static T max(const T& a, const T& b) { + return a < b ? b : a; +} + +void AnrTracker::insert(nsecs_t timeoutTime, sp token) { + mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token))); +} + +/** + * Erase a single entry only. If there are multiple duplicate entries + * (same time, same connection), then only remove one of them. + */ +void AnrTracker::erase(nsecs_t timeoutTime, const sp& token) { + auto pair = std::make_pair(timeoutTime, token); + auto it = mAnrTimeouts.find(pair); + if (it != mAnrTimeouts.end()) { + mAnrTimeouts.erase(it); + } +} + +void AnrTracker::eraseToken(const sp& token) { + for (auto it = mAnrTimeouts.begin(); it != mAnrTimeouts.end();) { + if (it->second == token) { + it = mAnrTimeouts.erase(it); + } else { + ++it; + } + } +} + +bool AnrTracker::empty() const { + return mAnrTimeouts.empty(); +} + +// If empty() is false, return the time at which the next connection should cause an ANR +// If empty() is true, return LONG_LONG_MAX +nsecs_t AnrTracker::firstTimeout() const { + if (mAnrTimeouts.empty()) { + return std::numeric_limits::max(); + } + return mAnrTimeouts.begin()->first; +} + +const sp& AnrTracker::firstToken() const { + return mAnrTimeouts.begin()->second; +} + +void AnrTracker::clear() { + mAnrTimeouts.clear(); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h new file mode 100644 index 0000000000..097dba5bea --- /dev/null +++ b/services/inputflinger/dispatcher/AnrTracker.h @@ -0,0 +1,60 @@ +/* + * 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 _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H +#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H + +#include +#include +#include + +namespace android::inputdispatcher { + +/** + * Keeps track of the times when each connection is going to ANR. + * Provides the ability to quickly find the connection that is going to cause ANR next. + */ +class AnrTracker { +public: + void insert(nsecs_t timeoutTime, sp token); + void erase(nsecs_t timeoutTime, const sp& token); + void eraseToken(const sp& token); + void clear(); + + bool empty() const; + // If empty() is false, return the time at which the next connection should cause an ANR + // If empty() is true, return LONG_LONG_MAX + nsecs_t firstTimeout() const; + // Return the token of the next connection that should cause an ANR. + // Do not call this unless empty() is false, you will encounter undefined behaviour. + const sp& firstToken() const; + +private: + // Optimization: use a multiset to keep track of the event timeouts. When an event is sent + // to the InputConsumer, we add an entry to this structure. We look at the smallest value to + // determine if any of the connections is unresponsive, and to determine when we should wake + // next for the future ANR check. + // Using a multiset helps quickly look up the next timeout due. + // + // We must use a multi-set, because it is plausible (although highly unlikely) to have entries + // from the same connection and same timestamp, but different sequence numbers. + // We are not tracking sequence numbers, and just allow duplicates to exist. + std::multiset /*connectionToken*/>> mAnrTimeouts; +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index 188212bccf..f5ea563311 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -26,8 +26,7 @@ Connection::Connection(const sp& inputChannel, bool monitor, inputChannel(inputChannel), monitor(monitor), inputPublisher(inputChannel), - inputState(idGenerator), - inputPublisherBlocked(false) {} + inputState(idGenerator) {} Connection::~Connection() {} diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index bb3f2fee19..3b33f29dff 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -47,9 +47,10 @@ public: InputPublisher inputPublisher; InputState inputState; - // True if the socket is full and no further events can be published until - // the application consumes some of the input. - bool inputPublisherBlocked; + // True if this connection is responsive. + // If this connection is not responsive, avoid publishing more events to it until the + // application consumes some of the input. + bool responsive = true; // Queue of events that need to be published to the connection. std::deque outboundQueue; diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 21c8ae165d..fdbb1d1b55 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -28,38 +28,6 @@ using android::base::StringPrintf; namespace android::inputdispatcher { -static std::string motionActionToString(int32_t action) { - // Convert MotionEvent action to string - switch (action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - return "DOWN"; - case AMOTION_EVENT_ACTION_MOVE: - return "MOVE"; - case AMOTION_EVENT_ACTION_UP: - return "UP"; - case AMOTION_EVENT_ACTION_CANCEL: - return "CANCEL"; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - return "POINTER_DOWN"; - case AMOTION_EVENT_ACTION_POINTER_UP: - return "POINTER_UP"; - } - return StringPrintf("%" PRId32, action); -} - -static std::string keyActionToString(int32_t action) { - // Convert KeyEvent action to string - switch (action) { - case AKEY_EVENT_ACTION_DOWN: - return "DOWN"; - case AKEY_EVENT_ACTION_UP: - return "UP"; - case AKEY_EVENT_ACTION_MULTIPLE: - return "MULTIPLE"; - } - return StringPrintf("%" PRId32, action); -} - VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) { return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source, entry.displayId}, @@ -191,7 +159,7 @@ void KeyEntry::appendDescription(std::string& msg) const { msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", - deviceId, source, displayId, keyActionToString(action).c_str(), flags, + deviceId, source, displayId, KeyEvent::actionToString(action), flags, keyCode, scanCode, metaState, repeatCount, policyFlags); } @@ -253,7 +221,7 @@ void MotionEntry::appendDescription(std::string& msg) const { "buttonState=0x%08x, " "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", - deviceId, source, displayId, motionActionToString(action).c_str(), + deviceId, source, displayId, MotionEvent::actionToString(action), actionButton, flags, metaState, buttonState, motionClassificationToString(classification), edgeFlags, xPrecision, yPrecision, xCursorPosition, yCursorPosition); diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index a135409365..6b7697dde6 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -198,7 +198,11 @@ struct DispatchEntry { float globalScaleFactor; float windowXScale = 1.0f; float windowYScale = 1.0f; + // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app, + // and will be undefined before that. nsecs_t deliveryTime; // time when the event was actually delivered + // An ANR will be triggered if a response for this entry is not received by timeoutTime + nsecs_t timeoutTime; // Set to the resolved ID, action and flags when the event is enqueued. int32_t resolvedEventId; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 959eeea41e..677bf7e2ec 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -90,18 +90,17 @@ constexpr nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec // before considering it stale and dropping it. constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec -// Amount of time to allow touch events to be streamed out to a connection before requiring -// that the first event be finished. This value extends the ANR timeout by the specified -// amount. For example, if streaming is allowed to get ahead by one second relative to the -// queue of waiting unfinished events, then ANRs will similarly be delayed by one second. -constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec - // Log a warning when an event takes longer than this to process, even if an ANR does not occur. constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec // Log a warning when an interception call takes longer than this to process. constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; +// Additional key latency in case a connection is still processing some motion events. +// This will help with the case when a user touched a button that opens a new window, +// and gives us the chance to dispatch the key to this new window. +constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms; + // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; @@ -409,8 +408,7 @@ InputDispatcher::InputDispatcher(const sp& polic // To avoid leaking stack in case that call never comes, and for tests, // initialize it here anyways. mInTouchMode(true), - mFocusedDisplayId(ADISPLAY_ID_DEFAULT), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { + mFocusedDisplayId(ADISPLAY_ID_DEFAULT) { mLooper = new Looper(false); mReporter = createInputReporter(); @@ -470,6 +468,11 @@ void InputDispatcher::dispatchOnce() { nextWakeupTime = LONG_LONG_MIN; } + // If we are still waiting for ack on some events, + // we might have to wake up earlier to check if an app is anr'ing. + const nsecs_t nextAnrCheck = processAnrsLocked(); + nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck); + // We are about to enter an infinitely long sleep, because we have no commands or // pending or queued events if (nextWakeupTime == LONG_LONG_MAX) { @@ -483,6 +486,55 @@ void InputDispatcher::dispatchOnce() { mLooper->pollOnce(timeoutMillis); } +/** + * Check if any of the connections' wait queues have events that are too old. + * If we waited for events to be ack'ed for more than the window timeout, raise an ANR. + * Return the time at which we should wake up next. + */ +nsecs_t InputDispatcher::processAnrsLocked() { + const nsecs_t currentTime = now(); + nsecs_t nextAnrCheck = LONG_LONG_MAX; + // Check if we are waiting for a focused window to appear. Raise ANR if waited too long + if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) { + if (currentTime >= *mNoFocusedWindowTimeoutTime) { + onAnrLocked(mAwaitedFocusedApplication); + mAwaitedFocusedApplication.clear(); + return LONG_LONG_MIN; + } else { + // Keep waiting + const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime); + ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining); + nextAnrCheck = *mNoFocusedWindowTimeoutTime; + } + } + + // Check if any connection ANRs are due + nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout()); + if (currentTime < nextAnrCheck) { // most likely scenario + return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck + } + + // If we reached here, we have an unresponsive connection. + sp connection = getConnectionLocked(mAnrTracker.firstToken()); + if (connection == nullptr) { + ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout()); + return nextAnrCheck; + } + connection->responsive = false; + // Stop waking up for this unresponsive connection + mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); + onAnrLocked(connection); + return LONG_LONG_MIN; +} + +nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp& token) { + sp window = getWindowHandleLocked(token); + if (window != nullptr) { + return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count(); + } + return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count(); +} + void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { nsecs_t currentTime = now(); @@ -546,9 +598,6 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { pokeUserActivityLocked(*mPendingEvent); } - - // Get ready to dispatch the event. - resetAnrTimeoutsLocked(); } // Now we have an event to dispatch. @@ -642,11 +691,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { * Return false otherwise (the default behaviour) */ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { - bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && + const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER); - if (isPointerDownEvent && - mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && - mInputTargetWaitApplicationToken != nullptr) { + + // Optimize case where the current application is unresponsive and the user + // decides to touch a window in a different application. + // If the application takes too long to catch up then we drop all events preceding + // the touch into the other window. + if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) { int32_t displayId = motionEntry.displayId; int32_t x = static_cast( motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); @@ -655,12 +707,41 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt sp touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, nullptr); if (touchedWindowHandle != nullptr && - touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) { + touchedWindowHandle->getApplicationToken() != + mAwaitedFocusedApplication->getApplicationToken()) { // User touched a different application than the one we are waiting on. - // Flag the event, and start pruning the input queue. - ALOGI("Pruning input queue because user touched a different application"); + ALOGI("Pruning input queue because user touched a different application while waiting " + "for %s", + mAwaitedFocusedApplication->getName().c_str()); return true; } + + // Alternatively, maybe there's a gesture monitor that could handle this event + std::vector gestureMonitors = + findTouchedGestureMonitorsLocked(displayId, {}); + for (TouchedMonitor& gestureMonitor : gestureMonitors) { + sp connection = + getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken()); + if (connection->responsive) { + // This monitor could take more input. Drop all events preceding this + // event, so that gesture monitor could get a chance to receive the stream + ALOGW("Pruning the input queue because %s is unresponsive, but we have a " + "responsive gesture monitor that may handle the event", + mAwaitedFocusedApplication->getName().c_str()); + return true; + } + } + } + + // Prevent getting stuck: if we have a pending key event, and some motion events that have not + // yet been processed by some connections, the dispatcher will wait for these motion + // events to be processed before dispatching the key event. This is because these motion events + // may cause a new window to be launched, which the user might expect to receive focus. + // To prevent waiting forever for such events, just send the key to the currently focused window + if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { + ALOGD("Received a new pointer down event, stop waiting for events to process and " + "just send the pending key event to the focused window."); + mKeyIsWaitingForEventsTimeout = now(); } return false; } @@ -694,10 +775,6 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { } case EventEntry::Type::MOTION: { - // Optimize case where the current application is unresponsive and the user - // decides to touch a window in a different application. - // If the application takes too long to catch up then we drop all events preceding - // the touch into the other window. if (shouldPruneInboundQueueLocked(static_cast(*entry))) { mNextUnblockedEvent = entry; needWake = true; @@ -912,7 +989,6 @@ void InputDispatcher::drainInboundQueueLocked() { void InputDispatcher::releasePendingEventLocked() { if (mPendingEvent) { - resetAnrTimeoutsLocked(); releaseInboundEventLocked(mPendingEvent); mPendingEvent = nullptr; } @@ -1294,109 +1370,29 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event } } -int32_t InputDispatcher::handleTargetsNotReadyLocked( - nsecs_t currentTime, const EventEntry& entry, - const sp& applicationHandle, - const sp& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { - if (applicationHandle == nullptr && windowHandle == nullptr) { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { - if (DEBUG_FOCUS) { - ALOGD("Waiting for system to become ready for input. Reason: %s", reason); - } - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = LONG_LONG_MAX; - mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationToken.clear(); - } - } else { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - ALOGI("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); - std::chrono::nanoseconds timeout; - if (windowHandle != nullptr) { - timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else if (applicationHandle != nullptr) { - timeout = - applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else { - timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; - } - - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout.count(); - mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationToken.clear(); - - if (windowHandle != nullptr) { - mInputTargetWaitApplicationToken = windowHandle->getApplicationToken(); - } - if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) { - mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken(); - } - } - } - - if (mInputTargetWaitTimeoutExpired) { - return INPUT_EVENT_INJECTION_TIMED_OUT; - } - - if (currentTime >= mInputTargetWaitTimeoutTime) { - onAnrLocked(currentTime, applicationHandle, windowHandle, entry.eventTime, - mInputTargetWaitStartTime, reason); - - // Force poll loop to wake up immediately on next iteration once we get the - // ANR response back from the policy. - *nextWakeupTime = LONG_LONG_MIN; - return INPUT_EVENT_INJECTION_PENDING; - } else { - // Force poll loop to wake up when timeout is due. - if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { - *nextWakeupTime = mInputTargetWaitTimeoutTime; - } - return INPUT_EVENT_INJECTION_PENDING; - } -} - -void InputDispatcher::removeWindowByTokenLocked(const sp& token) { - for (std::pair& pair : mTouchStatesByDisplay) { - TouchState& state = pair.second; - state.removeWindowByToken(token); - } -} - -void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t timeoutExtension, const sp& inputConnectionToken) { - if (timeoutExtension > 0) { - // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + timeoutExtension; - } else { - // Give up. - mInputTargetWaitTimeoutExpired = true; - - // Input state will not be realistic. Mark it out of sync. - sp connection = getConnectionLocked(inputConnectionToken); - if (connection != nullptr) { - removeWindowByTokenLocked(inputConnectionToken); - - if (connection->status == Connection::STATUS_NORMAL) { - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "application not responding"); - synthesizeCancelationEventsForConnectionLocked(connection, options); - } - } +void InputDispatcher::cancelEventsForAnrLocked(const sp& connection) { + // We will not be breaking any connections here, even if the policy wants us to abort dispatch. + // If the policy decides to close the app, we will get a channel removal event via + // unregisterInputChannel, and will clean up the connection that way. We are already not + // sending new pointers to the connection when it blocked, but focused events will continue to + // pile up. + ALOGW("Canceling events for %s because it is unresponsive", + connection->inputChannel->getName().c_str()); + if (connection->status == Connection::STATUS_NORMAL) { + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, + "application not responding"); + synthesizeCancelationEventsForConnectionLocked(connection, options); } } -void InputDispatcher::resetAnrTimeoutsLocked() { +void InputDispatcher::resetNoFocusedWindowTimeoutLocked() { if (DEBUG_FOCUS) { ALOGD("Resetting ANR timeouts."); } // Reset input target wait timeout. - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; - mInputTargetWaitApplicationToken.clear(); + mNoFocusedWindowTimeoutTime = std::nullopt; + mAwaitedFocusedApplication.clear(); } /** @@ -1427,6 +1423,36 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId; } +bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, + const char* focusedWindowName) { + if (mAnrTracker.empty()) { + // already processed all events that we waited for + mKeyIsWaitingForEventsTimeout = std::nullopt; + return false; + } + + if (!mKeyIsWaitingForEventsTimeout.has_value()) { + // Start the timer + ALOGD("Waiting to send key to %s because there are unprocessed events that may cause " + "focus to change", + focusedWindowName); + mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count(); + return true; + } + + // We still have pending events, and already started the timer + if (currentTime < *mKeyIsWaitingForEventsTimeout) { + return true; // Still waiting + } + + // Waited too long, and some connection still hasn't processed all motions + // Just send the key to the focused window + ALOGW("Dispatching key to %s even though there are other unprocessed events", + focusedWindowName); + mKeyIsWaitingForEventsTimeout = std::nullopt; + return false; +} + int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, std::vector& inputTargets, @@ -1441,31 +1467,70 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // If there is no currently focused window and no focused application // then drop the event. - if (focusedWindowHandle == nullptr) { - if (focusedApplicationHandle != nullptr) { - return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, - nullptr, nextWakeupTime, - "Waiting because no window has focus but there is " - "a focused application that may eventually add a " - "window when it finishes starting up."); - } - - ALOGI("Dropping event because there is no focused window or focused application in display " - "%" PRId32 ".", - displayId); + if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) { + ALOGI("Dropping %s event because there is no focused window or focused application in " + "display %" PRId32 ".", + EventEntry::typeToString(entry.type), displayId); return INPUT_EVENT_INJECTION_FAILED; } + // Compatibility behavior: raise ANR if there is a focused application, but no focused window. + // Only start counting when we have a focused event to dispatch. The ANR is canceled if we + // start interacting with another application via touch (app switch). This code can be removed + // if the "no focused window ANR" is moved to the policy. Input doesn't know whether + // an app is expected to have a focused window. + if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) { + if (!mNoFocusedWindowTimeoutTime.has_value()) { + // We just discovered that there's no focused window. Start the ANR timer + const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT.count()); + mNoFocusedWindowTimeoutTime = currentTime + timeout; + mAwaitedFocusedApplication = focusedApplicationHandle; + ALOGW("Waiting because no window has focus but %s may eventually add a " + "window when it finishes starting up. Will wait for %" PRId64 "ms", + mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout)); + *nextWakeupTime = *mNoFocusedWindowTimeoutTime; + return INPUT_EVENT_INJECTION_PENDING; + } else if (currentTime > *mNoFocusedWindowTimeoutTime) { + // Already raised ANR. Drop the event + ALOGE("Dropping %s event because there is no focused window", + EventEntry::typeToString(entry.type)); + return INPUT_EVENT_INJECTION_FAILED; + } else { + // Still waiting for the focused window + return INPUT_EVENT_INJECTION_PENDING; + } + } + + // we have a valid, non-null focused window + resetNoFocusedWindowTimeoutLocked(); + // Check permissions. if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { return INPUT_EVENT_INJECTION_PERMISSION_DENIED; } - // Check whether the window is ready for more input. - reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused"); - if (!reason.empty()) { - return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, - focusedWindowHandle, nextWakeupTime, reason.c_str()); + if (focusedWindowHandle->getInfo()->paused) { + ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str()); + return INPUT_EVENT_INJECTION_PENDING; + } + + // If the event is a key event, then we must wait for all previous events to + // complete before delivering it because previous events may have the + // side-effect of transferring focus to a different window and we want to + // ensure that the following keys are sent to the new window. + // + // Suppose the user touches a button in a window then immediately presses "A". + // If the button causes a pop-up window to appear then we want to ensure that + // the "A" key is delivered to the new pop-up window. This is because users + // often anticipate pending UI changes when typing on a keyboard. + // To obtain this behavior, we must serialize key events with respect to all + // prior input events. + if (entry.type == EventEntry::Type::KEY) { + if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { + *nextWakeupTime = *mKeyIsWaitingForEventsTimeout; + return INPUT_EVENT_INJECTION_PENDING; + } } // Success! Output targets. @@ -1477,6 +1542,32 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, return INPUT_EVENT_INJECTION_SUCCEEDED; } +/** + * Given a list of monitors, remove the ones we cannot find a connection for, and the ones + * that are currently unresponsive. + */ +std::vector InputDispatcher::selectResponsiveMonitorsLocked( + const std::vector& monitors) const { + std::vector responsiveMonitors; + std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors), + [this](const TouchedMonitor& monitor) REQUIRES(mLock) { + sp connection = getConnectionLocked( + monitor.monitor.inputChannel->getConnectionToken()); + if (connection == nullptr) { + ALOGE("Could not find connection for monitor %s", + monitor.monitor.inputChannel->getName().c_str()); + return false; + } + if (!connection->responsive) { + ALOGW("Unresponsive monitor %s will not get the new gesture", + connection->inputChannel->getName().c_str()); + return false; + } + return true; + }); + return responsiveMonitors; +} + int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry, std::vector& inputTargets, @@ -1592,6 +1683,29 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); } + if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) { + ALOGI("Not sending touch event to %s because it is paused", + newTouchedWindowHandle->getName().c_str()); + newTouchedWindowHandle = nullptr; + } + + if (newTouchedWindowHandle != nullptr) { + sp connection = getConnectionLocked(newTouchedWindowHandle->getToken()); + if (connection == nullptr) { + ALOGI("Could not find connection for %s", + newTouchedWindowHandle->getName().c_str()); + newTouchedWindowHandle = nullptr; + } else if (!connection->responsive) { + // don't send the new touch to an unresponsive window + ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64, + newTouchedWindowHandle->getName().c_str(), entry.eventTime); + newTouchedWindowHandle = nullptr; + } + } + + // Also don't send the new touch event to unresponsive gesture monitors + newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors); + if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { ALOGI("Dropping event because there is no touchable window or gesture monitor at " "(%d, %d) in display %" PRId32 ".", @@ -1758,21 +1872,6 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } } - // Ensure all touched foreground windows are ready for new input. - for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - // Check whether the window is ready for more input. - std::string reason = - checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, - entry, "touched"); - if (!reason.empty()) { - return handleTargetsNotReadyLocked(currentTime, entry, nullptr, - touchedWindow.windowHandle, nextWakeupTime, - reason.c_str()); - } - } - } - // If this is the first pointer going down and the touched window has a wallpaper // then also add the touched wallpaper windows so they are locked in for the duration // of the touch gesture. @@ -2050,92 +2149,6 @@ bool InputDispatcher::isWindowObscuredLocked(const sp& window return false; } -std::string InputDispatcher::checkWindowReadyForMoreInputLocked( - nsecs_t currentTime, const sp& windowHandle, - const EventEntry& eventEntry, const char* targetType) { - // If the window is paused then keep waiting. - if (windowHandle->getInfo()->paused) { - return StringPrintf("Waiting because the %s window is paused.", targetType); - } - - // If the window's connection is not registered then keep waiting. - sp connection = getConnectionLocked(windowHandle->getToken()); - if (connection == nullptr) { - return StringPrintf("Waiting because the %s window's input channel is not " - "registered with the input dispatcher. The window may be in the " - "process of being removed.", - targetType); - } - - // If the connection is dead then keep waiting. - if (connection->status != Connection::STATUS_NORMAL) { - return StringPrintf("Waiting because the %s window's input connection is %s." - "The window may be in the process of being removed.", - targetType, connection->getStatusLabel()); - } - - // If the connection is backed up then keep waiting. - if (connection->inputPublisherBlocked) { - return StringPrintf("Waiting because the %s window's input channel is full. " - "Outbound queue length: %zu. Wait queue length: %zu.", - targetType, connection->outboundQueue.size(), - connection->waitQueue.size()); - } - - // Ensure that the dispatch queues aren't too far backed up for this event. - if (eventEntry.type == EventEntry::Type::KEY) { - // If the event is a key event, then we must wait for all previous events to - // complete before delivering it because previous events may have the - // side-effect of transferring focus to a different window and we want to - // ensure that the following keys are sent to the new window. - // - // Suppose the user touches a button in a window then immediately presses "A". - // If the button causes a pop-up window to appear then we want to ensure that - // the "A" key is delivered to the new pop-up window. This is because users - // often anticipate pending UI changes when typing on a keyboard. - // To obtain this behavior, we must serialize key events with respect to all - // prior input events. - if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) { - return StringPrintf("Waiting to send key event because the %s window has not " - "finished processing all of the input events that were previously " - "delivered to it. Outbound queue length: %zu. Wait queue length: " - "%zu.", - targetType, connection->outboundQueue.size(), - connection->waitQueue.size()); - } - } else { - // Touch events can always be sent to a window immediately because the user intended - // to touch whatever was visible at the time. Even if focus changes or a new - // window appears moments later, the touch event was meant to be delivered to - // whatever window happened to be on screen at the time. - // - // Generic motion events, such as trackball or joystick events are a little trickier. - // Like key events, generic motion events are delivered to the focused window. - // Unlike key events, generic motion events don't tend to transfer focus to other - // windows and it is not important for them to be serialized. So we prefer to deliver - // generic motion events as soon as possible to improve efficiency and reduce lag - // through batching. - // - // The one case where we pause input event delivery is when the wait queue is piling - // up with lots of events because the application is not responding. - // This condition ensures that ANRs are detected reliably. - if (!connection->waitQueue.empty() && - currentTime >= - connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { - return StringPrintf("Waiting to send non-key event because the %s window has not " - "finished processing certain input events that were delivered to " - "it over " - "%0.1fms ago. Wait queue length: %zu. Wait queue head age: " - "%0.1fms.", - targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, - connection->waitQueue.size(), - (currentTime - connection->waitQueue.front()->deliveryTime) * - 0.000001f); - } - } - return ""; -} - std::string InputDispatcher::getApplicationWindowLabel( const sp& applicationHandle, const sp& windowHandle) { @@ -2535,6 +2548,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; + const nsecs_t timeout = + getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken()); + dispatchEntry->timeoutTime = currentTime + timeout; // Publish the event. status_t status; @@ -2654,7 +2670,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, "waiting for the application to catch up", connection->getInputChannelName().c_str()); #endif - connection->inputPublisherBlocked = true; } } else { ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " @@ -2671,6 +2686,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, dispatchEntry)); traceOutboundQueueLength(connection); connection->waitQueue.push_back(dispatchEntry); + if (connection->responsive) { + mAnrTracker.insert(dispatchEntry->timeoutTime, + connection->inputChannel->getConnectionToken()); + } traceWaitQueueLength(connection); } } @@ -2705,8 +2724,6 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str(), seq, toString(handled)); #endif - connection->inputPublisherBlocked = false; - if (connection->status == Connection::STATUS_BROKEN || connection->status == Connection::STATUS_ZOMBIE) { return; @@ -3862,15 +3879,17 @@ void InputDispatcher::setFocusedApplication( sp oldFocusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); + + if (oldFocusedApplicationHandle == mAwaitedFocusedApplication && + inputApplicationHandle != oldFocusedApplicationHandle) { + resetNoFocusedWindowTimeoutLocked(); + } + if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { if (oldFocusedApplicationHandle != inputApplicationHandle) { - if (oldFocusedApplicationHandle != nullptr) { - resetAnrTimeoutsLocked(); - } mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; } } else if (oldFocusedApplicationHandle != nullptr) { - resetAnrTimeoutsLocked(); oldFocusedApplicationHandle.clear(); mFocusedApplicationHandlesByDisplay.erase(displayId); } @@ -3951,7 +3970,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { if (mDispatchFrozen && !frozen) { - resetAnrTimeoutsLocked(); + resetNoFocusedWindowTimeoutLocked(); } if (mDispatchEnabled && !enabled) { @@ -4091,8 +4110,9 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetKeyRepeatLocked(); releasePendingEventLocked(); drainInboundQueueLocked(); - resetAnrTimeoutsLocked(); + resetNoFocusedWindowTimeoutLocked(); + mAnrTracker.clear(); mTouchStatesByDisplay.clear(); mLastHoverWindowHandle.clear(); mReplacedKeys.clear(); @@ -4289,11 +4309,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (const auto& pair : mConnectionsByFd) { const sp& connection = pair.second; dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " - "status=%s, monitor=%s, inputPublisherBlocked=%s\n", + "status=%s, monitor=%s, responsive=%s\n", pair.first, connection->getInputChannelName().c_str(), connection->getWindowName().c_str(), connection->getStatusLabel(), - toString(connection->monitor), - toString(connection->inputPublisherBlocked)); + toString(connection->monitor), toString(connection->responsive)); if (!connection->outboundQueue.empty()) { dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", @@ -4561,6 +4580,7 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn } void InputDispatcher::removeConnectionLocked(const sp& connection) { + mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); removeByValue(mConnectionsByFd, connection); } @@ -4598,17 +4618,69 @@ void InputDispatcher::onFocusChangedLocked(const sp& oldFocus postCommandLocked(std::move(commandEntry)); } -void InputDispatcher::onAnrLocked(nsecs_t currentTime, - const sp& applicationHandle, - const sp& windowHandle, nsecs_t eventTime, - nsecs_t waitStartTime, const char* reason) { - float dispatchLatency = (currentTime - eventTime) * 0.000001f; - float waitDuration = (currentTime - waitStartTime) * 0.000001f; - ALOGI("Application is not responding: %s. " - "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency, - waitDuration, reason); +void InputDispatcher::onAnrLocked(const sp& connection) { + // Since we are allowing the policy to extend the timeout, maybe the waitQueue + // is already healthy again. Don't raise ANR in this situation + if (connection->waitQueue.empty()) { + ALOGI("Not raising ANR because the connection %s has recovered", + connection->inputChannel->getName().c_str()); + return; + } + /** + * The "oldestEntry" is the entry that was first sent to the application. That entry, however, + * may not be the one that caused the timeout to occur. One possibility is that window timeout + * has changed. This could cause newer entries to time out before the already dispatched + * entries. In that situation, the newest entries caused ANR. But in all likelihood, the app + * processes the events linearly. So providing information about the oldest entry seems to be + * most useful. + */ + DispatchEntry* oldestEntry = *connection->waitQueue.begin(); + const nsecs_t currentWait = now() - oldestEntry->deliveryTime; + std::string reason = + android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s", + connection->inputChannel->getName().c_str(), + ns2ms(currentWait), + oldestEntry->eventEntry->getDescription().c_str()); + + updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()), + reason); + + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); + commandEntry->inputApplicationHandle = nullptr; + commandEntry->inputChannel = connection->inputChannel; + commandEntry->reason = std::move(reason); + postCommandLocked(std::move(commandEntry)); +} +void InputDispatcher::onAnrLocked(const sp& application) { + std::string reason = android::base::StringPrintf("%s does not have a focused window", + application->getName().c_str()); + + updateLastAnrStateLocked(application, reason); + + std::unique_ptr commandEntry = + std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); + commandEntry->inputApplicationHandle = application; + commandEntry->inputChannel = nullptr; + commandEntry->reason = std::move(reason); + postCommandLocked(std::move(commandEntry)); +} + +void InputDispatcher::updateLastAnrStateLocked(const sp& window, + const std::string& reason) { + const std::string windowLabel = getApplicationWindowLabel(nullptr, window); + updateLastAnrStateLocked(windowLabel, reason); +} + +void InputDispatcher::updateLastAnrStateLocked(const sp& application, + const std::string& reason) { + const std::string windowLabel = getApplicationWindowLabel(application, nullptr); + updateLastAnrStateLocked(windowLabel, reason); +} + +void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel, + const std::string& reason) { // Capture a record of the InputDispatcher state at the time of the ANR. time_t t = time(nullptr); struct tm tm; @@ -4618,21 +4690,9 @@ void InputDispatcher::onAnrLocked(nsecs_t currentTime, mLastAnrState.clear(); mLastAnrState += INDENT "ANR:\n"; mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr); - mLastAnrState += - StringPrintf(INDENT2 "Window: %s\n", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); - mLastAnrState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); - mLastAnrState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); - mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason); + mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str()); + mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str()); dumpDispatchStateLocked(mLastAnrState); - - std::unique_ptr commandEntry = - std::make_unique(&InputDispatcher::doNotifyAnrLockedInterruptible); - commandEntry->inputApplicationHandle = applicationHandle; - commandEntry->inputChannel = - windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; - commandEntry->reason = reason; - postCommandLocked(std::move(commandEntry)); } void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) { @@ -4673,13 +4733,50 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token); + if (timeoutExtension > 0) { + extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension); + } else { + // stop waking up for events in this connection, it is already not responding + sp connection = getConnectionLocked(token); + if (connection == nullptr) { + return; + } + cancelEventsForAnrLocked(connection); + } +} + +void InputDispatcher::extendAnrTimeoutsLocked(const sp& application, + const sp& connectionToken, + nsecs_t timeoutExtension) { + sp connection = getConnectionLocked(connectionToken); + if (connection == nullptr) { + if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) { + // Maybe ANR happened because there's no focused window? + mNoFocusedWindowTimeoutTime = now() + timeoutExtension; + mAwaitedFocusedApplication = application; + } else { + // It's also possible that the connection already disappeared. No action necessary. + } + return; + } + + ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer", + connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension)); + + connection->responsive = true; + const nsecs_t newTimeout = now() + timeoutExtension; + for (DispatchEntry* entry : connection->waitQueue) { + if (newTimeout >= entry->timeoutTime) { + // Already removed old entries when connection was marked unresponsive + entry->timeoutTime = newTimeout; + mAnrTracker.insert(entry->timeoutTime, connectionToken); + } + } } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( CommandEntry* commandEntry) { KeyEntry* entry = commandEntry->keyEntry; - KeyEvent event = createKeyEvent(*entry); mLock.unlock(); @@ -4713,6 +4810,20 @@ void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntr mLock.lock(); } +/** + * Connection is responsive if it has no events in the waitQueue that are older than the + * current time. + */ +static bool isConnectionResponsive(const Connection& connection) { + const nsecs_t currentTime = now(); + for (const DispatchEntry* entry : connection.waitQueue) { + if (entry->timeoutTime < currentTime) { + return false; + } + } + return true; +} + void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) { sp connection = commandEntry->connection; const nsecs_t finishTime = commandEntry->eventTime; @@ -4725,7 +4836,6 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c return; } DispatchEntry* dispatchEntry = *dispatchEntryIt; - const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), @@ -4754,6 +4864,11 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c if (dispatchEntryIt != connection->waitQueue.end()) { dispatchEntry = *dispatchEntryIt; connection->waitQueue.erase(dispatchEntryIt); + mAnrTracker.erase(dispatchEntry->timeoutTime, + connection->inputChannel->getConnectionToken()); + if (!connection->responsive) { + connection->responsive = isConnectionResponsive(*connection); + } traceWaitQueueLength(connection); if (restartEvent && connection->status == Connection::STATUS_NORMAL) { connection->outboundQueue.push_front(dispatchEntry); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 1980435b5a..7c2028ab29 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -17,6 +17,7 @@ #ifndef _UI_INPUT_DISPATCHER_H #define _UI_INPUT_DISPATCHER_H +#include "AnrTracker.h" #include "CancelationOptions.h" #include "Entry.h" #include "InjectionState.h" @@ -216,7 +217,6 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); @@ -274,6 +274,9 @@ private: bool runCommandsLockedInterruptible() REQUIRES(mLock); void postCommandLocked(std::unique_ptr commandEntry) REQUIRES(mLock); + nsecs_t processAnrsLocked() REQUIRES(mLock); + nsecs_t getDispatchingTimeoutLocked(const sp& token) REQUIRES(mLock); + // Input filter processing. bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock); @@ -344,38 +347,53 @@ private: void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry); - // Keeping track of ANR timeouts. - enum InputTargetWaitCause { - INPUT_TARGET_WAIT_CAUSE_NONE, - INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, - INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, - }; - - InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock); - nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock); - nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock); - bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); - sp mInputTargetWaitApplicationToken GUARDED_BY(mLock); + /** + * This field is set if there is no focused window, and we have an event that requires + * a focused window to be dispatched (for example, a KeyEvent). + * When this happens, we will wait until *mNoFocusedWindowTimeoutTime before + * dropping the event and raising an ANR for that application. + * This is useful if an application is slow to add a focused window. + */ + std::optional mNoFocusedWindowTimeoutTime GUARDED_BY(mLock); bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); - // Contains the last window which received a hover event. - sp mLastHoverWindowHandle GUARDED_BY(mLock); + /** + * Time to stop waiting for the events to be processed while trying to dispatch a key. + * When this time expires, we just send the pending key event to the currently focused window, + * without waiting on other events to be processed first. + */ + std::optional mKeyIsWaitingForEventsTimeout GUARDED_BY(mLock); + bool shouldWaitToSendKeyLocked(nsecs_t currentTime, const char* focusedWindowName) + REQUIRES(mLock); - // Finding targets for input events. - int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry, - const sp& applicationHandle, - const sp& windowHandle, - nsecs_t* nextWakeupTime, const char* reason) + /** + * The focused application at the time when no focused window was present. + * Used to raise an ANR when we have no focused window. + */ + sp mAwaitedFocusedApplication GUARDED_BY(mLock); + + // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next. + // AnrTracker must be kept in-sync with all responsive connection.waitQueues. + // If a connection is not responsive, then the entries should not be added to the AnrTracker. + // Once a connection becomes unresponsive, its entries are removed from AnrTracker to + // prevent unneeded wakeups. + AnrTracker mAnrTracker GUARDED_BY(mLock); + void extendAnrTimeoutsLocked(const sp& application, + const sp& connectionToken, nsecs_t timeoutExtension) REQUIRES(mLock); - void removeWindowByTokenLocked(const sp& token) REQUIRES(mLock); + // Contains the last window which received a hover event. + sp mLastHoverWindowHandle GUARDED_BY(mLock); - void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp& inputConnectionToken) - REQUIRES(mLock); + void cancelEventsForAnrLocked(const sp& connection) REQUIRES(mLock); nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); - void resetAnrTimeoutsLocked() REQUIRES(mLock); + // If a focused application changes, we should stop counting down the "no focused window" time, + // because we will have no way of knowing when the previous application actually added a window. + // This also means that we will miss cases like pulling down notification shade when the + // focused application does not have a focused window (no ANR will be raised if notification + // shade is pulled down while we are counting down the timeout). + void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock); int32_t getTargetDisplayId(const EventEntry& entry); int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, @@ -388,6 +406,8 @@ private: std::vector findTouchedGestureMonitorsLocked( int32_t displayId, const std::vector>& portalWindows) const REQUIRES(mLock); + std::vector selectResponsiveMonitorsLocked( + const std::vector& gestureMonitors) const REQUIRES(mLock); void addWindowTargetLocked(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) @@ -406,11 +426,6 @@ private: std::string getApplicationWindowLabel(const sp& applicationHandle, const sp& windowHandle); - std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, - const sp& windowHandle, - const EventEntry& eventEntry, - const char* targetType) REQUIRES(mLock); - // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work // with the mutex held makes it easier to ensure that connection invariants are maintained. @@ -480,9 +495,14 @@ private: REQUIRES(mLock); void onFocusChangedLocked(const sp& oldFocus, const sp& newFocus) REQUIRES(mLock); - void onAnrLocked(nsecs_t currentTime, const sp& applicationHandle, - const sp& windowHandle, nsecs_t eventTime, - nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); + void onAnrLocked(const sp& connection) REQUIRES(mLock); + void onAnrLocked(const sp& application) REQUIRES(mLock); + void updateLastAnrStateLocked(const sp& window, const std::string& reason) + REQUIRES(mLock); + void updateLastAnrStateLocked(const sp& application, + const std::string& reason) REQUIRES(mLock); + void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason) + REQUIRES(mLock); // Outbound policy interactions. void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 73d22727f0..a0d2f4f172 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -27,6 +27,7 @@ cc_test { "libinputflinger_defaults", ], srcs: [ + "AnrTracker_test.cpp", "BlockingQueue_test.cpp", "EventHub_test.cpp", "TestInputListener.cpp", diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp new file mode 100644 index 0000000000..b561da107d --- /dev/null +++ b/services/inputflinger/tests/AnrTracker_test.cpp @@ -0,0 +1,167 @@ +/* + * 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 "../AnrTracker.h" + +#include +#include + +namespace android { + +namespace inputdispatcher { + +// --- AnrTrackerTest --- + +/** + * Add a single entry and ensure it's returned as first, even if the token isn't valid + */ +TEST(AnrTrackerTest, SingleEntry_First) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(tracker.firstToken(), nullptr); +} + +TEST(AnrTrackerTest, MultipleEntries_RemoveToken) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(1, token1); + tracker.insert(2, token2); + tracker.insert(3, token1); + tracker.insert(4, token2); + tracker.insert(5, token1); + + tracker.eraseToken(token1); + + ASSERT_EQ(2, tracker.firstTimeout()); +} + +TEST(AnrTrackerTest, AddAndRemove_Empty) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + tracker.insert(1, nullptr); + ASSERT_FALSE(tracker.empty()); + + tracker.erase(1, nullptr); + ASSERT_TRUE(tracker.empty()); +} + +TEST(AnrTrackerTest, Clear) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + tracker.clear(); + ASSERT_TRUE(tracker.empty()); +} + +TEST(AnrTrackerTest, SingleToken_MaintainsOrder) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + tracker.insert(2, nullptr); + tracker.insert(5, nullptr); + tracker.insert(0, nullptr); + + ASSERT_EQ(0, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(5, token2); + tracker.insert(0, token2); + + ASSERT_EQ(0, tracker.firstTimeout()); + ASSERT_EQ(token2, tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(2, token2); + tracker.insert(10, token2); + + ASSERT_EQ(2, tracker.firstTimeout()); + // Doesn't matter which token is returned - both are valid results + ASSERT_TRUE(token1 == tracker.firstToken() || token2 == tracker.firstToken()); +} + +TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) { + AnrTracker tracker; + + sp token1 = new BBinder(); + sp token2 = new BBinder(); + + tracker.insert(2, token1); + tracker.insert(2, token2); + tracker.insert(10, token2); + + tracker.erase(2, token2); + + ASSERT_EQ(2, tracker.firstTimeout()); + ASSERT_EQ(token1, tracker.firstToken()); +} + +TEST(AnrTrackerTest, Empty_DoesntCrash) { + AnrTracker tracker; + + ASSERT_TRUE(tracker.empty()); + + ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout()); + // Can't call firstToken() if tracker.empty() +} + +TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) { + AnrTracker tracker; + + tracker.insert(1, nullptr); + + // Remove with non-matching timestamp + tracker.erase(2, nullptr); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); + + // Remove with non-matching token + tracker.erase(1, new BBinder()); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); + + // Remove with both non-matching + tracker.erase(2, new BBinder()); + ASSERT_EQ(1, tracker.firstTimeout()); + ASSERT_EQ(nullptr, tracker.firstToken()); +} + +} // namespace inputdispatcher + +} // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 13e835427f..1a133dc01c 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -126,6 +126,14 @@ public: void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout, const sp& expectedApplication, const sp& expectedToken) { + std::pair, sp> anrData; + ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout)); + ASSERT_EQ(expectedApplication, anrData.first); + ASSERT_EQ(expectedToken, anrData.second); + } + + std::pair, sp> getNotifyAnrData( + std::chrono::nanoseconds timeout) { const std::chrono::time_point start = std::chrono::steady_clock::now(); std::unique_lock lock(mLock); std::chrono::duration timeToWait = timeout + 100ms; // provide some slack @@ -136,16 +144,33 @@ public: // before checking if ANR was called. // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide // it some time to act. 100ms seems reasonable. - mNotifyAnr.wait_for(lock, timeToWait, - [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; }); + mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) { + return !mAnrApplications.empty() && !mAnrWindowTokens.empty(); + }); const std::chrono::duration waited = std::chrono::steady_clock::now() - start; - ASSERT_TRUE(mNotifyAnrWasCalled); + if (mAnrApplications.empty() || mAnrWindowTokens.empty()) { + ADD_FAILURE() << "Did not receive ANR callback"; + } // Ensure that the ANR didn't get raised too early. We can't be too strict here because // the dispatcher started counting before this function was called - ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for - mNotifyAnrWasCalled = false; - ASSERT_EQ(expectedApplication, mLastAnrApplication); - ASSERT_EQ(expectedToken, mLastAnrWindowToken); + if (std::chrono::abs(timeout - waited) > 100ms) { + ADD_FAILURE() << "ANR was raised too early or too late. Expected " + << std::chrono::duration_cast(timeout).count() + << "ms, but waited " + << std::chrono::duration_cast(waited).count() + << "ms instead"; + } + std::pair, sp> result = + std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front()); + mAnrApplications.pop(); + mAnrWindowTokens.pop(); + return result; + } + + void assertNotifyAnrWasNotCalled() { + std::scoped_lock lock(mLock); + ASSERT_TRUE(mAnrApplications.empty()); + ASSERT_TRUE(mAnrWindowTokens.empty()); } void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { @@ -153,6 +178,8 @@ public: mConfig.keyRepeatDelay = delay; } + void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; } + private: std::mutex mLock; std::unique_ptr mFilteredEvent GUARDED_BY(mLock); @@ -161,9 +188,8 @@ private: std::optional mLastNotifySwitch GUARDED_BY(mLock); // ANR handling - bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false; - sp mLastAnrApplication GUARDED_BY(mLock); - sp mLastAnrWindowToken GUARDED_BY(mLock); + std::queue> mAnrApplications GUARDED_BY(mLock); + std::queue> mAnrWindowTokens GUARDED_BY(mLock); std::condition_variable mNotifyAnr; std::chrono::nanoseconds mAnrTimeout = 0ms; @@ -175,9 +201,8 @@ private: virtual nsecs_t notifyAnr(const sp& application, const sp& windowToken, const std::string&) override { std::scoped_lock lock(mLock); - mLastAnrApplication = application; - mLastAnrWindowToken = windowToken; - mNotifyAnrWasCalled = true; + mAnrApplications.push(application); + mAnrWindowTokens.push(windowToken); mNotifyAnr.notify_all(); return mAnrTimeout.count(); } @@ -643,7 +668,7 @@ public: ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; ASSERT_EQ(expectedEventType, event->getType()) - << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType) + << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType) << " event, got " << inputEventTypeToString(event->getType()) << " event"; EXPECT_EQ(expectedDisplayId, event->getDisplayId()); @@ -688,9 +713,24 @@ public: void assertNoEvents() { InputEvent* event = consume(); - ASSERT_EQ(nullptr, event) - << mName.c_str() - << ": should not have received any events, so consume() should return NULL"; + if (event == nullptr) { + return; + } + if (event->getType() == AINPUT_EVENT_TYPE_KEY) { + KeyEvent& keyEvent = static_cast(*event); + ADD_FAILURE() << "Received key event " + << KeyEvent::actionToString(keyEvent.getAction()); + } else if (event->getType() == AINPUT_EVENT_TYPE_MOTION) { + MotionEvent& motionEvent = static_cast(*event); + ADD_FAILURE() << "Received motion event " + << MotionEvent::actionToString(motionEvent.getAction()); + } else if (event->getType() == AINPUT_EVENT_TYPE_FOCUS) { + FocusEvent& focusEvent = static_cast(*event); + ADD_FAILURE() << "Received focus event, hasFocus = " + << (focusEvent.getHasFocus() ? "true" : "false"); + } + FAIL() << mName.c_str() + << ": should not have received any events, so consume() should return NULL"; } sp getToken() { return mConsumer->getChannel()->getConnectionToken(); } @@ -754,6 +794,8 @@ public: mInfo.dispatchingTimeout = timeout.count(); } + void setPaused(bool paused) { mInfo.paused = paused; } + void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; @@ -775,6 +817,10 @@ public: expectedFlags); } + void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags); + } + void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId, @@ -826,12 +872,12 @@ public: expectedFlags); } - std::optional receiveEvent() { + std::optional receiveEvent(InputEvent** outEvent = nullptr) { if (mInputReceiver == nullptr) { ADD_FAILURE() << "Invalid receive event on window with no receiver"; return std::nullopt; } - return mInputReceiver->receiveEvent(); + return mInputReceiver->receiveEvent(outEvent); } void finishEvent(uint32_t sequenceNum) { @@ -865,7 +911,9 @@ private: std::atomic FakeWindowHandle::sId{1}; static int32_t injectKey(const sp& dispatcher, int32_t action, int32_t repeatCount, - int32_t displayId = ADISPLAY_ID_NONE) { + int32_t displayId = ADISPLAY_ID_NONE, + int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); @@ -875,10 +923,9 @@ static int32_t injectKey(const sp& dispatcher, int32_t action, repeatCount, currentTime, currentTime); // Inject event until dispatch out. - return dispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, - INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode, + injectionTimeout, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } static int32_t injectKeyDown(const sp& dispatcher, @@ -886,11 +933,19 @@ static int32_t injectKeyDown(const sp& dispatcher, return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); } +static int32_t injectKeyUp(const sp& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId); +} + static int32_t injectMotionEvent( const sp& dispatcher, int32_t action, int32_t source, int32_t displayId, const PointF& position, const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION}) { + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, + int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -903,7 +958,6 @@ static int32_t injectMotionEvent( pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0, @@ -911,14 +965,13 @@ static int32_t injectMotionEvent( /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y, - currentTime, currentTime, + eventTime, eventTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. - return dispatcher->injectInputEvent( - &event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, - INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode, + injectionTimeout, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } static int32_t injectMotionDown(const sp& dispatcher, int32_t source, @@ -1429,6 +1482,10 @@ public: expectedDisplayId, expectedFlags); } + std::optional receiveEvent() { return mInputReceiver->receiveEvent(); } + + void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); } + void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); @@ -1507,6 +1564,21 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); } +TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); + std::optional consumeSeq = monitor.receiveEvent(); + ASSERT_TRUE(consumeSeq); + + mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken()); + monitor.finishEvent(*consumeSeq); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + TEST_F(InputDispatcherTest, TestMoveEvent) { sp application = new FakeApplicationHandle(); sp window = @@ -2329,23 +2401,40 @@ protected: } }; +// Send a tap and respond, which should not cause an ANR. +TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) { + tapOnWindow(); + mWindow->consumeMotionDown(); + mWindow->consumeMotionUp(); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// Send a regular key and respond, which should not cause an ANR. +TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + // Send an event to the app and have the app not respond right away. -// Make sure that ANR is raised +// When ANR is raised, policy will tell the dispatcher to cancel the events for that window. +// So InputDispatcher will enqueue ACTION_CANCEL event as well. TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); - // Also, overwhelm the socket to make sure ANR starts - for (size_t i = 0; i < 100; i++) { - injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i}); - } - std::optional sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + + // The remaining lines are not really needed for the test, but kept as a sanity check + mWindow->finishEvent(*sequenceNum); + mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); ASSERT_TRUE(mDispatcher->waitForIdle()); } @@ -2355,14 +2444,591 @@ TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); std::optional sequenceNum = mWindow->receiveEvent(); ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + + // taps on the window work as normal + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown()); + mDispatcher->waitForIdle(); + mFakePolicy->assertNotifyAnrWasNotCalled(); + + // Once a focused event arrives, we get an ANR for this application + // We specify the injection timeout to be smaller than the application timeout, to ensure that + // injection times out (instead of failing). + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +// If the policy wants to keep waiting on the focused window to be added, make sure +// that this timeout extension is honored and ANR is raised again. +TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + const std::chrono::duration timeout = 5ms; + mFakePolicy->setAnrTimeout(timeout); + + // Once a focused event arrives, we get an ANR for this application + // We specify the injection timeout to be smaller than the application timeout, to ensure that + // injection times out (instead of failing). + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + const std::chrono::duration appTimeout = + mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/); + + // After the extended time has passed, ANR should be raised again + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + + // If we stop extending the timeout, dispatcher should go to idle. + // Another ANR may be raised during this time + mFakePolicy->setAnrTimeout(0ms); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// We have a focused application, but no focused window +TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) { + mWindow->setFocus(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + + // Once a focused event arrives, we get an ANR for this application + const int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); - // Start ANR process by sending a 2nd key, which would trigger the check for whether - // waitQueue is empty - injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1); + const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); + // Future focused events get dropped right away + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mWindow->assertNoEvents(); +} + +/** + * Ensure that the implementation is valid. Since we are using multiset to keep track of the + * ANR timeouts, we are allowing entries with identical timestamps in the same connection. + * If we process 1 of the events, but ANR on the second event with the same timestamp, + * the ANR mechanism should still work. + * + * In this test, we are injecting DOWN and UP events with the same timestamps, and acknowledging the + * DOWN event, while not responding on the second one. + */ +TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) { + nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, WINDOW_LOCATION, + {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime); + + // Now send ACTION_UP, with identical timestamp + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, WINDOW_LOCATION, + {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}, + 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime); + + // We have now sent down and up. Let's consume first event and then ANR on the second. + mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); +} + +// If an app is not responding to a key event, gesture monitors should continue to receive +// new motion events +TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)); + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT)); + + // Stuck on the ACTION_UP + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken()); + + // New tap will go to the gesture monitor, but not to the window + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + monitor.assertNoEvents(); +} + +// If an app is not responding to a motion event, gesture monitors should continue to receive +// new motion events +TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) { + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeMotionDown(); + // Stuck on the ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken()); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken()); + + // New tap will go to the gesture monitor, but not to the window + tapOnWindow(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + monitor.assertNoEvents(); +} + +// If a window is unresponsive, then you get anr. if the window later catches up and starts to +// process events, you don't get an anr. When the window later becomes unresponsive again, you +// get an ANR again. +// 1. tap -> block on ACTION_UP -> receive ANR +// 2. consume all pending events (= queue becomes healthy again) +// 3. tap again -> block on ACTION_UP again -> receive ANR second time +TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) { + tapOnWindow(); + + mWindow->consumeMotionDown(); + // Block on ACTION_UP + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mWindow->consumeMotionUp(); // Now the connection should be healthy again + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); + + tapOnWindow(); + mWindow->consumeMotionDown(); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mWindow->consumeMotionUp(); + + mDispatcher->waitForIdle(); + mWindow->assertNoEvents(); +} + +// If the policy tells us to raise ANR again after some time, ensure that the timeout extension +// is honored +TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) { + const std::chrono::duration timeout = 5ms; + mFakePolicy->setAnrTimeout(timeout); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/, + mWindow->getToken()); + + // Since the policy wanted to extend ANR, make sure it is called again after the extension + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + mFakePolicy->setAnrTimeout(0ms); + std::this_thread::sleep_for(windowTimeout); + // We are not checking if ANR has been called, because it may have been called again by the + // time we set the timeout to 0 + + // When the policy finally says stop, we should get ACTION_CANCEL + mWindow->consumeMotionDown(); + mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + mWindow->assertNoEvents(); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not to to the focused window until the motion is processed. + * + * Warning!!! + * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT + * and the injection timeout that we specify when injecting the key. + * We must have the injection timeout (10ms) be smaller than + * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). + * + * If that value changes, this test should also change. + */ +TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { + mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + + tapOnWindow(); + std::optional downSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection will "succeed" because we will eventually give up and send the key to the focused + // window even if motions are still being processed. But because the injection timeout is short, + // we will receive INJECTION_TIMED_OUT as the result. + + int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + // Key will not be sent to the window, yet, because the window is still processing events + // and the key remains pending, waiting for the touch events to be processed + std::optional keySequenceNum = mWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + std::this_thread::sleep_for(500ms); + // if we wait long enough though, dispatcher will give up, and still send the key + // to the focused window, even though we have not yet finished the motion event + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + mWindow->finishEvent(*downSequenceNum); + mWindow->finishEvent(*upSequenceNum); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not go to the focused window until the motion is processed. + * If then a new motion comes in, then the pending key event should be going to the currently + * focused window right away. + */ +TEST_F(InputDispatcherSingleWindowAnr, + PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) { + mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + + tapOnWindow(); + std::optional downSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection is async, so it will succeed + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, + ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE)); + // At this point, key is still pending, and should not be sent to the application yet. + std::optional keySequenceNum = mWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + // Now tap down again. It should cause the pending key to go to the focused window right away. + tapOnWindow(); + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd + // the other events yet. We can finish events in any order. + mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN + mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP + mWindow->consumeMotionDown(); + mWindow->consumeMotionUp(); + mWindow->assertNoEvents(); +} + +class InputDispatcherMultiWindowAnr : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApplication = new FakeApplicationHandle(); + mApplication->setDispatchingTimeout(10ms); + mUnfocusedWindow = + new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT); + mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30)); + // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this + // window. + // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped + mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH | + InputWindowInfo::FLAG_SPLIT_TOUCH); + + mFocusedWindow = + new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT); + mFocusedWindow->setDispatchingTimeout(10ms); + mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); + mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | + InputWindowInfo::FLAG_SPLIT_TOUCH); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + mFocusedWindow->setFocus(true); + + // Expect one focus window exist in display. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + mFocusedWindow->consumeFocusEvent(true); + } + + virtual void TearDown() override { + InputDispatcherTest::TearDown(); + + mUnfocusedWindow.clear(); + mFocusedWindow.clear(); + } + +protected: + sp mApplication; + sp mUnfocusedWindow; + sp mFocusedWindow; + static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20}; + static constexpr PointF FOCUSED_WINDOW_LOCATION = {75, 75}; + static constexpr PointF LOCATION_OUTSIDE_ALL_WINDOWS = {40, 40}; + + void tapOnFocusedWindow() { tap(FOCUSED_WINDOW_LOCATION); } + + void tapOnUnfocusedWindow() { tap(UNFOCUSED_WINDOW_LOCATION); } + +private: + void tap(const PointF& location) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + location)); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + location)); + } +}; + +// If we have 2 windows that are both unresponsive, the one with the shortest timeout +// should be ANR'd first. +TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + mFocusedWindow->consumeMotionDown(); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + // We consumed all events, so no ANR + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); + + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + std::optional unfocusedSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(unfocusedSequenceNum); + std::optional focusedSequenceNum = mFocusedWindow->receiveEvent(); + ASSERT_TRUE(focusedSequenceNum); + + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + mFocusedWindow->finishEvent(*focusedSequenceNum); + mUnfocusedWindow->finishEvent(*unfocusedSequenceNum); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// If we have 2 windows with identical timeouts that are both unresponsive, +// it doesn't matter which order they should have ANR. +// But we should receive ANR for both. +TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) { + // Set the timeout for unfocused window to match the focused window + mUnfocusedWindow->setDispatchingTimeout(10ms); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + + tapOnFocusedWindow(); + // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window + std::pair, sp> anrData1 = + mFakePolicy->getNotifyAnrData(10ms); + std::pair, sp> anrData2 = + mFakePolicy->getNotifyAnrData(0ms); + + // We don't know which window will ANR first. But both of them should happen eventually. + ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second || + mFocusedWindow->getToken() == anrData2.second); + ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second || + mUnfocusedWindow->getToken() == anrData2.second); + + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// If a window is already not responding, the second tap on the same window should be ignored. +// We should also log an error to account for the dropped event (not tested here). +// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events. +TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { + tapOnFocusedWindow(); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + // Receive the events, but don't respond + std::optional downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(downEventSequenceNum); + std::optional upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP + ASSERT_TRUE(upEventSequenceNum); + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + // Tap once again + // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a + // valid touch target + mUnfocusedWindow->assertNoEvents(); + + // Consume the first tap + mFocusedWindow->finishEvent(*downEventSequenceNum); + mFocusedWindow->finishEvent(*upEventSequenceNum); + ASSERT_TRUE(mDispatcher->waitForIdle()); + // The second tap did not go to the focused window + mFocusedWindow->assertNoEvents(); + // should not have another ANR after the window just became healthy again + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// If you tap outside of all windows, there will not be ANR +TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + LOCATION_OUTSIDE_ALL_WINDOWS)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + +// Since the focused window is paused, tapping on it should not produce any events +TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) { + mFocusedWindow->setPaused(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + + ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + FOCUSED_WINDOW_LOCATION)); + + std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT)); + ASSERT_TRUE(mDispatcher->waitForIdle()); + // Should not ANR because the window is paused, and touches shouldn't go to it + mFakePolicy->assertNotifyAnrWasNotCalled(); + + mFocusedWindow->assertNoEvents(); + mUnfocusedWindow->assertNoEvents(); +} + +/** + * If a window is processing a motion event, and then a key event comes in, the key event should + * not to to the focused window until the motion is processed. + * If a different window becomes focused at this time, the key should go to that window instead. + * + * Warning!!! + * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT + * and the injection timeout that we specify when injecting the key. + * We must have the injection timeout (10ms) be smaller than + * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). + * + * If that value changes, this test should also change. + */ +TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) { + // Set a long ANR timeout to prevent it from triggering + mFocusedWindow->setDispatchingTimeout(2s); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); + + tapOnUnfocusedWindow(); + std::optional downSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(downSequenceNum); + std::optional upSequenceNum = mUnfocusedWindow->receiveEvent(); + ASSERT_TRUE(upSequenceNum); + // Don't finish the events yet, and send a key + // Injection will succeed because we will eventually give up and send the key to the focused + // window even if motions are still being processed. + + int32_t result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, + INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result); + // Key will not be sent to the window, yet, because the window is still processing events + // and the key remains pending, waiting for the touch events to be processed + std::optional keySequenceNum = mFocusedWindow->receiveEvent(); + ASSERT_FALSE(keySequenceNum); + + // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there + mFocusedWindow->setFocus(false); + mUnfocusedWindow->setFocus(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); + + // Focus events should precede the key events + mUnfocusedWindow->consumeFocusEvent(true); + mFocusedWindow->consumeFocusEvent(false); + + // Finish the tap events, which should unblock dispatcher + mUnfocusedWindow->finishEvent(*downSequenceNum); + mUnfocusedWindow->finishEvent(*upSequenceNum); + + // Now that all queues are cleared and no backlog in the connections, the key event + // can finally go to the newly focused "mUnfocusedWindow". + mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + mFocusedWindow->assertNoEvents(); + mUnfocusedWindow->assertNoEvents(); +} + +// When the touch stream is split across 2 windows, and one of them does not respond, +// then ANR should be raised and the touch should be canceled for the unresponsive window. +// The other window should not be affected by that. +TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { + // Touch Window 1 + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION}); + mDispatcher->notifyMotion(&motionArgs); + mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, + ADISPLAY_ID_DEFAULT, 0 /*flags*/); + + // Touch Window 2 + int32_t actionPointerDown = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + + motionArgs = + generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION}); + mDispatcher->notifyMotion(&motionArgs); + + const std::chrono::duration timeout = + mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, + mFocusedWindow->getToken()); + + mUnfocusedWindow->consumeMotionDown(); + mFocusedWindow->consumeMotionDown(); + // Focused window may or may not receive ACTION_MOVE + // But it should definitely receive ACTION_CANCEL due to the ANR + InputEvent* event; + std::optional moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event); + ASSERT_TRUE(moveOrCancelSequenceNum); + mFocusedWindow->finishEvent(*moveOrCancelSequenceNum); + ASSERT_NE(nullptr, event); + ASSERT_EQ(event->getType(), AINPUT_EVENT_TYPE_MOTION); + MotionEvent& motionEvent = static_cast(*event); + if (motionEvent.getAction() == AMOTION_EVENT_ACTION_MOVE) { + mFocusedWindow->consumeMotionCancel(); + } else { + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction()); + } + ASSERT_TRUE(mDispatcher->waitForIdle()); + mUnfocusedWindow->assertNoEvents(); + mFocusedWindow->assertNoEvents(); } } // namespace android::inputdispatcher -- cgit v1.2.3-59-g8ed1b