OSDN Git Service

Add associated function to physical keyboard
authorArthur Hung <arthurhung@google.com>
Tue, 23 Jul 2019 06:18:59 +0000 (14:18 +0800)
committerArthur Hung <arthurhung@google.com>
Mon, 26 Aug 2019 02:17:16 +0000 (10:17 +0800)
The function keys (HOME/BACK/DPAD...) may not always be virtual keys,
so we need provide a static input mapping mechanism to the physical
keyboard to make sure key events can dispatch to the corresponding
display.

- Find the associated viewport for keyboard when display info changed
  and has an associated display port specified.
- Should disable the input device when an associated port specified but
  no display viewport found.
- Changed getAssociatedDisplay -> getAssociatedDisplayId.
- Add test cases to test input device and keyboard associated mapping.

Bug: 137693244
Test: atest -a libinput_tests inputflinger_tests
Change-Id: If218f55645a78a4b50cec92b35b0dbae641dd1a1

services/inputflinger/InputReader.cpp
services/inputflinger/InputReader.h
services/inputflinger/tests/InputReader_test.cpp

index 4631bec..1cbf78e 100644 (file)
@@ -819,14 +819,19 @@ bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
     }
 
     InputDevice* device = mDevices.valueAt(deviceIndex);
-    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay();
+    if (!device->isEnabled()) {
+        ALOGW("Ignoring disabled device %s", device->getName().c_str());
+        return false;
+    }
+
+    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
     // No associated display. By default, can dispatch to all displays.
     if (!associatedDisplayId) {
         return true;
     }
 
     if (*associatedDisplayId == ADISPLAY_ID_NONE) {
-        ALOGW("Device has associated, but no associated display id.");
+        ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
         return true;
     }
 
@@ -1002,6 +1007,13 @@ bool InputDevice::isEnabled() {
 }
 
 void InputDevice::setEnabled(bool enabled, nsecs_t when) {
+    if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
+        ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
+              "but the corresponding viewport is not found",
+              getName().c_str(), *mAssociatedDisplayPort);
+        enabled = false;
+    }
+
     if (isEnabled() == enabled) {
         return;
     }
@@ -1103,6 +1115,7 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
              // In most situations, no port will be specified.
             mAssociatedDisplayPort = std::nullopt;
+            mAssociatedViewport = std::nullopt;
             // Find the display port that corresponds to the current input port.
             const std::string& inputPort = mIdentifier.location;
             if (!inputPort.empty()) {
@@ -1112,6 +1125,23 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config
                     mAssociatedDisplayPort = std::make_optional(displayPort->second);
                 }
             }
+
+            // If the device was explicitly disabled by the user, it would be present in the
+            // "disabledDevices" list. If it is associated with a specific display, and it was not
+            // explicitly disabled, then enable/disable the device based on whether we can find the
+            // corresponding viewport.
+            bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end());
+            if (mAssociatedDisplayPort) {
+                mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
+                if (!mAssociatedViewport) {
+                    ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+                          "but the corresponding viewport is not found.",
+                          getName().c_str(), *mAssociatedDisplayPort);
+                    enabled = false;
+                }
+            }
+
+            setEnabled(enabled, when);
         }
 
         for (InputMapper* mapper : mMappers) {
@@ -1276,9 +1306,15 @@ void InputDevice::notifyReset(nsecs_t when) {
     mContext->getListener()->notifyDeviceReset(&args);
 }
 
-std::optional<int32_t> InputDevice::getAssociatedDisplay() {
+std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
+    // Check if we had associated to the specific display.
+    if (mAssociatedViewport) {
+        return mAssociatedViewport->displayId;
+    }
+
+    // No associated display port, check if some InputMapper is associated.
     for (InputMapper* mapper : mMappers) {
-        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay();
+        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplayId();
         if (associatedDisplayId) {
             return associatedDisplayId;
         }
@@ -2229,6 +2265,22 @@ void KeyboardInputMapper::dump(std::string& dump) {
     dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
 }
 
+std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
+        nsecs_t when, const InputReaderConfiguration* config) {
+    const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+    if (displayPort) {
+        // Find the viewport that contains the same port
+        return mDevice->getAssociatedViewport();
+    }
+
+    // No associated display defined, try to find default display if orientationAware.
+    if (mParameters.orientationAware) {
+        return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    }
+
+    return std::nullopt;
+}
+
 void KeyboardInputMapper::configure(nsecs_t when,
         const InputReaderConfiguration* config, uint32_t changes) {
     InputMapper::configure(when, config, changes);
@@ -2239,9 +2291,7 @@ void KeyboardInputMapper::configure(nsecs_t when,
     }
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        if (mParameters.orientationAware) {
-            mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
-        }
+        mViewport = findViewport(when, config);
     }
 }
 
@@ -2521,6 +2571,12 @@ void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState,
     }
 }
 
+std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
+    if (mViewport) {
+        return std::make_optional(mViewport->displayId);
+    }
+    return std::nullopt;
+}
 
 // --- CursorInputMapper ---
 
@@ -2935,7 +2991,7 @@ void CursorInputMapper::fadePointer() {
     }
 }
 
