SF: Update the cached display modes in HWComposer on hotplug

Currently configs in HWComposer are cached only once
per display and never updated on subseqent onHotplug
events. This may cause setActiveConfigsWithConstraints
to fail for a valid config.

This CL also removes the test HandleTransactionLockedTest::
processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected,
since it's testing an unsupported use case and its
incompatible with the tests we've added.

Bug: 159590486
Test: atest HWComposerConfigsTest
Change-Id: Ifb22c33ba5078bde35ae20a2f94a8630316da024
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 01cdb28..012b55b 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -364,7 +364,7 @@
     static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
 
     // The HWC active configuration id
-    static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
+    static constexpr hal::HWConfigId HWC_ACTIVE_CONFIG_ID = 2001;
     static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON;
 
     static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
@@ -415,6 +415,45 @@
                                                       ceDisplayArgs);
     }
 
+    static void setupHwcGetConfigsCallExpectations(DisplayTransactionTest* test) {
+        if (HWC_DISPLAY_TYPE == DisplayType::PHYSICAL) {
+            EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{
+                                                  HWC_ACTIVE_CONFIG_ID}),
+                                          Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::WIDTH, _))
+                    .WillRepeatedly(
+                            DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::HEIGHT, _))
+                    .WillRepeatedly(
+                            DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::VSYNC_PERIOD, _))
+                    .WillRepeatedly(
+                            DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::DPI_X, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::DPI_Y, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::CONFIG_GROUP, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
+        } else {
+            EXPECT_CALL(*test->mComposer, getDisplayConfigs(_, _)).Times(0);
+            EXPECT_CALL(*test->mComposer, getDisplayAttribute(_, _, _, _)).Times(0);
+        }
+    }
+
     static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
         constexpr auto CONNECTION_TYPE =
                 PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
@@ -426,33 +465,8 @@
 
         EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
                 .WillOnce(Return(hal::Error::NONE));
-        EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
-                                Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::WIDTH, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::HEIGHT, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::VSYNC_PERIOD, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::DPI_X, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::DPI_Y, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::CONFIG_GROUP, _))
-                .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
+
+        setupHwcGetConfigsCallExpectations(test);
 
         if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
             EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
@@ -559,6 +573,11 @@
                                                       ceDisplayArgs);
     }
 
