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

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

Notify the policy when a stylus gesture starts

This will serve as a hint to the policy that a stylus is currently being
used.

Bug: 243005009
Test: atest inputflinger_tests
Change-Id: I2cc187af226fc3a1d4fda36becc280ea7934673a
parent 2607219c
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -393,6 +393,8 @@ public:
    /* Gets the affine calibration associated with the specified device. */
    /* Gets the affine calibration associated with the specified device. */
    virtual TouchAffineTransformation getTouchAffineTransformation(
    virtual TouchAffineTransformation getTouchAffineTransformation(
            const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
            const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
    /* Notifies the input reader policy that a stylus gesture has started. */
    virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
};
};


} // namespace android
} // namespace android
+26 −2
Original line number Original line Diff line number Diff line
@@ -58,6 +58,18 @@ static bool isSubDevice(const InputDeviceIdentifier& identifier1,
            identifier1.location == identifier2.location);
            identifier1.location == identifier2.location);
}
}


static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
    const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
    if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
        actionMasked != AMOTION_EVENT_ACTION_DOWN &&
        actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN) {
        return false;
    }
    const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action);
    return motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
            motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
}

// --- InputReader ---
// --- InputReader ---


InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -101,8 +113,10 @@ status_t InputReader::stop() {
void InputReader::loopOnce() {
void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t oldGeneration;
    int32_t timeoutMillis;
    int32_t timeoutMillis;
    // Copy some state so that we can access it outside the lock later.
    bool inputDevicesChanged = false;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    std::vector<InputDeviceInfo> inputDevices;
    std::list<NotifyArgs> notifyArgs;
    { // acquire lock
    { // acquire lock
        std::scoped_lock _l(mLock);
        std::scoped_lock _l(mLock);


@@ -127,7 +141,7 @@ void InputReader::loopOnce() {
        mReaderIsAliveCondition.notify_all();
        mReaderIsAliveCondition.notify_all();


        if (!events.empty()) {
        if (!events.empty()) {
            notifyAll(processEventsLocked(events.data(), events.size()));
            notifyArgs += processEventsLocked(events.data(), events.size());
        }
        }


        if (mNextTimeout != LLONG_MAX) {
        if (mNextTimeout != LLONG_MAX) {
@@ -137,7 +151,7 @@ void InputReader::loopOnce() {
                    ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
                    ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
                }
                }
                mNextTimeout = LLONG_MAX;
                mNextTimeout = LLONG_MAX;
                notifyAll(timeoutExpiredLocked(now));
                notifyArgs += timeoutExpiredLocked(now);
            }
            }
        }
        }


@@ -152,6 +166,16 @@ void InputReader::loopOnce() {
        mPolicy->notifyInputDevicesChanged(inputDevices);
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    }


    // Notify the policy of the start of every new stylus gesture outside the lock.
    for (const auto& args : notifyArgs) {
        const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
        if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {
            mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime);
        }
    }

    notifyAll(std::move(notifyArgs));

    // Flush queued events out to the listener.
    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
+83 −0
Original line number Original line Diff line number Diff line
@@ -244,6 +244,7 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface {
    bool mInputDevicesChanged GUARDED_BY(mLock){false};
    bool mInputDevicesChanged GUARDED_BY(mLock){false};
    std::vector<DisplayViewport> mViewports;
    std::vector<DisplayViewport> mViewports;
    TouchAffineTransformation transform;
    TouchAffineTransformation transform;
    std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};


protected:
protected:
    virtual ~FakeInputReaderPolicy() {}
    virtual ~FakeInputReaderPolicy() {}
@@ -268,6 +269,18 @@ public:
        });
        });
    }
    }


    void assertStylusGestureNotified(int32_t deviceId) {
        std::scoped_lock lock(mLock);
        ASSERT_TRUE(mStylusGestureNotified);
        ASSERT_EQ(deviceId, *mStylusGestureNotified);
        mStylusGestureNotified.reset();
    }

    void assertStylusGestureNotNotified() {
        std::scoped_lock lock(mLock);
        ASSERT_FALSE(mStylusGestureNotified);
    }

    virtual void clearViewports() {
    virtual void clearViewports() {
        mViewports.clear();
        mViewports.clear();
        mConfig.setDisplayViewports(mViewports);
        mConfig.setDisplayViewports(mViewports);
@@ -428,6 +441,11 @@ private:
        ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
        ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
        mInputDevicesChanged = false;
        mInputDevicesChanged = false;
    }
    }

    void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override {
        std::scoped_lock<std::mutex> lock(mLock);
        mStylusGestureNotified = deviceId;
    }
};
};


