Add physical port to display viewport

DisplayViewport now also stores the physical port information of the
associated display.
The physical port of the display can be used to match an input device to
the corresponding viewport, therefore ensuring that a specific input
device generates events that go to a particular display.

The associations are provided to InputReaderConfiguration by the JNI
callback.

Change getViewport strategy once more. Now, uniqueId match by itself is
sufficient (viewport type does not need to match if the viewport with
the matching uniqueId is found).
Added a check for assumptions about the number of viewports that can
have the same type. These should be relaxed once (and if) support for
multiple internal viewports is added.

Bug: 116239493
Test: atest -a libinput_tests inputflinger_tests
Change-Id: I1c84f281e8c255f12aea0d15939c70685fe94c41
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 0de3bb7..fa456bb 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -20,6 +20,8 @@
 #include <android-base/stringprintf.h>
 #include <ui/DisplayInfo.h>
 #include <input/Input.h>
+#include <inttypes.h>
+#include <optional>
 
 using android::base::StringPrintf;
 
@@ -66,13 +68,17 @@
     int32_t deviceWidth;
     int32_t deviceHeight;
     std::string uniqueId;
+    // The actual (hardware) port that the associated display is connected to.
+    // Not all viewports will have this specified.
+    std::optional<uint8_t> physicalPort;
     ViewportType type;
 
     DisplayViewport() :
             displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
             logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
             physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
-            deviceWidth(0), deviceHeight(0), uniqueId(), type(ViewportType::VIEWPORT_INTERNAL) {
+            deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
+            type(ViewportType::VIEWPORT_INTERNAL) {
     }
 
     bool operator==(const DisplayViewport& other) const {
@@ -89,6 +95,7 @@
                 && deviceWidth == other.deviceWidth
                 && deviceHeight == other.deviceHeight
                 && uniqueId == other.uniqueId
+                && physicalPort == other.physicalPort
                 && type == other.type;
     }
 
@@ -114,16 +121,19 @@
         deviceWidth = width;
         deviceHeight = height;
         uniqueId.clear();
+        physicalPort = std::nullopt;
         type = ViewportType::VIEWPORT_INTERNAL;
     }
 
     std::string toString() const {
-        return StringPrintf("Viewport %s: displayId=%d, orientation=%d, "
+        return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
             "logicalFrame=[%d, %d, %d, %d], "
             "physicalFrame=[%d, %d, %d, %d], "
             "deviceSize=[%d, %d]",
-            viewportTypeToString(type),
-            displayId, orientation,
+            viewportTypeToString(type), displayId,
+            uniqueId.c_str(),
+            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>",
+            orientation,
             logicalLeft, logicalTop,
             logicalRight, logicalBottom,
             physicalLeft, physicalTop,
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 9dd14dc..9e748d8 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -991,6 +991,12 @@
             deviceInfo.getDisplayName().c_str());
     dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
     dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+    dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
+    if (mAssociatedDisplayPort) {
+        dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
+    } else {
+        dump += "<none>\n";
+    }
     dump += StringPrintf(INDENT2 "HasMic:     %s\n", toString(mHasMic));
     dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
     dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -1060,6 +1066,20 @@
             setEnabled(enabled, when);
         }
 
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+             // In most situations, no port will be specified.
+            mAssociatedDisplayPort = std::nullopt;
+            // Find the display port that corresponds to the current input port.
+            const std::string& inputPort = mIdentifier.location;
+            if (!inputPort.empty()) {
+                const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
+                const auto& displayPort = ports.find(inputPort);
+                if (displayPort != ports.end()) {
+                    mAssociatedDisplayPort = std::make_optional(displayPort->second);
+                }
+            }
+        }
+
         size_t numMappers = mMappers.size();
         for (size_t i = 0; i < numMappers; i++) {
             InputMapper* mapper = mMappers[i];
@@ -2203,7 +2223,7 @@
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         if (mParameters.orientationAware) {
-            mViewport = config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+            mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
         }
     }
 }
@@ -2613,7 +2633,7 @@
         mOrientation = DISPLAY_ORIENTATION_0;
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
             std::optional<DisplayViewport> internalViewport =
