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

Commit b27497c4 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Check if the window is partially obscured for slippery enters" into rvc-dev

parents 294e7b87 d8c6ef21
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -1782,6 +1782,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
                }
                }
                if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
                if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
                    targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
                    targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
                } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
                    targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
                }
                }


                BitSet32 pointerIds;
                BitSet32 pointerIds;
+143 −62
Original line number Original line Diff line number Diff line
@@ -19,10 +19,10 @@
#include <android-base/stringprintf.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
#include <binder/Binder.h>
#include <input/Input.h>

#include <gtest/gtest.h>
#include <gtest/gtest.h>
#include <input/Input.h>
#include <linux/input.h>
#include <linux/input.h>

#include <cinttypes>
#include <cinttypes>
#include <thread>
#include <thread>
#include <unordered_set>
#include <unordered_set>
@@ -68,12 +68,10 @@ class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
    InputDispatcherConfiguration mConfig;
    InputDispatcherConfiguration mConfig;


protected:
protected:
    virtual ~FakeInputDispatcherPolicy() {
    virtual ~FakeInputDispatcherPolicy() {}
    }


public:
public:
    FakeInputDispatcherPolicy() {
    FakeInputDispatcherPolicy() {}
    }


    void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
    void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
@@ -391,7 +389,6 @@ protected:
    }
    }
};
};



TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
    KeyEvent event;
    KeyEvent event;


@@ -589,9 +586,7 @@ public:
    }
    }
    virtual ~FakeApplicationHandle() {}
    virtual ~FakeApplicationHandle() {}


    virtual bool updateInfo() override {
    virtual bool updateInfo() override { return true; }
        return true;
    }


    void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
    void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
        mInfo.dispatchingTimeout = timeout.count();
        mInfo.dispatchingTimeout = timeout.count();
@@ -840,16 +835,17 @@ public:
    }
    }


    void consumeMotionPointerDown(int32_t pointerIdx,
    void consumeMotionPointerDown(int32_t pointerIdx,
            int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) {
                                  int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN
                                  int32_t expectedFlags = 0) {
                | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
                (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
    }
    }


    void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
    void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
                                int32_t expectedFlags = 0) {
                                int32_t expectedFlags = 0) {
        int32_t action = AMOTION_EVENT_ACTION_POINTER_UP
        int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
                | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
                (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
    }
    }


@@ -902,6 +898,11 @@ public:


    const std::string& getName() { return mName; }
    const std::string& getName() { return mName; }


    void setOwnerInfo(int32_t ownerPid, int32_t ownerUid) {
        mInfo.ownerPid = ownerPid;
        mInfo.ownerUid = ownerUid;
    }

private:
private:
    const std::string mName;
    const std::string mName;
    std::unique_ptr<FakeInputReceiver> mInputReceiver;
    std::unique_ptr<FakeInputReceiver> mInputReceiver;
@@ -1261,17 +1262,18 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();


    // Create a couple of windows
    // Create a couple of windows
    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
    sp<FakeWindowHandle> firstWindow =
            "First Window", ADISPLAY_ID_DEFAULT);
            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
    sp<FakeWindowHandle> secondWindow =
            "Second Window", ADISPLAY_ID_DEFAULT);
            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);


    // Add the windows to the dispatcher
    // Add the windows to the dispatcher
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});


    // Send down to the first window
    // Send down to the first window
    NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
    NotifyMotionArgs downMotionArgs =
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&downMotionArgs);
    mDispatcher->notifyMotion(&downMotionArgs);
    // Only the first window should get the down event
    // Only the first window should get the down event
    firstWindow->consumeMotionDown();
    firstWindow->consumeMotionDown();
@@ -1284,8 +1286,9 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
    secondWindow->consumeMotionDown();
    secondWindow->consumeMotionDown();


    // Send up event to the second window
    // Send up event to the second window
    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
    NotifyMotionArgs upMotionArgs =
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&upMotionArgs);
    mDispatcher->notifyMotion(&upMotionArgs);
    // The first  window gets no events and the second gets up
    // The first  window gets no events and the second gets up
    firstWindow->assertNoEvents();
    firstWindow->assertNoEvents();
@@ -1298,26 +1301,29 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
    PointF touchPoint = {10, 10};
    PointF touchPoint = {10, 10};


    // Create a couple of windows
    // Create a couple of windows
    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
    sp<FakeWindowHandle> firstWindow =
            "First Window", ADISPLAY_ID_DEFAULT);
            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
    sp<FakeWindowHandle> secondWindow =
            "Second Window", ADISPLAY_ID_DEFAULT);
            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);


    // Add the windows to the dispatcher
    // Add the windows to the dispatcher
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});


    // Send down to the first window
    // Send down to the first window
    NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
    NotifyMotionArgs downMotionArgs =
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint});
            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT, {touchPoint});
    mDispatcher->notifyMotion(&downMotionArgs);
    mDispatcher->notifyMotion(&downMotionArgs);
    // Only the first window should get the down event
    // Only the first window should get the down event
    firstWindow->consumeMotionDown();
    firstWindow->consumeMotionDown();
    secondWindow->assertNoEvents();
    secondWindow->assertNoEvents();


    // Send pointer down to the first window
    // Send pointer down to the first window
    NotifyMotionArgs pointerDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
    NotifyMotionArgs pointerDownMotionArgs =
            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                               {touchPoint, touchPoint});
    mDispatcher->notifyMotion(&pointerDownMotionArgs);
    mDispatcher->notifyMotion(&pointerDownMotionArgs);
    // Only the first window should get the pointer down event
    // Only the first window should get the pointer down event
    firstWindow->consumeMotionPointerDown(1);
    firstWindow->consumeMotionPointerDown(1);
@@ -1331,17 +1337,20 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
    secondWindow->consumeMotionPointerDown(1);
    secondWindow->consumeMotionPointerDown(1);


    // Send pointer up to the second window
    // Send pointer up to the second window
    NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
    NotifyMotionArgs pointerUpMotionArgs =
            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                               {touchPoint, touchPoint});
    mDispatcher->notifyMotion(&pointerUpMotionArgs);
    mDispatcher->notifyMotion(&pointerUpMotionArgs);
    // The first window gets nothing and the second gets pointer up
    // The first window gets nothing and the second gets pointer up
    firstWindow->assertNoEvents();
    firstWindow->assertNoEvents();
    secondWindow->consumeMotionPointerUp(1);
    secondWindow->consumeMotionPointerUp(1);


    // Send up event to the second window
    // Send up event to the second window
    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
    NotifyMotionArgs upMotionArgs =
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&upMotionArgs);
    mDispatcher->notifyMotion(&upMotionArgs);
    // The first window gets nothing and the second gets up
    // The first window gets nothing and the second gets up
    firstWindow->assertNoEvents();
    firstWindow->assertNoEvents();
@@ -1352,15 +1361,15 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();


    // Create a non touch modal window that supports split touch
    // Create a non touch modal window that supports split touch
    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
    sp<FakeWindowHandle> firstWindow =
            "First Window", ADISPLAY_ID_DEFAULT);
            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
    firstWindow->setFrame(Rect(0, 0, 600, 400));
    firstWindow->setFrame(Rect(0, 0, 600, 400));
    firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
    firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
            | InputWindowInfo::FLAG_SPLIT_TOUCH);
            | InputWindowInfo::FLAG_SPLIT_TOUCH);


    // Create a non touch modal window that supports split touch
    // Create a non touch modal window that supports split touch
    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
    sp<FakeWindowHandle> secondWindow =
            "Second Window", ADISPLAY_ID_DEFAULT);
            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
    secondWindow->setFrame(Rect(0, 400, 600, 800));
    secondWindow->setFrame(Rect(0, 400, 600, 800));
    secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
    secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
            | InputWindowInfo::FLAG_SPLIT_TOUCH);
            | InputWindowInfo::FLAG_SPLIT_TOUCH);
@@ -1372,17 +1381,20 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
    PointF pointInSecond = {300, 600};
    PointF pointInSecond = {300, 600};


    // Send down to the first window
    // Send down to the first window
    NotifyMotionArgs firstDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
    NotifyMotionArgs firstDownMotionArgs =
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst});
            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT, {pointInFirst});
    mDispatcher->notifyMotion(&firstDownMotionArgs);
    mDispatcher->notifyMotion(&firstDownMotionArgs);
    // Only the first window should get the down event
    // Only the first window should get the down event
    firstWindow->consumeMotionDown();
    firstWindow->consumeMotionDown();
    secondWindow->assertNoEvents();
    secondWindow->assertNoEvents();


    // Send down to the second window
    // Send down to the second window
    NotifyMotionArgs secondDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
    NotifyMotionArgs secondDownMotionArgs =
            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                               {pointInFirst, pointInSecond});
    mDispatcher->notifyMotion(&secondDownMotionArgs);
    mDispatcher->notifyMotion(&secondDownMotionArgs);
    // The first window gets a move and the second a down
    // The first window gets a move and the second a down
    firstWindow->consumeMotionMove();
    firstWindow->consumeMotionMove();
