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
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3dea9ce..e3912b3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4789,6 +4789,18 @@
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 @@
{ // 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;
}
+
+ 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;
+ }
+
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.");
- }
- return false;
+ touchedWindow->windowHandle->getName().c_str(),
+ toWindowHandle->getName().c_str());
}
- 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;
+ // Erase old window.
+ int32_t oldTargetFlags = touchedWindow->targetFlags;
+ BitSet32 pointerIds = touchedWindow->pointerIds;
+ state->removeWindowByToken(fromToken);
- state.windows.erase(state.windows.begin() + i);
+ // Add new window.
+ int32_t newTargetFlags = oldTargetFlags &
+ (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
+ InputTarget::FLAG_DISPATCH_AS_IS);
+ state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
- int32_t newTargetFlags = oldTargetFlags &
- (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
- InputTarget::FLAG_DISPATCH_AS_IS);
- state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
-
- // Store the dragging window.
- if (isDragDrop) {
- mDragState = std::make_unique<DragState>(toWindowHandle);
- }
-
- found = true;
- goto Found;
- }
- }
- }
- Found:
-
- 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 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+ 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 = it->first;
+ sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
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;
- }
+ 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 2436e73..1aa78d9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -653,6 +653,10 @@
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 50e1854..8235ebf 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -46,7 +46,8 @@
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 @@
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 @@
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 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 @@
class InputFilterTest : public InputDispatcherTest {
protected:
- static constexpr int32_t SECOND_DISPLAY_ID = 1;
-
void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
NotifyMotionArgs motionArgs;