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

Commit fd6d3515 authored by chaviw's avatar chaviw
Browse files

Add onPointerDownOutsideFocus for events outside the focused window (2/4)

Post the onPointerDownOutsideFocus command when the conditions are satisfied.
Added onPointerDownOutsideFocus to the InputDispatcherPolicyInterface so the
callback can be sent to the client.

Bug: 122535136
Test: InputDispatcherOnPointerDownOutsideFocus
Change-Id: Ifec75daf1fff4ef83e0c230829400abb5a065d7f
parent 8ef26834
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -2055,7 +2055,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(
            return; // skip the inconsistent event
        }

        dispatchPointerDownOutsideFocusIfNecessary(motionEntry->source,
        dispatchPointerDownOutsideFocus(motionEntry->source,
                dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());

        break;
@@ -2073,10 +2073,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(

}

void InputDispatcher::dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
        const sp<IBinder>& newToken) {
    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
    if (source != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
    uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
    if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
        return;
    }

@@ -2095,7 +2096,9 @@ void InputDispatcher::dispatchPointerDownOutsideFocusIfNecessary(uint32_t source
        return;
    }

    // Dispatch onPointerDownOutsideFocus to the policy.
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
    commandEntry->newToken = newToken;
}

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3997,6 +4000,12 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
    entry->release();
}

void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
    mLock.unlock();
    mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
    mLock.lock();
}

void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
        CommandEntry* commandEntry) {
    sp<Connection> connection = commandEntry->connection;
+10 −1
Original line number Diff line number Diff line
@@ -272,6 +272,13 @@ public:
     */
    virtual bool checkInjectEventsPermissionNonReentrant(
            int32_t injectorPid, int32_t injectorUid) = 0;

    /* Notifies the policy that a pointer down event has occurred outside the current focused
     * window.
     *
     * The touchedToken passed as an argument is the window that received the input event.
     */
    virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
};


@@ -1149,7 +1156,7 @@ private:
    void releaseDispatchEntry(DispatchEntry* dispatchEntry);
    static int handleReceiveCallback(int fd, int events, void* data);
    // The action sent should only be of type AMOTION_EVENT_*
    void dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
    void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
            const sp<IBinder>& newToken) REQUIRES(mLock);

    void synthesizeCancelationEventsForAllConnectionsLocked(
@@ -1204,6 +1211,8 @@ private:
            DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
    void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
    void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry)
            REQUIRES(mLock);

    // Statistics gathering.
    void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
+129 −12
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ public:
        mTime = -1;
        mAction = -1;
        mDisplayId = -1;
        mOnPointerDownToken.clear();
    }

    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
@@ -87,11 +88,18 @@ public:
                << "Expected filterInputEvent() to not have been called.";
    }

    void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
        ASSERT_EQ(mOnPointerDownToken, touchedToken)
                << "Expected token from onPointerDownOutsideFocus was not matched";
        reset();
    }

private:
    bool mInputEventFiltered;
    nsecs_t mTime;
    int32_t mAction;
    int32_t mDisplayId;
    sp<IBinder> mOnPointerDownToken;

    virtual void notifyConfigurationChanged(nsecs_t) {
    }
@@ -161,11 +169,16 @@ private:
        return false;
    }

    virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) {
        mOnPointerDownToken = newToken;
    }

    void reset() {
        mInputEventFiltered = false;
        mTime = -1;
        mAction = -1;
        mDisplayId = -1;
        mOnPointerDownToken.clear();
    }
};