// --- FakeEventHub ---
// --- FakeEventHub ---
@@ -2329,6 +2347,15 @@ protected:
        mTestListener.reset();
        mTestListener.reset();
        mFakePolicy.clear();
        mFakePolicy.clear();
    }
    }

    std::optional<InputDeviceInfo> findDeviceByName(const std::string& name) {
        const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
        const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(),
                                      [&name](const InputDeviceInfo& info) {
                                          return info.getIdentifier().name == name;
                                      });
        return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
    }
};
};


TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
@@ -2450,6 +2477,9 @@ protected:
        mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
        mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
        const auto info = findDeviceByName(mDevice->getName());
        ASSERT_TRUE(info);
        mDeviceInfo = *info;
    }
    }


    void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
    void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
@@ -2473,6 +2503,7 @@ protected:
    }
    }


    std::unique_ptr<UinputTouchScreen> mDevice;
    std::unique_ptr<UinputTouchScreen> mDevice;
    InputDeviceInfo mDeviceInfo;
};
};


TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
@@ -2689,6 +2720,58 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
}


TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
    const Point centerPoint = mDevice->getCenterPoint();

    // Send down with the pen tool selected. The policy should be notified of the stylus presence.
    mDevice->sendSlot(FIRST_SLOT);
    mDevice->sendTrackingId(FIRST_TRACKING_ID);
    mDevice->sendToolType(MT_TOOL_PEN);
    mDevice->sendDown(centerPoint);
    mDevice->sendSync();
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));

    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));

    // Release the stylus touch.
    mDevice->sendUp();
    mDevice->sendSync();
    ASSERT_NO_FATAL_FAILURE(
            mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));

    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());

    // Touch down with the finger, without the pen tool selected. The policy is not notified.
    mDevice->sendTrackingId(FIRST_TRACKING_ID);
    mDevice->sendToolType(MT_TOOL_FINGER);
    mDevice->sendDown(centerPoint);
    mDevice->sendSync();
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
                  WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));

    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());

    mDevice->sendUp();
    mDevice->sendSync();
    ASSERT_NO_FATAL_FAILURE(
            mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));

    // Send a move event with the stylus tool without BTN_TOUCH to generate a hover enter.
    // The policy should be notified of the stylus presence.
    mDevice->sendTrackingId(FIRST_TRACKING_ID);
    mDevice->sendToolType(MT_TOOL_PEN);
    mDevice->sendMove(centerPoint);
    mDevice->sendSync();
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));

    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
}

// --- InputDeviceTest ---
// --- InputDeviceTest ---
class InputDeviceTest : public testing::Test {
class InputDeviceTest : public testing::Test {
protected:
protected:
+8 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@
#include <android/input.h>
#include <android/input.h>
#include <gmock/gmock.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <gtest/gtest.h>
#include <input/Input.h>


namespace android {
namespace android {


@@ -62,6 +63,13 @@ MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
    return argPressure;
    return argPressure;
}
}


MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
    const auto argToolType = arg.pointerProperties[0].toolType;
    *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got "
                     << motionToolTypeToString(argToolType);
    return argToolType == toolType;
}

MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
    *result_listener << "expected flags " << flags << ", but got " << arg.flags;
    *result_listener << "expected flags " << flags << ", but got " << arg.flags;
    return arg.flags == flags;
    return arg.flags == flags;
+2 −0
Original line number Original line Diff line number Diff line
@@ -158,6 +158,8 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
    device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
    device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
    device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
    device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
    device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
    device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
    device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER;
    device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX;
}
}


void UinputTouchScreen::sendSlot(int32_t slot) {
void UinputTouchScreen::sendSlot(int32_t slot) {
Loading