Loading libs/gui/WindowInfo.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,10 @@ bool WindowInfo::isSpy() const { return inputFeatures.test(Feature::SPY); } bool WindowInfo::interceptsStylus() const { return inputFeatures.test(Feature::INTERCEPTS_STYLUS); } bool WindowInfo::overlaps(const WindowInfo* other) const { return frameLeft < other->frameRight && frameRight > other->frameLeft && frameTop < other->frameBottom && frameBottom > other->frameTop; Loading libs/gui/include/gui/WindowInfo.h +3 −0 Original line number Diff line number Diff line Loading @@ -138,6 +138,7 @@ struct WindowInfo : public Parcelable { DROP_INPUT = 1u << 3, DROP_INPUT_IF_OBSCURED = 1u << 4, SPY = 1u << 5, INTERCEPTS_STYLUS = 1u << 6, }; /* These values are filled in by the WM and passed through SurfaceFlinger Loading Loading @@ -218,6 +219,8 @@ struct WindowInfo : public Parcelable { bool isSpy() const; bool interceptsStylus() const; bool overlaps(const WindowInfo* other) const; bool operator==(const WindowInfo& inputChannel) const; Loading services/inputflinger/dispatcher/InputDispatcher.cpp +49 −16 Original line number Diff line number Diff line Loading @@ -524,12 +524,14 @@ bool isUserActivityEvent(const EventEntry& eventEntry) { } // Returns true if the given window can accept pointer events at the given display location. bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y) { bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y, bool isStylus) { if (windowInfo.displayId != displayId || !windowInfo.visible) { return false; } const auto flags = windowInfo.flags; if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) { const bool windowCanInterceptTouch = isStylus && windowInfo.interceptsStylus(); if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE) && !windowCanInterceptTouch) { return false; } const bool isModalWindow = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) && Loading @@ -540,6 +542,12 @@ bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32 return true; } bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) && (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER); } } // namespace // --- InputDispatcher --- Loading Loading @@ -933,8 +941,10 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = static_cast<int32_t>( motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/); sp<WindowInfoHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, nullptr); findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus); if (touchedWindowHandle != nullptr && touchedWindowHandle->getApplicationToken() != mAwaitedFocusedApplication->getApplicationToken()) { Loading Loading @@ -1039,6 +1049,7 @@ void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) { sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus, bool addOutsideTargets, bool ignoreDragWindow) { if (addOutsideTargets && touchState == nullptr) { Loading @@ -1052,7 +1063,7 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI } const WindowInfo& info = *windowHandle->getInfo(); if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y)) { if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) { return windowHandle; } Loading @@ -1064,16 +1075,15 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI return nullptr; } std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(int32_t displayId, int32_t x, int32_t y) const { std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked( int32_t displayId, int32_t x, int32_t y, bool isStylus) 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)) { if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) { continue; } if (!info.isSpy()) { Loading Loading @@ -2056,8 +2066,9 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); } const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; const bool isStylus = isPointerFromStylus(entry, pointerIndex); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isDown /*addOutsideTargets*/); isStylus, isDown /*addOutsideTargets*/); // Handle the case where we did not find a window. if (newTouchedWindowHandle == nullptr) { Loading Loading @@ -2094,7 +2105,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } std::vector<sp<WindowInfoHandle>> newTouchedWindows = findTouchedSpyWindowsAtLocked(displayId, x, y); findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus); if (newTouchedWindowHandle != nullptr) { // Process the foreground window first so that it is the first to receive the event. newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle); Loading Loading @@ -2207,9 +2218,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/); sp<WindowInfoHandle> oldTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus); // Drop touch events if requested by input feature if (newTouchedWindowHandle != nullptr && Loading Loading @@ -2471,8 +2484,12 @@ Failed: } void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) { // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until we // have an explicit reason to support it. constexpr bool isStylus = false; const sp<WindowInfoHandle> dropWindow = findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, isStylus, false /*addOutsideTargets*/, true /*ignoreDragWindow*/); if (dropWindow) { vec2 local = dropWindow->getInfo()->transform.transform(x, y); Loading Loading @@ -2505,8 +2522,12 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { return; } // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until // we have an explicit reason to support it. constexpr bool isStylus = false; const sp<WindowInfoHandle> hoverWindowHandle = findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, isStylus, false /*addOutsideTargets*/, true /*ignoreDragWindow*/); // enqueue drag exit if needed. if (hoverWindowHandle != mDragState->dragHoverWindowHandle && Loading Loading @@ -4670,15 +4691,27 @@ void InputDispatcher::setInputWindowsLocked( ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); } // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL // Check preconditions for new input windows for (const sp<WindowInfoHandle>& window : windowInfoHandles) { const bool noInputWindow = window->getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); const WindowInfo& info = *window->getInfo(); // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL const bool noInputWindow = info.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); if (noInputWindow && window->getToken() != nullptr) { ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing", window->getName().c_str()); window->releaseChannel(); } // Ensure all spy windows are trusted overlays LOG_ALWAYS_FATAL_IF(info.isSpy() && !info.trustedOverlay, "%s has feature SPY, but is not a trusted overlay.", window->getName().c_str()); // Ensure all stylus interceptors are trusted overlays LOG_ALWAYS_FATAL_IF(info.interceptsStylus() && !info.trustedOverlay, "%s has feature INTERCEPTS_STYLUS, but is not a trusted overlay.", window->getName().c_str()); } // Copy old handles for release if they are no longer present. Loading services/inputflinger/dispatcher/InputDispatcher.h +5 −9 Original line number Diff line number Diff line Loading @@ -234,16 +234,12 @@ private: // to transfer focus to a new application. std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock); sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock); sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked( int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false, bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock); std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(int32_t displayId, int32_t x, int32_t y) const REQUIRES(mLock); std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked( int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock); sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const REQUIRES(mLock); Loading services/inputflinger/tests/InputDispatcher_test.cpp +108 −23 Original line number Diff line number Diff line Loading @@ -1003,6 +1003,7 @@ public: mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.displayId = displayId; mInfo.trustedOverlay = false; } sp<FakeWindowHandle> clone( Loading Loading @@ -1054,7 +1055,9 @@ public: void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; } void setInputFeatures(WindowInfo::Feature features) { mInfo.inputFeatures = features; } void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; } void setTrustedOverlay(bool trustedOverlay) { mInfo.trustedOverlay = trustedOverlay; } void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) { mInfo.transform.set(dsdx, dtdx, dtdy, dsdy); Loading Loading @@ -6324,6 +6327,7 @@ public: sp<FakeWindowHandle> spy = new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT); spy->setInputFeatures(WindowInfo::Feature::SPY); spy->setTrustedOverlay(true); spy->addFlags(flags); return spy; } Loading @@ -6341,6 +6345,16 @@ private: int mSpyCount{0}; }; /** * Adding a spy window that is not a trusted overlay causes Dispatcher to abort. */ TEST_F(InputDispatcherSpyWindowTest, UntrustedSpy_AbortsDispatcher) { auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL); spy->setTrustedOverlay(false); ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}), ".* not a trusted overlay"); } /** * Input injection into a display with a spy window but no foreground windows should succeed. */ Loading Loading @@ -6500,28 +6514,6 @@ TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) { spy->consumeMotionOutside(); } /** * When configured to block untrusted touches, events will not be dispatched to windows below a spy * window if it is not a trusted overly. */ TEST_F(InputDispatcherSpyWindowTest, BlockUntrustedTouches) { mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK); auto window = createForeground(); auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL); window->setOwnerInfo(111, 111); spy->setOwnerInfo(222, 222); spy->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); // Inject an event outside the spy window's frame and touchable region. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; spy->consumeMotionDown(); window->assertNoEvents(); } /** * A spy window can pilfer pointers. When this happens, touch gestures that are currently sent to * any other windows - including other spy windows - will also be cancelled. Loading Loading @@ -6627,4 +6619,97 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) { spyRight->consumeMotionDown(); } class InputDispatcherStylusInterceptorTest : public InputDispatcherTest { public: std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() { std::shared_ptr<FakeApplicationHandle> overlayApplication = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> overlay = new FakeWindowHandle(overlayApplication, mDispatcher, "Stylus interceptor window", ADISPLAY_ID_DEFAULT); overlay->setFocusable(false); overlay->setOwnerInfo(111, 111); overlay->setFlags(WindowInfo::Flag::NOT_TOUCHABLE | WindowInfo::Flag::SPLIT_TOUCH); overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS); overlay->setTrustedOverlay(true); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Application window", ADISPLAY_ID_DEFAULT); window->setFocusable(true); window->setOwnerInfo(222, 222); window->setFlags(WindowInfo::Flag::SPLIT_TOUCH); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); setFocusedWindow(window); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); return {std::move(overlay), std::move(window)}; } void sendFingerEvent(int32_t action) { NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, {PointF{20, 20}}); mDispatcher->notifyMotion(&motionArgs); } void sendStylusEvent(int32_t action) { NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, {PointF{30, 40}}); motionArgs.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mDispatcher->notifyMotion(&motionArgs); } }; TEST_F(InputDispatcherStylusInterceptorTest, UntrustedOverlay_AbortsDispatcher) { auto [overlay, window] = setupStylusOverlayScenario(); overlay->setTrustedOverlay(false); // Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort. ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}), ".* not a trusted overlay"); } TEST_F(InputDispatcherStylusInterceptorTest, ConsmesOnlyStylusEvents) { auto [overlay, window] = setupStylusOverlayScenario(); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); sendStylusEvent(AMOTION_EVENT_ACTION_DOWN); overlay->consumeMotionDown(); sendStylusEvent(AMOTION_EVENT_ACTION_UP); overlay->consumeMotionUp(); sendFingerEvent(AMOTION_EVENT_ACTION_DOWN); window->consumeMotionDown(); sendFingerEvent(AMOTION_EVENT_ACTION_UP); window->consumeMotionUp(); overlay->assertNoEvents(); window->assertNoEvents(); } TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) { auto [overlay, window] = setupStylusOverlayScenario(); overlay->setInputFeatures(overlay->getInfo()->inputFeatures | WindowInfo::Feature::SPY); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); sendStylusEvent(AMOTION_EVENT_ACTION_DOWN); overlay->consumeMotionDown(); window->consumeMotionDown(); sendStylusEvent(AMOTION_EVENT_ACTION_UP); overlay->consumeMotionUp(); window->consumeMotionUp(); sendFingerEvent(AMOTION_EVENT_ACTION_DOWN); window->consumeMotionDown(); sendFingerEvent(AMOTION_EVENT_ACTION_UP); window->consumeMotionUp(); overlay->assertNoEvents(); window->assertNoEvents(); } } // namespace android::inputdispatcher Loading
libs/gui/WindowInfo.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,10 @@ bool WindowInfo::isSpy() const { return inputFeatures.test(Feature::SPY); } bool WindowInfo::interceptsStylus() const { return inputFeatures.test(Feature::INTERCEPTS_STYLUS); } bool WindowInfo::overlaps(const WindowInfo* other) const { return frameLeft < other->frameRight && frameRight > other->frameLeft && frameTop < other->frameBottom && frameBottom > other->frameTop; Loading
libs/gui/include/gui/WindowInfo.h +3 −0 Original line number Diff line number Diff line Loading @@ -138,6 +138,7 @@ struct WindowInfo : public Parcelable { DROP_INPUT = 1u << 3, DROP_INPUT_IF_OBSCURED = 1u << 4, SPY = 1u << 5, INTERCEPTS_STYLUS = 1u << 6, }; /* These values are filled in by the WM and passed through SurfaceFlinger Loading Loading @@ -218,6 +219,8 @@ struct WindowInfo : public Parcelable { bool isSpy() const; bool interceptsStylus() const; bool overlaps(const WindowInfo* other) const; bool operator==(const WindowInfo& inputChannel) const; Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +49 −16 Original line number Diff line number Diff line Loading @@ -524,12 +524,14 @@ bool isUserActivityEvent(const EventEntry& eventEntry) { } // Returns true if the given window can accept pointer events at the given display location. bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y) { bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y, bool isStylus) { if (windowInfo.displayId != displayId || !windowInfo.visible) { return false; } const auto flags = windowInfo.flags; if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) { const bool windowCanInterceptTouch = isStylus && windowInfo.interceptsStylus(); if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE) && !windowCanInterceptTouch) { return false; } const bool isModalWindow = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) && Loading @@ -540,6 +542,12 @@ bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32 return true; } bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) && (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER); } } // namespace // --- InputDispatcher --- Loading Loading @@ -933,8 +941,10 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = static_cast<int32_t>( motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/); sp<WindowInfoHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, nullptr); findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus); if (touchedWindowHandle != nullptr && touchedWindowHandle->getApplicationToken() != mAwaitedFocusedApplication->getApplicationToken()) { Loading Loading @@ -1039,6 +1049,7 @@ void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) { sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus, bool addOutsideTargets, bool ignoreDragWindow) { if (addOutsideTargets && touchState == nullptr) { Loading @@ -1052,7 +1063,7 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI } const WindowInfo& info = *windowHandle->getInfo(); if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y)) { if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) { return windowHandle; } Loading @@ -1064,16 +1075,15 @@ sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayI return nullptr; } std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(int32_t displayId, int32_t x, int32_t y) const { std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked( int32_t displayId, int32_t x, int32_t y, bool isStylus) 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)) { if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) { continue; } if (!info.isSpy()) { Loading Loading @@ -2056,8 +2066,9 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); } const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; const bool isStylus = isPointerFromStylus(entry, pointerIndex); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isDown /*addOutsideTargets*/); isStylus, isDown /*addOutsideTargets*/); // Handle the case where we did not find a window. if (newTouchedWindowHandle == nullptr) { Loading Loading @@ -2094,7 +2105,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } std::vector<sp<WindowInfoHandle>> newTouchedWindows = findTouchedSpyWindowsAtLocked(displayId, x, y); findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus); if (newTouchedWindowHandle != nullptr) { // Process the foreground window first so that it is the first to receive the event. newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle); Loading Loading @@ -2207,9 +2218,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/); sp<WindowInfoHandle> oldTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState); newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus); // Drop touch events if requested by input feature if (newTouchedWindowHandle != nullptr && Loading Loading @@ -2471,8 +2484,12 @@ Failed: } void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) { // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until we // have an explicit reason to support it. constexpr bool isStylus = false; const sp<WindowInfoHandle> dropWindow = findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, isStylus, false /*addOutsideTargets*/, true /*ignoreDragWindow*/); if (dropWindow) { vec2 local = dropWindow->getInfo()->transform.transform(x, y); Loading Loading @@ -2505,8 +2522,12 @@ void InputDispatcher::addDragEventLocked(const MotionEntry& entry) { return; } // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until // we have an explicit reason to support it. constexpr bool isStylus = false; const sp<WindowInfoHandle> hoverWindowHandle = findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, isStylus, false /*addOutsideTargets*/, true /*ignoreDragWindow*/); // enqueue drag exit if needed. if (hoverWindowHandle != mDragState->dragHoverWindowHandle && Loading Loading @@ -4670,15 +4691,27 @@ void InputDispatcher::setInputWindowsLocked( ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); } // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL // Check preconditions for new input windows for (const sp<WindowInfoHandle>& window : windowInfoHandles) { const bool noInputWindow = window->getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); const WindowInfo& info = *window->getInfo(); // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL const bool noInputWindow = info.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL); if (noInputWindow && window->getToken() != nullptr) { ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing", window->getName().c_str()); window->releaseChannel(); } // Ensure all spy windows are trusted overlays LOG_ALWAYS_FATAL_IF(info.isSpy() && !info.trustedOverlay, "%s has feature SPY, but is not a trusted overlay.", window->getName().c_str()); // Ensure all stylus interceptors are trusted overlays LOG_ALWAYS_FATAL_IF(info.interceptsStylus() && !info.trustedOverlay, "%s has feature INTERCEPTS_STYLUS, but is not a trusted overlay.", window->getName().c_str()); } // Copy old handles for release if they are no longer present. Loading
services/inputflinger/dispatcher/InputDispatcher.h +5 −9 Original line number Diff line number Diff line Loading @@ -234,16 +234,12 @@ private: // to transfer focus to a new application. std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock); sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock); sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked( int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false, bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock); std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(int32_t displayId, int32_t x, int32_t y) const REQUIRES(mLock); std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked( int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock); sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const REQUIRES(mLock); Loading
services/inputflinger/tests/InputDispatcher_test.cpp +108 −23 Original line number Diff line number Diff line Loading @@ -1003,6 +1003,7 @@ public: mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; mInfo.displayId = displayId; mInfo.trustedOverlay = false; } sp<FakeWindowHandle> clone( Loading Loading @@ -1054,7 +1055,9 @@ public: void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; } void setInputFeatures(WindowInfo::Feature features) { mInfo.inputFeatures = features; } void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; } void setTrustedOverlay(bool trustedOverlay) { mInfo.trustedOverlay = trustedOverlay; } void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) { mInfo.transform.set(dsdx, dtdx, dtdy, dsdy); Loading Loading @@ -6324,6 +6327,7 @@ public: sp<FakeWindowHandle> spy = new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT); spy->setInputFeatures(WindowInfo::Feature::SPY); spy->setTrustedOverlay(true); spy->addFlags(flags); return spy; } Loading @@ -6341,6 +6345,16 @@ private: int mSpyCount{0}; }; /** * Adding a spy window that is not a trusted overlay causes Dispatcher to abort. */ TEST_F(InputDispatcherSpyWindowTest, UntrustedSpy_AbortsDispatcher) { auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL); spy->setTrustedOverlay(false); ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}), ".* not a trusted overlay"); } /** * Input injection into a display with a spy window but no foreground windows should succeed. */ Loading Loading @@ -6500,28 +6514,6 @@ TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) { spy->consumeMotionOutside(); } /** * When configured to block untrusted touches, events will not be dispatched to windows below a spy * window if it is not a trusted overly. */ TEST_F(InputDispatcherSpyWindowTest, BlockUntrustedTouches) { mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK); auto window = createForeground(); auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL); window->setOwnerInfo(111, 111); spy->setOwnerInfo(222, 222); spy->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); // Inject an event outside the spy window's frame and touchable region. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; spy->consumeMotionDown(); window->assertNoEvents(); } /** * A spy window can pilfer pointers. When this happens, touch gestures that are currently sent to * any other windows - including other spy windows - will also be cancelled. Loading Loading @@ -6627,4 +6619,97 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) { spyRight->consumeMotionDown(); } class InputDispatcherStylusInterceptorTest : public InputDispatcherTest { public: std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() { std::shared_ptr<FakeApplicationHandle> overlayApplication = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> overlay = new FakeWindowHandle(overlayApplication, mDispatcher, "Stylus interceptor window", ADISPLAY_ID_DEFAULT); overlay->setFocusable(false); overlay->setOwnerInfo(111, 111); overlay->setFlags(WindowInfo::Flag::NOT_TOUCHABLE | WindowInfo::Flag::SPLIT_TOUCH); overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS); overlay->setTrustedOverlay(true); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Application window", ADISPLAY_ID_DEFAULT); window->setFocusable(true); window->setOwnerInfo(222, 222); window->setFlags(WindowInfo::Flag::SPLIT_TOUCH); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); setFocusedWindow(window); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); return {std::move(overlay), std::move(window)}; } void sendFingerEvent(int32_t action) { NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, {PointF{20, 20}}); mDispatcher->notifyMotion(&motionArgs); } void sendStylusEvent(int32_t action) { NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, {PointF{30, 40}}); motionArgs.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; mDispatcher->notifyMotion(&motionArgs); } }; TEST_F(InputDispatcherStylusInterceptorTest, UntrustedOverlay_AbortsDispatcher) { auto [overlay, window] = setupStylusOverlayScenario(); overlay->setTrustedOverlay(false); // Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort. ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}), ".* not a trusted overlay"); } TEST_F(InputDispatcherStylusInterceptorTest, ConsmesOnlyStylusEvents) { auto [overlay, window] = setupStylusOverlayScenario(); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); sendStylusEvent(AMOTION_EVENT_ACTION_DOWN); overlay->consumeMotionDown(); sendStylusEvent(AMOTION_EVENT_ACTION_UP); overlay->consumeMotionUp(); sendFingerEvent(AMOTION_EVENT_ACTION_DOWN); window->consumeMotionDown(); sendFingerEvent(AMOTION_EVENT_ACTION_UP); window->consumeMotionUp(); overlay->assertNoEvents(); window->assertNoEvents(); } TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) { auto [overlay, window] = setupStylusOverlayScenario(); overlay->setInputFeatures(overlay->getInfo()->inputFeatures | WindowInfo::Feature::SPY); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); sendStylusEvent(AMOTION_EVENT_ACTION_DOWN); overlay->consumeMotionDown(); window->consumeMotionDown(); sendStylusEvent(AMOTION_EVENT_ACTION_UP); overlay->consumeMotionUp(); window->consumeMotionUp(); sendFingerEvent(AMOTION_EVENT_ACTION_DOWN); window->consumeMotionDown(); sendFingerEvent(AMOTION_EVENT_ACTION_UP); window->consumeMotionUp(); overlay->assertNoEvents(); window->assertNoEvents(); } } // namespace android::inputdispatcher