[CD Cursor] Apply selection logic to change cursor displayId

In connected displays scenario, this tracks the latest display the
cursor is at, within the DisplayTopology. By default, this will be set
to topology primary display, and updated when mouse crossed to another
display.

Note: If associatedDisplay is in different topology, mouse will simply
be on the associatedDisplay, keeping existing behavior

Doc: go/connected-displays-cursor-behavior

Bug: 396568321
Test: atest inputflinger_tests
Test: atest InputTests
Test: atest CtsInputTestCases
Flag: com.android.input.flags.connected_displays_associated_display_cursor_bugfix

Change-Id: I6a68d9cd5a4de06896e0e29104c25b8a4493b852
diff --git a/include/input/InputFlags.h b/include/input/InputFlags.h
index 0e194ea..4b42f77 100644
--- a/include/input/InputFlags.h
+++ b/include/input/InputFlags.h
@@ -25,6 +25,11 @@
      * override.
      */
     static bool connectedDisplaysCursorEnabled();
+
+    /**
+     * Check if both connectedDisplaysCursor and associatedDisplayCursorBugfix is enabled.
+     */
+    static bool connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled();
 };
 
 } // namespace android
diff --git a/libs/input/InputFlags.cpp b/libs/input/InputFlags.cpp
index 555b138..f866f9b 100644
--- a/libs/input/InputFlags.cpp
+++ b/libs/input/InputFlags.cpp
@@ -39,4 +39,9 @@
     return com::android::input::flags::connected_displays_cursor();
 }
 
+bool InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled() {
+    return connectedDisplaysCursorEnabled() &&
+            com::android::input::flags::connected_displays_associated_display_cursor_bugfix();
+}
+
 } // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index e06ff6b..98b514b 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -132,7 +132,7 @@
         }),
         mNextListener(listener),
         mPolicy(policy),
-        mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
+        mCurrentMouseDisplayId(ui::LogicalDisplayId::INVALID),
         mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
         mShowTouchesEnabled(false),
         mStylusPointerIconEnabled(false),
@@ -361,7 +361,7 @@
         LOG(FATAL) << "A cursor already exists on destination display"
                    << destinationViewport.displayId;
     }
-    mDefaultMouseDisplayId = destinationViewport.displayId;
+    mCurrentMouseDisplayId = destinationViewport.displayId;
     auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId);
     pcNode.key() = destinationViewport.displayId;
     mMousePointersByDisplay.insert(std::move(pcNode));
@@ -609,9 +609,9 @@
 
         // make primary display default mouse display, if it was not set or
         // the existing display was removed
-        if (mDefaultMouseDisplayId == ui::LogicalDisplayId::INVALID ||
-            mTopology.graph.find(mDefaultMouseDisplayId) == mTopology.graph.end()) {
-            mDefaultMouseDisplayId = mTopology.primaryDisplayId;
+        if (mCurrentMouseDisplayId == ui::LogicalDisplayId::INVALID ||
+            mTopology.graph.find(mCurrentMouseDisplayId) == mTopology.graph.end()) {
+            mCurrentMouseDisplayId = mTopology.primaryDisplayId;
             pointerDisplayChange = updatePointerControllersLocked();
         }
     } // release lock
@@ -665,7 +665,19 @@
 
 ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
         ui::LogicalDisplayId associatedDisplayId) const {
-    return associatedDisplayId.isValid() ? associatedDisplayId : mDefaultMouseDisplayId;
+    if (!InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled()) {
+        if (associatedDisplayId.isValid()) {
+            return associatedDisplayId;
+        }
+        return mCurrentMouseDisplayId.isValid() ? mCurrentMouseDisplayId
+                                                : ui::LogicalDisplayId::DEFAULT;
+    }
+    // Associated display is not included in the topology, return this associated display.
+    if (associatedDisplayId.isValid() &&
+        mTopology.graph.find(associatedDisplayId) == mTopology.graph.end()) {
+        return associatedDisplayId;
+    }
+    return mCurrentMouseDisplayId.isValid() ? mCurrentMouseDisplayId : mTopology.primaryDisplayId;
 }
 
 std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
@@ -774,7 +786,7 @@
 PointerChoreographer::calculatePointerDisplayChangeToNotify() {
     ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
     vec2 cursorPosition = {0, 0};
-    if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
+    if (const auto it = mMousePointersByDisplay.find(mCurrentMouseDisplayId);
         it != mMousePointersByDisplay.end()) {
         const auto& pointerController = it->second;
         // Use the displayId from the pointerController, because it accurately reflects whether
@@ -800,7 +812,7 @@
     { // acquire lock
         std::scoped_lock _l(getLock());
 
-        mDefaultMouseDisplayId = displayId;
+        mCurrentMouseDisplayId = displayId;
         pointerDisplayChange = updatePointerControllersLocked();
     } // release lock
 
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 2435125..67bdca1 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -231,7 +231,12 @@
     std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
             GUARDED_BY(getLock());
 
-    ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock());
+    // In connected displays scenario, this tracks the latest display the cursor is at, within the
+    // DisplayTopology. By default, this will be set to topology primary display, and updated when
+    // mouse crossed to another display.
+    // In non-connected displays scenario, this will be treated as the default display cursor
+    // will be on, when mouse doesn't have associated display.
+    ui::LogicalDisplayId mCurrentMouseDisplayId GUARDED_BY(getLock());
     ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock());
     std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(getLock());
     std::set<DeviceId> mMouseDevices GUARDED_BY(getLock());
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 3aad16f..38d0679 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -3072,6 +3072,90 @@
     ASSERT_TRUE(pc->isPointerShown());
 }
 
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+       UsePrimaryDisplayIfAssociatedDisplayIsInTopology) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+    SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+    // Add two displays
+    mChoreographer.setDisplayViewports(
+            {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/SECOND_DISPLAY_ID,
+                                   /*adjacentDisplays=*/{FIRST_DISPLAY_ID});
+
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, FIRST_DISPLAY_ID)}});
+
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(SECOND_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+       AllowCrossingDisplayEvenWithAssociatedDisplaySet) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+    SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+    // Add two displays
+    mChoreographer.setDisplayViewports(
+            {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+                                   /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)}});
+
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(FIRST_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Move cursor to the secondary display
+    auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+                                  .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+                                  .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(pointerBuilder)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ui::LogicalDisplayId::INVALID)
+                    .build());
+
+    assertPointerControllerNotCreated();
+    pc->assertViewportSet(SECOND_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+       AddAssociatedDisplayCursorOutsideOfDisplayTopology) {
+    SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+    SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+    // Add three displays, with only first and second display in DisplayTopolgoy
+    mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID),
+                                        createViewport(SECOND_DISPLAY_ID),
+                                        createViewport(THIRD_DISPLAY_ID)});
+    setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+                                   /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+                                     ui::LogicalDisplayId::INVALID)}});
+
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(FIRST_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Adds a new mouse associated with third display
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/1, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, THIRD_DISPLAY_ID)}});
+
+    pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    pc->assertViewportSet(THIRD_DISPLAY_ID);
+    ASSERT_TRUE(pc->isPointerShown());
+}
+
 class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
 
 TEST_F_WITH_FLAGS(