-                    config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+                    config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
             if (internalViewport) {
                 mOrientation = internalViewport->orientation;
             }
@@ -2929,7 +2949,7 @@
     }
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         std::optional<DisplayViewport> internalViewport =
-                config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+                config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
         if (internalViewport) {
             mOrientation = internalViewport->orientation;
         } else {
@@ -3310,6 +3330,9 @@
             mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
         }
     }
+    if (getDevice()->getAssociatedDisplayPort()) {
+        mParameters.hasAssociatedDisplay = true;
+    }
 
     // Initial downs on external touch devices should wake the device.
     // Normally we don't do this for internal touch screens to prevent them from waking
@@ -3384,6 +3407,49 @@
     return mExternalStylusConnected;
 }
 
+/**
+ * Determine which DisplayViewport to use.
+ * 1. If display port is specified, return the matching viewport. If matching viewport not
+ * found, then return.
+ * 2. If a device has associated display, get the matching viewport by either unique id or by
+ * the display type (internal or external).
+ * 3. Otherwise, use a non-display viewport.
+ */
+std::optional<DisplayViewport> TouchInputMapper::findViewport() {
+    if (mParameters.hasAssociatedDisplay) {
+        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+        if (displayPort) {
+            // Find the viewport that contains the same port
+            std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort);
+            if (!v) {
+                ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+                        "but the corresponding viewport is not found.",
+                        getDeviceName().c_str(), *displayPort);
+            }
+            return v;
+        }
+
+        if (!mParameters.uniqueDisplayId.empty()) {
+            return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
+        }
+
+        ViewportType viewportTypeToUse;
+        if (mParameters.associatedDisplayIsExternal) {
+            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+        } else {
+            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+        }
+        return mConfig.getDisplayViewportByType(viewportTypeToUse);
+    }
+
+    DisplayViewport newViewport;
+    // Raw width and height in the natural orientation.
+    int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    int32_t rawHeight = mRawPointerAxes.getRawHeight();
+    newViewport.setNonDisplayViewport(rawWidth, rawHeight);
+    return std::make_optional(newViewport);
+}
+
 void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
     int32_t oldDeviceMode = mDeviceMode;
 
@@ -3417,50 +3483,30 @@
 
     // Ensure we have valid X and Y axes.
     if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
-        ALOGW(INDENT "Touch device '%s' did not report support for X or Y axis!  "
+        ALOGW("Touch device '%s' did not report support for X or Y axis!  "
                 "The device will be inoperable.", getDeviceName().c_str());
         mDeviceMode = DEVICE_MODE_DISABLED;
         return;
     }
 
+    // Get associated display dimensions.
+    std::optional<DisplayViewport> newViewport = findViewport();
+    if (!newViewport) {
+        ALOGI("Touch device '%s' could not query the properties of its associated "
+                "display.  The device will be inoperable until the display size "
+                "becomes available.",
+                getDeviceName().c_str());
+        mDeviceMode = DEVICE_MODE_DISABLED;
+        return;
+    }
+
     // Raw width and height in the natural orientation.
     int32_t rawWidth = mRawPointerAxes.getRawWidth();
     int32_t rawHeight = mRawPointerAxes.getRawHeight();
 
