Send cancel events to the correct display when mirroring
When a display is mirrored, such as when screen recording is enabled,
there will be clones of the input windows on the mirrored display that
are on the mirror display.
When there is a stream of input going to the channel through one of
those windows and that stream should be canceled, the generated cancel
event should be sent to the correct window on the correct display.
Bug: 299074463
Test: atest inputflinger_tests
Change-Id: I7b9eab69c9e9086750cf9fe8a10998a12c30d084
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 276f75c..d0a72ee 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3958,28 +3958,24 @@
android_log_event_list(LOGTAG_INPUT_CANCEL)
<< connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
- sp<WindowInfoHandle> windowHandle;
- if (options.displayId) {
- windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken(),
- options.displayId.value());
- } else {
- windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken());
- }
-
const bool wasEmpty = connection->outboundQueue.empty();
+ // The target to use if we don't find a window associated with the channel.
+ const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
+ .flags = InputTarget::Flags::DISPATCH_AS_IS};
+ const auto& token = connection->inputChannel->getConnectionToken();
for (size_t i = 0; i < cancelationEvents.size(); i++) {
std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
std::vector<InputTarget> targets{};
- // The target to use if we don't find a window associated with the channel.
- const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
- .flags = InputTarget::Flags::DISPATCH_AS_IS};
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry);
- if (windowHandle != nullptr) {
- addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
+ const std::optional<int32_t> targetDisplay = keyEntry.displayId != ADISPLAY_ID_NONE
+ ? std::make_optional(keyEntry.displayId)
+ : std::nullopt;
+ if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) {
+ addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS,
/*pointerIds=*/{}, keyEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
@@ -3989,14 +3985,18 @@
}
case EventEntry::Type::MOTION: {
const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry);
- if (windowHandle != nullptr) {
+ const std::optional<int32_t> targetDisplay =
+ motionEntry.displayId != ADISPLAY_ID_NONE
+ ? std::make_optional(motionEntry.displayId)
+ : std::nullopt;
+ if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
- pointerIds, motionEntry.downTime, targets);
+ addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, pointerIds,
+ motionEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
const auto it = mDisplayInfos.find(motionEntry.displayId);
@@ -4905,29 +4905,26 @@
}
sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
- const sp<IBinder>& windowHandleToken) const {
+ const sp<IBinder>& windowHandleToken, std::optional<int32_t> displayId) const {
if (windowHandleToken == nullptr) {
return nullptr;
}
- for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
- for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
- if (windowHandle->getToken() == windowHandleToken) {
- return windowHandle;
+ if (!displayId) {
+ // Look through all displays.
+ for (auto& it : mWindowHandlesByDisplay) {
+ const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
+ for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
+ if (windowHandle->getToken() == windowHandleToken) {
+ return windowHandle;
+ }
}
}
- }
- return nullptr;
-}
-
-sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
- int displayId) const {
- if (windowHandleToken == nullptr) {
return nullptr;
}
- for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+ // Only look through the requested display.
+ for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesLocked(*displayId)) {
if (windowHandle->getToken() == windowHandleToken) {
return windowHandle;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ee5a797..0020301 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -354,14 +354,11 @@
// Get a reference to window handles by display, return an empty vector if not found.
const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesLocked(
int32_t displayId) const REQUIRES(mLock);
- sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
- const sp<IBinder>& windowHandleToken) const REQUIRES(mLock);
ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock);
- // Same function as above, but faster. Since displayId is provided, this avoids the need
- // to loop through all displays.
- sp<android::gui::WindowInfoHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
- int displayId) const REQUIRES(mLock);
+ sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
+ const sp<IBinder>& windowHandleToken, std::optional<int32_t> displayId = {}) const
+ REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 2f63b2a..ff3eff1 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -4699,6 +4699,36 @@
firstWindow->assertNoEvents();
}
+TEST_F(InputDispatcherDisplayProjectionTest,
+ SynthesizeHoverCancelationWithCorrectCoordinatesWhenMirrored) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set(matrix);
+ addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform);
+
+ sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID);
+ secondWindowClone->setWindowTransform(1.1, 2.2, 3.3, 4.4);
+ addWindow(secondWindowClone);
+
+ // Send hover enter to second window
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithCoords(100, 80), WithRawCoords(300, 880),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+ mDispatcher->cancelCurrentTouch();
+
+ // Ensure the cancelation happens with the correct displayId and the correct coordinates.
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+ WithRawCoords(300, 880),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ secondWindow->assertNoEvents();
+ firstWindow->assertNoEvents();
+}
+
/** Ensure consistent behavior of InputDispatcher in all orientations. */
class InputDispatcherDisplayOrientationFixture
: public InputDispatcherDisplayProjectionTest,