Fix WM input limitations on secondary displays (2/4)

- Change setInputWindow of InputManagerService to carry displayId,
  so InputWindowHandle can be updated by each DisplayContent.
- If focus window exist in current display, we need to reset it to nullptr
  in the intermediate state before the new focus set,
  to prevent it will access the released info.
- use INDENT2 for dump window under display, and likewise below.
- Some test cases for setInputWindow

Bug: 111363643
Test: atest WindowManagerSmokeTest ActivityManagerMultiDisplayTests
Test: atest com.android.server.wm.DisplayContentTests
Test: atest libinput_tests inputflinger_tests
Change-Id: I658eacb62433e3e70d6c58b47215291078cc2076
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index c805805..d609573 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -515,9 +515,10 @@
 sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
         int32_t x, int32_t y) {
     // Traverse windows from front to back to find touched window.
-    size_t numWindows = mWindowHandles.size();
+    const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    size_t numWindows = windowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
-        sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
+        sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i);
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
         if (windowInfo->displayId == displayId) {
             int32_t flags = windowInfo->layoutParamsFlags;
@@ -1247,9 +1248,10 @@
         bool isTouchModal = false;
 
         // Traverse windows from front to back to find touched window and outside targets.
-        size_t numWindows = mWindowHandles.size();
+        const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+        size_t numWindows = windowHandles.size();
         for (size_t i = 0; i < numWindows; i++) {
-            sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
+            sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i);
             const InputWindowInfo* windowInfo = windowHandle->getInfo();
             if (windowInfo->displayId != displayId) {
                 continue; // wrong display
@@ -1472,8 +1474,10 @@
         sp<InputWindowHandle> foregroundWindowHandle =
                 mTempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle->getInfo()->hasWallpaper) {
-            for (size_t i = 0; i < mWindowHandles.size(); i++) {
-                sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
+            const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+            size_t numWindows = windowHandles.size();
+            for (size_t i = 0; i < numWindows; i++) {
+                sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i);
                 const InputWindowInfo* info = windowHandle->getInfo();
                 if (info->displayId == displayId
                         && windowHandle->getInfo()->layoutParamsType
@@ -1658,9 +1662,10 @@
 bool InputDispatcher::isWindowObscuredAtPointLocked(
         const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
-    size_t numWindows = mWindowHandles.size();
+    const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    size_t numWindows = windowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
-        sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
+        sp<InputWindowHandle> otherHandle = windowHandles.itemAt(i);
         if (otherHandle == windowHandle) {
             break;
         }
@@ -1678,10 +1683,11 @@
 
 bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
+    const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
-    size_t numWindows = mWindowHandles.size();
+    size_t numWindows = windowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
-        sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
+        sp<InputWindowHandle> otherHandle = windowHandles.itemAt(i);
         if (otherHandle == windowHandle) {
             break;
         }
@@ -2909,13 +2915,27 @@
     }
 }
 
+Vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(int32_t displayId) const {
+    std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>>::const_iterator it =
+        mWindowHandlesByDisplay.find(displayId);
+    if(it != mWindowHandlesByDisplay.end()) {
+        return it->second;
+    }
+
+    // Return an empty one if nothing found.
+    return Vector<sp<InputWindowHandle>>();
+}
+
 sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
         const sp<InputChannel>& inputChannel) const {
-    size_t numWindows = mWindowHandles.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-        if (windowHandle->getInputChannel() == inputChannel) {
-            return windowHandle;
+    for (auto& it : mWindowHandlesByDisplay) {
+        const Vector<sp<InputWindowHandle>> windowHandles = it.second;
+        size_t numWindows = windowHandles.size();
+        for (size_t i = 0; i < numWindows; i++) {
+            const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
+            if (windowHandle->getInputChannel() == inputChannel) {
+                return windowHandle;
+            }
         }
     }
     return nullptr;
@@ -2923,45 +2943,90 @@
 
 bool InputDispatcher::hasWindowHandleLocked(
         const sp<InputWindowHandle>& windowHandle) const {
-    size_t numWindows = mWindowHandles.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        if (mWindowHandles.itemAt(i) == windowHandle) {
-            return true;
+    for (auto& it : mWindowHandlesByDisplay) {
+        const Vector<sp<InputWindowHandle>> windowHandles = it.second;
+        size_t numWindows = windowHandles.size();
+        for (size_t i = 0; i < numWindows; i++) {
+            if (windowHandles.itemAt(i) == windowHandle) {
+                if (windowHandle->getInfo()->displayId != it.first) {
+                    ALOGE("Found window %s in display %d, but it should belong to display %d",
+                        windowHandle->getName().c_str(), it.first,
+                        windowHandle->getInfo()->displayId);
+                }
+                return true;
+            }
         }
     }
     return false;
 }
 
-void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
+/**
+ * Called from InputManagerService, update window handle list by displayId that can receive input.
+ * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
+ * If set an empty list, remove all handles from the specific display.
+ * For focused handle, check if need to change and send a cancel event to previous one.
+ * For removed handle, check if need to send a cancel event if already in touch.
+ */
+void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle>>& inputWindowHandles,
+        int32_t displayId) {
 #if DEBUG_FOCUS
     ALOGD("setInputWindows");
 #endif
     { // acquire lock
         AutoMutex _l(mLock);
 
-        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
-        mWindowHandles = inputWindowHandles;
+        // Copy old handles for release if they are no longer present.
+        const Vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
 
-        sp<InputWindowHandle> newFocusedWindowHandle;
+        // TODO(b/111361570): multi-display focus, one focus window per display.
+        sp<InputWindowHandle> newFocusedWindowHandle = mFocusedWindowHandle;
+        // Reset newFocusedWindowHandle to nullptr if current display own the focus window,
+        // that will be updated below when going through all window handles in current display.
+        // And if list of window handles becomes empty then it will be updated by other display.
+        if (mFocusedWindowHandle != nullptr) {
+            const InputWindowInfo* info = mFocusedWindowHandle->getInfo();
+            if (info == nullptr || info->displayId == displayId) {
+                newFocusedWindowHandle = nullptr;
+            }
+        }
+
         bool foundHoveredWindow = false;
-        for (size_t i = 0; i < mWindowHandles.size(); i++) {
-            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == nullptr) {
-                mWindowHandles.removeAt(i--);
-                continue;
+
+        if (inputWindowHandles.isEmpty()) {
+            // Remove all handles on a display if there are no windows left.
+            mWindowHandlesByDisplay.erase(displayId);
+        } else {
+            size_t numWindows = inputWindowHandles.size();
+            for (size_t i = 0; i < numWindows; i++) {
+                const sp<InputWindowHandle>& windowHandle = inputWindowHandles.itemAt(i);
+                if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == nullptr) {
+                    continue;
+                }
+
+                if (windowHandle->getInfo()->displayId != displayId) {
+                    ALOGE("Window %s updated by wrong display %d, should belong to display %d",
+                        windowHandle->getName().c_str(), displayId,
+                        windowHandle->getInfo()->displayId);
+                    continue;
+                }
+
+                if (windowHandle->getInfo()->hasFocus) {
+                    newFocusedWindowHandle = windowHandle;
+                }
+                if (windowHandle == mLastHoverWindowHandle) {
+                    foundHoveredWindow = true;
+                }
             }
-            if (windowHandle->getInfo()->hasFocus) {
-                newFocusedWindowHandle = windowHandle;
-            }
-            if (windowHandle == mLastHoverWindowHandle) {
-                foundHoveredWindow = true;
-            }
+
+            // Insert or replace
+            mWindowHandlesByDisplay[displayId] = inputWindowHandles;
         }
 
         if (!foundHoveredWindow) {
             mLastHoverWindowHandle = nullptr;
         }
 
+        // TODO(b/111361570): multi-display focus, one focus in all display in current.
         if (mFocusedWindowHandle != newFocusedWindowHandle) {
             if (mFocusedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
@@ -2985,8 +3050,9 @@
             mFocusedWindowHandle = newFocusedWindowHandle;
         }
 
-        for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
-            TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
+        if (stateIndex >= 0) {
+            TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
             for (size_t i = 0; i < state.windows.size(); ) {
                 TouchedWindow& touchedWindow = state.windows.editItemAt(i);
                 if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
@@ -3013,7 +3079,8 @@
         // This ensures that unused input channels are released promptly.
         // Otherwise, they might stick around until the window handle is destroyed
         // which might not happen until the next GC.
-        for (size_t i = 0; i < oldWindowHandles.size(); i++) {
+        size_t numWindows = oldWindowHandles.size();
+        for (size_t i = 0; i < numWindows; i++) {
             const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
             if (!hasWindowHandleLocked(oldWindowHandle)) {
 #if DEBUG_FOCUS
@@ -3266,36 +3333,44 @@
         dump += INDENT "TouchStates: <no displays touched>\n";
     }
 
-    if (!mWindowHandles.isEmpty()) {
-        dump += INDENT "Windows:\n";
-        for (size_t i = 0; i < mWindowHandles.size(); i++) {
-            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
-            const InputWindowInfo* windowInfo = windowHandle->getInfo();
+    if (!mWindowHandlesByDisplay.empty()) {
+       for (auto& it : mWindowHandlesByDisplay) {
+            const Vector<sp<InputWindowHandle>> windowHandles = it.second;
+            dump += StringPrintf(INDENT "Display: %d\n", it.first);
+            if (!windowHandles.isEmpty()) {
+                dump += INDENT2 "Windows:\n";
+                for (size_t i = 0; i < windowHandles.size(); i++) {
+                    const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
+                    const InputWindowInfo* windowInfo = windowHandle->getInfo();
 
-            dump += StringPrintf(INDENT2 "%zu: name='%s', displayId=%d, "
-                    "paused=%s, hasFocus=%s, hasWallpaper=%s, "
-                    "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
-                    "frame=[%d,%d][%d,%d], scale=%f, "
-                    "touchableRegion=",
-                    i, windowInfo->name.c_str(), windowInfo->displayId,
-                    toString(windowInfo->paused),
-                    toString(windowInfo->hasFocus),
-                    toString(windowInfo->hasWallpaper),
-                    toString(windowInfo->visible),
-                    toString(windowInfo->canReceiveKeys),
-                    windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
-                    windowInfo->layer,
-                    windowInfo->frameLeft, windowInfo->frameTop,
-                    windowInfo->frameRight, windowInfo->frameBottom,
-                    windowInfo->scaleFactor);
-            dumpRegion(dump, windowInfo->touchableRegion);
-            dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
-            dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
-                    windowInfo->ownerPid, windowInfo->ownerUid,
-                    windowInfo->dispatchingTimeout / 1000000.0);
+                    dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
+                            "paused=%s, hasFocus=%s, hasWallpaper=%s, "
+                            "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
+                            "frame=[%d,%d][%d,%d], scale=%f, "
+                            "touchableRegion=",
+                            i, windowInfo->name.c_str(), windowInfo->displayId,
+                            toString(windowInfo->paused),
+                            toString(windowInfo->hasFocus),
+                            toString(windowInfo->hasWallpaper),
+                            toString(windowInfo->visible),
+                            toString(windowInfo->canReceiveKeys),
+                            windowInfo->layoutParamsFlags, windowInfo->layoutParamsType,
+                            windowInfo->layer,
+                            windowInfo->frameLeft, windowInfo->frameTop,
+                            windowInfo->frameRight, windowInfo->frameBottom,
+                            windowInfo->scaleFactor);
+                    dumpRegion(dump, windowInfo->touchableRegion);
+                    dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
+                    dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                            windowInfo->ownerPid, windowInfo->ownerUid,
+                            windowInfo->dispatchingTimeout / 1000000.0);
+                }
+            } else {
+                dump += INDENT2 "Windows: <none>\n";
+            }
         }
     } else {
-        dump += INDENT "Windows: <none>\n";
+        dump += INDENT "Displays: <none>\n";
     }
 
     if (!mMonitoringChannels.isEmpty()) {
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 31ab339..fdf75f6 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -31,6 +31,7 @@
 #include <stddef.h>
 #include <unistd.h>
 #include <limits.h>
+#include <unordered_map>
 
 #include "InputWindow.h"
 #include "InputApplication.h"
@@ -307,7 +308,8 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) = 0;
+    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles,
+            int32_t displayId) = 0;
 
     /* Sets the focused application.
      *
@@ -387,7 +389,8 @@
             int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
             uint32_t policyFlags);
 
-    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles);
+    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles,
+            int32_t displayId);
     virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle);
     virtual void setInputDispatchMode(bool enabled, bool frozen);
     virtual void setInputFilterEnabled(bool enabled);
@@ -956,8 +959,9 @@
     bool mDispatchFrozen;
     bool mInputFilterEnabled;
 
-    Vector<sp<InputWindowHandle> > mWindowHandles;
-
+    std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay;
+    // Get window handles by display, return an empty vector if not found.
+    Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const;
     sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const;
     bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const;
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 0e26d4a..61dcdd9 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -103,13 +103,20 @@
 protected:
     sp<FakeInputDispatcherPolicy> mFakePolicy;
     sp<InputDispatcher> mDispatcher;
+    sp<InputDispatcherThread> mDispatcherThread;
 
     virtual void SetUp() {
         mFakePolicy = new FakeInputDispatcherPolicy();
         mDispatcher = new InputDispatcher(mFakePolicy);
+        mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+        //Start InputDispatcher thread
+        mDispatcherThread = new InputDispatcherThread(mDispatcher);
+        mDispatcherThread->run("InputDispatcherTest", PRIORITY_URGENT_DISPLAY);
     }
 
     virtual void TearDown() {
+        mDispatcherThread->requestExit();
+        mDispatcherThread.clear();
         mFakePolicy.clear();
         mDispatcher.clear();
     }
@@ -253,4 +260,297 @@
             << "Should reject motion events with duplicate pointer ids.";
 }
 
+// --- InputDispatcherTest SetInputWindowTest ---
+static const int32_t INJECT_EVENT_TIMEOUT = 500;
+static const int32_t DISPATCHING_TIMEOUT = 100;
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+    FakeApplicationHandle() {}
+    virtual ~FakeApplicationHandle() {}
+
+    virtual bool updateInfo() {
+        if (!mInfo) {
+            mInfo = new InputApplicationInfo();
+        }
+        mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+        return true;
+    }
+};
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+    static const int32_t WIDTH = 600;
+    static const int32_t HEIGHT = 800;
+
+    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+        const sp<InputDispatcher>& dispatcher, const std::string name) :
+            InputWindowHandle(inputApplicationHandle), mDispatcher(dispatcher),
+            mName(name), mFocused(false), mDisplayId(ADISPLAY_ID_DEFAULT) {
+        InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+        mConsumer = new InputConsumer(mClientChannel);
+        mDispatcher->registerInputChannel(mServerChannel, this, false);
+    }
+
+    virtual ~FakeWindowHandle() {
+        mDispatcher->unregisterInputChannel(mServerChannel);
+        mServerChannel.clear();
+        mClientChannel.clear();
+        mDispatcher.clear();
+
+        if (mConsumer != nullptr) {
+            delete mConsumer;
+        }
+    }
+
+    virtual bool updateInfo() {
+        if (!mInfo) {
+            mInfo = new InputWindowInfo();
+        }
+        mInfo->inputChannel = mServerChannel;
+        mInfo->name = mName;
+        mInfo->layoutParamsFlags = 0;
+        mInfo->layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+        mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo->frameLeft = 0;
+        mInfo->frameTop = 0;
+        mInfo->frameRight = WIDTH;
+        mInfo->frameBottom = HEIGHT;
+        mInfo->scaleFactor = 1.0;
+        mInfo->addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+        mInfo->visible = true;
+        mInfo->canReceiveKeys = true;
+        mInfo->hasFocus = mFocused;
+        mInfo->hasWallpaper = false;
+        mInfo->paused = false;
+        mInfo->layer = 0;
+        mInfo->ownerPid = INJECTOR_PID;
+        mInfo->ownerUid = INJECTOR_UID;
+        mInfo->inputFeatures = 0;
+        mInfo->displayId = mDisplayId;
+
+        return true;
+    }
+
+    void setFocus() {
+        mFocused = true;
+    }
+
+    void setDisplayId(int32_t displayId) {
+        mDisplayId = displayId;
+    }
+
+    void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId) {
+        uint32_t consumeSeq;
+        InputEvent* event;
+        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
+            &consumeSeq, &event);
+
+        ASSERT_EQ(OK, status)
+                << mName.c_str() << ": consumer consume should return OK.";
+        ASSERT_TRUE(event != nullptr)
+                << mName.c_str() << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(expectedEventType, event->getType())
+                << mName.c_str() << ": consumer type should same as expected one.";
+
+        ASSERT_EQ(expectedDisplayId, event->getDisplayId())
+                << mName.c_str() << ": consumer displayId should same as expected one.";
+
+        status = mConsumer->sendFinishedSignal(consumeSeq, true /*handled*/);
+        ASSERT_EQ(OK, status)
+                << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+    }
+
+    void assertNoEvents() {
+        uint32_t consumeSeq;
+        InputEvent* event;
+        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
+            &consumeSeq, &event);
+        ASSERT_NE(OK, status)
+                << mName.c_str()
+                << ": should not have received any events, so consume(..) should not return OK.";
+    }
+
+    private:
+        sp<InputDispatcher> mDispatcher;
+        sp<InputChannel> mServerChannel, mClientChannel;
+        InputConsumer *mConsumer;
+        PreallocatedInputEventFactory mEventFactory;
+
+        std::string mName;
+        bool mFocused;
+        int32_t mDisplayId;
+};
+
+static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher) {
+    KeyEvent event;
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    // Define a valid key down event.
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+            AKEY_EVENT_ACTION_DOWN, /* flags */ 0,
+            AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime);
+
+    // Inject event until dispatch out.
+    return dispatcher->injectInputEvent(
+            &event,
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+            INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
+static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+    MotionEvent event;
+    PointerProperties pointerProperties[1];
+    PointerCoords pointerCoords[1];
+
+    pointerProperties[0].clear();
+    pointerProperties[0].id = 0;
+    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);
+
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    // Define a valid motion down event.
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, displayId,
+            AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0,
+            AMETA_NONE, /* buttonState */ 0, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+            /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
+            pointerCoords);
+
+    // Inject event until dispatch out.
+    return dispatcher->injectInputEvent(
+            &event,
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+            INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window");
+
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(window);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Window should receive motion event.
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+}
+
+// The foreground window should receive the first touch down event.
+TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top");
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second");
+
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(windowTop);
+    inputWindowHandles.add(windowSecond);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Top window should receive the touch down event. Second window should not receive anything.
+    windowTop->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowSecond->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top");
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second");
+
+    // Set focus application.
+    mDispatcher->setFocusedApplication(application);
+
+    // Expect one focus window exist in display.
+    windowSecond->setFocus();
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(windowTop);
+    inputWindowHandles.add(windowSecond);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Focused window should receive event.
+    windowTop->assertNoEvents();
+    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTest, SetInputWindow_MultiDisplayTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowInPrimary = new FakeWindowHandle(application, mDispatcher, "D_1");
+    sp<FakeWindowHandle> windowInSecondary = new FakeWindowHandle(application, mDispatcher, "D_2");
+
+    // Test the primary display touch down.
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.push(windowInPrimary);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowInSecondary->assertNoEvents();
+
+    // Test the second display touch down.
+    constexpr int32_t SECOND_DISPLAY_ID = 1;
+    windowInSecondary->setDisplayId(SECOND_DISPLAY_ID);
+    Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
+    inputWindowHandles_Second.push(windowInSecondary);
+
+    mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, SECOND_DISPLAY_ID))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+}
+
+// TODO(b/111361570): multi-display focus, one focus window per display.
+TEST_F(InputDispatcherTest, SetInputWindow_FocusedInMultiDisplay) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowInPrimary = new FakeWindowHandle(application, mDispatcher, "D_1");
+    sp<FakeApplicationHandle> application2 = new FakeApplicationHandle();
+    sp<FakeWindowHandle> windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2");
+
+    // Set focus to second display window.
+    mDispatcher->setFocusedApplication(application2);
+    windowInSecondary->setFocus();
+
+    // Update all windows per displays.
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.push(windowInPrimary);
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+
+    constexpr int32_t SECOND_DISPLAY_ID = 1;
+    windowInSecondary->setDisplayId(SECOND_DISPLAY_ID);
+    Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
+    inputWindowHandles_Second.push(windowInSecondary);
+    mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+
+    // Test inject a key down.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    windowInPrimary->assertNoEvents();
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+
+    // Remove secondary display.
+    inputWindowHandles_Second.clear();
+    mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+
+    // Expect old focus should receive a cancel event.
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+
+    // Test inject a key down, should timeout because of no target window.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+    windowInPrimary->assertNoEvents();
+    windowInSecondary->assertNoEvents();
+}
+
 } // namespace android