From 84b087ec591626e8dd7d3d2c877afb74f0de1b09 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 --- include/input/Input.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'include/input/Input.h') 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 -- cgit v1.2.3-59-g8ed1b From 4cc839fe01cd5bbb16650e85913070386f637e75 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 --- 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 'include/input/Input.h') 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 750c7f4b2c5077d7f62401f84689b6e4c0a5abeb Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Mon, 18 May 2020 17:41:46 -0700 Subject: Avoid shifting 1s into size bit. Shifting 1 into size bit of sized integers is undefined until C++20, so let's avoid undefined behavior. Bug: None Test: Event ID in systrace seems to bear correct source bits. Change-Id: Id2ef5dc05a744476a713a111678dea966f1a5e32 --- include/input/Input.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/input/Input.h') diff --git a/include/input/Input.h b/include/input/Input.h index 9e47318203..7dcbaf0686 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -280,9 +280,9 @@ private: 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 + INPUT_READER = static_cast(0x0u << SOURCE_SHIFT), + INPUT_DISPATCHER = static_cast(0x1u << SOURCE_SHIFT), + OTHER = static_cast(0x3u << SOURCE_SHIFT), // E.g. app injected events }; IdGenerator(Source source); @@ -294,7 +294,7 @@ public: private: const Source mSource; - static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT; + static constexpr int32_t SOURCE_MASK = static_cast(0x3u << SOURCE_SHIFT); }; /** -- cgit v1.2.3-59-g8ed1b From d44dddf67b923ff4f4a19f76228e6091c2bab742 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 --- 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 'include/input/Input.h') diff --git a/include/input/Input.h b/include/input/Input.h index 7dcbaf0686..ac901ae88a 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 23dec769c8..c139dc54b4 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. @@ -2049,92 +2148,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) { @@ -2534,6 +2547,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; @@ -2653,7 +2669,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, " @@ -2670,6 +2685,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); } } @@ -2704,8 +2723,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; @@ -3861,15 +3878,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); } @@ -3950,7 +3969,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { if (mDispatchFrozen && !frozen) { - resetAnrTimeoutsLocked(); + resetNoFocusedWindowTimeoutLocked(); } if (mDispatchEnabled && !enabled) { @@ -4090,8 +4109,9 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetKeyRepeatLocked(); releasePendingEventLocked(); drainInboundQueueLocked(); - resetAnrTimeoutsLocked(); + resetNoFocusedWindowTimeoutLocked(); + mAnrTracker.clear(); mTouchStatesByDisplay.clear(); mLastHoverWindowHandle.clear(); mReplacedKeys.clear(); @@ -4291,11 +4311,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", @@ -4563,6 +4582,7 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn } void InputDispatcher::removeConnectionLocked(const sp& connection) { + mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); removeByValue(mConnectionsByFd, connection); } @@ -4600,17 +4620,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; @@ -4620,21 +4692,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) { @@ -4675,13 +4735,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(); @@ -4715,6 +4812,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; @@ -4727,7 +4838,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(), @@ -4756,6 +4866,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 From fd9c1edef5e398a46a92ad0f9f9dd1bff3e8af48 Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 1 Jul 2020 10:57:59 -0700 Subject: Added libui dependencies for input Allow Input to support Transform class from libui and add Transform objects to MotionEvent and InputWindowInfo Test: Builds Bug: 158476194 Change-Id: Ic6f2507dbead97c2124dde8c8946add377d724c6 --- include/input/Input.h | 2 ++ include/input/InputWindow.h | 4 ++++ include/ui/Rotation.h | 1 + include/ui/Transform.h | 1 + libs/input/Android.bp | 8 ++++++++ libs/ui/Android.bp | 11 ++++++++--- libs/ui/include/ui/Transform.h | 7 +++---- services/inputflinger/Android.bp | 1 + services/inputflinger/host/Android.bp | 1 + 9 files changed, 29 insertions(+), 7 deletions(-) create mode 120000 include/ui/Rotation.h create mode 120000 include/ui/Transform.h (limited to 'include/input/Input.h') diff --git a/include/input/Input.h b/include/input/Input.h index ac901ae88a..194db1c6b5 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -741,6 +742,7 @@ protected: float mYScale; float mXOffset; float mYOffset; + ui::Transform mTransform; float mXPrecision; float mYPrecision; float mRawXCursorPosition; diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index f8c759c85d..6a42ff6eb4 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -160,6 +161,9 @@ struct InputWindowInfo { float windowXScale = 1.0f; float windowYScale = 1.0f; + // Transform applied to individual windows. + ui::Transform transform; + /* * This is filled in by the WM relative to the frame and then translated * to absolute coordinates by SurfaceFlinger once the frame is computed. diff --git a/include/ui/Rotation.h b/include/ui/Rotation.h new file mode 120000 index 0000000000..095d2cee88 --- /dev/null +++ b/include/ui/Rotation.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/Rotation.h \ No newline at end of file diff --git a/include/ui/Transform.h b/include/ui/Transform.h new file mode 120000 index 0000000000..323f1fd1b4 --- /dev/null +++ b/include/ui/Transform.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/Transform.h \ No newline at end of file diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 7037680935..0aa325f9df 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -43,6 +43,14 @@ cc_library { "libcutils", ], + static_libs: [ + "libui-types", + ], + + export_static_lib_headers: [ + "libui-types", + ], + target: { android: { srcs: [ diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index f3edd3c791..2acc5bb14c 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -51,22 +51,30 @@ cc_library_static { min_sdk_version: "apex_inherit", shared_libs: [ + "libbase", "libutils", ], static_libs: [ + "libarect", "libmath", ], srcs: [ "ColorSpace.cpp", + "Rect.cpp", + "Region.cpp", + "Transform.cpp", ], export_include_dirs: [ + "include", + "include_private", "include_types", ], export_static_lib_headers: [ + "libarect", "libmath", ], @@ -111,10 +119,7 @@ cc_library_shared { "HdrCapabilities.cpp", "PixelFormat.cpp", "PublicFormat.cpp", - "Rect.cpp", - "Region.cpp", "Size.cpp", - "Transform.cpp", "UiConfig.cpp", ], diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h index c6bb598d7f..3e8caf46c5 100644 --- a/libs/ui/include/ui/Transform.h +++ b/libs/ui/include/ui/Transform.h @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -44,9 +43,9 @@ public: enum RotationFlags : uint32_t { ROT_0 = 0, - FLIP_H = HAL_TRANSFORM_FLIP_H, - FLIP_V = HAL_TRANSFORM_FLIP_V, - ROT_90 = HAL_TRANSFORM_ROT_90, + FLIP_H = 1, // HAL_TRANSFORM_FLIP_H + FLIP_V = 2, // HAL_TRANSFORM_FLIP_V + ROT_90 = 4, // HAL_TRANSFORM_ROT_90 ROT_180 = FLIP_H | FLIP_V, ROT_270 = ROT_180 | ROT_90, ROT_INVALID = 0x80 diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index f685628a6c..5930f0a884 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -111,6 +111,7 @@ cc_defaults { "libcutils", "libinput", "liblog", + "libui", "libutils", ], header_libs: [ diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp index b56f356dfd..2a84dcbd2a 100644 --- a/services/inputflinger/host/Android.bp +++ b/services/inputflinger/host/Android.bp @@ -63,5 +63,6 @@ cc_binary { ], static_libs: [ "libarect", + "libui-types", ], } -- cgit v1.2.3-59-g8ed1b From c01e1379d5d654234861c0881b4d5fb558524f58 Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 1 Jul 2020 12:37:31 -0700 Subject: Added helper functions in Transform and PointerCoords 1. Getter for each element in the Transform matrix. 2. Setter for Transform using a nine element array. 3. New multiply in Transform to multiply all elements by a single value 4. transform function in PointerCoords to apply the transform to x and y coordinate Test: Builds Bug: 158476194 Change-Id: Iafe07813c6ce8127875b06e6e6e11554d1862f6f --- include/input/Input.h | 2 ++ libs/input/Input.cpp | 5 +++++ libs/ui/Transform.cpp | 40 +++++++++++++++++++++++++++++++++------ libs/ui/include/ui/Transform.h | 11 ++++++++--- services/surfaceflinger/Layer.cpp | 4 ++-- 5 files changed, 51 insertions(+), 11 deletions(-) (limited to 'include/input/Input.h') diff --git a/include/input/Input.h b/include/input/Input.h index 194db1c6b5..40d655fb43 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -342,6 +342,8 @@ struct PointerCoords { void scale(float globalScale, float windowXScale, float windowYScale); void applyOffset(float xOffset, float yOffset); + void transform(const ui::Transform& transform); + inline float getX() const { return getAxisValue(AMOTION_EVENT_AXIS_X); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 31aa685391..2ac079ec52 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -301,6 +301,11 @@ void PointerCoords::copyFrom(const PointerCoords& other) { } } +void PointerCoords::transform(const ui::Transform& transform) { + vec2 newCoords = transform.transform(getX(), getY()); + setAxisValue(AMOTION_EVENT_AXIS_X, newCoords.x); + setAxisValue(AMOTION_EVENT_AXIS_Y, newCoords.y); +} // --- PointerProperties --- diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp index 06b6bfe797..3bf3903929 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -55,7 +55,6 @@ bool Transform::operator==(const Transform& other) const { mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] && mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] && mMatrix[2][2] == other.mMatrix[2][2]; - ; } Transform Transform::operator * (const Transform& rhs) const @@ -87,6 +86,19 @@ Transform Transform::operator * (const Transform& rhs) const return r; } +Transform Transform::operator * (float value) const { + Transform r(*this); + const mat33& M(mMatrix); + mat33& R(r.mMatrix); + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 2; j++) { + R[i][j] = M[i][j] * value; + } + } + r.type(); + return r; +} + Transform& Transform::operator=(const Transform& other) { mMatrix = other.mMatrix; mType = other.mType; @@ -105,11 +117,19 @@ float Transform::ty() const { return mMatrix[2][1]; } -float Transform::sx() const { +float Transform::dsdx() const { return mMatrix[0][0]; } -float Transform::sy() const { +float Transform::dtdx() const { + return mMatrix[1][0]; +} + +float Transform::dtdy() const { + return mMatrix[0][1]; +} + +float Transform::dsdy() const { return mMatrix[1][1]; } @@ -187,6 +207,15 @@ status_t Transform::set(uint32_t flags, float w, float h) return NO_ERROR; } +void Transform::set(const std::array& matrix) { + mat33& M(mMatrix); + M[0][0] = matrix[0]; M[1][0] = matrix[1]; M[2][0] = matrix[2]; + M[0][1] = matrix[3]; M[1][1] = matrix[4]; M[2][1] = matrix[5]; + M[0][2] = matrix[6]; M[1][2] = matrix[7]; M[2][2] = matrix[8]; + mType = UNKNOWN_TYPE; + type(); +} + vec2 Transform::transform(const vec2& v) const { vec2 r; const mat33& M(mMatrix); @@ -204,9 +233,8 @@ vec3 Transform::transform(const vec3& v) const { return r; } -vec2 Transform::transform(int x, int y) const -{ - return transform(vec2(x,y)); +vec2 Transform::transform(float x, float y) const { + return transform(vec2(x, y)); } Rect Transform::makeBounds(int w, int h) const diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h index 3e8caf46c5..cf59467367 100644 --- a/libs/ui/include/ui/Transform.h +++ b/libs/ui/include/ui/Transform.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -68,24 +69,28 @@ public: const vec3& operator [] (size_t i) const; // returns column i float tx() const; float ty() const; - float sx() const; - float sy() const; + float dsdx() const; + float dtdx() const; + float dtdy() const; + float dsdy() const; // modify the transform void reset(); void set(float tx, float ty); void set(float a, float b, float c, float d); status_t set(uint32_t flags, float w, float h); + void set(const std::array& matrix); // transform data Rect makeBounds(int w, int h) const; - vec2 transform(int x, int y) const; + vec2 transform(float x, float y) const; Region transform(const Region& reg) const; Rect transform(const Rect& bounds, bool roundOutwards = false) const; FloatRect transform(const FloatRect& bounds) const; Transform& operator = (const Transform& other); Transform operator * (const Transform& rhs) const; + Transform operator * (float value) const; // assumes the last row is < 0 , 0 , 1 > vec2 transform(const vec2& v) const; vec3 transform(const vec3& v) const; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 8cfb908e2a..67becab0d5 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2385,8 +2385,8 @@ InputWindowInfo Layer::fillInputInfo() { } ui::Transform t = getTransform(); - const float xScale = t.sx(); - const float yScale = t.sy(); + const float xScale = t.dsdx(); + const float yScale = t.dsdy(); int32_t xSurfaceInset = info.surfaceInset; int32_t ySurfaceInset = info.surfaceInset; if (xScale != 1.0f || yScale != 1.0f) { -- cgit v1.2.3-59-g8ed1b From cc7f98045f3b64af894c9454ca536e7e9d19c18c Mon Sep 17 00:00:00 2001 From: arthurhung Date: Thu, 30 Apr 2020 17:55:40 +0800 Subject: Allow touch if some pointers are MT_TOOL_PALM We would cancel all touch when we recevied MT_TOOL_PALM because it becomes accidental touch, and that would break user gesture when another non-palm pointer is still active. - Send ACTION_POINTER_UP with AKEY_EVENT_FLAG_CANCELED when recevie MT_TOOL_PALM if at least 2 pointers are active. - Send ACTION_CANEL when receive MT_TOOL_PALM if only one pointer. - Add test caseis for touch cancel scenarios, to test it would keep dispatching non-palm event. Test: atest inputflinger_tests Bug: 137221719 Change-Id: Ife35a692c71e45159bfcc771317dff47fa764dda --- include/input/Input.h | 7 + .../reader/mapper/MultiTouchInputMapper.cpp | 21 +- .../reader/mapper/MultiTouchInputMapper.h | 2 + .../reader/mapper/TouchInputMapper.cpp | 22 +- .../inputflinger/reader/mapper/TouchInputMapper.h | 5 +- services/inputflinger/tests/InputReader_test.cpp | 305 +++++++++++++++++---- services/inputflinger/tests/UinputDevice.cpp | 5 + services/inputflinger/tests/UinputDevice.h | 1 + 8 files changed, 299 insertions(+), 69 deletions(-) (limited to 'include/input/Input.h') diff --git a/include/input/Input.h b/include/input/Input.h index 40d655fb43..9525bcbef4 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -87,6 +87,13 @@ constexpr int32_t VERIFIED_KEY_EVENT_FLAGS = AKEY_EVENT_FLAG_CANCELED; constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; +/** + * This flag indicates that the point up event has been canceled. + * Typically this is used for palm event when the user has accidental touches. + * TODO: Adjust flag to public api + */ +constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20; + enum { /* Used when a motion event is not associated with any display. * Typically used for non-pointer events. */ diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 43bd9f1f99..0440f49f02 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -236,6 +236,20 @@ void MultiTouchInputMapper::process(const RawEvent* rawEvent) { mMultiTouchMotionAccumulator.process(rawEvent); } +std::optional MultiTouchInputMapper::getActiveBitId( + const MultiTouchMotionAccumulator::Slot& inSlot) { + if (mHavePointerIds) { + int32_t trackingId = inSlot.getTrackingId(); + for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { + int32_t n = idBits.clearFirstMarkedBit(); + if (mPointerTrackingIdMap[n] == trackingId) { + return std::make_optional(n); + } + } + } + return std::nullopt; +} + void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); size_t outCount = 0; @@ -250,10 +264,9 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { } if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) { - if (!mCurrentMotionAborted) { - ALOGI("Canceling touch gesture from device %s because the palm event was detected", - getDeviceName().c_str()); - cancelTouch(when); + std::optional id = getActiveBitId(*inSlot); + if (id) { + outState->rawPointerData.canceledIdBits.markBit(id.value()); } continue; } diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index 190282f428..ea6f2078d7 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -103,6 +103,8 @@ protected: bool hasStylus() const override; private: + // If the slot is in use, return the bit id. Return std::nullopt otherwise. + std::optional getActiveBitId(const MultiTouchMotionAccumulator::Slot& inSlot); MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; // Specifies the pointer id bits that are in use, and their associated tracking id. diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 4274ca8b0f..15d528896f 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -104,6 +104,7 @@ void RawPointerData::copyFrom(const RawPointerData& other) { pointerCount = other.pointerCount; hoveringIdBits = other.hoveringIdBits; touchingIdBits = other.touchingIdBits; + canceledIdBits = other.canceledIdBits; for (uint32_t i = 0; i < pointerCount; i++) { pointers[i] = other.pointers[i]; @@ -140,6 +141,7 @@ void CookedPointerData::clear() { pointerCount = 0; hoveringIdBits.clear(); touchingIdBits.clear(); + canceledIdBits.clear(); } void CookedPointerData::copyFrom(const CookedPointerData& other) { @@ -1444,10 +1446,11 @@ void TouchInputMapper::sync(nsecs_t when) { #if DEBUG_RAW_EVENTS ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " - "hovering ids 0x%08x -> 0x%08x", + "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x", last->rawPointerData.pointerCount, next->rawPointerData.pointerCount, last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value, - last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value); + last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value, + next->rawPointerData.canceledIdBits.value); #endif processRawTouches(false /*timeout*/); @@ -1892,14 +1895,15 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { // Dispatch pointer up events. while (!upIdBits.isEmpty()) { uint32_t upId = upIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, - metaState, buttonState, 0, + bool isCanceled = mCurrentCookedState.cookedPointerData.canceledIdBits.hasBit(upId); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, + isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0, mLastCookedState.cookedPointerData.pointerProperties, mLastCookedState.cookedPointerData.pointerCoords, mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); dispatchedIdBits.clearBit(upId); + mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId); } // Dispatch move events if any of the remaining pointers moved from their old locations. @@ -2025,6 +2029,8 @@ void TouchInputMapper::cookPointerData() { mCurrentRawState.rawPointerData.hoveringIdBits; mCurrentCookedState.cookedPointerData.touchingIdBits = mCurrentRawState.rawPointerData.touchingIdBits; + mCurrentCookedState.cookedPointerData.canceledIdBits = + mCurrentRawState.rawPointerData.canceledIdBits; if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { mCurrentCookedState.buttonState = 0; @@ -3563,7 +3569,11 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { action = AMOTION_EVENT_ACTION_DOWN; } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { - action = AMOTION_EVENT_ACTION_UP; + if ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) { + action = AMOTION_EVENT_ACTION_CANCEL; + } else { + action = AMOTION_EVENT_ACTION_UP; + } } else { // Can't happen. ALOG_ASSERT(false); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index d437e0124f..94486a67b0 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -71,7 +71,7 @@ struct RawPointerData { uint32_t pointerCount; Pointer pointers[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; + BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits; uint32_t idToIndex[MAX_POINTER_ID + 1]; RawPointerData(); @@ -90,6 +90,7 @@ struct RawPointerData { inline void clearIdBits() { hoveringIdBits.clear(); touchingIdBits.clear(); + canceledIdBits.clear(); } inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; } @@ -102,7 +103,7 @@ struct CookedPointerData { uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; + BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits; uint32_t idToIndex[MAX_POINTER_ID + 1]; CookedPointerData(); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index fae7e64c5e..b2c16d05c9 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -45,16 +45,24 @@ static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms; static const nsecs_t ARBITRARY_TIME = 1234; // Arbitrary display properties. -static const int32_t DISPLAY_ID = 0; -static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1; -static const int32_t DISPLAY_WIDTH = 480; -static const int32_t DISPLAY_HEIGHT = 800; -static const int32_t VIRTUAL_DISPLAY_ID = 1; -static const int32_t VIRTUAL_DISPLAY_WIDTH = 400; -static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500; +static constexpr int32_t DISPLAY_ID = 0; +static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1; +static constexpr int32_t DISPLAY_WIDTH = 480; +static constexpr int32_t DISPLAY_HEIGHT = 800; +static constexpr int32_t VIRTUAL_DISPLAY_ID = 1; +static constexpr int32_t VIRTUAL_DISPLAY_WIDTH = 400; +static constexpr int32_t VIRTUAL_DISPLAY_HEIGHT = 500; static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1"; static constexpr std::optional NO_PORT = std::nullopt; // no physical port is specified +static constexpr int32_t FIRST_SLOT = 0; +static constexpr int32_t SECOND_SLOT = 1; +static constexpr int32_t THIRD_SLOT = 2; +static constexpr int32_t INVALID_TRACKING_ID = -1; +static constexpr int32_t FIRST_TRACKING_ID = 0; +static constexpr int32_t SECOND_TRACKING_ID = 1; +static constexpr int32_t THIRD_TRACKING_ID = 2; + // Error tolerance for floating point assertions. static const float EPSILON = 0.001f; @@ -1880,10 +1888,6 @@ TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) { // --- TouchProcessTest --- class TouchIntegrationTest : public InputReaderIntegrationTest { protected: - static const int32_t FIRST_SLOT = 0; - static const int32_t SECOND_SLOT = 1; - static const int32_t FIRST_TRACKING_ID = 0; - static const int32_t SECOND_TRACKING_ID = 1; const std::string UNIQUE_ID = "local:0"; virtual void SetUp() override { @@ -1954,9 +1958,9 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) { ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); // ACTION_POINTER_UP (Second slot) - mDevice->sendUp(); + mDevice->sendPointerUp(); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), args.action); // ACTION_UP @@ -1971,11 +1975,13 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) { const Point centerPoint = mDevice->getCenterPoint(); // ACTION_DOWN + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); mDevice->sendDown(centerPoint); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - // ACTION_POINTER_DOWN (Second slot) + // ACTION_POINTER_DOWN (second slot) const Point secondPoint = centerPoint + Point(100, 100); mDevice->sendSlot(SECOND_SLOT); mDevice->sendTrackingId(SECOND_TRACKING_ID); @@ -1984,26 +1990,31 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) { ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), args.action); - // ACTION_MOVE (Second slot) + // ACTION_MOVE (second slot) mDevice->sendMove(secondPoint + Point(1, 1)); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event. - // Expect to receive ACTION_CANCEL, to abort the entire gesture. + // Send MT_TOOL_PALM (second slot), which indicates that the touch IC has determined this to be + // a palm event. + // Expect to receive the ACTION_POINTER_UP with cancel flag. mDevice->sendToolType(MT_TOOL_PALM); ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + args.action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, args.flags); - // ACTION_POINTER_UP (Second slot) - mDevice->sendUp(); + // Send up to second slot, expect first slot send moving. + mDevice->sendPointerUp(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - // ACTION_UP + // Send ACTION_UP (first slot) mDevice->sendSlot(FIRST_SLOT); mDevice->sendUp(); - // Expect no event received after abort the entire gesture. - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); } // --- InputDeviceTest --- @@ -7064,10 +7075,10 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) { } /** - * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and - * UP events should be ignored. + * Test single touch should be canceled when received the MT_TOOL_PALM event, and the following + * MOVE and UP events should be ignored. */ -TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); @@ -7077,7 +7088,7 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { // default tool type is finger constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240; - processId(mapper, 1); + processId(mapper, FIRST_TRACKING_ID); processPosition(mapper, x1, y1); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -7091,19 +7102,19 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); // Ignore the following MOVE and UP events if had detect a palm event. - processId(mapper, 1); + processId(mapper, FIRST_TRACKING_ID); processPosition(mapper, x2, y2); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); // finger up. - processId(mapper, -1); + processId(mapper, INVALID_TRACKING_ID); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); // new finger down + processId(mapper, FIRST_TRACKING_ID); processToolType(mapper, MT_TOOL_FINGER); - processId(mapper, 1); processPosition(mapper, x3, y3); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -7112,11 +7123,10 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) { } /** - * Test multi-touch should be canceled when received the MT_TOOL_PALM event from some finger, - * and could be allowed again after all non-MT_TOOL_PALM is release and the new point is - * MT_TOOL_FINGER. + * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger, + * and the rest active fingers could still be allowed to receive the events */ -TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) { +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); @@ -7125,8 +7135,85 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) { NotifyMotionArgs motionArgs; // default tool type is finger + constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220; + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // Second finger down. + processSlot(mapper, SECOND_SLOT); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType); + + // If the tool type of the first finger changes to MT_TOOL_PALM, + // we expect to receive ACTION_POINTER_UP with cancel flag. + processSlot(mapper, FIRST_SLOT); + processId(mapper, FIRST_TRACKING_ID); + processToolType(mapper, MT_TOOL_PALM); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags); + + // The following MOVE events of second finger should be processed. + processSlot(mapper, SECOND_SLOT); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2 + 1, y2 + 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(uint32_t(1), motionArgs.pointerCount); + + // First finger up. It used to be in palm mode, and we already generated ACTION_POINTER_UP for + // it. Second finger receive move. + processSlot(mapper, FIRST_SLOT); + processId(mapper, INVALID_TRACKING_ID); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(uint32_t(1), motionArgs.pointerCount); + + // Second finger keeps moving. + processSlot(mapper, SECOND_SLOT); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2 + 2, y2 + 2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(uint32_t(1), motionArgs.pointerCount); + + // Second finger up. + processId(mapper, INVALID_TRACKING_ID); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags); +} + +/** + * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event, if only 1 finger + * is active, it should send CANCEL after receiving the MT_TOOL_PALM event. + */ +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); + + NotifyMotionArgs motionArgs; + constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240; - processId(mapper, 1); + // First finger down. + processId(mapper, FIRST_TRACKING_ID); processPosition(mapper, x1, y1); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -7134,51 +7221,155 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) { ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); // Second finger down. - processSlot(mapper, 1); + processSlot(mapper, SECOND_SLOT); + processId(mapper, SECOND_TRACKING_ID); processPosition(mapper, x2, y2); - processId(mapper, 2); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), motionArgs.action); ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); - // If the tool type of the first pointer changes to MT_TOOL_PALM, - // the entire gesture should be aborted, so we expect to receive ACTION_CANCEL. - processSlot(mapper, 0); - processId(mapper, 1); + // If the tool type of the first finger changes to MT_TOOL_PALM, + // we expect to receive ACTION_POINTER_UP with cancel flag. + processSlot(mapper, FIRST_SLOT); + processId(mapper, FIRST_TRACKING_ID); + processToolType(mapper, MT_TOOL_PALM); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags); + + // Second finger keeps moving. + processSlot(mapper, SECOND_SLOT); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2 + 1, y2 + 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + + // second finger becomes palm, receive cancel due to only 1 finger is active. + processId(mapper, SECOND_TRACKING_ID); processToolType(mapper, MT_TOOL_PALM); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); - // Ignore the following MOVE and UP events if had detect a palm event. - processSlot(mapper, 1); - processId(mapper, 2); + // third finger down. + processSlot(mapper, THIRD_SLOT); + processId(mapper, THIRD_TRACKING_ID); + processToolType(mapper, MT_TOOL_FINGER); processPosition(mapper, x3, y3); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + ASSERT_EQ(uint32_t(1), motionArgs.pointerCount); - // second finger up. - processId(mapper, -1); + // third finger move + processId(mapper, THIRD_TRACKING_ID); + processPosition(mapper, x3 + 1, y3 + 1); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - // first finger move, but still in palm - processSlot(mapper, 0); - processId(mapper, 1); - processPosition(mapper, x1 - 1, y1 - 1); + // first finger up, third finger receive move. + processSlot(mapper, FIRST_SLOT); + processId(mapper, INVALID_TRACKING_ID); processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(uint32_t(1), motionArgs.pointerCount); - // second finger down, expect as new finger down. - processSlot(mapper, 1); - processId(mapper, 2); - processPosition(mapper, x2, y2); + // second finger up, third finger receive move. + processSlot(mapper, SECOND_SLOT); + processId(mapper, INVALID_TRACKING_ID); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(uint32_t(1), motionArgs.pointerCount); + + // third finger up. + processSlot(mapper, THIRD_SLOT); + processId(mapper, INVALID_TRACKING_ID); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags); +} + +/** + * Test multi-touch should sent POINTER_UP when received the MT_TOOL_PALM event from some finger, + * and the active finger could still be allowed to receive the events + */ +TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION | ID | SLOT | TOOL_TYPE); + MultiTouchInputMapper& mapper = addMapperAndConfigure(); + + NotifyMotionArgs motionArgs; + + // default tool type is finger + constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220; + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1, y1); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // Second finger down. + processSlot(mapper, SECOND_SLOT); + processId(mapper, SECOND_TRACKING_ID); + processPosition(mapper, x2, y2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType); + + // If the tool type of the second finger changes to MT_TOOL_PALM, + // we expect to receive ACTION_POINTER_UP with cancel flag. + processId(mapper, SECOND_TRACKING_ID); + processToolType(mapper, MT_TOOL_PALM); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags); + + // The following MOVE event should be processed. + processSlot(mapper, FIRST_SLOT); + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1 + 1, y1 + 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(uint32_t(1), motionArgs.pointerCount); + + // second finger up. + processSlot(mapper, SECOND_SLOT); + processId(mapper, INVALID_TRACKING_ID); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + + // first finger keep moving + processSlot(mapper, FIRST_SLOT); + processId(mapper, FIRST_TRACKING_ID); + processPosition(mapper, x1 + 2, y1 + 2); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + + // first finger up. + processId(mapper, INVALID_TRACKING_ID); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags); } // --- MultiTouchInputMapperTest_ExternalDevice --- diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 0659511d25..7fec2c8b4b 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -179,6 +179,11 @@ void UinputTouchScreen::sendMove(const Point& point) { injectEvent(EV_SYN, SYN_REPORT, 0); } +void UinputTouchScreen::sendPointerUp() { + sendTrackingId(0xffffffff); + injectEvent(EV_SYN, SYN_REPORT, 0); +} + void UinputTouchScreen::sendUp() { sendTrackingId(0xffffffff); injectEvent(EV_KEY, BTN_TOUCH, 0); diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index 22d1f630fc..01a557cc70 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -139,6 +139,7 @@ public: void sendTrackingId(int32_t trackingId); void sendDown(const Point& point); void sendMove(const Point& point); + void sendPointerUp(); void sendUp(); void sendToolType(int32_t toolType); -- cgit v1.2.3-59-g8ed1b From 9eaa22cf6e0cdb872be3bd4eb2bdb568880653f3 Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 1 Jul 2020 16:21:27 -0700 Subject: Updated MotionEvent to use Transform Updated MotionEvent to use Transform instead of offset and scale. This allowed a lot of simplification since it no longer needs to manually do the transformation calculation and instead can rely on functions provided by the Transform class. Test: Input works Test: inputflinger_tests Test: libinput_tests Bug: 158476194 Change-Id: Id40ae0184ca778507166e60d0d3eaf9564f3172b --- include/input/Input.h | 18 +- include/input/InputTransport.h | 20 +- libs/input/Input.cpp | 170 +++++++-------- libs/input/InputTransport.cpp | 59 +++--- libs/input/InputWindow.cpp | 2 +- libs/input/tests/InputEvent_test.cpp | 30 +-- .../input/tests/InputPublisherAndConsumer_test.cpp | 30 +-- libs/input/tests/InputWindow_test.cpp | 2 +- libs/input/tests/StructLayout_test.cpp | 22 +- libs/input/tests/VelocityTracker_test.cpp | 27 ++- libs/input/tests/VerifiedInputEvent_test.cpp | 9 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 4 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 15 +- .../inputflinger/tests/InputDispatcher_test.cpp | 232 +++++++++------------ .../tests/InputFlingerService_test.cpp | 4 +- 15 files changed, 313 insertions(+), 331 deletions(-) (limited to 'include/input/Input.h') diff --git a/include/input/Input.h b/include/input/Input.h index 9525bcbef4..d40ba439db 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -527,13 +527,11 @@ public: inline void setActionButton(int32_t button) { mActionButton = button; } - inline float getXScale() const { return mXScale; } + inline float getXOffset() const { return mTransform.tx(); } - inline float getYScale() const { return mYScale; } + inline float getYOffset() const { return mTransform.ty(); } - inline float getXOffset() const { return mXOffset; } - - inline float getYOffset() const { return mYOffset; } + inline ui::Transform getTransform() const { return mTransform; } inline float getXPrecision() const { return mXPrecision; } @@ -695,8 +693,8 @@ public: 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, - float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition, + MotionClassification classification, const ui::Transform& transform, + float xPrecision, float yPrecision, float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); @@ -713,7 +711,7 @@ public: // Apply 3x3 perspective matrix transformation. // Matrix is in row-major form and compatible with SkMatrix. - void transform(const float matrix[9]); + void transform(const std::array& matrix); #ifdef __ANDROID__ status_t readFromParcel(Parcel* parcel); @@ -747,10 +745,6 @@ protected: int32_t mMetaState; int32_t mButtonState; MotionClassification mClassification; - float mXScale; - float mYScale; - float mXOffset; - float mYOffset; ui::Transform mTransform; float mXPrecision; float mYPrecision; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 24f8e77446..8e009699a3 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -37,6 +37,7 @@ #include #include