diff options
author | 2021-09-14 13:30:25 +0000 | |
---|---|---|
committer | 2021-09-14 13:30:25 +0000 | |
commit | a6efb77a590aba4bc7929b0e87a506c74e27e05d (patch) | |
tree | 211e4fd34ff5050e9d2ada63c886c60bd0202618 | |
parent | 07ef7db9c8f75d7092eaee8ed6dcce9535c95790 (diff) | |
parent | abbb9d8a0d43df618b75c8a4b7fd796f0b1cb9c2 (diff) |
Fix drag and drop failed while recording video am: abbb9d8a0d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/15738384
Change-Id: I8f5ef602c7c04ec37c067b762506bb43b3e1210e
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 106 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 4 | ||||
-rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 143 |
3 files changed, 191 insertions, 62 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 3dea9ce14c..e3912b3e79 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4789,6 +4789,18 @@ void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mod mBlockUntrustedTouchesMode = mode; } +std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked( + const sp<IBinder>& token) { + for (auto& [displayId, state] : mTouchStatesByDisplay) { + for (TouchedWindow& w : state.windows) { + if (w.windowHandle->getToken() == token) { + return std::make_pair(&state, &w); + } + } + } + return std::make_pair(nullptr, nullptr); +} + bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, bool isDragDrop) { if (fromToken == toToken) { @@ -4801,58 +4813,43 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< { // acquire lock std::scoped_lock _l(mLock); - sp<WindowInfoHandle> fromWindowHandle = getWindowHandleLocked(fromToken); - sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken); - if (fromWindowHandle == nullptr || toWindowHandle == nullptr) { - ALOGW("Cannot transfer focus because from or to window not found."); + // Find the target touch state and touched window by fromToken. + auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken); + if (state == nullptr || touchedWindow == nullptr) { + ALOGD("Focus transfer failed because from window is not being touched."); return false; } - if (DEBUG_FOCUS) { - ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", - fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); - } - if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) { - if (DEBUG_FOCUS) { - ALOGD("Cannot transfer focus because windows are on different displays."); - } + + const int32_t displayId = state->displayId; + sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); + if (toWindowHandle == nullptr) { + ALOGW("Cannot transfer focus because to window not found."); return false; } - bool found = false; - for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) { - TouchState& state = pair.second; - for (size_t i = 0; i < state.windows.size(); i++) { - const TouchedWindow& touchedWindow = state.windows[i]; - if (touchedWindow.windowHandle == fromWindowHandle) { - int32_t oldTargetFlags = touchedWindow.targetFlags; - BitSet32 pointerIds = touchedWindow.pointerIds; - - state.windows.erase(state.windows.begin() + i); - - int32_t newTargetFlags = oldTargetFlags & - (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | - InputTarget::FLAG_DISPATCH_AS_IS); - state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); + if (DEBUG_FOCUS) { + ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", + touchedWindow->windowHandle->getName().c_str(), + toWindowHandle->getName().c_str()); + } - // Store the dragging window. - if (isDragDrop) { - mDragState = std::make_unique<DragState>(toWindowHandle); - } + // Erase old window. + int32_t oldTargetFlags = touchedWindow->targetFlags; + BitSet32 pointerIds = touchedWindow->pointerIds; + state->removeWindowByToken(fromToken); - found = true; - goto Found; - } - } - } - Found: + // Add new window. + int32_t newTargetFlags = oldTargetFlags & + (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | + InputTarget::FLAG_DISPATCH_AS_IS); + state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); - if (!found) { - if (DEBUG_FOCUS) { - ALOGD("Focus transfer failed because from window did not have focus."); - } - return false; + // Store the dragging window. + if (isDragDrop) { + mDragState = std::make_unique<DragState>(toWindowHandle); } + // Synthesize cancel for old window and down for new window. sp<Connection> fromConnection = getConnectionLocked(fromToken); sp<Connection> toConnection = getConnectionLocked(toToken); if (fromConnection != nullptr && toConnection != nullptr) { @@ -4880,27 +4877,20 @@ bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) { { // acquire lock std::scoped_lock _l(mLock); - sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken); - if (toWindowHandle == nullptr) { - ALOGW("Could not find window associated with token=%p", destChannelToken.get()); + auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(), + [](const auto& pair) { return pair.second.windows.size() == 1; }); + if (it == mTouchStatesByDisplay.end()) { + ALOGW("Cannot transfer touch state because there is no exact window being touched"); 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); + const int32_t displayId = it->first; + sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId); + if (toWindowHandle == nullptr) { + ALOGW("Could not find window associated with token=%p", destChannelToken.get()); 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; - } + TouchState& state = it->second; const TouchedWindow& touchedWindow = state.windows[0]; fromToken = touchedWindow.windowHandle->getToken(); } // release lock diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 2436e73367..1aa78d9c90 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -653,6 +653,10 @@ private: void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + // Find touched state and touched window by token. + std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token) + REQUIRES(mLock); + // Statistics gathering. LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); LatencyTracker mLatencyTracker GUARDED_BY(mLock); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 50e185469f..8235ebff3e 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -46,7 +46,8 @@ static const nsecs_t ARBITRARY_TIME = 1234; static const int32_t DEVICE_ID = 1; // An arbitrary display id. -static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; +static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; +static constexpr int32_t SECOND_DISPLAY_ID = 1; // An arbitrary injector pid / uid pair that has permission to inject events. static const int32_t INJECTOR_PID = 999; @@ -937,6 +938,15 @@ public: mInfo.displayId = displayId; } + sp<FakeWindowHandle> clone( + const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, + const sp<InputDispatcher>& dispatcher, int32_t displayId) { + sp<FakeWindowHandle> handle = + new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)", + displayId, mInfo.token); + return handle; + } + void setFocusable(bool focusable) { mInfo.focusable = focusable; } void setVisible(bool visible) { mInfo.visible = visible; } @@ -1996,6 +2006,134 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { secondWindow->assertNoEvents(); } +// This case will create two windows and one mirrored window on the default display and mirror +// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put +// the windows info of second display before default display. +TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> firstWindowInPrimary = + new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); + firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100)); + firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + sp<FakeWindowHandle> secondWindowInPrimary = + new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); + secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); + secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> mirrorWindowInPrimary = + firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT); + mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200)); + mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> firstWindowInSecondary = + firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID); + firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100)); + firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> secondWindowInSecondary = + secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID); + secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); + secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + // Update window info, let it find window handle of second display first. + mDispatcher->setInputWindows( + {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}}, + {ADISPLAY_ID_DEFAULT, + {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Window should receive motion event. + firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + // Transfer touch focus + ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(), + secondWindowInPrimary->getToken())); + // The first window gets cancel. + firstWindowInPrimary->consumeMotionCancel(); + secondWindowInPrimary->consumeMotionDown(); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + firstWindowInPrimary->assertNoEvents(); + secondWindowInPrimary->consumeMotionMove(); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + firstWindowInPrimary->assertNoEvents(); + secondWindowInPrimary->consumeMotionUp(); +} + +// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use +// 'transferTouch' api. +TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> firstWindowInPrimary = + new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT); + firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100)); + firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + sp<FakeWindowHandle> secondWindowInPrimary = + new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); + secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); + secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> mirrorWindowInPrimary = + firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT); + mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200)); + mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> firstWindowInSecondary = + firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID); + firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100)); + firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + sp<FakeWindowHandle> secondWindowInSecondary = + secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID); + secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100)); + secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL); + + // Update window info, let it find window handle of second display first. + mDispatcher->setInputWindows( + {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}}, + {ADISPLAY_ID_DEFAULT, + {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}}); + + // Touch on second display. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Window should receive motion event. + firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID); + + // Transfer touch focus + ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken())); + + // The first window gets cancel. + firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID); + secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + SECOND_DISPLAY_ID, {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + firstWindowInPrimary->assertNoEvents(); + secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + firstWindowInPrimary->assertNoEvents(); + secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID); +} + TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = @@ -2916,7 +3054,6 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEvent /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: - static constexpr int32_t SECOND_DISPLAY_ID = 1; virtual void SetUp() override { InputDispatcherTest::SetUp(); @@ -3079,8 +3216,6 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) class InputFilterTest : public InputDispatcherTest { protected: - static constexpr int32_t SECOND_DISPLAY_ID = 1; - void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) { NotifyMotionArgs motionArgs; |