-std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() {
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
     if (mParameters.hasAssociatedDisplay) {
         if (mParameters.mode == Parameters::MODE_POINTER) {
             return std::make_optional(mPointerController->getDisplayId());
@@ -3467,15 +3523,10 @@ std::optional<DisplayViewport> TouchInputMapper::findViewport() {
         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;
+            return mDevice->getAssociatedViewport();
         }
 
+        // Check if uniqueDisplayId is specified in idc file.
         if (!mParameters.uniqueDisplayId.empty()) {
             return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
         }
@@ -3499,6 +3550,7 @@ std::optional<DisplayViewport> TouchInputMapper::findViewport() {
         return viewport;
     }
 
+    // No associated display, return a non-display viewport.
     DisplayViewport newViewport;
     // Raw width and height in the natural orientation.
     int32_t rawWidth = mRawPointerAxes.getRawWidth();
@@ -6508,7 +6560,7 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32
     if (mDeviceMode == DEVICE_MODE_POINTER) {
         mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
     }
-    const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
+    const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
     const int32_t deviceId = getDeviceId();
     std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
     std::for_each(frames.begin(), frames.end(),
@@ -6817,7 +6869,7 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCode
     return true;
 }
 
-std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() {
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
     if (mParameters.hasAssociatedDisplay) {
         if (mDeviceMode == DEVICE_MODE_POINTER) {
             return std::make_optional(mPointerController->getDisplayId());
index b7f94c1..0666ca5 100644 (file)
@@ -265,7 +265,9 @@ public:
     inline std::optional<uint8_t> getAssociatedDisplayPort() const {
         return mAssociatedDisplayPort;
     }
-
+    inline std::optional<DisplayViewport> getAssociatedViewport() const {
+        return mAssociatedViewport;
+    }
     inline void setMic(bool hasMic) { mHasMic = hasMic; }
     inline bool hasMic() const { return mHasMic; }
 
@@ -324,7 +326,8 @@ public:
         return value;
     }
 
-    std::optional<int32_t> getAssociatedDisplay();
+    std::optional<int32_t> getAssociatedDisplayId();
+
 private:
     InputReaderContext* mContext;
     int32_t mId;
@@ -339,6 +342,7 @@ private:
     uint32_t mSources;
     bool mIsExternal;
     std::optional<uint8_t> mAssociatedDisplayPort;
+    std::optional<DisplayViewport> mAssociatedViewport;
     bool mHasMic;
     bool mDropUntilNextSync;
 
@@ -718,9 +722,8 @@ public:
     virtual void updateExternalStylusState(const StylusState& state);
 
     virtual void fadePointer();
-    virtual std::optional<int32_t> getAssociatedDisplay() {
-        return std::nullopt;
-    }
+    virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
+
 protected:
     InputDevice* mDevice;
     InputReaderContext* mContext;
@@ -802,6 +805,7 @@ public:
 
     virtual int32_t getMetaState();
     virtual void updateMetaState(int32_t keyCode);
+    virtual std::optional<int32_t> getAssociatedDisplayId();
 
 private:
     // The current viewport.
@@ -855,6 +859,8 @@ private:
     void updateLedState(bool reset);
     void updateLedStateForModifier(LedState& ledState, int32_t led,
             int32_t modifier, bool reset);
+    std::optional<DisplayViewport> findViewport(nsecs_t when,
+                                                const InputReaderConfiguration* config);
 };
 
 
@@ -874,7 +880,8 @@ public:
 
     virtual void fadePointer();
 
-    virtual std::optional<int32_t> getAssociatedDisplay();
+    virtual std::optional<int32_t> getAssociatedDisplayId();
+
 private:
     // Amount that trackball needs to move in order to generate a key event.
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -968,7 +975,8 @@ public:
     virtual void cancelTouch(nsecs_t when);
     virtual void timeoutExpired(nsecs_t when);
     virtual void updateExternalStylusState(const StylusState& state);
-    virtual std::optional<int32_t> getAssociatedDisplay();
+    virtual std::optional<int32_t> getAssociatedDisplayId();
+
 protected:
     CursorButtonAccumulator mCursorButtonAccumulator;
     CursorScrollAccumulator mCursorScrollAccumulator;
index d95ac96..348a12b 100644 (file)
@@ -1556,10 +1556,14 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     mReader->loopOnce();
 
-    // Check device.
+    // Device should only dispatch to the specified display.
     ASSERT_EQ(deviceId, device->getId());
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID));
     ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
+
+    // Can't dispatch event from a disabled device.
+    disableDevice(deviceId, device);
+    ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
 
@@ -1568,6 +1572,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
 class InputDeviceTest : 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;
@@ -1589,6 +1594,7 @@ protected:
         mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
+        identifier.location = DEVICE_LOCATION;
         mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
                 DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
     }
@@ -1603,6 +1609,7 @@ protected:
 };
 
 const char* InputDeviceTest::DEVICE_NAME = "device";
+const char* InputDeviceTest::DEVICE_LOCATION = "USB1";
 const int32_t InputDeviceTest::DEVICE_ID = 1;
 const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
 const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0;
