diff options
10 files changed, 139 insertions, 74 deletions
diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h index 8d14d3c169..ff15e0c660 100644 --- a/services/inputflinger/reader/include/StylusState.h +++ b/services/inputflinger/reader/include/StylusState.h @@ -24,27 +24,19 @@ namespace android { struct StylusState { /* Time the stylus event was received. */ - nsecs_t when; - /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ - float pressure; + nsecs_t when{}; + /* + * Pressure as reported by the stylus if supported, normalized to the range [0, 1.0]. + * The presence of a pressure value indicates that the stylus is able to tell whether it is + * touching the display. + */ + std::optional<float> pressure{}; /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ - uint32_t buttons; + uint32_t buttons{}; /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ - int32_t toolType; + int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN}; - void copyFrom(const StylusState& other) { - when = other.when; - pressure = other.pressure; - buttons = other.buttons; - toolType = other.toolType; - } - - void clear() { - when = LLONG_MAX; - pressure = 0.f; - buttons = 0; - toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - } + void clear() { *this = StylusState{}; } }; } // namespace android diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 56fc5fa99a..2809939c86 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -32,8 +32,10 @@ uint32_t ExternalStylusInputMapper::getSources() const { void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f); + if (mRawPressureAxis.valid) { + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f); + } } void ExternalStylusInputMapper::dump(std::string& dump) { @@ -79,13 +81,12 @@ std::list<NotifyArgs> ExternalStylusInputMapper::sync(nsecs_t when) { mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; } - int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); if (mRawPressureAxis.valid) { - mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; - } else if (mTouchButtonAccumulator.isToolActive()) { - mStylusState.pressure = 1.0f; - } else { - mStylusState.pressure = 0.0f; + auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure()); + mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) / + static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue); + } else if (mTouchButtonAccumulator.hasButtonTouch()) { + mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f; } mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index 844afe0522..8e3539c3a6 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -19,6 +19,7 @@ #include "InputMapper.h" #include "InputDevice.h" +#include "input/PrintTools.h" namespace android { @@ -129,7 +130,7 @@ void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAx void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); - dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); + dump += StringPrintf(INDENT4 "Pressure: %s\n", toString(state.pressure).c_str()); dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index d4473688b8..f8d6cf9e7f 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1705,22 +1705,24 @@ void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; + if (!mFusedStylusPointerId || !currentPointerData.isTouching(*mFusedStylusPointerId)) { + return; + } - if (mFusedStylusPointerId && currentPointerData.isTouching(*mFusedStylusPointerId)) { - float pressure = mExternalStylusState.pressure; - if (pressure == 0.0f && lastPointerData.isTouching(*mFusedStylusPointerId)) { - const PointerCoords& coords = - lastPointerData.pointerCoordsForId(*mFusedStylusPointerId); - pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - } - PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId); - coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + float pressure = lastPointerData.isTouching(*mFusedStylusPointerId) + ? lastPointerData.pointerCoordsForId(*mFusedStylusPointerId) + .getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) + : 0.f; + if (mExternalStylusState.pressure && *mExternalStylusState.pressure > 0.f) { + pressure = *mExternalStylusState.pressure; + } + PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { PointerProperties& properties = currentPointerData.editPointerPropertiesWithId(*mFusedStylusPointerId); - if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - properties.toolType = mExternalStylusState.toolType; - } + properties.toolType = mExternalStylusState.toolType; } } @@ -1729,36 +1731,48 @@ bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeou return false; } - const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && - state.rawPointerData.pointerCount != 0; - if (initialDown) { - if (mExternalStylusState.pressure != 0.0f) { - ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion"); - mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit(); - } else if (timeout) { - ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus."); - mFusedStylusPointerId.reset(); - mExternalStylusFusionTimeout = LLONG_MAX; - } else { - if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; - } - ALOGD_IF(DEBUG_STYLUS_FUSION, - "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)", - mExternalStylusFusionTimeout); - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - return true; - } - } - // Check if the stylus pointer has gone up. if (mFusedStylusPointerId && !state.rawPointerData.touchingIdBits.hasBit(*mFusedStylusPointerId)) { ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up"); mFusedStylusPointerId.reset(); + return false; + } + + const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + state.rawPointerData.pointerCount != 0; + if (!initialDown) { + return false; + } + + if (!mExternalStylusState.pressure) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus does not support pressure, no pointer fusion needed"); + return false; + } + + if (*mExternalStylusState.pressure != 0.0f) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion"); + mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit(); + return false; + } + + if (timeout) { + ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus."); + mFusedStylusPointerId.reset(); + mExternalStylusFusionTimeout = LLONG_MAX; + return false; } - return false; + // We are waiting for the external stylus to report a pressure value. Withhold touches from + // being processed until we either get pressure data or timeout. + if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; + } + ALOGD_IF(DEBUG_STYLUS_FUSION, + "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)", + mExternalStylusFusionTimeout); + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + return true; } std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) { @@ -1782,7 +1796,7 @@ std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) { std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) { std::list<NotifyArgs> out; const bool buttonsChanged = mExternalStylusState.buttons != state.buttons; - mExternalStylusState.copyFrom(state); + mExternalStylusState = state; if (mFusedStylusPointerId || mExternalStylusFusionTimeout != LLONG_MAX || buttonsChanged) { // The following three cases are handled here: // - We're in the middle of a fused stream of data; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index d2faf3fe1d..85af1f785a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -795,6 +795,8 @@ private: [[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags); + // Attempts to assign a pointer id to the external stylus. Returns true if the state should be + // withheld from further processing while waiting for data from the stylus. bool assignExternalStylusId(const RawState& state, bool timeout); void applyExternalStylusButtonState(nsecs_t when); void applyExternalStylusTouchState(nsecs_t when); diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp index 1891205ed6..bc23a8ec91 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp @@ -167,4 +167,8 @@ bool TouchButtonAccumulator::hasStylus() const { return mHaveStylus; } +bool TouchButtonAccumulator::hasButtonTouch() const { + return mHaveBtnTouch; +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h index 65b0a62747..c2de23cee4 100644 --- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h @@ -40,6 +40,7 @@ public: bool isToolActive() const; bool isHovering() const; bool hasStylus() const; + bool hasButtonTouch() const; private: bool mHaveBtnTouch{}; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index fe36d42db7..3ce03f34c1 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -3156,7 +3156,10 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { // Set a pressure value of 0 on the stylus. It doesn't generate any events. const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX; + // Send a non-zero value first to prevent the kernel from consuming the zero event. + stylus->setPressure(100); stylus->setPressure(0); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); // Start a finger gesture. The touch device will withhold generating any touches for // up to 72 milliseconds while waiting for pressure data from the external stylus. @@ -3203,6 +3206,45 @@ TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) { ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled()); } +TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Create an external stylus device that does not support pressure. It should not affect any + // touch pointers. + std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto stylusInfo = findDeviceByName(stylus->getName()); + ASSERT_TRUE(stylusInfo); + + ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources()); + + const auto touchscreenId = mDeviceInfo.getId(); + + // Start a finger gesture and ensure a finger pointer is generated for it, without waiting for + // pressure data from the external stylus. + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendToolType(MT_TOOL_FINGER); + mDevice->sendDown(centerPoint); + auto waitUntil = std::chrono::system_clock::now() + + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT)); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE( + mTestListener + ->assertNotifyMotionWasCalled(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithToolType( + AMOTION_EVENT_TOOL_TYPE_FINGER), + WithButtonState(0), + WithDeviceId(touchscreenId), + WithPressure(1.f)), + waitUntil)); + + // The external stylus did not generate any events. + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled()); +} + // --- InputDeviceTest --- class InputDeviceTest : public testing::Test { protected: diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index a1299eedb4..2801072b6a 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -69,16 +69,18 @@ void TestInputListener::assertNotifyKeyWasNotCalled() { ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called.")); } -void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) { +void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs, + std::optional<TimePoint> waitUntil) { ASSERT_NO_FATAL_FAILURE( assertCalled<NotifyMotionArgs>(outEventArgs, - "Expected notifyMotion() to have been called.")); + "Expected notifyMotion() to have been called.", + waitUntil)); } void TestInputListener::assertNotifyMotionWasCalled( - const ::testing::Matcher<NotifyMotionArgs>& matcher) { + const ::testing::Matcher<NotifyMotionArgs>& matcher, std::optional<TimePoint> waitUntil) { NotifyMotionArgs outEventArgs; - ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs)); + ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs, waitUntil)); ASSERT_THAT(outEventArgs, matcher); } @@ -119,15 +121,18 @@ void TestInputListener::assertNotifyCaptureWasNotCalled() { } template <class NotifyArgsType> -void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) { +void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message, + std::optional<TimePoint> waitUntil) { std::unique_lock<std::mutex> lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); if (queue.empty()) { - const bool eventReceived = - mCondition.wait_for(lock, mEventHappenedTimeout, - [&queue]() REQUIRES(mLock) { return !queue.empty(); }); + const auto time = + waitUntil.value_or(std::chrono::system_clock::now() + mEventHappenedTimeout); + const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) { + return !queue.empty(); + }); if (!eventReceived) { FAIL() << "Timed out waiting for event: " << message.c_str(); } diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index c53f8e0497..9665f702a1 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -50,9 +50,11 @@ public: void assertNotifyKeyWasNotCalled(); - void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr); + void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr, + std::optional<TimePoint> waitUntil = {}); - void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher); + void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher, + std::optional<TimePoint> waitUntil = {}); void assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil = {}); @@ -65,7 +67,8 @@ public: private: template <class NotifyArgsType> - void assertCalled(NotifyArgsType* outEventArgs, std::string message); + void assertCalled(NotifyArgsType* outEventArgs, std::string message, + std::optional<TimePoint> waitUntil = {}); template <class NotifyArgsType> void assertNotCalled(std::string message, std::optional<TimePoint> timeout = {}); |