@@ -432,7 +445,7 @@ public:
    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
        const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
            FakeInputReceiver(dispatcher, name, displayId),
            mFocused(false) {
            mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
            mServerChannel->setToken(new BBinder());
            mDispatcher->registerInputChannel(mServerChannel, displayId);

@@ -443,15 +456,15 @@ public:
    virtual bool updateInfo() {
        mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
        mInfo.name = mName;
        mInfo.layoutParamsFlags = 0;
        mInfo.layoutParamsFlags = mLayoutParamFlags;
        mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
        mInfo.frameLeft = 0;
        mInfo.frameTop = 0;
        mInfo.frameRight = WIDTH;
        mInfo.frameBottom = HEIGHT;
        mInfo.frameLeft = mFrame.left;
        mInfo.frameTop = mFrame.top;
        mInfo.frameRight = mFrame.right;
        mInfo.frameBottom = mFrame.bottom;
        mInfo.globalScaleFactor = 1.0;
        mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
        mInfo.addTouchableRegion(mFrame);
        mInfo.visible = true;
        mInfo.canReceiveKeys = true;
        mInfo.hasFocus = mFocused;
@@ -470,6 +483,14 @@ public:
        mFocused = true;
    }

    void setFrame(const Rect& frame) {
        mFrame.set(frame);
    }

    void setLayoutParamFlags(int32_t flags) {
        mLayoutParamFlags = flags;
    }

    void releaseChannel() {
        mServerChannel.clear();
        InputWindowHandle::releaseChannel();
@@ -480,6 +501,8 @@ protected:
    }

    bool mFocused;
    Rect mFrame;
    int32_t mLayoutParamFlags;
};

static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -500,7 +523,7 @@ static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
}

static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
        int32_t displayId) {
        int32_t displayId, int32_t x = 100, int32_t y = 200) {
    MotionEvent event;
    PointerProperties pointerProperties[1];
    PointerCoords pointerCoords[1];
@@ -510,8 +533,8 @@ static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t s
    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;

    pointerCoords[0].clear();
    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);

    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
    // Define a valid motion down event.
@@ -907,4 +930,98 @@ TEST_F(InputFilterTest, KeyEvent_InputFilter) {
    testNotifyKey(/*expectToBeFiltered*/ false);
}

class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
    virtual void SetUp() {
        InputDispatcherTest::SetUp();

        sp<FakeApplicationHandle> application = new FakeApplicationHandle();
        mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
                ADISPLAY_ID_DEFAULT);
        mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
        // window.
        mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);

        mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
                ADISPLAY_ID_DEFAULT);
        mWindowFocused->setFrame(Rect(50, 50, 100, 100));
        mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
        mWindowFocusedTouchPoint = 60;

        // Set focused application.
        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
        mWindowFocused->setFocus();

        // Expect one focus window exist in display.
        std::vector<sp<InputWindowHandle>> inputWindowHandles;
        inputWindowHandles.push_back(mUnfocusedWindow);
        inputWindowHandles.push_back(mWindowFocused);
        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
    }

    virtual void TearDown() {
        InputDispatcherTest::TearDown();

        mUnfocusedWindow.clear();
        mWindowFocused.clear();
    }

protected:
    sp<FakeWindowHandle> mUnfocusedWindow;
    sp<FakeWindowHandle> mWindowFocused;
    int32_t mWindowFocusedTouchPoint;
};

// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
// DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
// the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
    // Call monitor to wait for the command queue to get flushed.
    mDispatcher->monitor();

    mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
}

// Have two windows, one with focus. Inject MotionEvent with source TRACKBALL and action
// DOWN on the window that doesn't have focus. Ensure no window received the
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
            AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
    // Call monitor to wait for the command queue to get flushed.
    mDispatcher->monitor();

    mFakePolicy->assertOnPointerDownEquals(nullptr);
}

// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
    // Call monitor to wait for the command queue to get flushed.
    mDispatcher->monitor();

    mFakePolicy->assertOnPointerDownEquals(nullptr);
}

// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
// DOWN on the window that already has focus. Ensure no window received the
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus,
        OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
            mWindowFocusedTouchPoint))
            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
    // Call monitor to wait for the command queue to get flushed.
    mDispatcher->monitor();

    mFakePolicy->assertOnPointerDownEquals(nullptr);
}

} // namespace android