diff options
4 files changed, 190 insertions, 47 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 9a43ed9b2e..8ea71fe69d 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4741,6 +4741,40 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< return true; } +// Binder call +bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) { + sp<IBinder> fromToken; + { // acquire lock + std::scoped_lock _l(mLock); + + sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken); + if (toWindowHandle == nullptr) { + ALOGW("Could not find window associated with token=%p", destChannelToken.get()); + return false; + } + + const int32_t displayId = toWindowHandle->getInfo()->displayId; + + auto touchStateIt = mTouchStatesByDisplay.find(displayId); + if (touchStateIt == mTouchStatesByDisplay.end()) { + ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched", + displayId); + return false; + } + + TouchState& state = touchStateIt->second; + if (state.windows.size() != 1) { + ALOGW("Cannot transfer touch state because there are %zu windows being touched", + state.windows.size()); + return false; + } + const TouchedWindow& touchedWindow = state.windows[0]; + fromToken = touchedWindow.windowHandle->getToken(); + } // release lock + + return transferTouchFocus(fromToken, destChannelToken); +} + void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { if (DEBUG_FOCUS) { ALOGD("Resetting and dropping all events (%s).", reason); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 7ba03e8063..6edc5f1188 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -81,60 +81,60 @@ class Connection; */ class InputDispatcher : public android::InputDispatcherInterface { protected: - virtual ~InputDispatcher(); + ~InputDispatcher() override; public: explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy); - virtual void dump(std::string& dump) override; - virtual void monitor() override; - virtual bool waitForIdle() override; - virtual status_t start() override; - virtual status_t stop() override; - - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; - virtual void notifyKey(const NotifyKeyArgs* args) override; - virtual void notifyMotion(const NotifyMotionArgs* args) override; - virtual void notifySwitch(const NotifySwitchArgs* args) override; - virtual void notifySensor(const NotifySensorArgs* args) override; - virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override; - virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; - virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; - - virtual android::os::InputEventInjectionResult injectInputEvent( + void dump(std::string& dump) override; + void monitor() override; + bool waitForIdle() override; + status_t start() override; + status_t stop() override; + + 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; + + android::os::InputEventInjectionResult injectInputEvent( const InputEvent* event, int32_t injectorPid, int32_t injectorUid, android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) override; - virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override; + std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override; - virtual void setInputWindows( - const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& - handlesPerDisplay) override; - virtual void setFocusedApplication( + void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& + handlesPerDisplay) override; + void setFocusedApplication( int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override; - virtual void setFocusedDisplay(int32_t displayId) override; - virtual void setInputDispatchMode(bool enabled, bool frozen) override; - virtual void setInputFilterEnabled(bool enabled) override; - virtual void setInTouchMode(bool inTouchMode) override; - virtual void setMaximumObscuringOpacityForTouch(float opacity) override; - virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override; - - virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop = false) override; - - virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel( + void setFocusedDisplay(int32_t displayId) override; + void setInputDispatchMode(bool enabled, bool frozen) override; + void setInputFilterEnabled(bool enabled) override; + void setInTouchMode(bool inTouchMode) override; + void setMaximumObscuringOpacityForTouch(float opacity) override; + void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override; + + bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool isDragDrop = false) override; + bool transferTouch(const sp<IBinder>& destChannelToken) override; + + base::Result<std::unique_ptr<InputChannel>> createInputChannel( const std::string& name) override; - virtual void setFocusedWindow(const FocusRequest&) override; - virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId, - bool isGestureMonitor, - const std::string& name, - int32_t pid) override; - virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override; - virtual status_t pilferPointers(const sp<IBinder>& token) override; - virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override; - virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override; + void setFocusedWindow(const FocusRequest&) override; + base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId, + bool isGestureMonitor, + const std::string& name, + int32_t pid) override; + status_t removeInputChannel(const sp<IBinder>& connectionToken) override; + status_t pilferPointers(const sp<IBinder>& token) override; + void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override; + bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override; std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index b601dfc8a4..7f85e5393b 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -151,6 +151,14 @@ public: */ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, bool isDragDrop) = 0; + + /** + * Transfer touch focus to the provided channel, no matter where the current touch is. + * + * Return true on success, false if there was no on-going touch. + */ + virtual bool transferTouch(const sp<IBinder>& destChannelToken) = 0; + /** * Sets focus on the specified window. */ diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 9687c83ca2..1da125a417 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1708,7 +1708,13 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } -TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { +using TransferFunction = + std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>; + +class TransferTouchFixture : public InputDispatcherTest, + public ::testing::WithParamInterface<TransferFunction> {}; + +TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); // Create a couple of windows @@ -1729,8 +1735,10 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { firstWindow->consumeMotionDown(); secondWindow->assertNoEvents(); - // Transfer touch focus to the second window - mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + // Transfer touch to the second window + TransferFunction f = GetParam(); + const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken()); + ASSERT_TRUE(success); // The first window gets cancel and the second gets down firstWindow->consumeMotionCancel(); secondWindow->consumeMotionDown(); @@ -1745,7 +1753,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { secondWindow->consumeMotionUp(); } -TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { +TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); PointF touchPoint = {10, 10}; @@ -1780,7 +1788,9 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { secondWindow->assertNoEvents(); // Transfer touch focus to the second window - mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + TransferFunction f = GetParam(); + bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken()); + ASSERT_TRUE(success); // The first window gets cancel and the second gets down and pointer down firstWindow->consumeMotionCancel(); secondWindow->consumeMotionDown(); @@ -1807,6 +1817,21 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { secondWindow->consumeMotionUp(); } +// For the cases of single pointer touch and two pointers non-split touch, the api's +// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ +// for the case where there are multiple pointers split across several windows. +INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture, + ::testing::Values( + [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/, + sp<IBinder> destChannelToken) { + return dispatcher->transferTouch(destChannelToken); + }, + [&](sp<InputDispatcher> dispatcher, sp<IBinder> from, + sp<IBinder> to) { + return dispatcher->transferTouchFocus(from, to, + false /*isDragAndDrop*/); + })); + TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -1877,6 +1902,82 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { secondWindow->consumeMotionUp(); } +// Same as TransferTouchFocus_TwoPointersSplitTouch, but using 'transferTouch' api. +// Unlike 'transferTouchFocus', calling 'transferTouch' when there are two windows receiving +// touch is not supported, so the touch should continue on those windows and the transferred-to +// window should get nothing. +TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + // Create a non touch modal window that supports split touch + sp<FakeWindowHandle> firstWindow = + new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + firstWindow->setFrame(Rect(0, 0, 600, 400)); + firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); + + // Create a non touch modal window that supports split touch + sp<FakeWindowHandle> secondWindow = + new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); + secondWindow->setFrame(Rect(0, 400, 600, 800)); + secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); + + // Add the windows to the dispatcher + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); + + PointF pointInFirst = {300, 200}; + PointF pointInSecond = {300, 600}; + + // Send down to the first window + NotifyMotionArgs firstDownMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {pointInFirst}); + mDispatcher->notifyMotion(&firstDownMotionArgs); + // Only the first window should get the down event + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); + + // Send down to the second window + NotifyMotionArgs secondDownMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInFirst, pointInSecond}); + mDispatcher->notifyMotion(&secondDownMotionArgs); + // The first window gets a move and the second a down + firstWindow->consumeMotionMove(); + secondWindow->consumeMotionDown(); + + // Transfer touch focus to the second window + const bool transferred = mDispatcher->transferTouch(secondWindow->getToken()); + // The 'transferTouch' call should not succeed, because there are 2 touched windows + ASSERT_FALSE(transferred); + firstWindow->assertNoEvents(); + secondWindow->assertNoEvents(); + + // The rest of the dispatch should proceed as normal + // Send pointer up to the second window + NotifyMotionArgs pointerUpMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInFirst, pointInSecond}); + mDispatcher->notifyMotion(&pointerUpMotionArgs); + // The first window gets MOVE and the second gets pointer up + firstWindow->consumeMotionMove(); + secondWindow->consumeMotionUp(); + + // Send up event to the first window + NotifyMotionArgs upMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&upMotionArgs); + // The first window gets nothing and the second gets up + firstWindow->consumeMotionUp(); + secondWindow->assertNoEvents(); +} + TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = |