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

Commit c4173b5f authored by Svetoslav Ganov's avatar Svetoslav Ganov Committed by Android (Google) Code Review
Browse files

Merge "Generate down events when transferring touch focus"

parents 11bb5de5 5d3bc374
Loading
Loading
Loading
Loading
+74 −13
Original line number Original line Diff line number Diff line
@@ -2686,6 +2686,19 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
          connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
          connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
          options.mode);
          options.mode);
#endif
#endif

    InputTarget target;
    sp<InputWindowHandle> windowHandle =
            getWindowHandleLocked(connection->inputChannel->getConnectionToken());
    if (windowHandle != nullptr) {
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
        target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
                                     windowInfo->windowXScale, windowInfo->windowYScale);
        target.globalScaleFactor = windowInfo->globalScaleFactor;
    }
    target.inputChannel = connection->inputChannel;
    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;

    for (size_t i = 0; i < cancelationEvents.size(); i++) {
    for (size_t i = 0; i < cancelationEvents.size(); i++) {
        EventEntry* cancelationEventEntry = cancelationEvents[i];
        EventEntry* cancelationEventEntry = cancelationEvents[i];
        switch (cancelationEventEntry->type) {
        switch (cancelationEventEntry->type) {
@@ -2711,6 +2724,35 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
            }
            }
        }
        }


        enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
                                   target, InputTarget::FLAG_DISPATCH_AS_IS);

        cancelationEventEntry->release();
    }

    startDispatchCycleLocked(currentTime, connection);
}

void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
        const sp<Connection>& connection) {
    if (connection->status == Connection::STATUS_BROKEN) {
        return;
    }

    nsecs_t currentTime = now();

    std::vector<EventEntry*> downEvents =
            connection->inputState.synthesizePointerDownEvents(currentTime);

    if (downEvents.empty()) {
        return;
    }

#if DEBUG_OUTBOUND_EVENT_DETAILS
        ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
              connection->getInputChannelName().c_str(), downEvents.size());
#endif

    InputTarget target;
    InputTarget target;
    sp<InputWindowHandle> windowHandle =
    sp<InputWindowHandle> windowHandle =
            getWindowHandleLocked(connection->inputChannel->getConnectionToken());
            getWindowHandleLocked(connection->inputChannel->getConnectionToken());
@@ -2723,10 +2765,28 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
    target.inputChannel = connection->inputChannel;
    target.inputChannel = connection->inputChannel;
    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;


        enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
    for (EventEntry* downEventEntry : downEvents) {
        switch (downEventEntry->type) {
            case EventEntry::Type::MOTION: {
                logOutboundMotionDetails("down - ",
                        static_cast<const MotionEntry&>(*downEventEntry));
                break;
            }

            case EventEntry::Type::KEY:
            case EventEntry::Type::FOCUS:
            case EventEntry::Type::CONFIGURATION_CHANGED:
            case EventEntry::Type::DEVICE_RESET: {
                LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
                                     EventEntry::typeToString(downEventEntry->type));
                break;
            }
        }

        enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref
                                   target, InputTarget::FLAG_DISPATCH_AS_IS);
                                   target, InputTarget::FLAG_DISPATCH_AS_IS);


        cancelationEventEntry->release();
        downEventEntry->release();
    }
    }


    startDispatchCycleLocked(currentTime, connection);
    startDispatchCycleLocked(currentTime, connection);
@@ -3770,11 +3830,12 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<
        sp<Connection> fromConnection = getConnectionLocked(fromToken);
        sp<Connection> fromConnection = getConnectionLocked(fromToken);
        sp<Connection> toConnection = getConnectionLocked(toToken);
        sp<Connection> toConnection = getConnectionLocked(toToken);
        if (fromConnection != nullptr && toConnection != nullptr) {
        if (fromConnection != nullptr && toConnection != nullptr) {
            fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
            fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
            CancelationOptions
            CancelationOptions
                    options(CancelationOptions::CANCEL_POINTER_EVENTS,
                    options(CancelationOptions::CANCEL_POINTER_EVENTS,
                            "transferring touch focus from this window to another window");
                            "transferring touch focus from this window to another window");
            synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
            synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
            synthesizePointerDownEventsForConnectionLocked(toConnection);
        }
        }


        if (DEBUG_FOCUS) {
        if (DEBUG_FOCUS) {
+3 −0
Original line number Original line Diff line number Diff line
@@ -417,6 +417,9 @@ private:
                                                        const CancelationOptions& options)
                                                        const CancelationOptions& options)
            REQUIRES(mLock);
            REQUIRES(mLock);


    void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection)
            REQUIRES(mLock);

    // Splitting motion events across windows.
    // Splitting motion events across windows.
    MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);
    MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);


+84 −10
Original line number Original line Diff line number Diff line
@@ -145,11 +145,14 @@ bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t f
                // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
                // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
                return true;
                return true;
            }
            }

            if (index >= 0) {
            if (index >= 0) {
                MotionMemento& memento = mMotionMementos[index];
                MotionMemento& memento = mMotionMementos[index];
                if (memento.firstNewPointerIdx < 0) {
                    memento.setPointers(entry);
                    memento.setPointers(entry);
                    return true;
                    return true;
                }
                }
            }
#if DEBUG_OUTBOUND_EVENT_DETAILS
#if DEBUG_OUTBOUND_EVENT_DETAILS
            ALOGD("Dropping inconsistent motion pointer up/down or move event: "
            ALOGD("Dropping inconsistent motion pointer up/down or move event: "
                  "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
                  "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
@@ -249,6 +252,17 @@ void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
    }
    }
}
}


void InputState::MotionMemento::mergePointerStateTo(MotionMemento& other) const {
    for (uint32_t i = 0; i < pointerCount; i++) {
        if (other.firstNewPointerIdx < 0) {
            other.firstNewPointerIdx = other.pointerCount;
        }
        other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]);
        other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]);
        other.pointerCount++;
    }
}

std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
        nsecs_t currentTime, const CancelationOptions& options) {
        nsecs_t currentTime, const CancelationOptions& options) {
    std::vector<EventEntry*> events;
    std::vector<EventEntry*> events;
@@ -282,30 +296,90 @@ std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
    return events;
    return events;
}
}


std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) {
    std::vector<EventEntry*> events;
    for (MotionMemento& memento : mMotionMementos) {
        if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
            continue;
        }

        if (memento.firstNewPointerIdx < 0) {
            continue;
        }

        uint32_t pointerCount = 0;
        PointerProperties pointerProperties[MAX_POINTERS];
        PointerCoords pointerCoords[MAX_POINTERS];

        // We will deliver all pointers the target already knows about
        for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
            pointerProperties[i].copyFrom(memento.pointerProperties[i]);
            pointerCoords[i].copyFrom(memento.pointerCoords[i]);
            pointerCount++;
        }

        // We will send explicit events for all pointers the target doesn't know about
        for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
                i < memento.pointerCount; i++) {

            pointerProperties[i].copyFrom(memento.pointerProperties[i]);
            pointerCoords[i].copyFrom(memento.pointerCoords[i]);
            pointerCount++;

            // Down only if the first pointer, pointer down otherwise
            const int32_t action = (pointerCount <= 1)
                    ? AMOTION_EVENT_ACTION_DOWN
                    : AMOTION_EVENT_ACTION_POINTER_DOWN
                            | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);

            events.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
                                             memento.deviceId, memento.source, memento.displayId,
                                             memento.policyFlags, action, 0 /*actionButton*/,
                                             memento.flags, AMETA_NONE, 0 /*buttonState*/,
                                             MotionClassification::NONE,
                                             AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
                                             memento.yPrecision, memento.xCursorPosition,
                                             memento.yCursorPosition, memento.downTime,
                                             pointerCount, pointerProperties,
                                             pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
        }

        memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
    }

    return events;
}

void InputState::clear() {
void InputState::clear() {
    mKeyMementos.clear();
    mKeyMementos.clear();
    mMotionMementos.clear();
    mMotionMementos.clear();
    mFallbackKeys.clear();
    mFallbackKeys.clear();
}
}


void InputState::copyPointerStateTo(InputState& other) const {
void InputState::mergePointerStateTo(InputState& other) {
    for (size_t i = 0; i < mMotionMementos.size(); i++) {
    for (size_t i = 0; i < mMotionMementos.size(); i++) {
        const MotionMemento& memento = mMotionMementos[i];
        MotionMemento& memento = mMotionMementos[i];
        // Since we support split pointers we need to merge touch events
        // from the same source + device + screen.
        if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
        if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
            for (size_t j = 0; j < other.mMotionMementos.size();) {
            bool merged = false;
                const MotionMemento& otherMemento = other.mMotionMementos[j];
            for (size_t j = 0; j < other.mMotionMementos.size(); j++) {
                MotionMemento& otherMemento = other.mMotionMementos[j];
                if (memento.deviceId == otherMemento.deviceId &&
                if (memento.deviceId == otherMemento.deviceId &&
                    memento.source == otherMemento.source &&
                    memento.source == otherMemento.source &&
                    memento.displayId == otherMemento.displayId) {
                    memento.displayId == otherMemento.displayId) {
                    other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
                    memento.mergePointerStateTo(otherMemento);
                } else {
                    merged = true;
                    j += 1;
                    break;
                }
                }
            }
            }
            if (!merged) {
                memento.firstNewPointerIdx = 0;
                other.mMotionMementos.push_back(memento);
                other.mMotionMementos.push_back(memento);
            }
            }
        }
        }
    }
    }
}


