Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5b4d7a8 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

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
parent edb0ba73
Loading
Loading
Loading
Loading
+12 −13
Original line number Diff line number Diff line
@@ -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,
+39 −6
Original line number Diff line number Diff line
@@ -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.
+10 −4
Original line number Diff line number Diff line
@@ -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") {