Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 41f77b8f authored by Vishnu Nair's avatar Vishnu Nair
Browse files

InputFlinger: Add DROP_INPUT and DROP_INPUT_IF_OBSCURED feature flags

If a window has the feature DROP_INPUT set, then all touch and
key events directed to the window will be dropped. For touch events,
the events will not go to the window behind it.

If a window has the feature DROP_INPUT_IF_OBSCURED set, then all
touch and key events directed to the window will be dropped if
the window is considered partially or fully obscured.

These flags are used to enable features that allow for a less trusted
interaction model between apps. See the bug for more details.

Test: atest libgui_test InputDispatcherDropInputFeatureTest
Bug:197364677

Merged-In: I71d7cf5064c8ce4626cff09b92e15ca38b39cbbe
Change-Id: I71d7cf5064c8ce4626cff09b92e15ca38b39cbbe
parent ad87cd09
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -121,9 +121,11 @@ struct WindowInfo : public Parcelable {
    };

    enum class Feature {
        DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
        NO_INPUT_CHANNEL = 0x00000002,
        DISABLE_USER_ACTIVITY = 0x00000004,
        DISABLE_TOUCH_PAD_GESTURES = 1u << 0,
        NO_INPUT_CHANNEL = 1u << 1,
        DISABLE_USER_ACTIVITY = 1u << 2,
        DROP_INPUT = 1u << 3,
        DROP_INPUT_IF_OBSCURED = 1u << 4,
    };

    /* These values are filled in by the WM and passed through SurfaceFlinger
+32 −0
Original line number Diff line number Diff line
@@ -1828,6 +1828,11 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
        return InputEventInjectionResult::FAILED;
    }

    // Drop key events if requested by input feature
    if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
        return InputEventInjectionResult::FAILED;
    }

    // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
    // Only start counting when we have a focused event to dispatch. The ANR is canceled if we
    // start interacting with another application via touch (app switch). This code can be removed
@@ -2074,6 +2079,11 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
            }
        }

        // Drop touch events if requested by input feature
        if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) {
            newTouchedWindowHandle = nullptr;
        }

        // Also don't send the new touch event to unresponsive gesture monitors
        newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);

@@ -2139,6 +2149,13 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
            sp<WindowInfoHandle> oldTouchedWindowHandle =
                    tempTouchState.getFirstForegroundWindowHandle();
            newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);

            // Drop touch events if requested by input feature
            if (newTouchedWindowHandle != nullptr &&
                shouldDropInput(entry, newTouchedWindowHandle)) {
                newTouchedWindowHandle = nullptr;
            }

            if (oldTouchedWindowHandle != newTouchedWindowHandle &&
                oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
                if (DEBUG_FOCUS) {
@@ -6256,4 +6273,19 @@ void InputDispatcher::onWindowInfosChanged(const std::vector<gui::WindowInfo>& w
    setInputWindows(handlesPerDisplay);
}

bool InputDispatcher::shouldDropInput(
        const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const {
    if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) ||
        (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) &&
         isWindowObscuredLocked(windowHandle))) {
        ALOGW("Dropping %s event targeting %s as requested by input feature %s on display "
              "%" PRId32 ".",
              NamedEnum::string(entry.type).c_str(), windowHandle->getName().c_str(),
              windowHandle->getInfo()->inputFeatures.string().c_str(),
              windowHandle->getInfo()->displayId);
        return true;
    }
    return false;
}

} // namespace android::inputdispatcher
+4 −0
Original line number Diff line number Diff line
@@ -542,6 +542,10 @@ private:
    std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
                                          const sp<android::gui::WindowInfoHandle>& windowHandle);

    bool shouldDropInput(const EventEntry& entry,
                         const sp<android::gui::WindowInfoHandle>& windowHandle) const
            REQUIRES(mLock);

    // Manage the dispatch cycle for a single connection.
    // These methods are deliberately not Interruptible because doing all of the work
    // with the mutex held makes it easier to ensure that connection invariants are maintained.
+130 −0
Original line number Diff line number Diff line
@@ -5456,4 +5456,134 @@ TEST_F(InputDispatcherDragTests, DragAndDrop_InvalidWindow) {
    mSecondWindow->assertNoEvents();
}

class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};

TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT);
    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
    window->setFocusable(true);
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
    setFocusedWindow(window);
    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);

    // With the flag set, window should not get any input
    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyKey(&keyArgs);
    window->assertNoEvents();

    NotifyMotionArgs motionArgs =
            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&motionArgs);
    window->assertNoEvents();

    // With the flag cleared, the window should get input
    window->setInputFeatures({});
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});

    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyKey(&keyArgs);
    window->consumeKeyUp(ADISPLAY_ID_DEFAULT);

    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                    ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&motionArgs);
    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
    window->assertNoEvents();
}

TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) {
    std::shared_ptr<FakeApplicationHandle> obscuringApplication =
            std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> obscuringWindow =
            new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow",
                                 ADISPLAY_ID_DEFAULT);
    obscuringWindow->setFrame(Rect(0, 0, 50, 50));
    obscuringWindow->setOwnerInfo(111, 111);
    obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
    window->setOwnerInfo(222, 222);
    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
    window->setFocusable(true);
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
    setFocusedWindow(window);
    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);

    // With the flag set, window should not get any input
    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyKey(&keyArgs);
    window->assertNoEvents();

    NotifyMotionArgs motionArgs =
            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&motionArgs);
    window->assertNoEvents();

    // With the flag cleared, the window should get input
    window->setInputFeatures({});
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});

    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyKey(&keyArgs);
    window->consumeKeyUp(ADISPLAY_ID_DEFAULT);

    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                    ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&motionArgs);
    window->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
    window->assertNoEvents();
}

TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) {
    std::shared_ptr<FakeApplicationHandle> obscuringApplication =
            std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> obscuringWindow =
            new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow",
                                 ADISPLAY_ID_DEFAULT);
    obscuringWindow->setFrame(Rect(0, 0, 50, 50));
    obscuringWindow->setOwnerInfo(111, 111);
    obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
    window->setOwnerInfo(222, 222);
    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
    window->setFocusable(true);
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
    setFocusedWindow(window);
    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);

    // With the flag set, window should not get any input
    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyKey(&keyArgs);
    window->assertNoEvents();

    NotifyMotionArgs motionArgs =
            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&motionArgs);
    window->assertNoEvents();

    // When the window is no longer obscured because it went on top, it should get input
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}});

    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyKey(&keyArgs);
    window->consumeKeyUp(ADISPLAY_ID_DEFAULT);

    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                    ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&motionArgs);
    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
    window->assertNoEvents();
}

} // namespace android::inputdispatcher