+    static void setupHwcGetConfigsCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayConfigs(_, _)).Times(0);
+        EXPECT_CALL(*test->mComposer, getDisplayAttribute(_, _, _, _)).Times(0);
+    }
+
     static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
     }
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index fa12315..bc1e88a 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -42,7 +42,10 @@
 namespace android {
 namespace {
 
-namespace hal = android::hardware::graphics::composer::hal;
+namespace V2_1 = hardware::graphics::composer::V2_1;
+namespace V2_4 = hardware::graphics::composer::V2_4;
+
+using Hwc2::Config;
 
 using ::testing::_;
 using ::testing::DoAll;
@@ -170,5 +173,88 @@
     EXPECT_EQ(hal::Error::UNSUPPORTED, result);
 }
 
+class HWComposerConfigsTest : public testing::Test {
+public:
+    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+    MockHWC2ComposerCallback mCallback;
+
+    void setActiveConfig(Config config) {
+        EXPECT_CALL(*mHal, getActiveConfig(_, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(config), Return(V2_1::Error::NONE)));
+    }
+
+    void setDisplayConfigs(std::vector<Config> configs) {
+        EXPECT_CALL(*mHal, getDisplayConfigs(_, _))
+                .WillOnce(DoAll(SetArgPointee<1>(configs), Return(V2_1::Error::NONE)));
+        EXPECT_CALL(*mHal, getDisplayAttribute(_, _, _, _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(1), Return(V2_1::Error::NONE)));
+    }
+
+    void testSetActiveConfigWithConstraintsCommon(bool isVsyncPeriodSwitchSupported);
+};
+
+void HWComposerConfigsTest::testSetActiveConfigWithConstraintsCommon(
+        bool isVsyncPeriodSwitchSupported) {
+    EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+    EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<hal::Capability>{}));
+    EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_)).WillOnce(Return(V2_4::Error::UNSUPPORTED));
+    EXPECT_CALL(*mHal, registerCallback(_));
+    EXPECT_CALL(*mHal, setVsyncEnabled(_, _)).WillRepeatedly(Return(V2_1::Error::NONE));
+    EXPECT_CALL(*mHal, getDisplayIdentificationData(_, _, _))
+            .WillRepeatedly(Return(V2_1::Error::UNSUPPORTED));
+    EXPECT_CALL(*mHal, setClientTargetSlotCount(_)).WillRepeatedly(Return(V2_1::Error::NONE));
+
+    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported())
+            .WillRepeatedly(Return(isVsyncPeriodSwitchSupported));
+
+    if (isVsyncPeriodSwitchSupported) {
+        EXPECT_CALL(*mHal, setActiveConfigWithConstraints(_, _, _, _))
+                .WillRepeatedly(Return(V2_4::Error::NONE));
+    } else {
+        EXPECT_CALL(*mHal, setActiveConfig(_, _)).WillRepeatedly(Return(V2_1::Error::NONE));
+    }
+
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    hwc.setConfiguration(&mCallback, 123);
+
+    setDisplayConfigs({15});
+    setActiveConfig(15);
+
+    const auto physicalId = PhysicalDisplayId::fromPort(0);
+    const hal::HWDisplayId hwcId = 0;
+    hwc.allocatePhysicalDisplay(hwcId, physicalId);
+
+    hal::VsyncPeriodChangeConstraints constraints;
+    constraints.desiredTimeNanos = systemTime();
+    constraints.seamlessRequired = false;
+
+    hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
+    constexpr size_t kConfigIndex = 0;
+    const auto status =
+            hwc.setActiveConfigWithConstraints(physicalId, kConfigIndex, constraints, &timeline);
+    EXPECT_EQ(NO_ERROR, status);
+
+    const std::vector<Config> kConfigs{7, 8, 9, 10, 11};
+    // Change the set of supported modes.
+    setDisplayConfigs(kConfigs);
+    setActiveConfig(11);
+    hwc.onHotplug(hwcId, hal::Connection::CONNECTED);
+    hwc.allocatePhysicalDisplay(hwcId, physicalId);
+
+    for (size_t configIndex = 0; configIndex < kConfigs.size(); configIndex++) {
+        const auto status =
+                hwc.setActiveConfigWithConstraints(physicalId, configIndex, constraints, &timeline);
+        EXPECT_EQ(NO_ERROR, status) << "Error when switching to config " << configIndex;
+    }
+}
+
+TEST_F(HWComposerConfigsTest, setActiveConfigWithConstraintsWithVsyncSwitchingSupported) {
+    testSetActiveConfigWithConstraintsCommon(/*supported=*/true);
+}
+
+TEST_F(HWComposerConfigsTest, setActiveConfigWithConstraintsWithVsyncSwitchingNotSupported) {
+    testSetActiveConfigWithConstraintsCommon(/*supported=*/false);
+}
+
 } // namespace
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
index cd3f6ab..65efc85 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -144,7 +144,7 @@
     // SF should have a display token.
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+    ASSERT_EQ(mFlinger.mutablePhysicalDisplayTokens().count(displayId), 1);
     auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId];
 
     verifyDisplayIsConnected<Case>(displayToken);
@@ -259,14 +259,6 @@
     processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest,
-       processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
-    // Inject an external display.
-    ExternalDisplayVariant::injectHwcDisplay(this);
-
-    processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
 TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
     // Inject a primary display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 61f0788..cedb404 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -223,6 +223,7 @@
     // Various native window calls will be made.
     Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
     Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+    Case::Display::setupHwcGetConfigsCallExpectations(this);
     Case::WideColorSupport::setupComposerCallExpectations(this);
     Case::HdrSupport::setupComposerCallExpectations(this);
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
@@ -236,6 +237,7 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
+        mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId);
         state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
     }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 25aaa14..b57d473 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -561,8 +561,15 @@
                 const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
                 LOG_ALWAYS_FATAL_IF(!physicalId);
                 flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
-                (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
-                            : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
+                if (mIsPrimary) {
+                    flinger->mutableInternalHwcDisplayId() = mHwcDisplayId;
+                } else {
+                    // If there is an external HWC display there should always be an internal ID
+                    // as well. Set it to some arbitrary value.
+                    auto& internalId = flinger->mutableInternalHwcDisplayId();
+                    if (!internalId) internalId = mHwcDisplayId - 1;
+                    flinger->mutableExternalHwcDisplayId() = mHwcDisplayId;
+                }
             }
         }