@@ -1395,17 +1407,20 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
    secondWindow->consumeMotionPointerDown(1);
    secondWindow->consumeMotionPointerDown(1);


    // Send pointer up to the second window
    // Send pointer up to the second window
    NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
    NotifyMotionArgs pointerUpMotionArgs =
            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                               {pointInFirst, pointInSecond});
    mDispatcher->notifyMotion(&pointerUpMotionArgs);
    mDispatcher->notifyMotion(&pointerUpMotionArgs);
    // The first window gets nothing and the second gets pointer up
    // The first window gets nothing and the second gets pointer up
    firstWindow->assertNoEvents();
    firstWindow->assertNoEvents();
    secondWindow->consumeMotionPointerUp(1);
    secondWindow->consumeMotionPointerUp(1);


    // Send up event to the second window
    // Send up event to the second window
    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
    NotifyMotionArgs upMotionArgs =
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&upMotionArgs);
    mDispatcher->notifyMotion(&upMotionArgs);
    // The first window gets nothing and the second gets up
    // The first window gets nothing and the second gets up
    firstWindow->assertNoEvents();
    firstWindow->assertNoEvents();
@@ -1722,6 +1737,72 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
    EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
    EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
}
}


/**
 * Launch two windows, with different owners. One window (slipperyExitWindow) has Flag::SLIPPERY,
 * and overlaps the other window, slipperyEnterWindow. The window 'slipperyExitWindow' is on top
 * of the 'slipperyEnterWindow'.
 *
 * Inject touch down into the top window. Upon receipt of the DOWN event, move the window in such
 * a way so that the touched location is no longer covered by the top window.
 *
 * Next, inject a MOVE event. Because the top window already moved earlier, this event is now
 * positioned over the bottom (slipperyEnterWindow) only. And because the top window had
 * Flag::SLIPPERY, this will cause the top window to lose the touch event (it will receive
 * ACTION_CANCEL instead), and the bottom window will receive a newly generated gesture (starting
 * with ACTION_DOWN).
 * Thus, the touch has been transferred from the top window into the bottom window, because the top
 * window moved itself away from the touched location and had Flag::SLIPPERY.
 *
 * Even though the top window moved away from the touched location, it is still obscuring the bottom
 * window. It's just not obscuring it at the touched location. That means, FLAG_WINDOW_IS_PARTIALLY_
 * OBSCURED should be set for the MotionEvent that reaches the bottom window.
 *
 * In this test, we ensure that the event received by the bottom window has
 * FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
 */
TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) {
    constexpr int32_t SLIPPERY_PID = INJECTOR_PID + 1;
    constexpr int32_t SLIPPERY_UID = INJECTOR_UID + 1;

    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);

    sp<FakeWindowHandle> slipperyExitWindow =
            new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
    slipperyExitWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
                                 InputWindowInfo::FLAG_SLIPPERY);
    // Make sure this one overlaps the bottom window
    slipperyExitWindow->setFrame(Rect(25, 25, 75, 75));
    // Change the owner uid/pid of the window so that it is considered to be occluding the bottom
    // one. Windows with the same owner are not considered to be occluding each other.
    slipperyExitWindow->setOwnerInfo(SLIPPERY_PID, SLIPPERY_UID);

    sp<FakeWindowHandle> slipperyEnterWindow =
            new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
    slipperyExitWindow->setFrame(Rect(0, 0, 100, 100));

    mDispatcher->setInputWindows(
            {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});

    // Use notifyMotion instead of injecting to avoid dealing with injection permissions
    NotifyMotionArgs args = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                               ADISPLAY_ID_DEFAULT, {{50, 50}});
    mDispatcher->notifyMotion(&args);
    slipperyExitWindow->consumeMotionDown();
    slipperyExitWindow->setFrame(Rect(70, 70, 100, 100));
    mDispatcher->setInputWindows(
            {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});

    args = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
                              ADISPLAY_ID_DEFAULT, {{51, 51}});
    mDispatcher->notifyMotion(&args);

    slipperyExitWindow->consumeMotionCancel();

    slipperyEnterWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT,
                                           AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
}

class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
protected:
    static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
    static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
@@ -1972,11 +2053,11 @@ protected:
    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
        NotifyMotionArgs motionArgs;
        NotifyMotionArgs motionArgs;


        motionArgs = generateMotionArgs(
        motionArgs =
                AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
                generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
        mDispatcher->notifyMotion(&motionArgs);
        mDispatcher->notifyMotion(&motionArgs);
        motionArgs = generateMotionArgs(
        motionArgs =
                AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
                generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
        mDispatcher->notifyMotion(&motionArgs);
        mDispatcher->notifyMotion(&motionArgs);
        ASSERT_TRUE(mDispatcher->waitForIdle());
        ASSERT_TRUE(mDispatcher->waitForIdle());
        if (expectToBeFiltered) {
        if (expectToBeFiltered) {