-    // Get associated display dimensions.
-    DisplayViewport newViewport;
-    if (mParameters.hasAssociatedDisplay) {
-        std::string uniqueDisplayId;
-        ViewportType viewportTypeToUse;
-
-        if (mParameters.associatedDisplayIsExternal) {
-            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
-        } else if (!mParameters.uniqueDisplayId.empty()) {
-            // If the IDC file specified a unique display Id, then it expects to be linked to a
-            // virtual display with the same unique ID.
-            uniqueDisplayId = mParameters.uniqueDisplayId;
-            viewportTypeToUse = ViewportType::VIEWPORT_VIRTUAL;
-        } else {
-            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
-        }
-
-        std::optional<DisplayViewport> viewportToUse =
-                mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId);
-        if (!viewportToUse) {
-            ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
-                    "display.  The device will be inoperable until the display size "
-                    "becomes available.",
-                    getDeviceName().c_str());
-            mDeviceMode = DEVICE_MODE_DISABLED;
-            return;
-        }
-        newViewport = *viewportToUse;
-    } else {
-        newViewport.setNonDisplayViewport(rawWidth, rawHeight);
-    }
-    bool viewportChanged = mViewport != newViewport;
+    bool viewportChanged = mViewport != *newViewport;
     if (viewportChanged) {
-        mViewport = newViewport;
+        mViewport = *newViewport;
 
         if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
             // Convert rotated viewport to natural surface coordinates.
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 1786fe8..13f1bed 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -252,6 +252,9 @@
 
     inline bool isExternal() { return mIsExternal; }
     inline void setExternal(bool external) { mIsExternal = external; }
+    inline std::optional<uint8_t> getAssociatedDisplayPort() const {
+        return mAssociatedDisplayPort;
+    }
 
     inline void setMic(bool hasMic) { mHasMic = hasMic; }
     inline bool hasMic() const { return mHasMic; }
@@ -324,6 +327,7 @@
 
     uint32_t mSources;
     bool mIsExternal;
+    std::optional<uint8_t> mAssociatedDisplayPort;
     bool mHasMic;
     bool mDropUntilNextSync;
 
@@ -1501,6 +1505,8 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
+    std::optional<DisplayViewport> findViewport();
+
     void resetExternalStylus();
     void clearStylusDataPendingFlags();
 
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 17a116e..f48a645 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -49,14 +49,51 @@
 
 // --- InputReaderConfiguration ---
 
-std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewport(
-        ViewportType viewportType, const std::string& uniqueDisplayId) const {
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
+        const std::string& uniqueDisplayId) const {
+    if (uniqueDisplayId.empty()) {
+        ALOGE("Empty string provided to %s", __func__);
+        return std::nullopt;
+    }
+    size_t count = 0;
+    std::optional<DisplayViewport> result = std::nullopt;
     for (const DisplayViewport& currentViewport : mDisplays) {
-        if (currentViewport.type == viewportType) {
-            if (uniqueDisplayId.empty() ||
-                    (!uniqueDisplayId.empty() && uniqueDisplayId == currentViewport.uniqueId)) {
-                return std::make_optional(currentViewport);
-            }
+        if (uniqueDisplayId == currentViewport.uniqueId) {
+            result = std::make_optional(currentViewport);
+            count++;
+        }
+    }
+    if (count > 1) {
+        ALOGE("Found %zu viewports with uniqueId %s, but expected 1 at most",
+            count, uniqueDisplayId.c_str());
+    }
+    return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByType(ViewportType type)
+        const {
+    size_t count = 0;
+    std::optional<DisplayViewport> result = std::nullopt;
+    for (const DisplayViewport& currentViewport : mDisplays) {
+        // Return the first match
+        if (currentViewport.type == type && !result) {
+            result = std::make_optional(currentViewport);
+            count++;
+        }
+    }
+    if (count > 1) {
+        ALOGE("Found %zu viewports with type %s, but expected 1 at most",
+                count, viewportTypeToString(type));
+    }
+    return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByPort(
+        uint8_t displayPort) const {
+    for (const DisplayViewport& currentViewport : mDisplays) {
+        const std::optional<uint8_t>& physicalPort = currentViewport.physicalPort;
+        if (physicalPort && (*physicalPort == displayPort)) {
+            return std::make_optional(currentViewport);
         }
     }
     return std::nullopt;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index fff8480..fe1c50b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -32,6 +32,7 @@
 #include <optional>
 #include <stddef.h>
 #include <unistd.h>
+#include <unordered_map>
 #include <vector>
 
 // Maximum supported size of a vibration pattern.
@@ -164,6 +165,10 @@
     // Devices with these names will be ignored.
     std::vector<std::string> excludedDeviceNames;
 
+    // The associations between input ports and display ports.
+    // Used to determine which DisplayViewport should be tied to which InputDevice.
+    std::unordered_map<std::string, uint8_t> portAssociations;
+
     // Velocity control parameters for mouse pointer movements.
     VelocityControlParameters pointerVelocityControlParameters;
 
@@ -262,8 +267,10 @@
             pointerGestureZoomSpeedRatio(0.3f),
             showTouches(false) { }
 
-    std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType,
-            const std::string& uniqueDisplayId) const;
+    std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
+    std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
+            const;
+    std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t physicalPort) const;
     void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
 
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 929424b..04b87d5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -28,12 +28,14 @@
 
 // Arbitrary display properties.
 static const int32_t DISPLAY_ID = 0;
+static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
 static const int32_t DISPLAY_WIDTH = 480;
 static const int32_t DISPLAY_HEIGHT = 800;
 static const int32_t VIRTUAL_DISPLAY_ID = 1;
 static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
 static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
 static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
+static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
 
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
@@ -147,15 +149,22 @@
         mConfig.setDisplayViewports(mViewports);
     }
 
-    std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType,
-            const std::string& uniqueId) {
-        return mConfig.getDisplayViewport(viewportType, uniqueId);
+    std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
+        return mConfig.getDisplayViewportByUniqueId(uniqueId);
+    }
+    std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
+        return mConfig.getDisplayViewportByType(type);
+    }
+
+    std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
+        return mConfig.getDisplayViewportByPort(displayPort);
     }
 
     void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
-            const std::string& uniqueId, ViewportType viewportType) {
+            const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+            ViewportType viewportType) {
         const DisplayViewport viewport = createDisplayViewport(displayId, width, height,
-                orientation, uniqueId, viewportType);
+                orientation, uniqueId, physicalPort, viewportType);
         mViewports.push_back(viewport);
         mConfig.setDisplayViewports(mViewports);
     }
@@ -164,6 +173,10 @@
         mConfig.excludedDeviceNames.push_back(deviceName);
     }
 
+    void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
+        mConfig.portAssociations.insert({inputPort, displayPort});
+    }
+
     void addDisabledDevice(int32_t deviceId) {
         ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
         bool currentlyEnabled = index < 0;
@@ -207,7 +220,8 @@
 
 private:
     DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation, const std::string& uniqueId, ViewportType type) {
+            int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+            ViewportType type) {
         bool isRotated = (orientation == DISPLAY_ORIENTATION_90
                 || orientation == DISPLAY_ORIENTATION_270);
         DisplayViewport v;
@@ -224,6 +238,7 @@
         v.deviceWidth = isRotated ? height : width;
         v.deviceHeight = isRotated ? width : height;
         v.uniqueId = uniqueId;
+        v.physicalPort = physicalPort;
         v.type = type;
         return v;
     }
@@ -1105,26 +1120,28 @@
 
     // We didn't add any viewports yet, so there shouldn't be any.
     std::optional<DisplayViewport> internalViewport =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
     ASSERT_FALSE(internalViewport);
 
     // Add an internal viewport, then clear it
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, uniqueId, ViewportType::VIEWPORT_INTERNAL);
+            DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
 
     // Check matching by uniqueId
-    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+    internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
     ASSERT_TRUE(internalViewport);
+    ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
 
     // Check matching by viewport type
-    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
     ASSERT_TRUE(internalViewport);
+    ASSERT_EQ(uniqueId, internalViewport->uniqueId);
 
     mFakePolicy->clearViewports();
     // Make sure nothing is found after clear
-    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+    internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
     ASSERT_FALSE(internalViewport);
-    internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+    internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
     ASSERT_FALSE(internalViewport);
 }
 
@@ -1138,40 +1155,42 @@
 
     // Add an internal viewport
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, internalUniqueId, ViewportType::VIEWPORT_INTERNAL);
+            DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     // Add an external viewport
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, externalUniqueId, ViewportType::VIEWPORT_EXTERNAL);
+            DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
     // Add an virtual viewport
     mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, virtualUniqueId1, ViewportType::VIEWPORT_VIRTUAL);
+            DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
     // Add another virtual viewport
     mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, virtualUniqueId2, ViewportType::VIEWPORT_VIRTUAL);
+            DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
 
     // Check matching by type for internal
     std::optional<DisplayViewport> internalViewport =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
     ASSERT_TRUE(internalViewport);
     ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
 
     // Check matching by type for external
     std::optional<DisplayViewport> externalViewport =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, "");
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
     ASSERT_TRUE(externalViewport);
     ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
 
     // Check matching by uniqueId for virtual viewport #1
     std::optional<DisplayViewport> virtualViewport1 =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId1);
+            mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
     ASSERT_TRUE(virtualViewport1);
+    ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
     ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
     ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
 
     // Check matching by uniqueId for virtual viewport #2
     std::optional<DisplayViewport> virtualViewport2 =
-            mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId2);
+            mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
     ASSERT_TRUE(virtualViewport2);
+    ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
     ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
     ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
 }
@@ -1194,18 +1213,20 @@
         mFakePolicy->clearViewports();
         // Add a viewport
         mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, uniqueId1, type);
+            DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT, type);
         // Add another viewport
         mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_0, uniqueId2, type);
+            DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT, type);
 
         // Check that correct display viewport was returned by comparing the display IDs.
-        std::optional<DisplayViewport> viewport1 = mFakePolicy->getDisplayViewport(type, uniqueId1);
+        std::optional<DisplayViewport> viewport1 =
+                mFakePolicy->getDisplayViewportByUniqueId(uniqueId1);
         ASSERT_TRUE(viewport1);
         ASSERT_EQ(displayId1, viewport1->displayId);
         ASSERT_EQ(type, viewport1->type);
 
-        std::optional<DisplayViewport> viewport2 = mFakePolicy->getDisplayViewport(type, uniqueId2);
+        std::optional<DisplayViewport> viewport2 =
+                mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
         ASSERT_TRUE(viewport2);
         ASSERT_EQ(displayId2, viewport2->displayId);
         ASSERT_EQ(type, viewport2->type);
@@ -1216,11 +1237,50 @@
         // is just implementation detail.
         // However, we can check that it still returns *a* viewport, we just cannot assert
         // which one specifically is returned.
-        std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewport(type, "");
+        std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewportByType(type);
         ASSERT_TRUE(someViewport);
     }
 }
 
+/**
+ * Check getDisplayViewportByPort
+ */
+TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+    const std::string uniqueId1 = "uniqueId1";
+    const std::string uniqueId2 = "uniqueId2";
+    constexpr int32_t displayId1 = 1;
+    constexpr int32_t displayId2 = 2;
+    const uint8_t hdmi1 = 0;
+    const uint8_t hdmi2 = 1;
+    const uint8_t hdmi3 = 2;
+
+    mFakePolicy->clearViewports();
+    // Add a viewport that's associated with some display port that's not of interest.
+    mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId1, hdmi3, type);
+    // Add another viewport, connected to HDMI1 port
+    mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, uniqueId2, hdmi1, type);
+
+    // Check that correct display viewport was returned by comparing the display ports.
+    std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
+    ASSERT_TRUE(hdmi1Viewport);
+    ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+    ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+
+    // Check that we can still get the same viewport using the uniqueId
+    hdmi1Viewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
+    ASSERT_TRUE(hdmi1Viewport);
+    ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+    ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+    ASSERT_EQ(type, hdmi1Viewport->type);
+
+    // Check that we cannot find a port with "HDMI2", because we never added one
+    std::optional<DisplayViewport> hdmi2Viewport = mFakePolicy->getDisplayViewportByPort(hdmi2);
+    ASSERT_FALSE(hdmi2Viewport);
+}
+
 // --- InputReaderTest ---
 
 class InputReaderTest : public testing::Test {
@@ -1696,6 +1756,7 @@
 class InputMapperTest : public testing::Test {
 protected:
     static const char* DEVICE_NAME;
+    static const char* DEVICE_LOCATION;
     static const int32_t DEVICE_ID;
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
@@ -1714,10 +1775,11 @@
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
+        identifier.location = DEVICE_LOCATION;
         mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
                 DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
 
-        mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
+        mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
     }
 
     virtual void TearDown() {
@@ -1729,7 +1791,7 @@
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
-        mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
+        mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value));
     }
 
     void configureDevice(uint32_t changes) {
@@ -1743,9 +1805,10 @@
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
-            int32_t orientation, const std::string& uniqueId, ViewportType viewportType) {
+            int32_t orientation, const std::string& uniqueId,
+            std::optional<uint8_t> physicalPort, ViewportType viewportType) {
         mFakePolicy->addDisplayViewport(
-                displayId, width, height, orientation, uniqueId, viewportType);
+                displayId, width, height, orientation, uniqueId, physicalPort, viewportType);
         configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