int32_t InputState::getFallbackKey(int32_t originalKeyCode) {
int32_t InputState::getFallbackKey(int32_t originalKeyCode) {
    ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
    ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
+10 −2
Original line number Original line Diff line number Diff line
@@ -24,6 +24,8 @@


namespace android::inputdispatcher {
namespace android::inputdispatcher {


static constexpr int32_t INVALID_POINTER_INDEX = -1;

/* Tracks dispatched key and motion event state so that cancellation events can be
/* Tracks dispatched key and motion event state so that cancellation events can be
 * synthesized when events are dropped. */
 * synthesized when events are dropped. */
class InputState {
class InputState {
@@ -52,11 +54,14 @@ public:
    std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
    std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
                                                         const CancelationOptions& options);
                                                         const CancelationOptions& options);


    // Synthesizes down events for the current state.
    std::vector<EventEntry*> synthesizePointerDownEvents(nsecs_t currentTime);

    // Clears the current state.
    // Clears the current state.
    void clear();
    void clear();


    // Copies pointer-related parts of the input state to another instance.
    // Merges pointer-related parts of the input state into another instance.
    void copyPointerStateTo(InputState& other) const;
    void mergePointerStateTo(InputState& other);


    // Gets the fallback key associated with a keycode.
    // Gets the fallback key associated with a keycode.
    // Returns -1 if none.
    // Returns -1 if none.
@@ -97,10 +102,13 @@ private:
        uint32_t pointerCount;
        uint32_t pointerCount;
        PointerProperties pointerProperties[MAX_POINTERS];
        PointerProperties pointerProperties[MAX_POINTERS];
        PointerCoords pointerCoords[MAX_POINTERS];
        PointerCoords pointerCoords[MAX_POINTERS];
        // Track for which pointers the target doesn't know about.
        int32_t firstNewPointerIdx = INVALID_POINTER_INDEX;
        bool hovering;
        bool hovering;
        uint32_t policyFlags;
        uint32_t policyFlags;


        void setPointers(const MotionEntry& entry);
        void setPointers(const MotionEntry& entry);
        void mergePointerStateTo(MotionMemento& other) const;
    };
    };


    std::vector<KeyMemento> mKeyMementos;
    std::vector<KeyMemento> mKeyMementos;
+185 −2
Original line number Original line Diff line number Diff line
@@ -594,12 +594,40 @@ public:
                     expectedFlags);
                     expectedFlags);
    }
    }


    void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
    void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
            int32_t expectedFlags = 0) {
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
                     expectedFlags);
    }

    void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
            int32_t expectedFlags = 0) {
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, expectedDisplayId,
                     expectedFlags);
    }

    void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
            int32_t expectedFlags = 0) {
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
                     expectedFlags);
                     expectedFlags);
    }
    }


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

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

    void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
            int32_t expectedFlags = 0) {
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
                     expectedFlags);
                     expectedFlags);
    }
    }
@@ -923,6 +951,161 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
                         0 /*expectedFlags*/);
                         0 /*expectedFlags*/);
}
}


TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();

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

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

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

    // Transfer touch focus to the second window
    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
    // The first window gets cancel and the second gets down
    firstWindow->consumeMotionCancel();
    secondWindow->consumeMotionDown();

    // Send up event to the second window
    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&upMotionArgs);
    // The first  window gets no events and the second gets up
    firstWindow->assertNoEvents();
    secondWindow->consumeMotionUp();
}

TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();

    PointF touchPoint = {10, 10};

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

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

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

    // Send pointer down to the first window
    NotifyMotionArgs pointerDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
    mDispatcher->notifyMotion(&pointerDownMotionArgs);
    // Only the first window should get the pointer down event
    firstWindow->consumeMotionPointerDown(1);
    secondWindow->assertNoEvents();

    // Transfer touch focus to the second window
    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
    // The first window gets cancel and the second gets down and pointer down
    firstWindow->consumeMotionCancel();
    secondWindow->consumeMotionDown();
    secondWindow->consumeMotionPointerDown(1);

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

    // Send up event to the second window
    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&upMotionArgs);
    // The first window gets nothing and the second gets up
    firstWindow->assertNoEvents();
    secondWindow->consumeMotionUp();
}

TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();

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

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

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

    PointF pointInFirst = {300, 200};
    PointF pointInSecond = {300, 600};

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

    // Send down to the second window
    NotifyMotionArgs secondDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
    mDispatcher->notifyMotion(&secondDownMotionArgs);
    // The first window gets a move and the second a down
    firstWindow->consumeMotionMove();
    secondWindow->consumeMotionDown();

    // Transfer touch focus to the second window
    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
    // The first window gets cancel and the new gets pointer down (it already saw down)
    firstWindow->consumeMotionCancel();
    secondWindow->consumeMotionPointerDown(1);

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

    // Send up event to the second window
    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&upMotionArgs);
    // The first window gets nothing and the second gets up
    firstWindow->assertNoEvents();
    secondWindow->consumeMotionUp();
}

TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
    sp<FakeWindowHandle> window =
    sp<FakeWindowHandle> window =