diff options
author | 2024-03-05 14:43:05 +0000 | |
---|---|---|
committer | 2024-03-05 14:43:05 +0000 | |
commit | 5e5645e860048bcdfef5db01b936e73357174caf (patch) | |
tree | 10532d531add31ec7ad698d7e195c7e85d365998 | |
parent | d26c9194865433ff01584921f17339e8a719c34a (diff) |
Move occlusion detection into logical space
Same as our "hit test" to find the target window, since WM works in
logical space, we consider the window's frame to be (l, t, r, b), and
the portion of the window that can be occluded in the x-direction is
[l, r), and the portion of the window that can be occluded in the
y-direction is [t, b). When the logical space is different from the
physical space, the opening and closing intervals in each of these
directions will be inconsistent, leading to abnormal detection of the
edge part. Here we move the occlusion detection to the logical
space as well, consistent with the "hit test" for finding the target
window.
Bug: 327712879
Test: atest inputflinger_tests
Test: Create a window that can cause occlusion and perform clicks on the edges, making sure that the occlusion range is x = [l, r), y = [t, b) under each direction of the screen.
Signed-off-by: Linnan Li <lilinnan@xiaomi.corp-partner.google.com>
(cherry picked from https://partner-android-review.googlesource.com/q/commit:1a0060e9bcd25bee6ce7a9f5d01b9446b0c4d73b)
Merged-In: I19b739bc8f1e48d8bc70020a2b07da227eaa6d8b
Change-Id: I19b739bc8f1e48d8bc70020a2b07da227eaa6d8b
-rw-r--r-- | libs/gui/WindowInfo.cpp | 8 | ||||
-rw-r--r-- | libs/gui/include/gui/WindowInfo.h | 4 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 21 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 4 | ||||
-rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 88 |
5 files changed, 107 insertions, 18 deletions
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 86bf0ee745..ad0d99d11e 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -73,14 +73,6 @@ void WindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); } -bool WindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { - return touchableRegion.contains(x, y); -} - -bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const { - return x >= frame.left && x < frame.right && y >= frame.top && y < frame.bottom; -} - bool WindowInfo::supportsSplitTouch() const { return !inputConfig.test(InputConfig::PREVENT_SPLITTING); } diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 32d60be612..2d1b51a418 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -254,10 +254,6 @@ struct WindowInfo : public Parcelable { void addTouchableRegion(const Rect& region); - bool touchableRegionContainsPoint(int32_t x, int32_t y) const; - - bool frameContainsPoint(int32_t x, int32_t y) const; - bool supportsSplitTouch() const; bool isSpy() const; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 7f54bf1945..55976137c5 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -573,6 +573,18 @@ bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float return true; } +// Returns true if the given window's frame can occlude pointer events at the given display +// location. +bool windowOccludesTouchAt(const WindowInfo& windowInfo, int displayId, float x, float y, + const ui::Transform& displayTransform) { + if (windowInfo.displayId != displayId) { + return false; + } + const auto frame = displayTransform.transform(windowInfo.frame); + const auto p = floor(displayTransform.transform(x, y)); + return p.x >= frame.left && p.x < frame.right && p.y >= frame.top && p.y < frame.bottom; +} + bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(entry.pointerProperties[pointerIndex].toolType); @@ -3056,7 +3068,7 @@ static bool canBeObscuredBy(const sp<WindowInfoHandle>& windowHandle, * If neither of those is true, then it means the touch can be allowed. */ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked( - const sp<WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const { + const sp<WindowInfoHandle>& windowHandle, float x, float y) const { const WindowInfo* windowInfo = windowHandle->getInfo(); int32_t displayId = windowInfo->displayId; const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); @@ -3070,7 +3082,8 @@ InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLo break; // All future windows are below us. Exit early. } const WindowInfo* otherInfo = otherHandle->getInfo(); - if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) && + if (canBeObscuredBy(windowHandle, otherHandle) && + windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId)) && !haveSameApplicationToken(windowInfo, otherInfo)) { if (DEBUG_TOUCH_OCCLUSION) { info.debugInfo.push_back( @@ -3140,7 +3153,7 @@ bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionIn } bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle, - int32_t x, int32_t y) const { + float x, float y) const { int32_t displayId = windowHandle->getInfo()->displayId; const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId); for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { @@ -3149,7 +3162,7 @@ bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& } const WindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && - otherInfo->frameContainsPoint(x, y)) { + windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId))) { return true; } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 269bfddb8c..9c8d5889d1 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -565,11 +565,11 @@ private: }; TouchOcclusionInfo computeTouchOcclusionInfoLocked( - const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const + const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const REQUIRES(mLock); bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock); bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, - int32_t x, int32_t y) const REQUIRES(mLock); + float x, float y) const REQUIRES(mLock); bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f0f4d93ecd..621d9eadb8 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -5361,6 +5361,94 @@ TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) window->assertNoEvents(); } +// This test verifies the occlusion detection for all rotations of the display by tapping +// in different locations on the display, specifically points close to the four corners of a +// window. +TEST_P(InputDispatcherDisplayOrientationFixture, BlockUntrustClickInDifferentOrientations) { + constexpr static int32_t displayWidth = 400; + constexpr static int32_t displayHeight = 800; + + std::shared_ptr<FakeApplicationHandle> untrustedWindowApplication = + std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + const auto rotation = GetParam(); + + // Set up the display with the specified rotation. + const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270; + const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth; + const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight; + const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation), + logicalDisplayWidth, logicalDisplayHeight); + addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform); + + // Create a window that not trusted. + const Rect untrustedWindowFrameInLogicalDisplay(100, 100, 200, 300); + + const Rect untrustedWindowFrameInDisplay = + displayTransform.inverse().transform(untrustedWindowFrameInLogicalDisplay); + + sp<FakeWindowHandle> untrustedWindow = + sp<FakeWindowHandle>::make(untrustedWindowApplication, mDispatcher, "UntrustedWindow", + ADISPLAY_ID_DEFAULT); + untrustedWindow->setFrame(untrustedWindowFrameInDisplay, displayTransform); + untrustedWindow->setTrustedOverlay(false); + untrustedWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); + untrustedWindow->setTouchable(false); + untrustedWindow->setAlpha(1.0f); + untrustedWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101}); + addWindow(untrustedWindow); + + // Create a simple app window below the untrusted window. + const Rect simpleAppWindowFrameInLogicalDisplay(0, 0, 300, 600); + const Rect simpleAppWindowFrameInDisplay = + displayTransform.inverse().transform(simpleAppWindowFrameInLogicalDisplay); + + sp<FakeWindowHandle> simpleAppWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "SimpleAppWindow", + ADISPLAY_ID_DEFAULT); + simpleAppWindow->setFrame(simpleAppWindowFrameInDisplay, displayTransform); + simpleAppWindow->setOwnerInfo(gui::Pid{2}, gui::Uid{202}); + addWindow(simpleAppWindow); + + // The following points in logical display space should be inside the untrusted window, so + // the simple window could not receive events that coordinate is these point. + static const std::array<vec2, 4> untrustedPoints{ + {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}}; + + for (const auto untrustedPoint : untrustedPoints) { + const vec2 p = displayTransform.inverse().transform(untrustedPoint); + const PointF pointInDisplaySpace{p.x, p.y}; + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + } + untrustedWindow->assertNoEvents(); + simpleAppWindow->assertNoEvents(); + // The following points in logical display space should be outside the untrusted window, so + // the simple window should receive events that coordinate is these point. + static const std::array<vec2, 5> trustedPoints{ + {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}}; + for (const auto trustedPoint : trustedPoints) { + const vec2 p = displayTransform.inverse().transform(trustedPoint); + const PointF pointInDisplaySpace{p.x, p.y}; + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + simpleAppWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInDisplaySpace})); + simpleAppWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, + AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED); + } + untrustedWindow->assertNoEvents(); +} + // Run the precision tests for all rotations. INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests, InputDispatcherDisplayOrientationFixture, |