InputDispatcher: Implement spy windows
We implement spy windows as outlined in go/spy-windows.
Bug: 162194035
Test: atest inputflinger_tests
Change-Id: Iea3404329184ab40492666f2a66396e9d8cb3594
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f50c9e1..00a3bb0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1051,14 +1051,14 @@
LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
}
// Traverse windows from front to back to find touched window.
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
continue;
}
const WindowInfo& info = *windowHandle->getInfo();
- if (windowAcceptsTouchAt(info, displayId, x, y)) {
+ if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y)) {
return windowHandle;
}
@@ -1070,6 +1070,27 @@
return nullptr;
}
+std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(int32_t displayId,
+ int32_t x,
+ int32_t y) const {
+ // Traverse windows from front to back and gather the touched spy windows.
+ std::vector<sp<WindowInfoHandle>> spyWindows;
+ const auto& windowHandles = getWindowHandlesLocked(displayId);
+ for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
+ const WindowInfo& info = *windowHandle->getInfo();
+
+ if (!windowAcceptsTouchAt(info, displayId, x, y)) {
+ continue;
+ }
+ if (!info.isSpy()) {
+ // The first touched non-spy window was found, so return the spy windows touched so far.
+ return spyWindows;
+ }
+ spyWindows.push_back(windowHandle);
+ }
+ return spyWindows;
+}
+
void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
@@ -2078,9 +2099,11 @@
}
}
- std::vector<sp<WindowInfoHandle>> newTouchedWindows;
+ std::vector<sp<WindowInfoHandle>> newTouchedWindows =
+ findTouchedSpyWindowsAtLocked(displayId, x, y);
if (newTouchedWindowHandle != nullptr) {
- newTouchedWindows.push_back(newTouchedWindowHandle);
+ // Process the foreground window first so that it is the first to receive the event.
+ newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
}
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
@@ -2128,8 +2151,10 @@
// Set target flags.
int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS;
- // There should only be one new foreground (non-spy) window at this location.
- targetFlags |= InputTarget::FLAG_FOREGROUND;
+ if (!info.isSpy()) {
+ // There should only be one new foreground (non-spy) window at this location.
+ targetFlags |= InputTarget::FLAG_FOREGROUND;
+ }
if (isSplit) {
targetFlags |= InputTarget::FLAG_SPLIT;
@@ -2268,10 +2293,17 @@
// Check permission to inject into all touched foreground windows and ensure there
// is at least one touched foreground window.
{
- bool haveForegroundWindow = false;
+ bool haveForegroundOrSpyWindow = false;
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
- haveForegroundWindow = true;
+ const bool isForeground =
+ (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0;
+ if (touchedWindow.windowHandle->getInfo()->isSpy()) {
+ haveForegroundOrSpyWindow = true;
+ LOG_ALWAYS_FATAL_IF(isForeground,
+ "Spy window cannot be dispatched as a foreground window.");
+ }
+ if (isForeground) {
+ haveForegroundOrSpyWindow = true;
if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -2280,8 +2312,8 @@
}
}
bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();
- if (!haveForegroundWindow && !hasGestureMonitor) {
- ALOGI("Dropping event because there is no touched foreground window in display "
+ if (!haveForegroundOrSpyWindow && !hasGestureMonitor) {
+ ALOGI("Dropping event because there is no touched window in display "
"%" PRId32 " or gesture monitor to receive it.",
displayId);
injectionResult = InputEventInjectionResult::FAILED;
@@ -5521,58 +5553,78 @@
status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
{ // acquire lock
std::scoped_lock _l(mLock);
- std::optional<int32_t> foundDisplayId = findGestureMonitorDisplayByTokenLocked(token);
- if (!foundDisplayId) {
- ALOGW("Attempted to pilfer pointers from an un-registered monitor or invalid token");
- return BAD_VALUE;
- }
- int32_t displayId = foundDisplayId.value();
-
- std::unordered_map<int32_t, TouchState>::iterator stateIt =
- mTouchStatesByDisplay.find(displayId);
- if (stateIt == mTouchStatesByDisplay.end()) {
- ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
- return BAD_VALUE;
- }
-
- TouchState& state = stateIt->second;
+ TouchState* statePtr = nullptr;
std::shared_ptr<InputChannel> requestingChannel;
- std::optional<int32_t> foundDeviceId;
- for (const auto& monitor : state.gestureMonitors) {
- if (monitor.inputChannel->getConnectionToken() == token) {
- requestingChannel = monitor.inputChannel;
- foundDeviceId = state.deviceId;
+ int32_t displayId;
+ int32_t deviceId;
+ const std::optional<int32_t> foundGestureMonitorDisplayId =
+ findGestureMonitorDisplayByTokenLocked(token);
+
+ // TODO: Optimize this function for pilfering from windows when removing gesture monitors.
+ if (foundGestureMonitorDisplayId) {
+ // A gesture monitor has requested to pilfer pointers.
+ displayId = *foundGestureMonitorDisplayId;
+ auto stateIt = mTouchStatesByDisplay.find(displayId);
+ if (stateIt == mTouchStatesByDisplay.end()) {
+ ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
+ return BAD_VALUE;
+ }
+ statePtr = &stateIt->second;
+
+ for (const auto& monitor : statePtr->gestureMonitors) {
+ if (monitor.inputChannel->getConnectionToken() == token) {
+ requestingChannel = monitor.inputChannel;
+ deviceId = statePtr->deviceId;
+ }
+ }
+ } else {
+ // Check if a window has requested to pilfer pointers.
+ for (auto& [curDisplayId, state] : mTouchStatesByDisplay) {
+ const sp<WindowInfoHandle>& windowHandle = state.getWindow(token);
+ if (windowHandle != nullptr) {
+ displayId = curDisplayId;
+ requestingChannel = getInputChannelLocked(token);
+ deviceId = state.deviceId;
+ statePtr = &state;
+ break;
+ }
}
}
- if (!foundDeviceId || !state.down) {
- ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams."
+
+ if (requestingChannel == nullptr) {
+ ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token");
+ return BAD_VALUE;
+ }
+ TouchState& state = *statePtr;
+ if (!state.down) {
+ ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
" Ignoring.");
return BAD_VALUE;
}
- int32_t deviceId = foundDeviceId.value();
// Send cancel events to all the input channels we're stealing from.
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "gesture monitor stole pointer stream");
+ "input channel stole pointer stream");
options.deviceId = deviceId;
options.displayId = displayId;
- std::string canceledWindows = "[";
+ std::string canceledWindows;
for (const TouchedWindow& window : state.windows) {
std::shared_ptr<InputChannel> channel =
getInputChannelLocked(window.windowHandle->getToken());
- if (channel != nullptr) {
+ if (channel != nullptr && channel->getConnectionToken() != token) {
synthesizeCancelationEventsForInputChannelLocked(channel, options);
- canceledWindows += channel->getName() + ", ";
+ canceledWindows += canceledWindows.empty() ? "[" : ", ";
+ canceledWindows += channel->getName();
}
}
- canceledWindows += "]";
- ALOGI("Monitor %s is stealing touch from %s", requestingChannel->getName().c_str(),
+ canceledWindows += canceledWindows.empty() ? "[]" : "]";
+ ALOGI("Channel %s is stealing touch from %s", requestingChannel->getName().c_str(),
canceledWindows.c_str());
// Then clear the current touch state so we stop dispatching to them as well.
state.split = false;
- state.filterNonMonitors();
+ state.filterWindowsExcept(token);
}
return OK;
}