From 98318de954ba00293cfd179266f09f266dc1c82b Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 19 May 2021 16:45:23 -0500 Subject: Renamed and moved InputWindow and related files In preparation for the hierarchy listener interface, moved the InputWindow structs into libgui and have libinput dependant on libgui. Also renamed InputWindow to exclude Input since it will be used for more generic purposes. Test: Builds and flashes Bug: 188792659 Change-Id: I24262cbc14d409c00273de0024a672394a959e5f --- services/inputflinger/InputManager.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'services/inputflinger/InputManager.cpp') diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index a50e5c70f7..e81dcfe4f1 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -31,6 +31,10 @@ namespace android { +using gui::FocusRequest; +using gui::WindowInfo; +using gui::WindowInfoHandle; + static int32_t exceptionCodeFromStatusT(status_t status) { switch (status) { case OK: @@ -110,9 +114,9 @@ sp InputManager::getDispatcher() { return mDispatcher; } -class BinderWindowHandle : public InputWindowHandle { +class BinderWindowHandle : public WindowInfoHandle { public: - BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; } + BinderWindowHandle(const WindowInfo& info) { mInfo = info; } bool updateInfo() override { return true; @@ -120,13 +124,13 @@ public: }; binder::Status InputManager::setInputWindows( - const std::vector& infos, + const std::vector& infos, const sp& setInputWindowsListener) { - std::unordered_map>> handlesPerDisplay; + std::unordered_map>> handlesPerDisplay; - std::vector> handles; + std::vector> handles; for (const auto& info : infos) { - handlesPerDisplay.emplace(info.displayId, std::vector>()); + handlesPerDisplay.emplace(info.displayId, std::vector>()); handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info)); } mDispatcher->setInputWindows(handlesPerDisplay); -- cgit v1.2.3-59-g8ed1b From 18050095be7d5b7d13861d721f2daf26605ed22b Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 1 Sep 2021 13:32:49 -0700 Subject: Remove RefBase from InputListener interface We don't need refbase for inputlisteners. Remove it, and switch to references, which cannot be null. This way, we can avoid dereferencing the pointers without checking for nullness. Bug: 198472780 Test: atest inputflinger_tests Change-Id: I2f469fd268472c7e78d36812353cff5c52a90163 --- services/inputflinger/InputClassifier.cpp | 20 ++++----- services/inputflinger/InputClassifier.h | 9 ++-- services/inputflinger/InputListener.cpp | 37 ++++++++-------- services/inputflinger/InputManager.cpp | 16 +++---- services/inputflinger/InputManager.h | 18 ++++---- .../benchmarks/InputDispatcher_benchmarks.cpp | 19 ++++---- services/inputflinger/dispatcher/Connection.h | 1 + services/inputflinger/dispatcher/DragState.h | 2 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 13 ++++-- services/inputflinger/dispatcher/InputDispatcher.h | 26 +++++++---- .../dispatcher/InputDispatcherFactory.cpp | 4 +- services/inputflinger/dispatcher/InputTarget.h | 1 - .../dispatcher/include/InputDispatcherFactory.h | 4 +- .../dispatcher/include/InputDispatcherInterface.h | 6 +-- services/inputflinger/include/InputListener.h | 34 +++++++-------- services/inputflinger/include/InputReaderBase.h | 5 +-- services/inputflinger/include/InputReaderFactory.h | 5 +-- .../include/PointerControllerInterface.h | 1 - services/inputflinger/reader/InputDevice.cpp | 2 +- services/inputflinger/reader/InputReader.cpp | 23 ++++------ .../inputflinger/reader/InputReaderFactory.cpp | 6 +-- services/inputflinger/reader/include/InputReader.h | 7 ++- .../reader/include/InputReaderContext.h | 2 +- .../reader/mapper/CursorInputMapper.cpp | 12 +++--- services/inputflinger/reader/mapper/InputMapper.h | 2 +- .../reader/mapper/JoystickInputMapper.cpp | 2 +- .../reader/mapper/KeyboardInputMapper.cpp | 2 +- .../reader/mapper/RotaryEncoderInputMapper.cpp | 2 +- .../reader/mapper/SensorInputMapper.cpp | 2 +- .../reader/mapper/SwitchInputMapper.cpp | 2 +- .../reader/mapper/TouchCursorInputMapperCommon.h | 2 +- .../reader/mapper/TouchInputMapper.cpp | 22 +++++----- .../reader/mapper/VibratorInputMapper.cpp | 4 +- .../inputflinger/tests/InputClassifier_test.cpp | 24 ++++------- .../inputflinger/tests/InputDispatcher_test.cpp | 50 +++++++++++----------- services/inputflinger/tests/InputReader_test.cpp | 48 +++++++++++---------- services/inputflinger/tests/TestInputListener.h | 4 +- 37 files changed, 215 insertions(+), 224 deletions(-) (limited to 'services/inputflinger/InputManager.cpp') diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index a9cbd5ad02..29d8a0f441 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -345,7 +345,7 @@ void InputClassifier::HalDeathRecipient::serviceDied( // --- InputClassifier --- -InputClassifier::InputClassifier(const sp& listener) +InputClassifier::InputClassifier(InputListenerInterface& listener) : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {} void InputClassifier::setMotionClassifierEnabled(bool enabled) { @@ -369,12 +369,12 @@ void InputClassifier::setMotionClassifierEnabled(bool enabled) { void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { // pass through - mListener->notifyConfigurationChanged(args); + mListener.notifyConfigurationChanged(args); } void InputClassifier::notifyKey(const NotifyKeyArgs* args) { // pass through - mListener->notifyKey(args); + mListener.notifyKey(args); } void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { @@ -382,28 +382,28 @@ void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { // MotionClassifier is only used for touch events, for now const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); if (!sendToMotionClassifier) { - mListener->notifyMotion(args); + mListener.notifyMotion(args); return; } NotifyMotionArgs newArgs(*args); newArgs.classification = mMotionClassifier->classify(newArgs); - mListener->notifyMotion(&newArgs); + mListener.notifyMotion(&newArgs); } void InputClassifier::notifySensor(const NotifySensorArgs* args) { // pass through - mListener->notifySensor(args); + mListener.notifySensor(args); } void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) { // pass through - mListener->notifyVibratorState(args); + mListener.notifyVibratorState(args); } void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { // pass through - mListener->notifySwitch(args); + mListener.notifySwitch(args); } void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { @@ -412,12 +412,12 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mMotionClassifier->reset(*args); } // continue to next stage - mListener->notifyDeviceReset(args); + mListener.notifyDeviceReset(args); } void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { // pass through - mListener->notifyPointerCaptureChanged(args); + mListener.notifyPointerCaptureChanged(args); } void InputClassifier::setMotionClassifier( diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 1eef02006e..deeae7c6f4 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -18,7 +18,6 @@ #define _UI_INPUT_CLASSIFIER_H #include -#include #include #include @@ -88,7 +87,7 @@ public: * Base interface for an InputListener stage. * Provides classification to events. */ -class InputClassifierInterface : public virtual RefBase, public InputListenerInterface { +class InputClassifierInterface : public InputListenerInterface { public: virtual void setMotionClassifierEnabled(bool enabled) = 0; /** @@ -96,7 +95,7 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void dump(std::string& dump) = 0; -protected: + InputClassifierInterface() { } virtual ~InputClassifierInterface() { } }; @@ -223,7 +222,7 @@ private: */ class InputClassifier : public InputClassifierInterface { public: - explicit InputClassifier(const sp& listener); + explicit InputClassifier(InputListenerInterface& listener); virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -245,7 +244,7 @@ private: // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; // The next stage to pass input events to - sp mListener; + InputListenerInterface& mListener; std::unique_ptr mMotionClassifier GUARDED_BY(mLock); std::thread mInitializeMotionClassifierThread; diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 71b0f5fec9..158d0ebd58 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -44,8 +44,8 @@ bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChanged return id == rhs.id && eventTime == rhs.eventTime; } -void NotifyConfigurationChangedArgs::notify(const sp& listener) const { - listener->notifyConfigurationChanged(this); +void NotifyConfigurationChangedArgs::notify(InputListenerInterface& listener) const { + listener.notifyConfigurationChanged(this); } // --- NotifyKeyArgs --- @@ -89,8 +89,8 @@ bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const { downTime == rhs.downTime; } -void NotifyKeyArgs::notify(const sp& listener) const { - listener->notifyKey(this); +void NotifyKeyArgs::notify(InputListenerInterface& listener) const { + listener.notifyKey(this); } // --- NotifyMotionArgs --- @@ -188,8 +188,8 @@ bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { return true; } -void NotifyMotionArgs::notify(const sp& listener) const { - listener->notifyMotion(this); +void NotifyMotionArgs::notify(InputListenerInterface& listener) const { + listener.notifyMotion(this); } // --- NotifySwitchArgs --- @@ -212,8 +212,8 @@ bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const { switchValues == rhs.switchValues && switchMask == rhs.switchMask; } -void NotifySwitchArgs::notify(const sp& listener) const { - listener->notifySwitch(this); +void NotifySwitchArgs::notify(InputListenerInterface& listener) const { + listener.notifySwitch(this); } // --- NotifySensorArgs --- @@ -247,8 +247,8 @@ bool NotifySensorArgs::operator==(const NotifySensorArgs rhs) const { hwTimestamp == rhs.hwTimestamp && values == rhs.values; } -void NotifySensorArgs::notify(const sp& listener) const { - listener->notifySensor(this); +void NotifySensorArgs::notify(InputListenerInterface& listener) const { + listener.notifySensor(this); } // --- NotifyVibratorStateArgs --- @@ -265,8 +265,8 @@ bool NotifyVibratorStateArgs::operator==(const NotifyVibratorStateArgs rhs) cons isOn == rhs.isOn; } -void NotifyVibratorStateArgs::notify(const sp& listener) const { - listener->notifyVibratorState(this); +void NotifyVibratorStateArgs::notify(InputListenerInterface& listener) const { + listener.notifyVibratorState(this); } // --- NotifyDeviceResetArgs --- @@ -281,8 +281,8 @@ bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const { return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId; } -void NotifyDeviceResetArgs::notify(const sp& listener) const { - listener->notifyDeviceReset(this); +void NotifyDeviceResetArgs::notify(InputListenerInterface& listener) const { + listener.notifyDeviceReset(this); } // --- NotifyPointerCaptureChangedArgs --- @@ -299,8 +299,8 @@ bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChang return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request; } -void NotifyPointerCaptureChangedArgs::notify(const sp& listener) const { - listener->notifyPointerCaptureChanged(this); +void NotifyPointerCaptureChangedArgs::notify(InputListenerInterface& listener) const { + listener.notifyPointerCaptureChanged(this); } // --- QueuedInputListener --- @@ -312,9 +312,8 @@ static inline void traceEvent(const char* functionName, int32_t id) { } } -QueuedInputListener::QueuedInputListener(const sp& innerListener) : - mInnerListener(innerListener) { -} +QueuedInputListener::QueuedInputListener(InputListenerInterface& innerListener) + : mInnerListener(innerListener) {} QueuedInputListener::~QueuedInputListener() { size_t count = mArgsQueue.size(); diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 7b3658dfde..221e193136 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -58,8 +58,8 @@ InputManager::InputManager( const sp& readerPolicy, const sp& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); - mClassifier = new InputClassifier(mDispatcher); - mReader = createInputReader(readerPolicy, mClassifier); + mClassifier = std::make_unique(*mDispatcher); + mReader = createInputReader(readerPolicy, *mClassifier); } InputManager::~InputManager() { @@ -102,16 +102,16 @@ status_t InputManager::stop() { return status; } -sp InputManager::getReader() { - return mReader; +InputReaderInterface& InputManager::getReader() { + return *mReader; } -sp InputManager::getClassifier() { - return mClassifier; +InputClassifierInterface& InputManager::getClassifier() { + return *mClassifier; } -sp InputManager::getDispatcher() { - return mDispatcher; +InputDispatcherInterface& InputManager::getDispatcher() { + return *mDispatcher; } // Used by tests only. diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index f053568143..a6baf2f56d 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -75,13 +75,13 @@ public: virtual status_t stop() = 0; /* Gets the input reader. */ - virtual sp getReader() = 0; + virtual InputReaderInterface& getReader() = 0; /* Gets the input classifier */ - virtual sp getClassifier() = 0; + virtual InputClassifierInterface& getClassifier() = 0; /* Gets the input dispatcher. */ - virtual sp getDispatcher() = 0; + virtual InputDispatcherInterface& getDispatcher() = 0; }; class InputManager : public InputManagerInterface, public BnInputFlinger { @@ -96,9 +96,9 @@ public: status_t start() override; status_t stop() override; - sp getReader() override; - sp getClassifier() override; - sp getDispatcher() override; + InputReaderInterface& getReader() override; + InputClassifierInterface& getClassifier() override; + InputDispatcherInterface& getDispatcher() override; status_t dump(int fd, const Vector& args) override; binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; @@ -106,11 +106,11 @@ public: binder::Status setFocusedWindow(const gui::FocusRequest&) override; private: - sp mReader; + std::unique_ptr mReader; - sp mClassifier; + std::unique_ptr mClassifier; - sp mDispatcher; + std::unique_ptr mDispatcher; }; } // namespace android diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 68d25f9ec7..41e9ce2e41 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -21,6 +21,7 @@ #include #include "../dispatcher/InputDispatcher.h" +using android::base::Result; using android::gui::WindowInfo; using android::gui::WindowInfoHandle; using android::os::IInputConstants; @@ -162,15 +163,15 @@ public: } protected: - explicit FakeInputReceiver(const sp& dispatcher, const std::string name) - : mDispatcher(dispatcher) { - mClientChannel = *mDispatcher->createInputChannel(name); + explicit FakeInputReceiver(InputDispatcher& dispatcher, const std::string name) { + Result> channelResult = dispatcher.createInputChannel(name); + LOG_ALWAYS_FATAL_IF(!channelResult.ok()); + mClientChannel = std::move(*channelResult); mConsumer = std::make_unique(mClientChannel); } virtual ~FakeInputReceiver() {} - sp mDispatcher; std::shared_ptr mClientChannel; std::unique_ptr mConsumer; PreallocatedInputEventFactory mEventFactory; @@ -182,7 +183,7 @@ public: static const int32_t HEIGHT = 200; FakeWindowHandle(const std::shared_ptr& inputApplicationHandle, - const sp& dispatcher, const std::string name) + InputDispatcher& dispatcher, const std::string name) : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) { inputApplicationHandle->updateInfo(); updateInfo(); @@ -272,13 +273,13 @@ static NotifyMotionArgs generateMotionArgs() { static void benchmarkNotifyMotion(benchmark::State& state) { // Create dispatcher sp fakePolicy = new FakeInputDispatcherPolicy(); - sp dispatcher = new InputDispatcher(fakePolicy); + std::unique_ptr dispatcher = std::make_unique(fakePolicy); dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); dispatcher->start(); // Create a window that will receive motion events std::shared_ptr application = std::make_shared(); - sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + sp window = new FakeWindowHandle(application, *dispatcher, "Fake Window"); dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -306,13 +307,13 @@ static void benchmarkNotifyMotion(benchmark::State& state) { static void benchmarkInjectMotion(benchmark::State& state) { // Create dispatcher sp fakePolicy = new FakeInputDispatcherPolicy(); - sp dispatcher = new InputDispatcher(fakePolicy); + std::unique_ptr dispatcher = std::make_unique(fakePolicy); dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); dispatcher->start(); // Create a window that will receive motion events std::shared_ptr application = std::make_shared(); - sp window = new FakeWindowHandle(application, dispatcher, "Fake Window"); + sp window = new FakeWindowHandle(application, *dispatcher, "Fake Window"); dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index c4262ad2d8..ba602831eb 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -20,6 +20,7 @@ #include "InputState.h" #include +#include #include namespace android::inputdispatcher { diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h index b3c5709cbc..4636820538 100644 --- a/services/inputflinger/dispatcher/DragState.h +++ b/services/inputflinger/dispatcher/DragState.h @@ -18,7 +18,7 @@ #define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H #include -#include +#include #include namespace android { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f094fee282..695dd17d38 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -562,6 +562,9 @@ InputDispatcher::InputDispatcher(const sp& polic mLooper = new Looper(false); mReporter = createInputReporter(); + mWindowInfoListener = new DispatcherWindowListener(*this); + SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener); + mKeyRepeatState.lastKeyEntry = nullptr; policy->getDispatcherConfiguration(&mConfig); @@ -582,10 +585,6 @@ InputDispatcher::~InputDispatcher() { } } -void InputDispatcher::onFirstRef() { - SurfaceComposerClient::getDefault()->addWindowInfosListener(this); -} - status_t InputDispatcher::start() { if (mThread) { return ALREADY_EXISTS; @@ -6284,4 +6283,10 @@ bool InputDispatcher::shouldDropInput( return false; } +void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged( + const std::vector& windowInfos, + const std::vector& displayInfos) { + mDispatcher.onWindowInfosChanged(windowInfos, displayInfos); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 2282d917b7..55ca6c943e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include @@ -81,12 +80,10 @@ class Connection; * * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. */ -class InputDispatcher : public android::InputDispatcherInterface, public gui::WindowInfosListener { -protected: - ~InputDispatcher() override; - +class InputDispatcher : public android::InputDispatcherInterface { public: explicit InputDispatcher(const sp& policy); + ~InputDispatcher() override; void dump(std::string& dump) override; void monitor() override; @@ -143,8 +140,9 @@ public: void displayRemoved(int32_t displayId) override; - void onWindowInfosChanged(const std::vector&, - const std::vector&) override; + // Public because it's also used by tests to simulate the WindowInfosListener callback + void onWindowInfosChanged(const std::vector& windowInfos, + const std::vector& displayInfos); private: enum class DropReason { @@ -339,6 +337,18 @@ private: float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock); android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock); + class DispatcherWindowListener : public gui::WindowInfosListener { + public: + explicit DispatcherWindowListener(InputDispatcher& dispatcher) : mDispatcher(dispatcher){}; + void onWindowInfosChanged( + const std::vector& windowInfos, + const std::vector& displayInfos) override; + + private: + InputDispatcher& mDispatcher; + }; + sp mWindowInfoListener; + std::unordered_map>> mWindowHandlesByDisplay GUARDED_BY(mLock); std::unordered_map mDisplayInfos @@ -663,8 +673,6 @@ private: sp mReporter; sp mCompatService; - - void onFirstRef() override; }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp index 8d7fa7573b..bca1600db9 100644 --- a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp +++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp @@ -19,9 +19,9 @@ namespace android { -sp createInputDispatcher( +std::unique_ptr createInputDispatcher( const sp& policy) { - return new android::inputdispatcher::InputDispatcher(policy); + return std::make_unique(policy); } } // namespace android diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 5b76eee343..0725389ed6 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -21,7 +21,6 @@ #include #include #include -#include namespace android::inputdispatcher { diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h index a359557f80..38d0c32529 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h @@ -17,7 +17,7 @@ #ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H #define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H -#include +#include #include "InputDispatcherInterface.h" #include "InputDispatcherPolicyInterface.h" @@ -25,7 +25,7 @@ namespace android { // This factory method is used to encapsulate implementation details in internal header files. -sp createInputDispatcher( +std::unique_ptr createInputDispatcher( const sp& policy); } // namespace android diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index b74f30466d..714e7a0ad9 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -33,12 +33,10 @@ namespace android { /* Notifies the system about input events generated by the input reader. * The dispatcher is expected to be mostly asynchronous. */ -class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { -protected: +class InputDispatcherInterface : public InputListenerInterface { +public: InputDispatcherInterface() {} virtual ~InputDispatcherInterface() {} - -public: /* Dumps the state of the input dispatcher. * * This method may be called on any thread (usually by the input manager). */ diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index fe74214b36..db6310478c 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -22,7 +22,6 @@ #include #include #include -#include namespace android { @@ -40,7 +39,7 @@ struct NotifyArgs { virtual ~NotifyArgs() { } - virtual void notify(const sp& listener) const = 0; + virtual void notify(InputListenerInterface& listener) const = 0; }; @@ -57,7 +56,7 @@ struct NotifyConfigurationChangedArgs : public NotifyArgs { virtual ~NotifyConfigurationChangedArgs() { } - virtual void notify(const sp& listener) const; + void notify(InputListenerInterface& listener) const override; }; @@ -88,7 +87,7 @@ struct NotifyKeyArgs : public NotifyArgs { virtual ~NotifyKeyArgs() { } - virtual void notify(const sp& listener) const; + void notify(InputListenerInterface& listener) const override; }; @@ -142,7 +141,7 @@ struct NotifyMotionArgs : public NotifyArgs { bool operator==(const NotifyMotionArgs& rhs) const; - virtual void notify(const sp& listener) const; + void notify(InputListenerInterface& listener) const override; }; /* Describes a sensor event. */ @@ -167,7 +166,7 @@ struct NotifySensorArgs : public NotifyArgs { ~NotifySensorArgs() override {} - void notify(const sp& listener) const override; + void notify(InputListenerInterface& listener) const override; }; /* Describes a switch event. */ @@ -187,7 +186,7 @@ struct NotifySwitchArgs : public NotifyArgs { virtual ~NotifySwitchArgs() { } - virtual void notify(const sp& listener) const; + void notify(InputListenerInterface& listener) const override; }; @@ -206,7 +205,7 @@ struct NotifyDeviceResetArgs : public NotifyArgs { virtual ~NotifyDeviceResetArgs() { } - virtual void notify(const sp& listener) const; + void notify(InputListenerInterface& listener) const override; }; /* Describes a change in the state of Pointer Capture. */ @@ -224,7 +223,7 @@ struct NotifyPointerCaptureChangedArgs : public NotifyArgs { virtual ~NotifyPointerCaptureChangedArgs() {} - virtual void notify(const sp& listener) const; + void notify(InputListenerInterface& listener) const override; }; /* Describes a vibrator state event. */ @@ -242,18 +241,19 @@ struct NotifyVibratorStateArgs : public NotifyArgs { virtual ~NotifyVibratorStateArgs() {} - virtual void notify(const sp& listener) const; + void notify(InputListenerInterface& listener) const override; }; /* * The interface used by the InputReader to notify the InputListener about input events. */ -class InputListenerInterface : public virtual RefBase { -protected: +class InputListenerInterface { +public: InputListenerInterface() { } + InputListenerInterface(const InputListenerInterface&) = delete; + InputListenerInterface& operator=(const InputListenerInterface&) = delete; virtual ~InputListenerInterface() { } -public: virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0; virtual void notifyKey(const NotifyKeyArgs* args) = 0; virtual void notifyMotion(const NotifyMotionArgs* args) = 0; @@ -264,17 +264,15 @@ public: virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0; }; - /* * An implementation of the listener interface that queues up and defers dispatch * of decoded events until flushed. */ class QueuedInputListener : public InputListenerInterface { -protected: - virtual ~QueuedInputListener(); public: - explicit QueuedInputListener(const sp& innerListener); + explicit QueuedInputListener(InputListenerInterface& innerListener); + virtual ~QueuedInputListener(); virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -288,7 +286,7 @@ public: void flush(); private: - sp mInnerListener; + InputListenerInterface& mInnerListener; std::vector mArgsQueue; }; diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 3c8ac1cf7b..1aab856205 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -51,12 +51,11 @@ namespace android { * The implementation must guarantee thread safety for this interface. However, since the input * listener is NOT thread safe, all calls to the listener must happen from the same thread. */ -class InputReaderInterface : public virtual RefBase { -protected: +class InputReaderInterface { +public: InputReaderInterface() { } virtual ~InputReaderInterface() { } -public: /* Dumps the state of the input reader. * * This method may be called on any thread (usually by the input manager). */ diff --git a/services/inputflinger/include/InputReaderFactory.h b/services/inputflinger/include/InputReaderFactory.h index 9db6233d28..dad14d6c92 100644 --- a/services/inputflinger/include/InputReaderFactory.h +++ b/services/inputflinger/include/InputReaderFactory.h @@ -22,8 +22,7 @@ class InputReaderInterface; class InputReaderPolicyInterface; class InputListenerInterface; -sp createInputReader( - const sp& policy, - const sp& listener); +std::unique_ptr createInputReader( + const sp& policy, InputListenerInterface& listener); } // namespace android diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 85d7247915..b1069497d3 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -20,7 +20,6 @@ #include #include #include -#include namespace android { diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 36344b5c3a..d07be3b025 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -574,7 +574,7 @@ void InputDevice::bumpGeneration() { void InputDevice::notifyReset(nsecs_t when) { NotifyDeviceResetArgs args(mContext->getNextId(), when, mId); - mContext->getListener()->notifyDeviceReset(&args); + mContext->getListener().notifyDeviceReset(&args); } std::optional InputDevice::getAssociatedDisplayId() { diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 86d4c26469..0b632f7fe2 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -42,10 +42,11 @@ namespace android { InputReader::InputReader(std::shared_ptr eventHub, const sp& policy, - const sp& listener) + InputListenerInterface& listener) : mContext(this), mEventHub(eventHub), mPolicy(policy), + mQueuedListener(listener), mGlobalMetaState(0), mLedMetaState(AMETA_NUM_LOCK_ON), mGeneration(1), @@ -53,14 +54,8 @@ InputReader::InputReader(std::shared_ptr eventHub, mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { - mQueuedListener = new QueuedInputListener(listener); - - { // acquire lock - std::scoped_lock _l(mLock); - - refreshConfigurationLocked(0); - updateGlobalMetaStateLocked(); - } // release lock + refreshConfigurationLocked(0); + updateGlobalMetaStateLocked(); } InputReader::~InputReader() {} @@ -144,7 +139,7 @@ void InputReader::loopOnce() { // resulting in a deadlock. This situation is actually quite plausible because the // listener is actually the input dispatcher, which calls into the window manager, // which occasionally calls into the input reader. - mQueuedListener->flush(); + mQueuedListener.flush(); } void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { @@ -340,7 +335,7 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) { // Enqueue configuration changed. NotifyConfigurationChangedArgs args(mContext.getNextId(), when); - mQueuedListener->notifyConfigurationChanged(&args); + mQueuedListener.notifyConfigurationChanged(&args); } void InputReader::refreshConfigurationLocked(uint32_t changes) { @@ -374,7 +369,7 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest; const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now, mCurrentPointerCaptureRequest); - mQueuedListener->notifyPointerCaptureChanged(&args); + mQueuedListener.notifyPointerCaptureChanged(&args); } } } @@ -952,8 +947,8 @@ InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { return mReader->mPolicy.get(); } -InputListenerInterface* InputReader::ContextImpl::getListener() { - return mReader->mQueuedListener.get(); +InputListenerInterface& InputReader::ContextImpl::getListener() { + return mReader->mQueuedListener; } EventHubInterface* InputReader::ContextImpl::getEventHub() { diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp index a8971411a4..2d9ffc3ec8 100644 --- a/services/inputflinger/reader/InputReaderFactory.cpp +++ b/services/inputflinger/reader/InputReaderFactory.cpp @@ -20,9 +20,9 @@ namespace android { -sp createInputReader(const sp& policy, - const sp& listener) { - return new InputReader(std::make_unique(), policy, listener); +std::unique_ptr createInputReader( + const sp& policy, InputListenerInterface& listener) { + return std::make_unique(std::make_unique(), policy, listener); } } // namespace android \ No newline at end of file diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index e44aa0fe27..fb1d16695d 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -52,8 +52,7 @@ struct StylusState; class InputReader : public InputReaderInterface { public: InputReader(std::shared_ptr eventHub, - const sp& policy, - const sp& listener); + const sp& policy, InputListenerInterface& listener); virtual ~InputReader(); void dump(std::string& dump) override; @@ -143,7 +142,7 @@ protected: void dispatchExternalStylusState(const StylusState& outState) REQUIRES(mReader->mLock) override; InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override; - InputListenerInterface* getListener() REQUIRES(mReader->mLock) override; + InputListenerInterface& getListener() REQUIRES(mReader->mLock) override; EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override; int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override; void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override; @@ -164,7 +163,7 @@ private: // in parallel to passing it to the InputReader. std::shared_ptr mEventHub; sp mPolicy; - sp mQueuedListener; + QueuedInputListener mQueuedListener; InputReaderConfiguration mConfig GUARDED_BY(mLock); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index dc807f7886..823d160f86 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -55,7 +55,7 @@ public: virtual void dispatchExternalStylusState(const StylusState& outState) = 0; virtual InputReaderPolicyInterface* getPolicy() = 0; - virtual InputListenerInterface* getListener() = 0; + virtual InputListenerInterface& getListener() = 0; virtual EventHubInterface* getEventHub() = 0; virtual int32_t getNextId() = 0; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 2ac41b1e67..f3d7cdc48b 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -176,7 +176,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* bumpGeneration(); if (changes) { NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); - getListener()->notifyDeviceReset(&args); + getListener().notifyDeviceReset(&args); } } @@ -424,7 +424,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&releaseArgs); + getListener().notifyMotion(&releaseArgs); } } @@ -434,7 +434,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); if (buttonsPressed) { BitSet32 pressed(buttonsPressed); @@ -449,7 +449,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&pressArgs); + getListener().notifyMotion(&pressArgs); } } @@ -464,7 +464,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&hoverArgs); + getListener().notifyMotion(&hoverArgs); } // Send scroll events. @@ -479,7 +479,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); + getListener().notifyMotion(&scrollArgs); } } diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index d9adc0fbc6..f1c0e5a570 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -48,7 +48,7 @@ public: inline const std::string getDeviceName() { return mDeviceContext.getName(); } inline InputReaderContext* getContext() { return mDeviceContext.getContext(); } inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); } - inline InputListenerInterface* getListener() { return getContext()->getListener(); } + inline InputListenerInterface& getListener() { return getContext()->getListener(); } virtual uint32_t getSources() = 0; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 0dc312ecd2..6bdb121336 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -344,7 +344,7 @@ void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) { &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 52af38dcaf..a8602a4c02 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -354,7 +354,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); - getListener()->notifyKey(&args); + getListener().notifyKey(&args); } ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index e9d0189f1f..b83a8fcf4e 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -127,7 +127,7 @@ void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); + getListener().notifyMotion(&scrollArgs); } mRotaryEncoderScrollAccumulator.finishSync(); diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index a1bd548403..677a372997 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -411,7 +411,7 @@ void SensorInputMapper::sync(nsecs_t when, bool force) { sensor.sensorInfo.accuracy /* accuracyChanged */, timestamp /* hwTimestamp */, values); - getListener()->notifySensor(&args); + getListener().notifySensor(&args); sensor.lastSampleTimeNs = timestamp; sensor.accuracy = sensor.sensorInfo.accuracy; } diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 4f736810bc..3237824994 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -58,7 +58,7 @@ void SwitchInputMapper::sync(nsecs_t when) { uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/, updatedSwitchValues, mUpdatedSwitchMask); - getListener()->notifySwitch(&args); + getListener().notifySwitch(&args); mUpdatedSwitchMask = 0; } diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 7347b2cec4..96c4378644 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -110,7 +110,7 @@ static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nse !(currentButtonState & buttonState))) { NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId, policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); - context->getListener()->notifyKey(&args); + context->getListener().notifyKey(&args); } } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 22a0b575db..fd33df9347 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -399,7 +399,7 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c // 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. NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); - getListener()->notifyDeviceReset(&args); + getListener().notifyDeviceReset(&args); } } @@ -1929,7 +1929,7 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32 NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); - getListener()->notifyKey(&args); + getListener().notifyKey(&args); } void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { @@ -2621,7 +2621,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } // Update state. @@ -3536,7 +3536,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } if (mPointerSimple.hovering && !hovering) { @@ -3550,7 +3550,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } if (down) { @@ -3566,7 +3566,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } // Send move. @@ -3577,7 +3577,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } if (hovering) { @@ -3592,7 +3592,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } // Send hover move. @@ -3603,7 +3603,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { @@ -3625,7 +3625,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } // Save state. @@ -3703,7 +3703,7 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t p MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime, std::move(frames)); - getListener()->notifyMotion(&args); + getListener().notifyMotion(&args); } bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 3df6f36db5..8c7879ba27 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -54,7 +54,7 @@ void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t rep // Request InputReader to notify InputManagerService for vibration started. NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true); - getListener()->notifyVibratorState(&args); + getListener().notifyVibratorState(&args); nextStep(); } @@ -133,7 +133,7 @@ void VibratorInputMapper::stopVibrating() { // Request InputReader to notify InputManagerService for vibration complete. NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false); - getListener()->notifyVibratorState(&args); + getListener().notifyVibratorState(&args); } void VibratorInputMapper::dump(std::string& dump) { diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 3a9994eed9..f13187ddb7 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -56,18 +56,10 @@ static NotifyMotionArgs generateBasicMotionArgs() { class InputClassifierTest : public testing::Test { protected: - sp mClassifier; - sp mTestListener; + TestInputListener mTestListener; + std::unique_ptr mClassifier; - virtual void SetUp() override { - mTestListener = new TestInputListener(); - mClassifier = new InputClassifier(mTestListener); - } - - virtual void TearDown() override { - mClassifier.clear(); - mTestListener.clear(); - } + void SetUp() override { mClassifier = std::make_unique(mTestListener); } }; /** @@ -80,7 +72,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyConfigurationChangedArgs) { mClassifier->notifyConfigurationChanged(&args); NotifyConfigurationChangedArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } @@ -93,7 +85,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) { mClassifier->notifyKey(&args); NotifyKeyArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } @@ -106,7 +98,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyMotionArgs) { NotifyMotionArgs motionArgs = generateBasicMotionArgs(); mClassifier->notifyMotion(&motionArgs); NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args)); ASSERT_EQ(motionArgs, args); } @@ -120,7 +112,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifySwitchArgs) { mClassifier->notifySwitch(&args); NotifySwitchArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } @@ -133,7 +125,7 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) { mClassifier->notifyDeviceReset(&args); NotifyDeviceResetArgs outArgs; - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs)); + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs)); ASSERT_EQ(args, outArgs); } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 7fb2ccf94b..42d3e58545 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -455,11 +455,11 @@ private: class InputDispatcherTest : public testing::Test { protected: sp mFakePolicy; - sp mDispatcher; + std::unique_ptr mDispatcher; void SetUp() override { mFakePolicy = new FakeInputDispatcherPolicy(); - mDispatcher = new InputDispatcher(mFakePolicy); + mDispatcher = std::make_unique(mFakePolicy); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); // Start InputDispatcher thread ASSERT_EQ(OK, mDispatcher->start()); @@ -468,7 +468,7 @@ protected: void TearDown() override { ASSERT_EQ(OK, mDispatcher->stop()); mFakePolicy.clear(); - mDispatcher.clear(); + mDispatcher.reset(); } /** @@ -915,7 +915,7 @@ public: static const int32_t HEIGHT = 800; FakeWindowHandle(const std::shared_ptr& inputApplicationHandle, - const sp& dispatcher, const std::string name, + const std::unique_ptr& dispatcher, const std::string name, int32_t displayId, std::optional> token = std::nullopt) : mName(name) { if (token == std::nullopt) { @@ -953,7 +953,7 @@ public: sp clone( const std::shared_ptr& inputApplicationHandle, - const sp& dispatcher, int32_t displayId) { + const std::unique_ptr& dispatcher, int32_t displayId) { sp handle = new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)", displayId, mInfo.token); @@ -1154,7 +1154,7 @@ private: std::atomic FakeWindowHandle::sId{1}; static InputEventInjectionResult injectKey( - const sp& dispatcher, int32_t action, int32_t repeatCount, + const std::unique_ptr& dispatcher, int32_t action, int32_t repeatCount, int32_t displayId = ADISPLAY_ID_NONE, InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT, std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, @@ -1176,7 +1176,7 @@ static InputEventInjectionResult injectKey( injectionTimeout, policyFlags); } -static InputEventInjectionResult injectKeyDown(const sp& dispatcher, +static InputEventInjectionResult injectKeyDown(const std::unique_ptr& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); } @@ -1184,14 +1184,14 @@ static InputEventInjectionResult injectKeyDown(const sp& dispat // Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without // sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it // has to be woken up to process the repeating key. -static InputEventInjectionResult injectKeyDownNoRepeat(const sp& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static InputEventInjectionResult injectKeyDownNoRepeat( + const std::unique_ptr& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId, InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT, /* allowKeyRepeat */ false); } -static InputEventInjectionResult injectKeyUp(const sp& dispatcher, +static InputEventInjectionResult injectKeyUp(const std::unique_ptr& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId); } @@ -1314,7 +1314,7 @@ private: }; static InputEventInjectionResult injectMotionEvent( - const sp& dispatcher, const MotionEvent& event, + const std::unique_ptr& dispatcher, const MotionEvent& event, std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) { return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode, @@ -1323,8 +1323,8 @@ static InputEventInjectionResult injectMotionEvent( } static InputEventInjectionResult injectMotionEvent( - const sp& dispatcher, int32_t action, int32_t source, int32_t displayId, - const PointF& position, + const std::unique_ptr& 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}, std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, @@ -1344,13 +1344,13 @@ static InputEventInjectionResult injectMotionEvent( return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode); } -static InputEventInjectionResult injectMotionDown(const sp& dispatcher, - int32_t source, int32_t displayId, - const PointF& location = {100, 200}) { +static InputEventInjectionResult injectMotionDown( + const std::unique_ptr& dispatcher, int32_t source, int32_t displayId, + const PointF& location = {100, 200}) { return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location); } -static InputEventInjectionResult injectMotionUp(const sp& dispatcher, +static InputEventInjectionResult injectMotionUp(const std::unique_ptr& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location); @@ -1945,8 +1945,8 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } -using TransferFunction = - std::function dispatcher, sp, sp)>; +using TransferFunction = std::function& dispatcher, + sp, sp)>; class TransferTouchFixture : public InputDispatcherTest, public ::testing::WithParamInterface {}; @@ -2059,12 +2059,12 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { // for the case where there are multiple pointers split across several windows. INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture, ::testing::Values( - [&](sp dispatcher, sp /*ignored*/, - sp destChannelToken) { + [&](const std::unique_ptr& dispatcher, + sp /*ignored*/, sp destChannelToken) { return dispatcher->transferTouch(destChannelToken); }, - [&](sp dispatcher, sp from, - sp to) { + [&](const std::unique_ptr& dispatcher, + sp from, sp to) { return dispatcher->transferTouchFocus(from, to, false /*isDragAndDrop*/); })); @@ -2473,7 +2473,7 @@ TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) { class FakeMonitorReceiver { public: - FakeMonitorReceiver(const sp& dispatcher, const std::string name, + FakeMonitorReceiver(const std::unique_ptr& dispatcher, const std::string name, int32_t displayId, bool isGestureMonitor = false) { base::Result> channel = dispatcher->createInputMonitor(displayId, isGestureMonitor, name, MONITOR_PID); @@ -3179,7 +3179,7 @@ protected: virtual void SetUp() override { mFakePolicy = new FakeInputDispatcherPolicy(); mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); - mDispatcher = new InputDispatcher(mFakePolicy); + mDispatcher = std::make_unique(mFakePolicy); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); ASSERT_EQ(OK, mDispatcher->start()); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index b28c1e2401..53b03ada3a 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1186,7 +1186,7 @@ class InstrumentedInputReader : public InputReader { public: InstrumentedInputReader(std::shared_ptr eventHub, const sp& policy, - const sp& listener) + InputListenerInterface& listener) : InputReader(eventHub, policy, listener), mFakeContext(this) {} virtual ~InstrumentedInputReader() {} @@ -1499,7 +1499,7 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByPort) { class InputReaderTest : public testing::Test { protected: - sp mFakeListener; + std::unique_ptr mFakeListener; sp mFakePolicy; std::shared_ptr mFakeEventHub; std::unique_ptr mReader; @@ -1507,14 +1507,14 @@ protected: void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = new TestInputListener(); + mFakeListener = std::make_unique(); mReader = std::make_unique(mFakeEventHub, mFakePolicy, - mFakeListener); + *mFakeListener); } void TearDown() override { - mFakeListener.clear(); + mFakeListener.reset(); mFakePolicy.clear(); } @@ -2145,9 +2145,9 @@ TEST_F(InputReaderTest, LightGetColor) { // the tests to fail. class InputReaderIntegrationTest : public testing::Test { protected: - sp mTestListener; + std::unique_ptr mTestListener; sp mFakePolicy; - sp mReader; + std::unique_ptr mReader; std::shared_ptr mFakePointerController; @@ -2155,10 +2155,11 @@ protected: mFakePolicy = new FakeInputReaderPolicy(); mFakePointerController = std::make_shared(); mFakePolicy->setPointerController(mFakePointerController); - mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/, - 30ms /*eventDidNotHappenTimeout*/); + mTestListener = std::make_unique(2000ms /*eventHappenedTimeout*/, + 30ms /*eventDidNotHappenTimeout*/); - mReader = new InputReader(std::make_shared(), mFakePolicy, mTestListener); + mReader = std::make_unique(std::make_shared(), mFakePolicy, + *mTestListener); ASSERT_EQ(mReader->start(), OK); // Since this test is run on a real device, all the input devices connected @@ -2170,7 +2171,8 @@ protected: void TearDown() override { ASSERT_EQ(mReader->stop(), OK); - mTestListener.clear(); + mReader.reset(); + mTestListener.reset(); mFakePolicy.clear(); } }; @@ -2424,16 +2426,16 @@ protected: std::shared_ptr mFakeEventHub; sp mFakePolicy; - sp mFakeListener; + std::unique_ptr mFakeListener; std::unique_ptr mReader; std::shared_ptr mDevice; void SetUp() override { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = new TestInputListener(); + mFakeListener = std::make_unique(); mReader = std::make_unique(mFakeEventHub, mFakePolicy, - mFakeListener); + *mFakeListener); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; @@ -2445,7 +2447,7 @@ protected: } void TearDown() override { - mFakeListener.clear(); + mFakeListener.reset(); mFakePolicy.clear(); } }; @@ -2697,16 +2699,16 @@ protected: std::shared_ptr mFakeEventHub; sp mFakePolicy; - sp mFakeListener; + std::unique_ptr mFakeListener; std::unique_ptr mReader; std::shared_ptr mDevice; virtual void SetUp(Flags classes) { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = new TestInputListener(); + mFakeListener = std::make_unique(); mReader = std::make_unique(mFakeEventHub, mFakePolicy, - mFakeListener); + *mFakeListener); mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); } @@ -2718,7 +2720,7 @@ protected: } void TearDown() override { - mFakeListener.clear(); + mFakeListener.reset(); mFakePolicy.clear(); sysprop::InputFlingerProperties::per_window_input_rotation( @@ -9021,23 +9023,23 @@ protected: std::shared_ptr mFakeEventHub; sp mFakePolicy; - sp mFakeListener; + std::unique_ptr mFakeListener; std::unique_ptr mReader; std::shared_ptr mDevice; virtual void SetUp(Flags classes) { mFakeEventHub = std::make_unique(); mFakePolicy = new FakeInputReaderPolicy(); - mFakeListener = new TestInputListener(); + mFakeListener = std::make_unique(); mReader = std::make_unique(mFakeEventHub, mFakePolicy, - mFakeListener); + *mFakeListener); mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); } void SetUp() override { SetUp(DEVICE_CLASSES); } void TearDown() override { - mFakeListener.clear(); + mFakeListener.reset(); mFakePolicy.clear(); } diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 0a1dc4b0b9..626cdfc8c8 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -28,12 +28,10 @@ namespace android { // --- TestInputListener --- class TestInputListener : public InputListenerInterface { -protected: - virtual ~TestInputListener(); - public: TestInputListener(std::chrono::milliseconds eventHappenedTimeout = 0ms, std::chrono::milliseconds eventDidNotHappenTimeout = 0ms); + virtual ~TestInputListener(); void assertNotifyConfigurationChangedWasCalled( NotifyConfigurationChangedArgs* outEventArgs = nullptr); -- cgit v1.2.3-59-g8ed1b From ba0a8758240241a852d7fd78603be5c10cb0f05c Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 14 Sep 2021 14:43:25 -0700 Subject: Add ChromeOS palm rejection model This model will be used to block palm presses. It takes in a stream of evdev events, and reports back the pointers which should be considered palm. Bug: 198472780 Test: atest libpalmrejection_test inputflinger_tests Test: "adb shell device_config put input_native_boot palm_rejection_enabled 0" and make sure that "adb shell dumpsys input" shows that there aren't any palm rejectors inside UnwantedInteractionBlocker Change-Id: If979d335af29cf5e93b26336fea56a3a895cc562 --- include/input/Input.h | 2 +- services/inputflinger/Android.bp | 3 + services/inputflinger/InputClassifierConverter.cpp | 7 +- services/inputflinger/InputListener.cpp | 19 + services/inputflinger/InputManager.cpp | 12 +- services/inputflinger/InputManager.h | 22 +- .../inputflinger/UnwantedInteractionBlocker.cpp | 687 +++++++++++++++ services/inputflinger/UnwantedInteractionBlocker.h | 153 ++++ services/inputflinger/include/InputListener.h | 2 + .../include/UnwantedInteractionBlockerInterface.h | 53 ++ services/inputflinger/tests/Android.bp | 1 + .../tests/UnwantedInteractionBlocker_test.cpp | 938 +++++++++++++++++++++ 12 files changed, 1886 insertions(+), 13 deletions(-) create mode 100644 services/inputflinger/UnwantedInteractionBlocker.cpp create mode 100644 services/inputflinger/UnwantedInteractionBlocker.h create mode 100644 services/inputflinger/include/UnwantedInteractionBlockerInterface.h create mode 100644 services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp (limited to 'services/inputflinger/InputManager.cpp') diff --git a/include/input/Input.h b/include/input/Input.h index 55ebb90ff6..e421dee275 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -534,7 +534,7 @@ public: inline int32_t getActionMasked() const { return getActionMasked(mAction); } - static int32_t getActionIndex(int32_t action) { + static uint8_t getActionIndex(int32_t action) { return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index ec5832515c..22a69e5a30 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -49,6 +49,7 @@ filegroup { srcs: [ "InputClassifier.cpp", "InputClassifierConverter.cpp", + "UnwantedInteractionBlocker.cpp", "InputManager.cpp", ], } @@ -60,6 +61,7 @@ cc_defaults { "android.hardware.input.classifier@1.0", "libbase", "libbinder", + "libchrome", "libcrypto", "libcutils", "libhidlbase", @@ -76,6 +78,7 @@ cc_defaults { ], static_libs: [ "libattestation", + "libpalmrejection", ], } diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp index fc8c7c39f9..b58a188a82 100644 --- a/services/inputflinger/InputClassifierConverter.cpp +++ b/services/inputflinger/InputClassifierConverter.cpp @@ -325,11 +325,6 @@ static std::vector convertVideoFrames( return out; } -static uint8_t getActionIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> - AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; -} - static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args, std::vector* outPointerProperties, std::vector* outPointerCoords) { @@ -360,7 +355,7 @@ common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArg event.eventTime = args.eventTime; event.deviceTimestamp = 0; event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); - event.actionIndex = getActionIndex(args.action); + event.actionIndex = MotionEvent::getActionIndex(args.action); event.actionButton = getActionButton(args.actionButton); event.flags = getFlags(args.flags); event.policyFlags = getPolicyFlags(args.policyFlags); diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 158d0ebd58..73b63e3141 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -188,6 +188,25 @@ bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { return true; } +std::string NotifyMotionArgs::dump() const { + std::string coords; + for (uint32_t i = 0; i < pointerCount; i++) { + if (!coords.empty()) { + coords += ", "; + } + coords += StringPrintf("{%" PRIu32 ": ", i); + coords += + StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f, pressure=%.1f", pointerProperties[i].id, + pointerCoords[i].getX(), pointerCoords[i].getY(), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + coords += "}"; + } + return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32 + ", source=%s, action=%s, pointerCount=%" PRIu32 " pointers=%s)", + id, eventTime, deviceId, inputEventSourceToString(source).c_str(), + MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str()); +} + void NotifyMotionArgs::notify(InputListenerInterface& listener) const { listener.notifyMotion(this); } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 221e193136..7a9862d065 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -21,6 +21,7 @@ #include "InputManager.h" #include "InputDispatcherFactory.h" #include "InputReaderFactory.h" +#include "UnwantedInteractionBlocker.h" #include @@ -54,12 +55,17 @@ static int32_t exceptionCodeFromStatusT(status_t status) { } } +/** + * The event flow is via the "InputListener" interface, as follows: + * InputReader -> UnwantedInteractionBlocker -> InputClassifier -> InputDispatcher + */ InputManager::InputManager( const sp& readerPolicy, const sp& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = std::make_unique(*mDispatcher); - mReader = createInputReader(readerPolicy, *mClassifier); + mUnwantedInteractionBlocker = std::make_unique(*mClassifier); + mReader = createInputReader(readerPolicy, *mUnwantedInteractionBlocker); } InputManager::~InputManager() { @@ -106,6 +112,10 @@ InputReaderInterface& InputManager::getReader() { return *mReader; } +UnwantedInteractionBlockerInterface& InputManager::getUnwantedInteractionBlocker() { + return *mUnwantedInteractionBlocker; +} + InputClassifierInterface& InputManager::getClassifier() { return *mClassifier; } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e00028364a..35d2b0fa19 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -23,6 +23,7 @@ #include "InputClassifier.h" #include "InputReaderBase.h" +#include "include/UnwantedInteractionBlockerInterface.h" #include #include @@ -46,11 +47,16 @@ class InputDispatcherThread; * The input manager has three components. * * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies - * policy, and posts messages to a queue managed by the InputClassifier. - * 2. The InputClassifier class starts a thread to communicate with the device-specific - * classifiers. It then waits on the queue of events from InputReader, applies a classification - * to them, and queues them for the InputDispatcher. - * 3. The InputDispatcher class starts a thread that waits for new events on the + * policy, and posts messages to a queue managed by the UnwantedInteractionBlocker. + * 2. The UnwantedInteractionBlocker is responsible for removing unwanted interactions. For example, + * this could be a palm on the screen. This stage would alter the event stream to remove either + * partially (some of the pointers) or fully (all touches) the unwanted interaction. The events + * are processed on the InputReader thread, without any additional queue. The events are then + * posted to the queue managed by the InputClassifier. + * 3. The InputClassifier class starts a thread to communicate with the device-specific + * classifiers. It then waits on the queue of events from UnwantedInteractionBlocker, applies + * a classification to them, and queues them for the InputDispatcher. + * 4. The InputDispatcher class starts a thread that waits for new events on the * previous queue and asynchronously dispatches them to applications. * * By design, none of these classes share any internal state. Moreover, all communication is @@ -76,6 +82,9 @@ public: /* Gets the input reader. */ virtual InputReaderInterface& getReader() = 0; + /* Gets the unwanted interaction blocker. */ + virtual UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() = 0; + /* Gets the input classifier */ virtual InputClassifierInterface& getClassifier() = 0; @@ -96,6 +105,7 @@ public: status_t stop() override; InputReaderInterface& getReader() override; + UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() override; InputClassifierInterface& getClassifier() override; InputDispatcherInterface& getDispatcher() override; @@ -107,6 +117,8 @@ public: private: std::unique_ptr mReader; + std::unique_ptr mUnwantedInteractionBlocker; + std::unique_ptr mClassifier; std::unique_ptr mDispatcher; diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp new file mode 100644 index 0000000000..64dbb8ceb4 --- /dev/null +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "UnwantedInteractionBlocker" +#include "UnwantedInteractionBlocker.h" + +#include +#include +#include +#include +#include + +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h" +#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h" + +using android::base::StringPrintf; + +namespace android { + +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +/** + * Feature flag name. This flag determines whether palm rejection is enabled. To enable, specify + * 'true' (not case sensitive) or '1'. To disable, specify any other value. + */ +static const char* PALM_REJECTION_ENABLED = "palm_rejection_enabled"; + +static std::string toLower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; +} + +static bool isFromTouchscreen(int32_t source) { + return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN); +} + +static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) { + return ::base::TimeTicks::UnixEpoch() + + ::base::Milliseconds(static_cast(ns2ms(eventTime))); +} + +/** + * Return true if palm rejection is enabled via the server configurable flags. Return false + * otherwise. + */ +static bool isPalmRejectionEnabled() { + std::string value = toLower( + server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT, + PALM_REJECTION_ENABLED, "false")); + if (value == "true" || value == "1") { + return true; + } + return false; +} + +static int getLinuxToolType(int32_t toolType) { + switch (toolType) { + case AMOTION_EVENT_TOOL_TYPE_FINGER: + return MT_TOOL_FINGER; + case AMOTION_EVENT_TOOL_TYPE_STYLUS: + return MT_TOOL_PEN; + case AMOTION_EVENT_TOOL_TYPE_PALM: + return MT_TOOL_PALM; + } + ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType); + return MT_TOOL_FINGER; +} + +static std::string addPrefix(std::string str, const std::string& prefix) { + std::stringstream ss; + bool newLineStarted = true; + for (const auto& ch : str) { + if (newLineStarted) { + ss << prefix; + newLineStarted = false; + } + if (ch == '\n') { + newLineStarted = true; + } + ss << ch; + } + return ss.str(); +} + +template +static std::string dumpSet(const std::set& v) { + static_assert(std::is_integral::value, "Only integral types can be printed."); + std::string out; + for (const T& entry : v) { + out += out.empty() ? "{" : ", "; + out += android::base::StringPrintf("%i", entry); + } + return out.empty() ? "{}" : (out + "}"); +} + +template +static std::string dumpMap(const std::map& map) { + static_assert(std::is_integral::value, "Keys should have integral type to be printed."); + static_assert(std::is_integral::value, "Values should have integral type to be printed."); + std::string out; + for (const auto& [k, v] : map) { + if (!out.empty()) { + out += "\n"; + } + out += android::base::StringPrintf("%i : %i", static_cast(k), static_cast(v)); + } + return out; +} + +static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) { + std::string out; + out += StringPrintf("max_x = %.2f\n", info.max_x); + out += StringPrintf("max_y = %.2f\n", info.max_y); + out += StringPrintf("x_res = %.2f\n", info.x_res); + out += StringPrintf("y_res = %.2f\n", info.y_res); + out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res); + out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res); + out += StringPrintf("minor_radius_supported = %s\n", + info.minor_radius_supported ? "true" : "false"); + out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res); + out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res); + return out; +} + +static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) { + for (size_t i = 0; i < args.pointerCount; i++) { + if (pointerId == args.pointerProperties[i].id) { + return AMOTION_EVENT_ACTION_POINTER_UP | + (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + } + LOG_ALWAYS_FATAL("Can't find pointerId %" PRId32 " in %s", pointerId, args.dump().c_str()); +} + +/** + * Find the action for individual pointer at the given pointer index. + * This is always equal to MotionEvent::getActionMasked, except for + * POINTER_UP or POINTER_DOWN events. For example, in a POINTER_UP event, the action for + * the active pointer is ACTION_POINTER_UP, while the action for the other pointers is ACTION_MOVE. + */ +static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) { + const int32_t actionMasked = MotionEvent::getActionMasked(action); + if (actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN && + actionMasked != AMOTION_EVENT_ACTION_POINTER_UP) { + return actionMasked; + } + // This is a POINTER_DOWN or POINTER_UP event + const uint8_t actionIndex = MotionEvent::getActionIndex(action); + if (pointerIndex == actionIndex) { + return actionMasked; + } + // When POINTER_DOWN or POINTER_UP happens, it's actually a MOVE for all of the other + // pointers + return AMOTION_EVENT_ACTION_MOVE; +} + +static const char* toString(bool value) { + return value ? "true" : "false"; +} + +std::string toString(const ::ui::InProgressTouchEvdev& touch) { + return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu," + " pressure=%.1f, major=%i, minor=%i, " + "tool_type=%i, altered=%s, was_touching=%s, touching=%s", + touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure, + touch.major, touch.minor, touch.tool_type, toString(touch.altered), + toString(touch.was_touching), toString(touch.touching)); +} + +/** + * Remove the data for the provided pointers from the args. The pointers are identified by their + * pointerId, not by the index inside the array. + * Return the new NotifyMotionArgs struct that has the remaining pointers. + * The only fields that may be different in the returned args from the provided args are: + * - action + * - pointerCount + * - pointerProperties + * - pointerCoords + * Action might change because it contains a pointer index. If another pointer is removed, the + * active pointer index would be shifted. + * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer + * id is the acting pointer id. + * + * @param args the args from which the pointers should be removed + * @param pointerIds the pointer ids of the pointers that should be removed + */ +NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, + const std::set& pointerIds) { + const uint8_t actionIndex = MotionEvent::getActionIndex(args.action); + const int32_t actionMasked = MotionEvent::getActionMasked(args.action); + const bool isPointerUpOrDownAction = actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN || + actionMasked == AMOTION_EVENT_ACTION_POINTER_UP; + + NotifyMotionArgs newArgs{args}; + newArgs.pointerCount = 0; + int32_t newActionIndex = 0; + for (uint32_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + if (pointerIds.find(pointerId) != pointerIds.end()) { + // skip this pointer + if (isPointerUpOrDownAction && i == actionIndex) { + // The active pointer is being removed, so the action is no longer valid. + // Set the action to 'UNKNOWN' here. The caller is responsible for updating this + // action later to a proper value. + newArgs.action = ACTION_UNKNOWN; + } + continue; + } + newArgs.pointerProperties[newArgs.pointerCount].copyFrom(args.pointerProperties[i]); + newArgs.pointerCoords[newArgs.pointerCount].copyFrom(args.pointerCoords[i]); + if (i == actionIndex) { + newActionIndex = newArgs.pointerCount; + } + newArgs.pointerCount++; + } + // Update POINTER_DOWN or POINTER_UP actions + if (isPointerUpOrDownAction && newArgs.action != ACTION_UNKNOWN) { + newArgs.action = + actionMasked | (newActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + // Convert POINTER_DOWN and POINTER_UP to DOWN and UP if there's only 1 pointer remaining + if (newArgs.pointerCount == 1) { + if (actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) { + newArgs.action = AMOTION_EVENT_ACTION_DOWN; + } else if (actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) { + newArgs.action = AMOTION_EVENT_ACTION_UP; + } + } + } + return newArgs; +} + +std::optional createPalmFilterDeviceInfo( + const InputDeviceInfo& deviceInfo) { + if (!isFromTouchscreen(deviceInfo.getSources())) { + return std::nullopt; + } + AndroidPalmFilterDeviceInfo out; + const InputDeviceInfo::MotionRange* axisX = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN); + if (axisX != nullptr) { + out.max_x = axisX->max; + out.x_res = axisX->resolution; + } else { + ALOGW("Palm rejection is disabled for %s because AXIS_X is not supported", + deviceInfo.getDisplayName().c_str()); + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisY = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN); + if (axisY != nullptr) { + out.max_y = axisY->max; + out.y_res = axisY->resolution; + } else { + ALOGW("Palm rejection is disabled for %s because AXIS_Y is not supported", + deviceInfo.getDisplayName().c_str()); + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisMajor = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN); + if (axisMajor != nullptr) { + out.major_radius_res = axisMajor->resolution; + out.touch_major_res = axisMajor->resolution; + } else { + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisMinor = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR, AINPUT_SOURCE_TOUCHSCREEN); + if (axisMinor != nullptr) { + out.minor_radius_res = axisMinor->resolution; + out.touch_minor_res = axisMinor->resolution; + out.minor_radius_supported = true; + } else { + out.minor_radius_supported = false; + } + + return out; +} + +/** + * Synthesize CANCEL events for any new pointers that should be canceled, while removing pointers + * that have already been canceled. + * The flow of the function is as follows: + * 1. Remove all already canceled pointers + * 2. Cancel all newly suppressed pointers + * 3. Decide what to do with the current event : keep it, or drop it + * The pointers can never be "unsuppressed": once a pointer is canceled, it will never become valid. + */ +std::vector cancelSuppressedPointers( + const NotifyMotionArgs& args, const std::set& oldSuppressedPointerIds, + const std::set& newSuppressedPointerIds) { + LOG_ALWAYS_FATAL_IF(args.pointerCount == 0, "0 pointers in %s", args.dump().c_str()); + + // First, let's remove the old suppressed pointers. They've already been canceled previously. + NotifyMotionArgs oldArgs = removePointerIds(args, oldSuppressedPointerIds); + + // Cancel any newly suppressed pointers. + std::vector out; + const int32_t activePointerId = + args.pointerProperties[MotionEvent::getActionIndex(args.action)].id; + const int32_t actionMasked = MotionEvent::getActionMasked(args.action); + // We will iteratively remove pointers from 'removedArgs'. + NotifyMotionArgs removedArgs{oldArgs}; + for (uint32_t i = 0; i < oldArgs.pointerCount; i++) { + const int32_t pointerId = oldArgs.pointerProperties[i].id; + if (newSuppressedPointerIds.find(pointerId) == newSuppressedPointerIds.end()) { + // This is a pointer that should not be canceled. Move on. + continue; + } + if (pointerId == activePointerId && actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // Remove this pointer, but don't cancel it. We'll just not send the POINTER_DOWN event + removedArgs = removePointerIds(removedArgs, {pointerId}); + continue; + } + + if (removedArgs.pointerCount == 1) { + // We are about to remove the last pointer, which means there will be no more gesture + // remaining. This is identical to canceling all pointers, so just send a single CANCEL + // event, without any of the preceding POINTER_UP with FLAG_CANCELED events. + oldArgs.flags |= AMOTION_EVENT_FLAG_CANCELED; + oldArgs.action = AMOTION_EVENT_ACTION_CANCEL; + return {oldArgs}; + } + // Cancel the current pointer + out.push_back(removedArgs); + out.back().flags |= AMOTION_EVENT_FLAG_CANCELED; + out.back().action = getActionUpForPointerId(out.back(), pointerId); + + // Remove the newly canceled pointer from the args + removedArgs = removePointerIds(removedArgs, {pointerId}); + } + + // Now 'removedArgs' contains only pointers that are valid. + if (removedArgs.pointerCount <= 0 || removedArgs.action == ACTION_UNKNOWN) { + return out; + } + out.push_back(removedArgs); + return out; +} + +UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener) + : UnwantedInteractionBlocker(listener, isPalmRejectionEnabled()){}; + +UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener, + bool enablePalmRejection) + : mListener(listener), mEnablePalmRejection(enablePalmRejection) {} + +void UnwantedInteractionBlocker::notifyConfigurationChanged( + const NotifyConfigurationChangedArgs* args) { + mListener.notifyConfigurationChanged(args); +} + +void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) { + mListener.notifyKey(args); +} + +void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { + auto it = mPalmRejectors.find(args->deviceId); + const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source); + if (!sendToPalmRejector) { + mListener.notifyMotion(args); + return; + } + + const std::vector newMotions = it->second.processMotion(*args); + for (const NotifyMotionArgs& newArgs : newMotions) { + mListener.notifyMotion(&newArgs); + } +} + +void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs* args) { + mListener.notifySwitch(args); +} + +void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs* args) { + mListener.notifySensor(args); +} + +void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs* args) { + mListener.notifyVibratorState(args); +} +void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + auto it = mPalmRejectors.find(args->deviceId); + if (it != mPalmRejectors.end()) { + AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo(); + // Re-create the object instead of resetting it + mPalmRejectors.erase(it); + mPalmRejectors.emplace(args->deviceId, info); + } + mListener.notifyDeviceReset(args); +} + +void UnwantedInteractionBlocker::notifyPointerCaptureChanged( + const NotifyPointerCaptureChangedArgs* args) { + mListener.notifyPointerCaptureChanged(args); +} + +void UnwantedInteractionBlocker::notifyInputDevicesChanged( + const std::vector& inputDevices) { + if (!mEnablePalmRejection) { + // Palm rejection is disabled. Don't create any palm rejector objects. + return; + } + + // Let's see which of the existing devices didn't change, so that we can keep them + // and prevent event stream disruption + std::set devicesToKeep; + for (const InputDeviceInfo& device : inputDevices) { + std::optional info = createPalmFilterDeviceInfo(device); + if (!info) { + continue; + } + + auto [it, emplaced] = mPalmRejectors.try_emplace(device.getId(), *info); + if (!emplaced && *info != it->second.getPalmFilterDeviceInfo()) { + // Re-create the PalmRejector because the device info has changed. + mPalmRejectors.erase(it); + mPalmRejectors.emplace(device.getId(), *info); + } + devicesToKeep.insert(device.getId()); + } + // Delete all devices that we don't need to keep + std::erase_if(mPalmRejectors, [&devicesToKeep](const auto& item) { + auto const& [deviceId, _] = item; + return devicesToKeep.find(deviceId) == devicesToKeep.end(); + }); +} + +void UnwantedInteractionBlocker::dump(std::string& dump) { + dump += "UnwantedInteractionBlocker:\n"; + dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection)); + dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n", + toString(isPalmRejectionEnabled())); + dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n"; + for (const auto& [deviceId, palmRejector] : mPalmRejectors) { + dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId); + dump += addPrefix(palmRejector.dump(), " "); + } +} + +void UnwantedInteractionBlocker::monitor() {} + +UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {} + +void SlotState::update(const NotifyMotionArgs& args) { + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + const int32_t resolvedAction = resolveActionForPointer(i, args.action); + processPointerId(pointerId, resolvedAction); + } +} + +size_t SlotState::findUnusedSlot() const { + size_t unusedSlot = 0; + // Since the collection is ordered, we can rely on the in-order traversal + for (const auto& [slot, trackingId] : mPointerIdsBySlot) { + if (unusedSlot != slot) { + break; + } + unusedSlot++; + } + return unusedSlot; +} + +void SlotState::processPointerId(int pointerId, int32_t actionMasked) { + switch (MotionEvent::getActionMasked(actionMasked)) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: { + // New pointer going down + size_t newSlot = findUnusedSlot(); + mPointerIdsBySlot[newSlot] = pointerId; + mSlotsByPointerId[pointerId] = newSlot; + return; + } + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + return; + } + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + auto it = mSlotsByPointerId.find(pointerId); + LOG_ALWAYS_FATAL_IF(it == mSlotsByPointerId.end()); + size_t slot = it->second; + // Erase this pointer from both collections + mPointerIdsBySlot.erase(slot); + mSlotsByPointerId.erase(pointerId); + return; + } + } + LOG_ALWAYS_FATAL("Unhandled action : %s", MotionEvent::actionToString(actionMasked).c_str()); + return; +} + +std::optional SlotState::getSlotForPointerId(int32_t pointerId) const { + auto it = mSlotsByPointerId.find(pointerId); + if (it == mSlotsByPointerId.end()) { + return std::nullopt; + } + return it->second; +} + +std::string SlotState::dump() const { + std::string out = "mSlotsByPointerId:\n"; + out += addPrefix(dumpMap(mSlotsByPointerId), " ") + "\n"; + out += "mPointerIdsBySlot:\n"; + out += addPrefix(dumpMap(mPointerIdsBySlot), " ") + "\n"; + return out; +} + +PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info, + std::unique_ptr<::ui::PalmDetectionFilter> filter) + : mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()), + mDeviceInfo(info), + mPalmDetectionFilter(std::move(filter)) { + if (mPalmDetectionFilter != nullptr) { + // This path is used for testing. Non-testing invocations should let this constructor + // create a real PalmDetectionFilter + return; + } + std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model = + std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>( + std::vector()); + mPalmDetectionFilter = + std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model), + mSharedPalmState.get()); +} + +std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, + const AndroidPalmFilterDeviceInfo& deviceInfo, + const SlotState& oldSlotState, + const SlotState& newSlotState) { + std::vector<::ui::InProgressTouchEvdev> touches; + + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + touches.emplace_back(::ui::InProgressTouchEvdev()); + touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); + touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType); + + // Whether there is new information for the touch. + touches.back().altered = true; + + // Whether the touch was cancelled. Touch events should be ignored till a + // new touch is initiated. + touches.back().was_cancelled = false; + + // Whether the touch is going to be canceled. + touches.back().cancelled = false; + + // Whether the touch is delayed at first appearance. Will not be reported yet. + touches.back().delayed = false; + + // Whether the touch was delayed before. + touches.back().was_delayed = false; + + // Whether the touch is held until end or no longer held. + touches.back().held = false; + + // Whether this touch was held before being sent. + touches.back().was_held = false; + + const int32_t resolvedAction = resolveActionForPointer(i, args.action); + const bool isDown = resolvedAction == AMOTION_EVENT_ACTION_POINTER_DOWN || + resolvedAction == AMOTION_EVENT_ACTION_DOWN; + touches.back().was_touching = !isDown; + + const bool isUpOrCancel = resolvedAction == AMOTION_EVENT_ACTION_CANCEL || + resolvedAction == AMOTION_EVENT_ACTION_UP || + resolvedAction == AMOTION_EVENT_ACTION_POINTER_UP; + + touches.back().x = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X); + touches.back().y = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y); + + std::optional slot = newSlotState.getSlotForPointerId(pointerId); + if (!slot) { + slot = oldSlotState.getSlotForPointerId(pointerId); + } + LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer %d", pointerId); + touches.back().slot = *slot; + touches.back().tracking_id = (!isUpOrCancel) ? pointerId : -1; + touches.back().touching = !isUpOrCancel; + + // The fields 'radius_x' and 'radius_x' are not used for palm rejection + touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + touches.back().tool_code = BTN_TOOL_FINGER; + // The field 'orientation' is not used for palm rejection + // The fields 'tilt_x' and 'tilt_y' are not used for palm rejection + touches.back().reported_tool_type = ::ui::EventPointerType::kTouch; + touches.back().stylus_button = false; + } + return touches; +} + +std::vector PalmRejector::processMotion(const NotifyMotionArgs& args) { + if (mPalmDetectionFilter == nullptr) { + return {args}; + } + const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER || + args.action == AMOTION_EVENT_ACTION_HOVER_MOVE || + args.action == AMOTION_EVENT_ACTION_HOVER_EXIT || + args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS || + args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE || + args.action == AMOTION_EVENT_ACTION_SCROLL; + if (skipThisEvent) { + // Lets not process hover events, button events, or scroll for now. + return {args}; + } + if (args.action == AMOTION_EVENT_ACTION_DOWN) { + mSuppressedPointerIds.clear(); + } + std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold; + std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress; + + // Store the slot state before we call getTouches and update it. This way, we can find + // the slots that have been removed due to the incoming event. + SlotState oldSlotState = mSlotState; + mSlotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = + getTouches(args, mDeviceInfo, oldSlotState, mSlotState); + ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime); + + mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress); + + // Now that we know which slots should be suppressed, let's convert those to pointer id's. + std::set oldSuppressedIds; + std::swap(oldSuppressedIds, mSuppressedPointerIds); + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + std::optional slot = oldSlotState.getSlotForPointerId(pointerId); + if (!slot) { + slot = mSlotState.getSlotForPointerId(pointerId); + LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId); + } + if (slotsToSuppress.test(*slot)) { + mSuppressedPointerIds.insert(pointerId); + } + } + + std::vector argsWithoutUnwantedPointers = + cancelSuppressedPointers(args, oldSuppressedIds, mSuppressedPointerIds); + for (const NotifyMotionArgs& checkArgs : argsWithoutUnwantedPointers) { + LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str()); + } + + if (mSuppressedPointerIds != oldSuppressedIds) { + if (argsWithoutUnwantedPointers.size() != 1 || + argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) { + ALOGI("Palm detected, removing pointer ids %s from %s", + dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str()); + } + } + + return argsWithoutUnwantedPointers; +} + +const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() { + return mDeviceInfo; +} + +std::string PalmRejector::dump() const { + std::string out; + out += "mDeviceInfo:\n"; + out += addPrefix(dumpDeviceInfo(mDeviceInfo), " "); + out += "mSlotState:\n"; + out += addPrefix(mSlotState.dump(), " "); + out += "mSuppressedPointerIds: "; + out += dumpSet(mSuppressedPointerIds) + "\n"; + return out; +} + +} // namespace android diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h new file mode 100644 index 0000000000..14068fd878 --- /dev/null +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once + +#include +#include + +#include "include/UnwantedInteractionBlockerInterface.h" +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h" +#include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h" + +namespace android { + +// --- Functions for manipulation of event streams + +struct AndroidPalmFilterDeviceInfo : ::ui::PalmFilterDeviceInfo { + // Additional fields from 'TouchEventConverterEvdev', added here for convenience + int32_t touch_major_res = 1; // info.GetAbsInfoByCode(ABS_MT_TOUCH_MAJOR).resolution; + int32_t touch_minor_res = 1; // info.GetAbsInfoByCode(ABS_MT_TOUCH_MINOR).resolution; + + auto operator<=>(const AndroidPalmFilterDeviceInfo&) const = default; +}; + +std::optional createPalmFilterDeviceInfo( + const InputDeviceInfo& deviceInfo); + +static constexpr int32_t ACTION_UNKNOWN = -1; + +NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, + const std::set& pointerIds); + +std::vector cancelSuppressedPointers( + const NotifyMotionArgs& args, const std::set& oldSuppressedPointerIds, + const std::set& newSuppressedPointerIds); + +std::string toString(const ::ui::InProgressTouchEvdev& touch); + +// --- Main classes and interfaces --- + +class PalmRejector; + +// --- Implementations --- + +/** + * Implementation of the UnwantedInteractionBlockerInterface. + * Represents a separate stage of input processing. All of the input events go through this stage. + * Acts as a passthrough for all input events except for motion events. + * + * The events of motion type are sent to PalmRejectors. PalmRejectors detect unwanted touches, + * and emit input streams with the bad pointers removed. + */ +class UnwantedInteractionBlocker : public UnwantedInteractionBlockerInterface { +public: + explicit UnwantedInteractionBlocker(InputListenerInterface& listener); + explicit UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection); + + void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; + void notifyKey(const NotifyKeyArgs* args) override; + void notifyMotion(const NotifyMotionArgs* args) override; + void notifySwitch(const NotifySwitchArgs* args) override; + void notifySensor(const NotifySensorArgs* args) override; + void notifyVibratorState(const NotifyVibratorStateArgs* args) override; + void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; + + void notifyInputDevicesChanged(const std::vector& inputDevices) override; + void dump(std::string& dump) override; + void monitor() override; + + ~UnwantedInteractionBlocker(); + +private: + // The next stage to pass input events to + InputListenerInterface& mListener; + const bool mEnablePalmRejection; + + // Detect and reject unwanted palms on screen + // Use a separate palm rejector for every touch device. + std::map mPalmRejectors; +}; + +class SlotState { +public: + /** + * Update the state using the new information provided in the NotifyMotionArgs + */ + void update(const NotifyMotionArgs& args); + std::optional getSlotForPointerId(int32_t pointerId) const; + std::string dump() const; + +private: + // Process a pointer with the provided action, and return the slot associated with it + void processPointerId(int32_t pointerId, int32_t action); + // The map from tracking id to slot state. Since the PalmRejectionFilter works close to the + // evdev level, the only way to tell it about UP or CANCEL events is by sending tracking id = -1 + // to the appropriate touch slot. So we need to reconstruct the original slot. + // The two collections below must always be in-sync. + // Use std::map instead of std::unordered_map because we rely on these collections being + // ordered. It also has better space efficiency than unordered_map because we only have a few + // pointers most of the time. + std::map mSlotsByPointerId; + std::map mPointerIdsBySlot; + + size_t findUnusedSlot() const; +}; + +/** + * Convert an Android event to a linux-like 'InProgressTouchEvdev'. The provided SlotState's + * are used to figure out which slot does each pointer belong to. + */ +std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, + const AndroidPalmFilterDeviceInfo& deviceInfo, + const SlotState& oldSlotState, + const SlotState& newSlotState); + +class PalmRejector { +public: + explicit PalmRejector(const AndroidPalmFilterDeviceInfo& info, + std::unique_ptr<::ui::PalmDetectionFilter> filter = nullptr); + std::vector processMotion(const NotifyMotionArgs& args); + + // Get the device info of this device, for comparison purposes + const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo(); + std::string dump() const; + +private: + PalmRejector(const PalmRejector&) = delete; + PalmRejector& operator=(const PalmRejector&) = delete; + + std::unique_ptr<::ui::SharedPalmDetectionFilterState> mSharedPalmState; + AndroidPalmFilterDeviceInfo mDeviceInfo; + std::unique_ptr<::ui::PalmDetectionFilter> mPalmDetectionFilter; + std::set mSuppressedPointerIds; + + // Used to help convert an Android touch stream to Linux input stream. + SlotState mSlotState; +}; + +} // namespace android diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index db6310478c..dff58949a7 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -142,6 +142,8 @@ struct NotifyMotionArgs : public NotifyArgs { bool operator==(const NotifyMotionArgs& rhs) const; void notify(InputListenerInterface& listener) const override; + + std::string dump() const; }; /* Describes a sensor event. */ diff --git a/services/inputflinger/include/UnwantedInteractionBlockerInterface.h b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h new file mode 100644 index 0000000000..2327266563 --- /dev/null +++ b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once + +#include "InputListener.h" + +namespace android { + +/** + * Base interface for an InputListener stage. + * Blocks unintentional input events. Not thread safe. Must be called from the same + * thread. All work is performed on the calling threads. + */ +class UnwantedInteractionBlockerInterface : public InputListenerInterface { +public: + /* Notifies the input reader policy that some input devices have changed + * and provides information about all current input devices. + * Important! This call should happen on the same thread as the calls to the + * InputListenerInterface methods. + * That is, same thread should call 'notifyMotion' and 'notifyInputDevicesChanged' and + * 'notifyDeviceReset'. If this architecture changes, we will need to make the implementation + * of this interface thread-safe. + */ + virtual void notifyInputDevicesChanged(const std::vector& inputDevices) = 0; + + /** + * Dump the state of the interaction blocker. + * This method may be called on any thread (usually by the input manager). + */ + virtual void dump(std::string& dump) = 0; + + /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ + virtual void monitor() = 0; + + UnwantedInteractionBlockerInterface() {} + ~UnwantedInteractionBlockerInterface() override {} +}; + +} // namespace android diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index d19dbaf009..f734e599b0 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -49,6 +49,7 @@ cc_test { "LatencyTracker_test.cpp", "TestInputListener.cpp", "UinputDevice.cpp", + "UnwantedInteractionBlocker_test.cpp", ], aidl: { include_dirs: [ diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp new file mode 100644 index 0000000000..a3220cc63a --- /dev/null +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2022 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 "../UnwantedInteractionBlocker.h" +#include +#include +#include +#include + +#include "TestInputListener.h" + +namespace android { + +constexpr int32_t DEVICE_ID = 3; +constexpr int32_t X_RESOLUTION = 11; +constexpr int32_t Y_RESOLUTION = 11; +constexpr int32_t MAJOR_RESOLUTION = 1; + +constexpr int POINTER_0_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_2_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_1_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_2_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int DOWN = AMOTION_EVENT_ACTION_DOWN; +constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE; +constexpr int UP = AMOTION_EVENT_ACTION_UP; +constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL; + +struct PointerData { + float x; + float y; + float major; +}; + +static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action, + const std::vector& points) { + size_t pointerCount = points.size(); + if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { + EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; + } + + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, points[i].major); + } + + // Define a valid motion event. + NotifyMotionArgs args(/* id */ 0, eventTime, 0 /*readTime*/, DEVICE_ID, + AINPUT_SOURCE_TOUCHSCREEN, 0 /*displayId*/, POLICY_FLAG_PASS_TO_USER, + action, /* actionButton */ 0, + /* flags */ 0, AMETA_NONE, /* buttonState */ 0, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, + pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /* videoFrames */ {}); + + return args; +} + +static InputDeviceInfo generateTestDeviceInfo() { + InputDeviceIdentifier identifier; + + auto info = InputDeviceInfo(); + info.initialize(DEVICE_ID, /*generation*/ 1, /*controllerNumber*/ 1, identifier, "alias", + /*isExternal*/ false, /*hasMic*/ false); + info.addSource(AINPUT_SOURCE_TOUCHSCREEN); + info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat*/ 0, + /*fuzz*/ 0, X_RESOLUTION); + info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat*/ 0, + /*fuzz*/ 0, Y_RESOLUTION); + info.addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN, 0, 255, + /*flat*/ 0, /*fuzz*/ 0, MAJOR_RESOLUTION); + + return info; +} + +static AndroidPalmFilterDeviceInfo generatePalmFilterDeviceInfo() { + InputDeviceInfo androidInfo = generateTestDeviceInfo(); + std::optional info = createPalmFilterDeviceInfo(androidInfo); + if (!info) { + ADD_FAILURE() << "Could not convert android device info to ::ui version"; + return {}; + } + return *info; +} + +TEST(DeviceInfoConversionTest, TabletDeviceTest) { + AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo(); + ASSERT_EQ(X_RESOLUTION, info.x_res); + ASSERT_EQ(Y_RESOLUTION, info.y_res); + ASSERT_EQ(MAJOR_RESOLUTION, info.touch_major_res); + ASSERT_EQ(1599, info.max_x); + ASSERT_EQ(2559, info.max_y); +} + +static void assertArgs(const NotifyMotionArgs& args, int32_t action, + const std::vector>& pointers) { + ASSERT_EQ(action, args.action); + ASSERT_EQ(pointers.size(), args.pointerCount); + for (size_t i = 0; i < args.pointerCount; i++) { + const auto& [pointerId, pointerData] = pointers[i]; + ASSERT_EQ(pointerId, args.pointerProperties[i].id); + ASSERT_EQ(pointerData.x, args.pointerCoords[i].getX()); + ASSERT_EQ(pointerData.y, args.pointerCoords[i].getY()); + ASSERT_EQ(pointerData.major, + args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR)); + } +} + +TEST(RemovePointerIdsTest, RemoveOnePointer) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, + AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}}); + + NotifyMotionArgs pointer1Only = removePointerIds(args, {0}); + assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}}); + + NotifyMotionArgs pointer0Only = removePointerIds(args, {1}); + assertArgs(pointer0Only, AMOTION_EVENT_ACTION_MOVE, {{0, {1, 2, 3}}}); +} + +/** + * Remove 2 out of 3 pointers during a MOVE event. + */ +TEST(RemovePointerIdsTest, RemoveTwoPointers) { + NotifyMotionArgs args = + generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, AMOTION_EVENT_ACTION_MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointer1Only = removePointerIds(args, {0, 2}); + assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}}); +} + +/** + * Remove an active pointer during a POINTER_DOWN event, and also remove a non-active + * pointer during a POINTER_DOWN event. + */ +TEST(RemovePointerIdsTest, ActionPointerDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointers0And2 = removePointerIds(args, {1}); + assertArgs(pointers0And2, ACTION_UNKNOWN, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); + + NotifyMotionArgs pointers1And2 = removePointerIds(args, {0}); + assertArgs(pointers1And2, POINTER_0_DOWN, {{1, {4, 5, 6}}, {2, {7, 8, 9}}}); +} + +/** + * Remove all pointers during a MOVE event. + */ +TEST(RemovePointerIdsTest, RemoveAllPointersDuringMove) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, + AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}}); + + NotifyMotionArgs noPointers = removePointerIds(args, {0, 1}); + ASSERT_EQ(0u, noPointers.pointerCount); +} + +/** + * If we have ACTION_POINTER_DOWN, and we remove all pointers except for the active pointer, + * then we should just have ACTION_DOWN. Likewise, a POINTER_UP event should become an UP event. + */ +TEST(RemovePointerIdsTest, PointerDownBecomesDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointer1 = removePointerIds(args, {0, 2}); + assertArgs(pointer1, DOWN, {{1, {4, 5, 6}}}); + + args.action = POINTER_1_UP; + pointer1 = removePointerIds(args, {0, 2}); + assertArgs(pointer1, UP, {{1, {4, 5, 6}}}); +} + +/** + * If a pointer that is now going down is canceled, then we can just drop the POINTER_DOWN event. + */ +TEST(CancelSuppressedPointersTest, CanceledPointerDownIsDropped) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_TRUE(result.empty()); +} + +/** + * If a pointer is already suppressed, the POINTER_UP event for this pointer should be dropped + */ +TEST(CancelSuppressedPointersTest, SuppressedPointerUpIsDropped) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_TRUE(result.empty()); +} + +/** + * If a pointer is already suppressed, it should be removed from a MOVE event. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointerIsRemovedDuringMove) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); +} + +/** + * If a pointer just got canceled during a MOVE event, we should see two events: + * 1) ACTION_POINTER_UP with FLAG_CANCELED so that this pointer is lifted + * 2) A MOVE event without this pointer + */ +TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(2u, result.size()); + assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); +} + +/** + * If we have a single pointer that gets canceled during a MOVE, the entire gesture + * should be canceled with ACTION_CANCEL. + */ +TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * If one of 3 pointers gets canceled during a POINTER_UP event, we should proceed with POINTER_UP, + * but this event should also have FLAG_CANCELED to indicate that this pointer was unintentional. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * Same test as above, but we change the pointer's index to 0 instead of 1. This helps detect + * errors with handling pointer index inside the action. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_0_UP, + {{1, 2, 3}, {4, 5, 6}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * If two pointers are canceled simultaneously during MOVE, we should see a single ACTION_CANCEL + * event. This event would cancel the entire gesture. + */ +TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) { + NotifyMotionArgs args = + generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}, {4, 5, 6}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * Similar test to above. During a POINTER_UP event, both pointers are detected as 'palm' and + * therefore should be removed. In this case, we should send a single ACTION_CANCEL that + * would undo the entire gesture. + */ +TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * When all pointers have been removed from the touch stream, and we have a new POINTER_DOWN, + * this should become a regular DOWN event because it's the only pointer that will be valid now. + */ +TEST(CancelSuppressedPointersTest, NewPointerDownBecomesDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_2_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {0, 1}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], DOWN, {{2, {7, 8, 9}}}); + ASSERT_EQ(0, result[0].flags); +} + +/** + * Call 'getTouches' for a DOWN event and check that the resulting 'InProgressTouchEvdev' + * struct is populated as expected. + */ +TEST(GetTouchesTest, ConvertDownEvent) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, DOWN, {{1, 2, 3}}); + AndroidPalmFilterDeviceInfo deviceInfo = generatePalmFilterDeviceInfo(); + SlotState slotState; + SlotState oldSlotState = slotState; + slotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = + getTouches(args, deviceInfo, oldSlotState, slotState); + ASSERT_EQ(1u, touches.size()); + ::ui::InProgressTouchEvdev expected; + + expected.major = 3; + expected.minor = 0; + expected.tool_type = MT_TOOL_FINGER; + expected.altered = true; + expected.was_cancelled = false; + expected.cancelled = false; + expected.delayed = false; + expected.was_delayed = false; + expected.held = false; + expected.was_held = false; + expected.was_touching = false; + expected.touching = true; + expected.x = 1; + expected.y = 2; + expected.tracking_id = 0; + std::optional slot = slotState.getSlotForPointerId(0); + ASSERT_TRUE(slot); + expected.slot = *slot; + expected.pressure = 0; + expected.tool_code = BTN_TOOL_FINGER; + expected.reported_tool_type = ::ui::EventPointerType::kTouch; + expected.stylus_button = false; + + ASSERT_EQ(expected, touches[0]) << toString(touches[0]); +} + +// --- UnwantedInteractionBlockerTest --- + +class UnwantedInteractionBlockerTest : public testing::Test { +protected: + TestInputListener mTestListener; + std::unique_ptr mBlocker; + + void SetUp() override { + mBlocker = std::make_unique(mTestListener, + /*enablePalmRejection*/ true); + } +}; + +/** + * Create a basic configuration change and send it to input classifier. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListener) { + // Create a basic configuration change and send to classifier + NotifyConfigurationChangedArgs args(1 /*sequenceNum*/, 2 /*eventTime*/); + + mBlocker->notifyConfigurationChanged(&args); + NotifyConfigurationChangedArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Keys are not handled in 'UnwantedInteractionBlocker' and should be passed + * to next stage unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, KeyIsPassedToNextListener) { + // Create a basic key event and send to classifier + NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/, + AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/, + AMETA_NONE, 6 /*downTime*/); + + mBlocker->notifyKey(&args); + NotifyKeyArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Create a basic motion event. Since it's just a DOWN event, it should not + * be detected as palm and should be sent to the next listener stage + * unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, DownEventIsPassedToNextListener) { + NotifyMotionArgs motionArgs = + generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&motionArgs); + NotifyMotionArgs args; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(motionArgs, args); +} + +/** + * Create a basic switch event and send it to the UnwantedInteractionBlocker. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, SwitchIsPassedToNextListener) { + NotifySwitchArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 3 /*policyFlags*/, 4 /*switchValues*/, + 5 /*switchMask*/); + + mBlocker->notifySwitch(&args); + NotifySwitchArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Create a basic device reset event and send it to UnwantedInteractionBlocker. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, DeviceResetIsPassedToNextListener) { + NotifyDeviceResetArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, DEVICE_ID); + + mBlocker->notifyDeviceReset(&args); + NotifyDeviceResetArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * The state should be reset when device reset happens. That means, we can reset in the middle of a + * gesture, and start a new stream. There should be no crash. If the state wasn't reset correctly, + * a crash due to inconsistent event stream could have occurred. + */ +TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenResetHappens) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + NotifyDeviceResetArgs resetArgs(1 /*sequenceNum*/, 3 /*eventTime*/, DEVICE_ID); + mBlocker->notifyDeviceReset(&resetArgs); + // Start a new gesture with a DOWN event, even though the previous event stream was incomplete. + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, DOWN, {{7, 8, 9}}))); +} + +/** + * If input devices have changed, but the important device info that's used by the + * UnwantedInteractionBlocker has not changed, there should not be a reset. + */ +TEST_F(UnwantedInteractionBlockerTest, NoResetIfDeviceInfoChanges) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + + // Now pretend the device changed, even though nothing is different for DEVICE_ID in practice. + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + + // The MOVE event continues the gesture that started before 'devices changed', so it should not + // cause a crash. + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, MOVE, {{7, 8, 9}}))); +} + +using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; + +/** + * The state should be reset when device reset happens. If we receive an inconsistent event after + * the reset happens, crash should occur. + */ +TEST_F(UnwantedInteractionBlockerTestDeathTest, InconsistentEventAfterResetCausesACrash) { + ScopedSilentDeath _silentDeath; + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + NotifyDeviceResetArgs resetArgs(1 /*sequenceNum*/, 3 /*eventTime*/, DEVICE_ID); + mBlocker->notifyDeviceReset(&resetArgs); + // Sending MOVE without a DOWN -> should crash! + ASSERT_DEATH( + { + mBlocker->notifyMotion(&(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, + MOVE, {{7, 8, 9}}))); + }, + "Could not find slot"); +} + +/** + * There should be a crash when an inconsistent event is received. + */ +TEST_F(UnwantedInteractionBlockerTestDeathTest, WhenMoveWithoutDownCausesACrash) { + ScopedSilentDeath _silentDeath; + NotifyMotionArgs args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 2, 3}}); + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + ASSERT_DEATH({ mBlocker->notifyMotion(&args); }, "Could not find slot"); +} + +class PalmRejectorTest : public testing::Test { +protected: + std::unique_ptr mPalmRejector; + + void SetUp() override { + AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo(); + mPalmRejector = std::make_unique(info); + } +}; + +using PalmRejectorTestDeathTest = PalmRejectorTest; + +TEST_F(PalmRejectorTestDeathTest, InconsistentEventCausesACrash) { + ScopedSilentDeath _silentDeath; + constexpr nsecs_t downTime = 0; + NotifyMotionArgs args = + generateMotionArgs(downTime, 2 /*eventTime*/, MOVE, {{1406.0, 650.0, 52.0}}); + ASSERT_DEATH({ mPalmRejector->processMotion(args); }, "Could not find slot"); +} + +/** + * Use PalmRejector with actual touchscreen data and real model. + * Two pointers that should both be classified as palms. + */ +TEST_F(PalmRejectorTest, TwoPointersAreCanceled) { + std::vector argsList; + constexpr nsecs_t downTime = 255955749837000; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955759313000, MOVE, {{1406.0, 650.0, 52.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955766361000, MOVE, {{1429.0, 672.0, 46.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955775989000, MOVE, {{1417.0, 685.0, 41.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955775989000, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955792536000, MOVE, + {{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955799474000, MOVE, + {{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955809177000, MOVE, + {{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955816131000, MOVE, + {{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955825907000, MOVE, + {{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955832736000, MOVE, + {{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}})); + ASSERT_EQ(2u, argsList.size()); + ASSERT_EQ(POINTER_0_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(MOVE, argsList[1].action); + ASSERT_EQ(1u, argsList[1].pointerCount); + ASSERT_EQ(0, argsList[1].flags); + + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955842432000, MOVE, + {{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955849380000, MOVE, + {{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955859046000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955869823000, MOVE, + {{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955875641000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955882693000, MOVE, + {{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955892324000, MOVE, + {{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955899425000, MOVE, + {{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955909400000, MOVE, + {{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955915885000, MOVE, + {{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955925607000, MOVE, + {{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955932580000, MOVE, + {{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955942231000, MOVE, + {{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955949204000, MOVE, + {{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955959103000, MOVE, + {{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955965884000, MOVE, + {{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955975649000, MOVE, + {{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955982537000, MOVE, + {{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955992284000, MOVE, + {{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955999348000, POINTER_1_UP, + {{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}})); + ASSERT_TRUE(argsList.empty()); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955999348000, MOVE, {{1362.0, 716.0, 55.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956008885000, MOVE, {{1347.0, 707.0, 54.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956015791000, MOVE, {{1340.0, 698.0, 54.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956025804000, MOVE, {{1338.0, 694.0, 55.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956032314000, MOVE, {{1336.0, 690.0, 53.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956042329000, MOVE, {{1334.0, 685.0, 47.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956048979000, MOVE, {{1333.0, 679.0, 46.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956058813000, MOVE, {{1332.0, 672.0, 45.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956065592000, MOVE, {{1333.0, 666.0, 40.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956075276000, MOVE, {{1336.0, 661.0, 24.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956082198000, MOVE, {{1338.0, 656.0, 16.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956092059000, MOVE, {{1341.0, 649.0, 1.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956098764000, UP, {{1341.0, 649.0, 1.0}})); + ASSERT_TRUE(argsList.empty()); +} + +/** + * A test implementation of PalmDetectionFilter that allows you to specify which pointer you want + * the model to consider 'suppressed'. The pointer is specified using its position (x, y). + * Current limitation: + * Pointers may not cross each other in space during motion. Otherwise, any pointer with the + * position matching the suppressed position will be considered "palm". + */ +class TestFilter : public ::ui::PalmDetectionFilter { +public: + TestFilter(::ui::SharedPalmDetectionFilterState* state, + std::vector>& suppressedPointers) + : ::ui::PalmDetectionFilter(state), mSuppressedPointers(suppressedPointers) {} + + void Filter(const std::vector<::ui::InProgressTouchEvdev>& touches, ::base::TimeTicks time, + std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_hold, + std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_suppress) override { + updateSuppressedSlots(touches); + *slots_to_suppress = mSuppressedSlots; + } + + std::string FilterNameForTesting() const override { return "test filter"; } + +private: + void updateSuppressedSlots(const std::vector<::ui::InProgressTouchEvdev>& touches) { + for (::ui::InProgressTouchEvdev touch : touches) { + for (const auto& [x, y] : mSuppressedPointers) { + const float dx = (touch.x - x); + const float dy = (touch.y - y); + const float distanceSquared = dx * dx + dy * dy; + if (distanceSquared < 1) { + mSuppressedSlots.set(touch.slot, true); + } + } + } + } + + std::bitset<::ui::kNumTouchEvdevSlots> mSuppressedSlots; + std::vector>& mSuppressedPointers; +}; + +class PalmRejectorFakeFilterTest : public testing::Test { +protected: + std::unique_ptr mPalmRejector; + + void SetUp() override { + std::unique_ptr<::ui::PalmDetectionFilter> filter = + std::make_unique(&mSharedPalmState, /*byref*/ mSuppressedPointers); + mPalmRejector = + std::make_unique(generatePalmFilterDeviceInfo(), std::move(filter)); + } + + void suppressPointerAtPosition(float x, float y) { mSuppressedPointers.push_back({x, y}); } + +private: + std::vector> mSuppressedPointers; + ::ui::SharedPalmDetectionFilterState mSharedPalmState; // unused, but we must retain ownership +}; + +/** + * When a MOVE event happens, the model identifies the pointer as palm. At that time, the palm + * rejector should send a POINTER_UP event for this pointer with FLAG_CANCELED, and subsequent + * events should have this pointer removed. + */ +TEST_F(PalmRejectorFakeFilterTest, OneOfTwoPointersIsCanceled) { + std::vector argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Cancel the second pointer + suppressPointerAtPosition(1059, 731); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + ASSERT_EQ(2u, argsList.size()); + // First event - cancel pointer 1 + ASSERT_EQ(POINTER_1_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + // Second event - send MOVE for the remaining pointer + ASSERT_EQ(MOVE, argsList[1].action); + ASSERT_EQ(0, argsList[1].flags); + + // Future move events only contain 1 pointer, because the second pointer will continue + // to be suppressed + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(MOVE, argsList[0].action); + ASSERT_EQ(1u, argsList[0].pointerCount); + ASSERT_EQ(1433, argsList[0].pointerCoords[0].getX()); + ASSERT_EQ(751, argsList[0].pointerCoords[0].getY()); +} + +/** + * Send two pointers, and suppress both of them. Check that ACTION_CANCEL is generated. + * Afterwards: + * 1) Future MOVE events are ignored. + * 2) When a new pointer goes down, ACTION_DOWN is generated + */ +TEST_F(PalmRejectorFakeFilterTest, NewDownEventAfterCancel) { + std::vector argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Cancel both pointers + suppressPointerAtPosition(1059, 731); + suppressPointerAtPosition(1400, 680); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, MOVE, {{1400, 680, 41}, {1059, 731, 10}})); + ASSERT_EQ(1u, argsList.size()); + // Cancel all + ASSERT_EQ(CANCEL, argsList[0].action); + ASSERT_EQ(2u, argsList[0].pointerCount); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + // Future move events are ignored + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + ASSERT_EQ(0u, argsList.size()); + + // When a new pointer goes down, a new DOWN event is generated + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, POINTER_2_DOWN, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}, {1000, 700, 10}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(DOWN, argsList[0].action); + ASSERT_EQ(1u, argsList[0].pointerCount); + ASSERT_EQ(2, argsList[0].pointerProperties[0].id); +} + +/** + * 2 pointers are classified as palm simultaneously. When they are later + * released by Android, make sure that we drop both of these POINTER_UP events. + * Since they are classified as palm at the same time, we just need to receive a single CANCEL + * event. From MotionEvent docs: """A pointer id remains valid until the pointer eventually goes up + * (indicated by ACTION_UP or ACTION_POINTER_UP) or when the gesture is canceled (indicated by + * ACTION_CANCEL).""" + * This means that generating additional POINTER_UP events is not necessary. + * The risk here is that "oldSuppressedPointerIds" will not be correct, because it will update after + * each motion, but pointers are canceled one at a time by Android. + */ +TEST_F(PalmRejectorFakeFilterTest, TwoPointersCanceledWhenOnePointerGoesUp) { + std::vector argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Suppress both pointers!! + suppressPointerAtPosition(1414, 702); + suppressPointerAtPosition(1059, 731); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, POINTER_1_UP, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + // Future move events should not go to the listener. + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783049000, MOVE, {{1435.0, 755.0, 43.0}})); + ASSERT_EQ(0u, argsList.size()); + + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783059000, UP, {{1436.0, 756.0, 43.0}})); + ASSERT_EQ(0u, argsList.size()); +} + +/** + * Send 3 pointers, and then cancel one of them during a MOVE event. We should see ACTION_POINTER_UP + * generated for that. Next, another pointer is canceled during ACTION_POINTER_DOWN. For that + * pointer, we simply shouldn't send the event. + */ +TEST_F(PalmRejectorFakeFilterTest, CancelTwoPointers) { + std::vector argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + + // Suppress second pointer (pointer 1) + suppressPointerAtPosition(1060, 700); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, MOVE, + {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}})); + ASSERT_EQ(2u, argsList.size()); + ASSERT_EQ(POINTER_1_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action); + ASSERT_EQ(0, argsList[1].flags); + + // A new pointer goes down and gets suppressed right away. It should just be dropped + suppressPointerAtPosition(1001, 601); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}})); + + ASSERT_EQ(0u, argsList.size()); + // Likewise, pointer that's already canceled should be ignored + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_UP, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}})); + ASSERT_EQ(0u, argsList.size()); + + // Cancel all pointers when pointer 1 goes up. Pointer 1 was already canceled earlier. + suppressPointerAtPosition(1417, 685); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_UP, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(CANCEL, argsList[0].action); +} + +} // namespace android -- cgit v1.2.3-59-g8ed1b From 8e9a856f72a004b6597deebceda91ceeea655c08 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Wed, 9 Feb 2022 13:44:29 +0000 Subject: Enable Android clang-tidy checks for inputflinger Mostly just adding CLOEXEC to any operations that open a new fd. Bug: 218657035 Test: build Change-Id: Iae1a32bfdbb663759fb347af7e3426b03d5f831f --- services/inputflinger/Android.bp | 9 +++++++++ services/inputflinger/InputManager.cpp | 2 -- services/inputflinger/reader/EventHub.cpp | 4 ++-- services/inputflinger/reader/TouchVideoDevice.cpp | 2 +- services/inputflinger/tests/InputDispatcher_test.cpp | 3 ++- services/inputflinger/tests/UinputDevice.cpp | 3 ++- 6 files changed, 16 insertions(+), 7 deletions(-) (limited to 'services/inputflinger/InputManager.cpp') diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 22a69e5a30..6c77c8c647 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -22,6 +22,10 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +inputflinger_tidy_checks = [ + "android-*", +] + cc_defaults { name: "inputflinger_defaults", cpp_std: "c++20", @@ -38,6 +42,11 @@ cc_defaults { sanitize: { misc_undefined: ["bounds"], }, + tidy: true, + tidy_checks: [ + "-*", // Disable all checks not explicitly enabled for now + ] + inputflinger_tidy_checks, + tidy_checks_as_errors: inputflinger_tidy_checks, } ///////////////////////////////////////////////// diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 7a9862d065..7b03631e1f 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -33,8 +33,6 @@ namespace android { using gui::FocusRequest; -using gui::WindowInfo; -using gui::WindowInfoHandle; static int32_t exceptionCodeFromStatusT(status_t status) { switch (status) { diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index db67877cc9..8bd3899469 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -685,7 +685,7 @@ EventHub::EventHub(void) mEpollFd = epoll_create1(EPOLL_CLOEXEC); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); - mINotifyFd = inotify_init(); + mINotifyFd = inotify_init1(IN_CLOEXEC); std::error_code errorCode; bool isDeviceInotifyAdded = false; @@ -713,7 +713,7 @@ EventHub::EventHub(void) LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); int wakeFds[2]; - result = pipe(wakeFds); + result = pipe2(wakeFds, O_CLOEXEC); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); mWakeReadPipeFd = wakeFds[0]; diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp index c7c8e28419..2f8138b832 100644 --- a/services/inputflinger/reader/TouchVideoDevice.cpp +++ b/services/inputflinger/reader/TouchVideoDevice.cpp @@ -49,7 +49,7 @@ TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& dev }; std::unique_ptr TouchVideoDevice::create(std::string devicePath) { - unique_fd fd(open(devicePath.c_str(), O_RDWR | O_NONBLOCK)); + unique_fd fd(open(devicePath.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC)); if (fd.get() == INVALID_FD) { ALOGE("Could not open video device %s: %s", devicePath.c_str(), strerror(errno)); return nullptr; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 872882e5d8..1950a7f463 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -6331,7 +6332,7 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesInputInOrder) { const std::vector> channels{spy1, spy2, window, spy3}; const size_t numChannels = channels.size(); - base::unique_fd epollFd(epoll_create1(0 /*flags*/)); + base::unique_fd epollFd(epoll_create1(EPOLL_CLOEXEC)); if (!epollFd.ok()) { FAIL() << "Failed to create epoll fd"; } diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 132b877bb9..9c939198f3 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -17,6 +17,7 @@ #include "UinputDevice.h" #include +#include namespace android { @@ -32,7 +33,7 @@ UinputDevice::~UinputDevice() { } void UinputDevice::init() { - mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK)); + mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK | O_CLOEXEC)); if (mDeviceFd < 0) { FAIL() << "Can't open /dev/uinput :" << strerror(errno); } -- cgit v1.2.3-59-g8ed1b From a91d8576d9fbb5c2fecbcad8a4f3eef0d2fcc30f Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 17 May 2022 05:03:42 -0700 Subject: Add lock to protect UnwantedInteractionBlocker The call to 'dump' may come from any thread, and therefore could cause a crash. Add a lock to protect this input stage. To run the test: adb shell -t "/data/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter='*Dump*' --gtest_repeat=100000 --gtest_break_on_failure" Before this patch, the test failed after ~5K - ~13K iterations (took 10-20 seconds to crash). Bug: 232645962 Test: m inputflinger_tests && adb sync data && Change-Id: I2a199690450bc5bb4a8576aa59075e99d37a531b (cherry picked from commit 9f330c542b48dc6edba9aeaff3b3f4bf305713f3) --- include/input/PrintTools.h | 9 ++ libs/input/PrintTools.cpp | 17 +++ services/inputflinger/InputClassifier.cpp | 62 ++++++---- services/inputflinger/InputClassifier.h | 6 +- services/inputflinger/InputManager.cpp | 13 +- services/inputflinger/InputManager.h | 6 +- .../inputflinger/UnwantedInteractionBlocker.cpp | 133 +++++++++------------ services/inputflinger/UnwantedInteractionBlocker.h | 11 +- .../include/UnwantedInteractionBlockerInterface.h | 4 +- .../tests/UnwantedInteractionBlocker_test.cpp | 22 ++++ 10 files changed, 171 insertions(+), 112 deletions(-) (limited to 'services/inputflinger/InputManager.cpp') diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h index 7c3b29b55f..0a75278494 100644 --- a/include/input/PrintTools.h +++ b/include/input/PrintTools.h @@ -58,4 +58,13 @@ std::string dumpMap(const std::map& map, std::string (*keyToString)(const const char* toString(bool value); +/** + * Add "prefix" to the beginning of each line in the provided string + * "str". + * The string 'str' is typically multi-line. + * The most common use case for this function is to add some padding + * when dumping state. + */ +std::string addLinePrefix(std::string str, const std::string& prefix); + } // namespace android \ No newline at end of file diff --git a/libs/input/PrintTools.cpp b/libs/input/PrintTools.cpp index 5d6ae4ed91..01f6bf514b 100644 --- a/libs/input/PrintTools.cpp +++ b/libs/input/PrintTools.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "PrintTools" #include +#include namespace android { @@ -24,4 +25,20 @@ const char* toString(bool value) { return value ? "true" : "false"; } +std::string addLinePrefix(std::string str, const std::string& prefix) { + std::stringstream ss; + bool newLineStarted = true; + for (const auto& ch : str) { + if (newLineStarted) { + ss << prefix; + newLineStarted = false; + } + if (ch == '\n') { + newLineStarted = true; + } + ss << ch; + } + return ss.str(); +} + } // namespace android diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 3ea0986d41..8ce2f35d7b 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -367,7 +367,7 @@ void MotionClassifier::dump(std::string& dump) { // --- InputClassifier --- -InputClassifier::InputClassifier(InputListenerInterface& listener) : mListener(listener) {} +InputClassifier::InputClassifier(InputListenerInterface& listener) : mQueuedListener(listener) {} void InputClassifier::onBinderDied(void* cookie) { InputClassifier* classifier = static_cast(cookie); @@ -417,55 +417,67 @@ void InputClassifier::setMotionClassifierEnabled(bool enabled) { void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { // pass through - mListener.notifyConfigurationChanged(args); + mQueuedListener.notifyConfigurationChanged(args); + mQueuedListener.flush(); } void InputClassifier::notifyKey(const NotifyKeyArgs* args) { // pass through - mListener.notifyKey(args); + mQueuedListener.notifyKey(args); + mQueuedListener.flush(); } void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { - std::scoped_lock lock(mLock); - // MotionClassifier is only used for touch events, for now - const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); - if (!sendToMotionClassifier) { - mListener.notifyMotion(args); - return; - } - - NotifyMotionArgs newArgs(*args); - newArgs.classification = mMotionClassifier->classify(newArgs); - mListener.notifyMotion(&newArgs); + { // acquire lock + std::scoped_lock lock(mLock); + // MotionClassifier is only used for touch events, for now + const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); + if (!sendToMotionClassifier) { + mQueuedListener.notifyMotion(args); + } else { + NotifyMotionArgs newArgs(*args); + newArgs.classification = mMotionClassifier->classify(newArgs); + mQueuedListener.notifyMotion(&newArgs); + } + } // release lock + mQueuedListener.flush(); } void InputClassifier::notifySensor(const NotifySensorArgs* args) { // pass through - mListener.notifySensor(args); + mQueuedListener.notifySensor(args); + mQueuedListener.flush(); } void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) { // pass through - mListener.notifyVibratorState(args); + mQueuedListener.notifyVibratorState(args); + mQueuedListener.flush(); } void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { // pass through - mListener.notifySwitch(args); + mQueuedListener.notifySwitch(args); + mQueuedListener.flush(); } void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { - std::scoped_lock lock(mLock); - if (mMotionClassifier) { - mMotionClassifier->reset(*args); - } + { // acquire lock + std::scoped_lock lock(mLock); + if (mMotionClassifier) { + mMotionClassifier->reset(*args); + } + } // release lock + // continue to next stage - mListener.notifyDeviceReset(args); + mQueuedListener.notifyDeviceReset(args); + mQueuedListener.flush(); } void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { // pass through - mListener.notifyPointerCaptureChanged(args); + mQueuedListener.notifyPointerCaptureChanged(args); + mQueuedListener.flush(); } void InputClassifier::setMotionClassifierLocked( @@ -490,6 +502,10 @@ void InputClassifier::dump(std::string& dump) { dump += "\n"; } +void InputClassifier::monitor() { + std::scoped_lock lock(mLock); +} + InputClassifier::~InputClassifier() { } diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index e2a0bc26f6..56cf760256 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -96,6 +96,9 @@ public: */ virtual void dump(std::string& dump) = 0; + /* Called by the heatbeat to ensures that the classifier has not deadlocked. */ + virtual void monitor() = 0; + InputClassifierInterface() { } virtual ~InputClassifierInterface() { } }; @@ -247,6 +250,7 @@ public: void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; void dump(std::string& dump) override; + void monitor() override; ~InputClassifier(); @@ -257,7 +261,7 @@ private: // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; // The next stage to pass input events to - InputListenerInterface& mListener; + QueuedInputListener mQueuedListener; std::unique_ptr mMotionClassifier GUARDED_BY(mLock); std::future mInitializeMotionClassifier GUARDED_BY(mLock); diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 7b03631e1f..9767cd9b71 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -62,8 +62,8 @@ InputManager::InputManager( const sp& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = std::make_unique(*mDispatcher); - mUnwantedInteractionBlocker = std::make_unique(*mClassifier); - mReader = createInputReader(readerPolicy, *mUnwantedInteractionBlocker); + mBlocker = std::make_unique(*mClassifier); + mReader = createInputReader(readerPolicy, *mBlocker); } InputManager::~InputManager() { @@ -111,7 +111,7 @@ InputReaderInterface& InputManager::getReader() { } UnwantedInteractionBlockerInterface& InputManager::getUnwantedInteractionBlocker() { - return *mUnwantedInteractionBlocker; + return *mBlocker; } InputClassifierInterface& InputManager::getClassifier() { @@ -122,6 +122,13 @@ InputDispatcherInterface& InputManager::getDispatcher() { return *mDispatcher; } +void InputManager::monitor() { + mReader->monitor(); + mBlocker->monitor(); + mClassifier->monitor(); + mDispatcher->monitor(); +} + // Used by tests only. binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { IPCThreadState* ipc = IPCThreadState::self(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 35d2b0fa19..8aad35bf1e 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -90,6 +90,9 @@ public: /* Gets the input dispatcher. */ virtual InputDispatcherInterface& getDispatcher() = 0; + + /* Check that the input stages have not deadlocked. */ + virtual void monitor() = 0; }; class InputManager : public InputManagerInterface, public BnInputFlinger { @@ -108,6 +111,7 @@ public: UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() override; InputClassifierInterface& getClassifier() override; InputDispatcherInterface& getDispatcher() override; + void monitor() override; status_t dump(int fd, const Vector& args) override; binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; @@ -117,7 +121,7 @@ public: private: std::unique_ptr mReader; - std::unique_ptr mUnwantedInteractionBlocker; + std::unique_ptr mBlocker; std::unique_ptr mClassifier; diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp index b69e16ac85..f57ff33d50 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -18,6 +18,7 @@ #include "UnwantedInteractionBlocker.h" #include +#include #include #include #include @@ -80,47 +81,6 @@ static int getLinuxToolType(int32_t toolType) { return MT_TOOL_FINGER; } -static std::string addPrefix(std::string str, const std::string& prefix) { - std::stringstream ss; - bool newLineStarted = true; - for (const auto& ch : str) { - if (newLineStarted) { - ss << prefix; - newLineStarted = false; - } - if (ch == '\n') { - newLineStarted = true; - } - ss << ch; - } - return ss.str(); -} - -template -static std::string dumpSet(const std::set& v) { - static_assert(std::is_integral::value, "Only integral types can be printed."); - std::string out; - for (const T& entry : v) { - out += out.empty() ? "{" : ", "; - out += android::base::StringPrintf("%i", entry); - } - return out.empty() ? "{}" : (out + "}"); -} - -template -static std::string dumpMap(const std::map& map) { - static_assert(std::is_integral::value, "Keys should have integral type to be printed."); - static_assert(std::is_integral::value, "Values should have integral type to be printed."); - std::string out; - for (const auto& [k, v] : map) { - if (!out.empty()) { - out += "\n"; - } - out += android::base::StringPrintf("%i : %i", static_cast(k), static_cast(v)); - } - return out; -} - static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) { std::string out; out += StringPrintf("max_x = %.2f\n", info.max_x); @@ -168,10 +128,6 @@ static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) { return AMOTION_EVENT_ACTION_MOVE; } -static const char* toString(bool value) { - return value ? "true" : "false"; -} - std::string toString(const ::ui::InProgressTouchEvdev& touch) { return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu," " pressure=%.1f, major=%i, minor=%i, " @@ -356,69 +312,87 @@ UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& l UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection) - : mListener(listener), mEnablePalmRejection(enablePalmRejection) {} + : mQueuedListener(listener), mEnablePalmRejection(enablePalmRejection) {} void UnwantedInteractionBlocker::notifyConfigurationChanged( const NotifyConfigurationChangedArgs* args) { - mListener.notifyConfigurationChanged(args); + mQueuedListener.notifyConfigurationChanged(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) { - mListener.notifyKey(args); + mQueuedListener.notifyKey(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { - const std::vector processedArgs = - mPreferStylusOverTouchBlocker.processMotion(*args); - for (const NotifyMotionArgs& loopArgs : processedArgs) { - notifyMotionInner(&loopArgs); - } + { // acquire lock + std::scoped_lock lock(mLock); + const std::vector processedArgs = + mPreferStylusOverTouchBlocker.processMotion(*args); + for (const NotifyMotionArgs& loopArgs : processedArgs) { + notifyMotionLocked(&loopArgs); + } + } // release lock + + // Call out to the next stage without holding the lock + mQueuedListener.flush(); } -void UnwantedInteractionBlocker::notifyMotionInner(const NotifyMotionArgs* args) { +void UnwantedInteractionBlocker::notifyMotionLocked(const NotifyMotionArgs* args) { auto it = mPalmRejectors.find(args->deviceId); const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source); if (!sendToPalmRejector) { - mListener.notifyMotion(args); + mQueuedListener.notifyMotion(args); return; } - const std::vector newMotions = it->second.processMotion(*args); - for (const NotifyMotionArgs& newArgs : newMotions) { - mListener.notifyMotion(&newArgs); + std::vector processedArgs = it->second.processMotion(*args); + for (const NotifyMotionArgs& loopArgs : processedArgs) { + mQueuedListener.notifyMotion(&loopArgs); } } void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs* args) { - mListener.notifySwitch(args); + mQueuedListener.notifySwitch(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs* args) { - mListener.notifySensor(args); + mQueuedListener.notifySensor(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs* args) { - mListener.notifyVibratorState(args); + mQueuedListener.notifyVibratorState(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* args) { - auto it = mPalmRejectors.find(args->deviceId); - if (it != mPalmRejectors.end()) { - AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo(); - // Re-create the object instead of resetting it - mPalmRejectors.erase(it); - mPalmRejectors.emplace(args->deviceId, info); - } - mListener.notifyDeviceReset(args); - mPreferStylusOverTouchBlocker.notifyDeviceReset(*args); + { // acquire lock + std::scoped_lock lock(mLock); + auto it = mPalmRejectors.find(args->deviceId); + if (it != mPalmRejectors.end()) { + AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo(); + // Re-create the object instead of resetting it + mPalmRejectors.erase(it); + mPalmRejectors.emplace(args->deviceId, info); + } + mQueuedListener.notifyDeviceReset(args); + mPreferStylusOverTouchBlocker.notifyDeviceReset(*args); + } // release lock + // Send events to the next stage without holding the lock + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyPointerCaptureChanged( const NotifyPointerCaptureChangedArgs* args) { - mListener.notifyPointerCaptureChanged(args); + mQueuedListener.notifyPointerCaptureChanged(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyInputDevicesChanged( const std::vector& inputDevices) { + std::scoped_lock lock(mLock); if (!mEnablePalmRejection) { // Palm rejection is disabled. Don't create any palm rejector objects. return; @@ -450,20 +424,23 @@ void UnwantedInteractionBlocker::notifyInputDevicesChanged( } void UnwantedInteractionBlocker::dump(std::string& dump) { + std::scoped_lock lock(mLock); dump += "UnwantedInteractionBlocker:\n"; dump += " mPreferStylusOverTouchBlocker:\n"; - dump += addPrefix(mPreferStylusOverTouchBlocker.dump(), " "); + dump += addLinePrefix(mPreferStylusOverTouchBlocker.dump(), " "); dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection)); dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n", toString(isPalmRejectionEnabled())); dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n"; for (const auto& [deviceId, palmRejector] : mPalmRejectors) { dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId); - dump += addPrefix(palmRejector.dump(), " "); + dump += addLinePrefix(palmRejector.dump(), " "); } } -void UnwantedInteractionBlocker::monitor() {} +void UnwantedInteractionBlocker::monitor() { + std::scoped_lock lock(mLock); +} UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {} @@ -529,9 +506,9 @@ std::optional SlotState::getSlotForPointerId(int32_t pointerId) const { std::string SlotState::dump() const { std::string out = "mSlotsByPointerId:\n"; - out += addPrefix(dumpMap(mSlotsByPointerId), " ") + "\n"; + out += addLinePrefix(dumpMap(mSlotsByPointerId), " ") + "\n"; out += "mPointerIdsBySlot:\n"; - out += addPrefix(dumpMap(mPointerIdsBySlot), " ") + "\n"; + out += addLinePrefix(dumpMap(mPointerIdsBySlot), " ") + "\n"; return out; } @@ -689,9 +666,9 @@ const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() { std::string PalmRejector::dump() const { std::string out; out += "mDeviceInfo:\n"; - out += addPrefix(dumpDeviceInfo(mDeviceInfo), " "); + out += addLinePrefix(dumpDeviceInfo(mDeviceInfo), " "); out += "mSlotState:\n"; - out += addPrefix(mSlotState.dump(), " "); + out += addLinePrefix(mSlotState.dump(), " "); out += "mSuppressedPointerIds: "; out += dumpSet(mSuppressedPointerIds) + "\n"; return out; diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h index 8a1cd7265e..a43376419f 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.h +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -19,6 +19,7 @@ #include #include +#include #include "include/UnwantedInteractionBlockerInterface.h" #include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h" #include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h" @@ -86,18 +87,20 @@ public: ~UnwantedInteractionBlocker(); private: + std::mutex mLock; // The next stage to pass input events to - InputListenerInterface& mListener; + + QueuedInputListener mQueuedListener; const bool mEnablePalmRejection; // When stylus is down, ignore touch - PreferStylusOverTouchBlocker mPreferStylusOverTouchBlocker; + PreferStylusOverTouchBlocker mPreferStylusOverTouchBlocker GUARDED_BY(mLock); // Detect and reject unwanted palms on screen // Use a separate palm rejector for every touch device. - std::map mPalmRejectors; + std::map mPalmRejectors GUARDED_BY(mLock); // TODO(b/210159205): delete this when simultaneous stylus and touch is supported - void notifyMotionInner(const NotifyMotionArgs* args); + void notifyMotionLocked(const NotifyMotionArgs* args) REQUIRES(mLock); }; class SlotState { diff --git a/services/inputflinger/include/UnwantedInteractionBlockerInterface.h b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h index 2327266563..1a6f8472a5 100644 --- a/services/inputflinger/include/UnwantedInteractionBlockerInterface.h +++ b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h @@ -39,11 +39,11 @@ public: /** * Dump the state of the interaction blocker. - * This method may be called on any thread (usually by the input manager). + * This method may be called on any thread (usually by the input manager on a binder thread). */ virtual void dump(std::string& dump) = 0; - /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ + /* Called by the heatbeat to ensures that the blocker has not deadlocked. */ virtual void monitor() = 0; UnwantedInteractionBlockerInterface() {} diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp index e378096df5..0062f426d7 100644 --- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "TestInputListener.h" @@ -547,6 +548,27 @@ TEST_F(UnwantedInteractionBlockerTest, StylusAfterTouchWorks) { mBlocker->notifyMotion(&args); } +/** + * Call dump, and on another thread, try to send some motions. The blocker should + * not crash. On 2022 hardware, this test requires ~ 13K executions (about 20 seconds) to reproduce + * the original bug. This is meant to be run with "--gtest_repeat=100000 --gtest_break_on_failure" + * options + */ +TEST_F(UnwantedInteractionBlockerTest, DumpCanBeAccessedOnAnotherThread) { + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&args1); + std::thread dumpThread([this]() { + std::string dump; + mBlocker->dump(dump); + }); + NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{4, 5, 6}}); + mBlocker->notifyMotion(&args2); + NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, UP, {{4, 5, 6}}); + mBlocker->notifyMotion(&args3); + dumpThread.join(); +} + using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; /** -- cgit v1.2.3-59-g8ed1b