@@ -1755,6 +1762,49 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe
     ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled());
 }
 
+// A single input device is associated with a specific display. Check that:
+// 1. Device is disabled if the viewport corresponding to the associated display is not found
+// 2. Device is disabled when setEnabled API is called
+TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) {
+    FakeInputMapper* mapper = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN);
+    mDevice->addMapper(mapper);
+
+    // First Configuration.
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+
+    // Device should be enabled by default.
+    ASSERT_TRUE(mDevice->isEnabled());
+
+    // Prepare associated info.
+    constexpr uint8_t hdmi = 1;
+    const std::string UNIQUE_ID = "local:1";
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    // Device should be disabled because it is associated with a specific display via
+    // input port <-> display port association, but the corresponding display is not found
+    ASSERT_FALSE(mDevice->isEnabled());
+
+    // Prepare displays.
+    mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                    DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi,
+                                    ViewportType::VIEWPORT_INTERNAL);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_TRUE(mDevice->isEnabled());
+
+    // Device should be disabled after set disable.
+    mFakePolicy->addDisabledDevice(mDevice->getId());
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    ASSERT_FALSE(mDevice->isEnabled());
+
+    // Device should still be disabled even found the associated display.
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_FALSE(mDevice->isEnabled());
+}
 
 // --- InputMapperTest ---
 
@@ -1926,8 +1976,9 @@ protected:
 
     void prepareDisplay(int32_t orientation);
 
-    void testDPadKeyRotation(KeyboardInputMapper* mapper,
-            int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode);
+    void testDPadKeyRotation(KeyboardInputMapper* mapper, int32_t originalScanCode,
+                             int32_t originalKeyCode, int32_t rotatedKeyCode,
+                             int32_t displayId = ADISPLAY_ID_NONE);
 };
 
 /* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
@@ -1939,7 +1990,8 @@ void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
 }
 
 void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
-        int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
+                                                  int32_t originalScanCode, int32_t originalKeyCode,
+                                                  int32_t rotatedKeyCode, int32_t displayId) {
     NotifyKeyArgs args;
 
     process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1);
@@ -1947,15 +1999,16 @@ void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
+    ASSERT_EQ(displayId, args.displayId);
 
     process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
+    ASSERT_EQ(displayId, args.displayId);
 }
 
-
 TEST_F(KeyboardInputMapperTest, GetSources) {
     KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
             AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
@@ -2136,47 +2189,47 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) {
     addMapperAndConfigure(mapper);
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_180);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
 
     // Special case: if orientation changes while key is down, we still emit the same keycode
     // in the key up as we did in the key down.
@@ -2360,6 +2413,84 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds)
     ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
 }
 
+TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
+    // keyboard 1.
+    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+    // keyboard 2.
+    const std::string USB2 = "USB2";
+    constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+    InputDeviceIdentifier identifier;
+    identifier.name = "KEYBOARD2";
+    identifier.location = USB2;
+    std::unique_ptr<InputDevice> device2 =
+            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
+                                          DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+    mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/);
+    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
+                                                          AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    addMapperAndConfigure(mapper);
+
+    KeyboardInputMapper* mapper2 = new KeyboardInputMapper(device2.get(), AINPUT_SOURCE_KEYBOARD,
+                                                           AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    device2->addMapper(mapper2);
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
+    device2->reset(ARBITRARY_TIME);
+
+    // Prepared displays and associated info.
+    constexpr uint8_t hdmi1 = 0;
+    constexpr uint8_t hdmi2 = 1;
+    const std::string SECONDARY_UNIQUE_ID = "local:1";
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+    mFakePolicy->addInputPortAssociation(USB2, hdmi2);
+
+    // No associated display viewport found, should disable the device.
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_FALSE(device2->isEnabled());
+
+    // Prepare second display.
+    constexpr int32_t newDisplayId = 2;
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+                                 UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL);
+    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+                                 SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL);
+    // Default device will reconfigure above, need additional reconfiguration for another device.
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // Device should be enabled after the associated display is found.
+    ASSERT_TRUE(mDevice->isEnabled());
+    ASSERT_TRUE(device2->isEnabled());
+
+    // Test pad key events
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, newDisplayId));
+}
 
 // --- CursorInputMapperTest ---
 
@@ -6276,12 +6407,13 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
 
     // Create the second touch screen device, and enable multi fingers.
     const std::string USB2 = "USB2";
-    const int32_t SECOND_DEVICE_ID = 2;
+    constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
     InputDeviceIdentifier identifier;
-    identifier.name = DEVICE_NAME;
+    identifier.name = "TOUCHSCREEN2";
     identifier.location = USB2;
-    InputDevice* device2 = new InputDevice(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-            DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+    std::unique_ptr<InputDevice> device2 =
+            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
+                                          DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
     mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/);
     mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
             0 /*flat*/, 0 /*fuzz*/);
@@ -6296,7 +6428,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
             String8("touchScreen"));
 
     // Setup the second touch screen device.
-    MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2);
+    MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2.get());
     device2->addMapper(mapper2);
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
     device2->reset(ARBITRARY_TIME);