diff options
| author | 2022-10-03 15:45:50 +0000 | |
|---|---|---|
| committer | 2022-10-10 19:09:05 +0000 | |
| commit | f5b4d7a828fe02044a92da0f2f512ee2763d6539 (patch) | |
| tree | 58f6f2b8b780fdf6d73392ae4dc7d61caac60d69 | |
| parent | edb0ba739a01d5f9c07e5a6bb88cd0b985f9986f (diff) | |
TouchInputMapper: Cancel ongoing gesture when resetting
Since reset is called when a device is being enabled or disabled, if it
doesn't cancel ongoing gestures, there is a possiblity of seeing an
inconsistent gesture stream.
Also, make sure we send FLAG_CANCELED when sending ACTION_CANCEL from
TouchInputMapper.
Bug: 245989146
Test: atest inputflinger_tests
Change-Id: I9921eee9acf365b28d97f3fbe9b4d6cd15fe7087
| -rw-r--r-- | services/inputflinger/reader/mapper/TouchInputMapper.cpp | 25 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputReader_test.cpp | 45 | ||||
| -rw-r--r-- | services/inputflinger/tests/TestInputListenerMatchers.h | 14 |
3 files changed, 61 insertions, 23 deletions
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 6980144428..c2454ac6d4 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -395,13 +395,11 @@ std::list<NotifyArgs> TouchInputMapper::configure(nsecs_t when, } if (changes && resetNeeded) { - // If the device needs to be reset, cancel any ongoing gestures and reset the state. - out += cancelTouch(when, when); out += reset(when); // Send reset, unless this is the first time the device has been configured, // in which case the reader will call reset itself after all mappers are ready. - out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId())); + out.emplace_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId())); } return out; } @@ -1440,6 +1438,9 @@ void TouchInputMapper::updateAffineTransformation() { } std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) { + std::list<NotifyArgs> out = cancelTouch(when, when); + updateTouchSpots(); + mCursorButtonAccumulator.reset(getDeviceContext()); mCursorScrollAccumulator.reset(getDeviceContext()); mTouchButtonAccumulator.reset(getDeviceContext()); @@ -1470,7 +1471,7 @@ std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) { mPointerController->clearSpots(); } - return InputMapper::reset(when); + return out += InputMapper::reset(when); } void TouchInputMapper::resetExternalStylus() { @@ -1554,10 +1555,7 @@ std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { std::list<NotifyArgs> TouchInputMapper::processRawTouches(bool timeout) { std::list<NotifyArgs> out; if (mDeviceMode == DeviceMode::DISABLED) { - // Drop all input if the device is disabled. - out += cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime); - mCurrentCookedState.clear(); - updateTouchSpots(); + // Do not process raw event while the device is disabled. return out; } @@ -1976,8 +1974,8 @@ std::list<NotifyArgs> TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readT int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentCookedState.buttonState; out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, + AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, @@ -2617,8 +2615,9 @@ std::list<NotifyArgs> TouchInputMapper::dispatchPointerGestures(nsecs_t when, ns BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); if (!dispatchedGestureIdBits.isEmpty()) { if (cancelPreviousGesture) { + const uint32_t cancelFlags = flags | AMOTION_EVENT_FLAG_CANCELED; out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, - AMOTION_EVENT_ACTION_CANCEL, 0, flags, metaState, + AMOTION_EVENT_ACTION_CANCEL, 0, cancelFlags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, @@ -2754,8 +2753,8 @@ std::list<NotifyArgs> TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentRawState.buttonState; out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, + AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index dded6a13e0..8ac8dfc3ac 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -3112,6 +3112,15 @@ protected: return processArgList; } + void resetMapper(InputMapper& mapper, nsecs_t when) { + const auto resetArgs = mapper.reset(when); + for (const auto args : resetArgs) { + mFakeListener->notify(args); + } + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); + } + static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) { const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source); @@ -6864,6 +6873,28 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsV toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); } +TEST_F(SingleTouchInputMapperTest, Reset_CancelsOngoingGesture) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); + prepareAxes(POSITION | PRESSURE); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + // Touch down. + processDown(mapper, 100, 200); + processPressure(mapper, 1); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + // Reset the mapper. This should cancel the ongoing gesture. + resetMapper(mapper, ARBITRARY_TIME); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(DISPLAY_ORIENTATION_0); @@ -6878,8 +6909,9 @@ TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) { mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1); // Reset the mapper. When the mapper is reset, we expect it to attempt to recreate the touch - // state by reading the current axis values. - std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME); + // state by reading the current axis values. Since there was no ongoing gesture, calling reset + // does not generate any events. + resetMapper(mapper, ARBITRARY_TIME); // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use // the recreated touch state to generate a down event. @@ -9530,9 +9562,10 @@ TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) { mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN))); // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be - // preserved. Resetting should not generate any events. - std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + // preserved. Resetting should cancel the ongoing gesture. + resetMapper(mapper, ARBITRARY_TIME); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))); // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use // the existing touch state to generate a down event. @@ -9566,7 +9599,7 @@ TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) // Reset the mapper. When the mapper is reset, we expect it to restore the latest // raw state where no pointers are down. - std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME); + resetMapper(mapper, ARBITRARY_TIME); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); // Send an empty sync frame. Since there are no pointers, no events are generated. diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h index 2580405c02..ff7455b452 100644 --- a/services/inputflinger/tests/TestInputListenerMatchers.h +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -23,13 +23,19 @@ namespace android { MATCHER_P(WithMotionAction, action, "InputEvent with specified action") { + bool matches = action == arg.action; + if (!matches) { + *result_listener << "expected action " << MotionEvent::actionToString(action) + << ", but got " << MotionEvent::actionToString(arg.action); + } if (action == AMOTION_EVENT_ACTION_CANCEL) { + if (!matches) { + *result_listener << "; "; + } *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set"; - return (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0; + matches &= (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0; } - *result_listener << "expected action " << MotionEvent::actionToString(action) << ", but got " - << MotionEvent::actionToString(arg.action); - return action == arg.action; + return matches; } MATCHER_P(WithSource, source, "InputEvent with specified source") { |