@@ -1801,6 +1864,7 @@
 };
 
 const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
 const int32_t InputMapperTest::DEVICE_ID = 1;
 const int32_t InputMapperTest::DEVICE_GENERATION = 2;
 const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
@@ -1867,7 +1931,7 @@
  */
 void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            orientation, UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+            orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
 }
 
 void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
@@ -2170,7 +2234,7 @@
     // ^--- already checked by the previous test
 
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
-            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+            UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2180,7 +2244,7 @@
     constexpr int32_t newDisplayId = 2;
     clearViewports();
     setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
-            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+            UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2305,11 +2369,18 @@
         InputMapperTest::SetUp();
 
         mFakePointerController = new FakePointerController();
-        mFakePolicy->setPointerController(DEVICE_ID, mFakePointerController);
+        mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
     }
 
     void testMotionRotation(CursorInputMapper* mapper,
             int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+
+    void prepareDisplay(int32_t orientation) {
+        const std::string uniqueId = "local:0";
+        const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                orientation, uniqueId, NO_PORT, viewportType);
+    }
 };
 
 const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -2599,12 +2670,7 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
-    const std::string uniqueId = "local:0";
-    const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
-
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            DISPLAY_ORIENTATION_90, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2621,11 +2687,7 @@
     addConfigurationProperty("cursor.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
-    const std::string uniqueId = "local:0";
-    const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
-
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2635,8 +2697,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
@@ -2646,8 +2707,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0, -1,  0));
@@ -2657,8 +2717,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
-    setDisplayInfoAndReconfigure(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270, uniqueId, viewportType);
+    prepareDisplay(DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
@@ -3104,6 +3163,7 @@
     static const VirtualKeyDefinition VIRTUAL_KEYS[2];
 
     const std::string UNIQUE_ID = "local:0";
+    const std::string SECONDARY_UNIQUE_ID = "local:1";
 
     enum Axes {
         POSITION = 1 << 0,
@@ -3119,7 +3179,8 @@
         TOOL_TYPE = 1 << 10,
     };
 
-    void prepareDisplay(int32_t orientation);
+    void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+    void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
     void prepareVirtualDisplay(int32_t orientation);
     void prepareVirtualKeys();
     void prepareLocationCalibration();
@@ -3172,15 +3233,20 @@
         { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
 };
 
-void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
+void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
-            UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+            UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL);
+}
+
+void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
+    setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+            DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
 }
 
 void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
     setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
         VIRTUAL_DISPLAY_HEIGHT, orientation,
-        VIRTUAL_DISPLAY_UNIQUE_ID, ViewportType::VIEWPORT_VIRTUAL);
+        VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
 }
 
 void TouchInputMapperTest::prepareVirtualKeys() {
@@ -6138,5 +6204,46 @@
     ASSERT_EQ(0U, args.deviceTimestamp);
 }
 
+/**
+ * Set the input device port <--> display port associations, and check that the
+ * events are routed to the display that matches the display port.
+ * This can be checked by looking at the displayId of the resulting NotifyMotionArgs.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    const std::string usb2 = "USB2";
+    const uint8_t hdmi1 = 0;
+    const uint8_t hdmi2 = 1;
+    const std::string secondaryUniqueId = "uniqueId2";
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+    mFakePolicy->addInputPortAssociation(usb2, hdmi2);
+
+    // We are intentionally not adding the viewport for display 1 yet. Since the port association
+    // for this input device is specified, and the matching viewport is not present,
+    // the input device should be disabled (at the mapper level).
+
+    // Add viewport for display 2 on hdmi2
+    prepareSecondaryDisplay(type, hdmi2);
+    // Send a touch event
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // Add viewport for display 1 on hdmi1
+    prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+    // Send a touch event again
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(DISPLAY_ID, args.displayId);
+}
 
 } // namespace android