SF: Introduce DisplayModeController

DisplayModeController will centralize the logic and state for selecting
the DisplayMode of each physical display. For now, register a display's
DisplaySnapshot with DMC during configure, and move the creation of its
RefreshRateSelector to DMC::registerDisplay.

Bug: 241285876
Test: presubmit
Change-Id: I673914328bea64636b8bcd193e710131926334a5
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index dc69b81..8ca796e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -158,6 +158,7 @@
         "BackgroundExecutor.cpp",
         "Client.cpp",
         "ClientCache.cpp",
+        "Display/DisplayModeController.cpp",
         "Display/DisplaySnapshot.cpp",
         "DisplayDevice.cpp",
         "DisplayHardware/AidlComposerHal.cpp",
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
new file mode 100644
index 0000000..f093384
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "DisplayModeController"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "Display/DisplayModeController.h"
+#include "Display/DisplaySnapshot.h"
+
+#include <log/log.h>
+
+namespace android::display {
+
+void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef,
+                                            DisplayModeId activeModeId,
+                                            scheduler::RefreshRateSelector::Config config) {
+    const auto& snapshot = snapshotRef.get();
+    const auto displayId = snapshot.displayId();
+
+    mDisplays.emplace_or_replace(displayId, snapshotRef, snapshot.displayModes(), activeModeId,
+                                 config);
+}
+
+void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) {
+    const bool ok = mDisplays.erase(displayId);
+    ALOGE_IF(!ok, "%s: Unknown display %s", __func__, to_string(displayId).c_str());
+}
+
+auto DisplayModeController::selectorPtrFor(PhysicalDisplayId displayId) -> RefreshRateSelectorPtr {
+    return mDisplays.get(displayId)
+            .transform([](const Display& display) { return display.selectorPtr; })
+            .value_or(nullptr);
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
new file mode 100644
index 0000000..b6a6bee
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <utility>
+
+#include <android-base/thread_annotations.h>
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
+#include "Display/DisplaySnapshotRef.h"
+#include "DisplayHardware/DisplayMode.h"
+#include "Scheduler/RefreshRateSelector.h"
+#include "ThreadContext.h"
+
+namespace android::display {
+
+// Selects the DisplayMode of each physical display, in accordance with DisplayManager policy and
+// certain heuristic signals.
+class DisplayModeController {
+public:
+    // The referenced DisplaySnapshot must outlive the registration.
+    void registerDisplay(DisplaySnapshotRef, DisplayModeId, scheduler::RefreshRateSelector::Config)
+            REQUIRES(kMainThreadContext);
+    void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+
+    // TODO(b/241285876): Remove once ownership is no longer shared with DisplayDevice.
+    using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>;
+
+    // Returns `nullptr` if the display is no longer registered (or never was).
+    RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+
+    // Used by tests to inject an existing RefreshRateSelector.
+    // TODO(b/241285876): Remove this.
+    void registerDisplay(PhysicalDisplayId displayId, DisplaySnapshotRef snapshotRef,
+                         RefreshRateSelectorPtr selectorPtr) {
+        mDisplays.emplace_or_replace(displayId, snapshotRef, selectorPtr);
+    }
+
+private:
+    struct Display {
+        Display(DisplaySnapshotRef snapshot, RefreshRateSelectorPtr selectorPtr)
+              : snapshot(snapshot), selectorPtr(std::move(selectorPtr)) {}
+
+        Display(DisplaySnapshotRef snapshot, DisplayModes modes, DisplayModeId activeModeId,
+                scheduler::RefreshRateSelector::Config config)
+              : Display(snapshot,
+                        std::make_shared<scheduler::RefreshRateSelector>(std::move(modes),
+                                                                         activeModeId, config)) {}
+
+        const DisplaySnapshotRef snapshot;
+        const RefreshRateSelectorPtr selectorPtr;
+    };
+
+    ui::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays;
+};
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshotRef.h b/services/surfaceflinger/Display/DisplaySnapshotRef.h
new file mode 100644
index 0000000..6cc5f7e
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplaySnapshotRef.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+
+namespace android::display {
+
+class DisplaySnapshot;
+
+using DisplaySnapshotRef = std::reference_wrapper<const DisplaySnapshot>;
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/PhysicalDisplay.h b/services/surfaceflinger/Display/PhysicalDisplay.h
index ef36234..9b1f1ed 100644
--- a/services/surfaceflinger/Display/PhysicalDisplay.h
+++ b/services/surfaceflinger/Display/PhysicalDisplay.h
@@ -25,6 +25,7 @@
 #include <utils/StrongPointer.h>
 
 #include "DisplaySnapshot.h"
+#include "DisplaySnapshotRef.h"
 
 namespace android::display {
 
@@ -45,8 +46,7 @@
 
     // Transformers for PhysicalDisplays::get.
 
-    using SnapshotRef = std::reference_wrapper<const DisplaySnapshot>;
-    SnapshotRef snapshotRef() const { return std::cref(mSnapshot); }
+    DisplaySnapshotRef snapshotRef() const { return std::cref(mSnapshot); }
 
     bool isInternal() const {
         return mSnapshot.connectionType() == ui::DisplayConnectionType::Internal;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index edd57cc..fc5089b 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -363,7 +363,6 @@
     hardware::graphics::composer::hal::PowerMode initialPowerMode{
             hardware::graphics::composer::hal::PowerMode::OFF};
     bool isPrimary{false};
-    DisplayModeId activeModeId;
     // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
     Fps requestedRefreshRate;
 };
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f80a6b6..b3ba0e9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -228,7 +228,7 @@
     return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
 }
 
-std::chrono::milliseconds getIdleTimerTimeout(DisplayId displayId) {
+std::chrono::milliseconds getIdleTimerTimeout(PhysicalDisplayId displayId) {
     if (const int32_t displayIdleTimerMs =
                 base::GetIntProperty("debug.sf.set_idle_timer_ms_"s +
                                              std::to_string(displayId.value),
@@ -242,7 +242,7 @@
     return std::chrono::milliseconds(millis);
 }
 
-bool getKernelIdleTimerSyspropConfig(DisplayId displayId) {
+bool getKernelIdleTimerSyspropConfig(PhysicalDisplayId displayId) {
     const bool displaySupportKernelIdleTimer =
             base::GetBoolProperty("debug.sf.support_kernel_idle_timer_"s +
                                           std::to_string(displayId.value),
@@ -3520,15 +3520,40 @@
                                             ')');
 
             if (connection == hal::Connection::CONNECTED) {
-                if (!processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
-                                           displayString.c_str())) {
+                const auto activeModeIdOpt =
+                        processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
+                                              displayString.c_str());
+                if (!activeModeIdOpt) {
                     if (FlagManager::getInstance().hotplug2()) {
                         mScheduler->dispatchHotplugError(
                                 static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
                     }
                     getHwComposer().disconnectDisplay(displayId);
+                    continue;
                 }
+
+                const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
+                        getKernelIdleTimerProperties(displayId);
+
+                using Config = scheduler::RefreshRateSelector::Config;
+                const Config config =
+                        {.enableFrameRateOverride = sysprop::enable_frame_rate_override(true)
+                                 ? Config::FrameRateOverride::Enabled
+                                 : Config::FrameRateOverride::Disabled,
+                         .frameRateMultipleThreshold =
+                                 base::GetIntProperty("debug.sf.frame_rate_multiple_threshold"s, 0),
+                         .legacyIdleTimerTimeout = idleTimerTimeoutMs,
+                         .kernelIdleTimerController = kernelIdleTimerController};
+
+                const auto snapshotOpt =
+                        mPhysicalDisplays.get(displayId).transform(&PhysicalDisplay::snapshotRef);
+                LOG_ALWAYS_FATAL_IF(!snapshotOpt);
+
+                mDisplayModeController.registerDisplay(*snapshotOpt, *activeModeIdOpt, config);
             } else {
+                // Unregister before destroying the DisplaySnapshot below.
+                mDisplayModeController.unregisterDisplay(displayId);
+
                 processHotplugDisconnect(displayId, displayString.c_str());
             }
         }
@@ -3537,16 +3562,17 @@
     return !events.empty();
 }
 
-bool SurfaceFlinger::processHotplugConnect(PhysicalDisplayId displayId,
-                                           hal::HWDisplayId hwcDisplayId,
-                                           DisplayIdentificationInfo&& info,
-                                           const char* displayString) {
+std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDisplayId displayId,
+                                                                   hal::HWDisplayId hwcDisplayId,
+                                                                   DisplayIdentificationInfo&& info,
+                                                                   const char* displayString) {
     auto [displayModes, activeMode] = loadDisplayModes(displayId);
     if (!activeMode) {
         ALOGE("Failed to hotplug %s", displayString);
-        return false;
+        return std::nullopt;
     }
 
+    const DisplayModeId activeModeId = activeMode->getId();
     ui::ColorModes colorModes = getHwComposer().getColorModes(displayId);
 
     if (const auto displayOpt = mPhysicalDisplays.get(displayId)) {
@@ -3569,7 +3595,7 @@
         state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
         state.physical->activeMode = std::move(activeMode);
         ALOGI("Reconnecting %s", displayString);
-        return true;
+        return activeModeId;
     }
 
     const sp<IBinder> token = sp<BBinder>::make();
@@ -3590,7 +3616,7 @@
 
     mCurrentState.displays.add(token, state);
     ALOGI("Connecting %s", displayString);
-    return true;
+    return activeModeId;
 }
 
 void SurfaceFlinger::processHotplugDisconnect(PhysicalDisplayId displayId,
@@ -3633,43 +3659,21 @@
     creationArgs.hasWideColorGamut = false;
     creationArgs.supportedPerFrameMetadata = 0;
 
-    if (const auto& physical = state.physical) {
-        creationArgs.activeModeId = physical->activeMode->getId();
-        const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
-                getKernelIdleTimerProperties(compositionDisplay->getId());
+    if (const auto physicalIdOpt = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+        const auto physicalId = *physicalIdOpt;
 
-        using Config = scheduler::RefreshRateSelector::Config;
-        const auto enableFrameRateOverride = sysprop::enable_frame_rate_override(true)
-                ? Config::FrameRateOverride::Enabled
-                : Config::FrameRateOverride::Disabled;
-        const Config config =
-                {.enableFrameRateOverride = enableFrameRateOverride,
-                 .frameRateMultipleThreshold =
-                         base::GetIntProperty("debug.sf.frame_rate_multiple_threshold"s, 0),
-                 .legacyIdleTimerTimeout = idleTimerTimeoutMs,
-                 .kernelIdleTimerController = kernelIdleTimerController};
-
+        creationArgs.isPrimary = physicalId == getPrimaryDisplayIdLocked();
         creationArgs.refreshRateSelector =
-                mPhysicalDisplays.get(physical->id)
-                        .transform(&PhysicalDisplay::snapshotRef)
-                        .transform([&](const display::DisplaySnapshot& snapshot) {
-                            return std::make_shared<
-                                    scheduler::RefreshRateSelector>(snapshot.displayModes(),
-                                                                    creationArgs.activeModeId,
-                                                                    config);
-                        })
-                        .value_or(nullptr);
+                FTL_FAKE_GUARD(kMainThreadContext,
+                               mDisplayModeController.selectorPtrFor(physicalId));
 
-        creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked();
-
-        mPhysicalDisplays.get(physical->id)
+        mPhysicalDisplays.get(physicalId)
                 .transform(&PhysicalDisplay::snapshotRef)
                 .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
                     for (const auto mode : snapshot.colorModes()) {
                         creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
                         creationArgs.hwcColorModes
-                                .emplace(mode,
-                                         getHwComposer().getRenderIntents(physical->id, mode));
+                                .emplace(mode, getHwComposer().getRenderIntents(physicalId, mode));
                     }
                 }));
     }
@@ -7672,13 +7676,12 @@
 }
 
 std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
-SurfaceFlinger::getKernelIdleTimerProperties(DisplayId displayId) {
+SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) {
     const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported(
             android::Hwc2::Composer::OptionalFeature::KernelIdleTimer);
     const auto timeout = getIdleTimerTimeout(displayId);
     if (isKernelIdleTimerHwcSupported) {
-        if (const auto id = PhysicalDisplayId::tryCast(displayId);
-            getHwComposer().hasDisplayIdleTimerCapability(*id)) {
+        if (getHwComposer().hasDisplayIdleTimerCapability(displayId)) {
             // In order to decide if we can use the HWC api for idle timer
             // we query DisplayCapability::DISPLAY_IDLE_TIMER directly on the composer
             // without relying on hasDisplayCapability.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 83b092d..6211e2d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -65,6 +65,7 @@
 #include <ui/FenceResult.h>
 
 #include <common/FlagManager.h>
+#include "Display/DisplayModeController.h"
 #include "Display/PhysicalDisplay.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
@@ -707,7 +708,7 @@
     // Get the controller and timeout that will help decide how the kernel idle timer will be
     // configured and what value to use as the timeout.
     std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
-            getKernelIdleTimerProperties(DisplayId) REQUIRES(mStateLock);
+            getKernelIdleTimerProperties(PhysicalDisplayId) REQUIRES(mStateLock);
     // Updates the kernel idle timer either through HWC or through sysprop
     // depending on which controller is provided
     void updateKernelIdleTimer(std::chrono::milliseconds timeoutMs, KernelIdleTimerController,
@@ -989,8 +990,7 @@
         return getDefaultDisplayDeviceLocked();
     }
 
-    using DisplayDeviceAndSnapshot =
-            std::pair<sp<DisplayDevice>, display::PhysicalDisplay::SnapshotRef>;
+    using DisplayDeviceAndSnapshot = std::pair<sp<DisplayDevice>, display::DisplaySnapshotRef>;
 
     // Combinator for ftl::Optional<PhysicalDisplay>::and_then.
     auto getDisplayDeviceAndSnapshot() REQUIRES(mStateLock) {
@@ -1059,9 +1059,11 @@
     bool configureLocked() REQUIRES(mStateLock) REQUIRES(kMainThreadContext)
             EXCLUDES(mHotplugMutex);
 
-    // Returns false on hotplug failure.
-    bool processHotplugConnect(PhysicalDisplayId, hal::HWDisplayId, DisplayIdentificationInfo&&,
-                               const char* displayString) REQUIRES(mStateLock, kMainThreadContext);
+    // Returns the active mode ID, or nullopt on hotplug failure.
+    std::optional<DisplayModeId> processHotplugConnect(PhysicalDisplayId, hal::HWDisplayId,
+                                                       DisplayIdentificationInfo&&,
+                                                       const char* displayString)
+            REQUIRES(mStateLock, kMainThreadContext);
     void processHotplugDisconnect(PhysicalDisplayId, const char* displayString)
             REQUIRES(mStateLock, kMainThreadContext);
 
@@ -1324,6 +1326,8 @@
     // reads from ISchedulerCallback::requestDisplayModes may happen concurrently.
     std::atomic<PhysicalDisplayId> mActiveDisplayId GUARDED_BY(mStateLock);
 
+    display::DisplayModeController mDisplayModeController;
+
     struct {
         DisplayIdGenerator<GpuVirtualDisplayId> gpu;
         std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
index 34e4ba5..d4c801f 100644
--- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
+++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
@@ -60,7 +60,7 @@
                            .setNativeWindow(mNativeWindow)
                            .setPowerMode(hal::PowerMode::ON)
                            .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector())
-                           .skipRegisterDisplay()
+                           .skipSchedulerRegistration()
                            .inject();
     }
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 7d8a30a..0ddddbd 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -282,7 +282,7 @@
                         .setSecure(Derived::IS_SECURE)
                         .setPowerMode(Derived::INIT_POWER_MODE)
                         .setRefreshRateSelector(test->mFlinger.scheduler()->refreshRateSelector())
-                        .skipRegisterDisplay()
+                        .skipSchedulerRegistration()
                         .inject();
         Mock::VerifyAndClear(test->mNativeWindow.get());
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index c0796df..1bae5ff 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -255,10 +255,15 @@
             colorModes.push_back(ColorMode::DISPLAY_P3);
         }
 
-        mFlinger.mutablePhysicalDisplays().emplace_or_replace(*displayId, displayToken, *displayId,
-                                                              *connectionType,
-                                                              makeModes(activeMode),
-                                                              std::move(colorModes), std::nullopt);
+        const auto it = mFlinger.mutablePhysicalDisplays()
+                                .emplace_or_replace(*displayId, displayToken, *displayId,
+                                                    *connectionType, makeModes(activeMode),
+                                                    std::move(colorModes), std::nullopt)
+                                .first;
+
+        FTL_FAKE_GUARD(kMainThreadContext,
+                       mFlinger.mutableDisplayModeController()
+                               .registerDisplay(it->second.snapshot(), activeMode->getId(), {}));
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 85b1717..72ea6ed 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -657,6 +657,7 @@
 
     auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
 
+    auto& mutableDisplayModeController() { return mFlinger->mDisplayModeController; }
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
     auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
     auto& mutableDisplays() { return mFlinger->mDisplays; }
@@ -967,14 +968,14 @@
 
         auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
             mDisplayModes = std::move(modes);
-            mCreationArgs.activeModeId = activeModeId;
+            mActiveModeId = activeModeId;
             mCreationArgs.refreshRateSelector = nullptr;
             return *this;
         }
 
         auto& setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
             mDisplayModes = selectorPtr->displayModes();
-            mCreationArgs.activeModeId = selectorPtr->getActiveMode().modePtr->getId();
+            mActiveModeId = selectorPtr->getActiveMode().modePtr->getId();
             mCreationArgs.refreshRateSelector = std::move(selectorPtr);
             return *this;
         }
@@ -1016,8 +1017,9 @@
             return *this;
         }
 
-        auto& skipRegisterDisplay() {
-            mRegisterDisplay = false;
+        // Used to avoid overwriting mocks injected by TestableSurfaceFlinger::setupMockScheduler.
+        auto& skipSchedulerRegistration() {
+            mSchedulerRegistration = false;
             return *this;
         }
 
@@ -1030,12 +1032,24 @@
                                  std::shared_ptr<android::scheduler::VSyncTracker> tracker)
                 NO_THREAD_SAFETY_ANALYSIS {
             const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
+            LOG_ALWAYS_FATAL_IF(!displayId);
 
             auto& modes = mDisplayModes;
-            auto& activeModeId = mCreationArgs.activeModeId;
+            auto& activeModeId = mActiveModeId;
+            std::optional<Fps> refreshRateOpt;
 
-            if (displayId && !mCreationArgs.refreshRateSelector) {
-                if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
+            DisplayDeviceState state;
+            state.isSecure = mCreationArgs.isSecure;
+
+            if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
+                LOG_ALWAYS_FATAL_IF(!mConnectionType);
+                LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
+
+                if (mCreationArgs.isPrimary) {
+                    mFlinger.mutableActiveDisplayId() = *physicalId;
+                }
+
+                if (!mCreationArgs.refreshRateSelector) {
                     if (modes.empty()) {
                         constexpr DisplayModeId kModeId{0};
                         DisplayModePtr mode =
@@ -1057,48 +1071,38 @@
                     mCreationArgs.refreshRateSelector =
                             std::make_shared<scheduler::RefreshRateSelector>(modes, activeModeId);
                 }
+
+                const auto activeModeOpt = modes.get(activeModeId);
+                LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+                refreshRateOpt = activeModeOpt->get()->getPeakFps();
+
+                state.physical = {.id = *physicalId,
+                                  .hwcDisplayId = *mHwcDisplayId,
+                                  .activeMode = activeModeOpt->get()};
+
+                const auto it = mFlinger.mutablePhysicalDisplays()
+                                        .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
+                                                            *mConnectionType, std::move(modes),
+                                                            ui::ColorModes(), std::nullopt)
+                                        .first;
+
+                mFlinger.mutableDisplayModeController()
+                        .registerDisplay(*physicalId, it->second.snapshot(),
+                                         mCreationArgs.refreshRateSelector);
+
+                if (mFlinger.scheduler() && mSchedulerRegistration) {
+                    mFlinger.scheduler()->registerDisplay(*physicalId,
+                                                          mCreationArgs.refreshRateSelector,
+                                                          std::move(controller),
+                                                          std::move(tracker));
+                }
             }
 
             sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
             mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
 
-            DisplayDeviceState state;
-            state.isSecure = mCreationArgs.isSecure;
-
-            if (mConnectionType) {
-                LOG_ALWAYS_FATAL_IF(!displayId);
-                const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId);
-                LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
-                const auto physicalId = *physicalIdOpt;
-
-                if (mCreationArgs.isPrimary) {
-                    mFlinger.mutableActiveDisplayId() = physicalId;
-                }
-
-                LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
-
-                const auto activeMode = modes.get(activeModeId);
-                LOG_ALWAYS_FATAL_IF(!activeMode);
-                const auto fps = activeMode->get()->getPeakFps();
-
-                state.physical = {.id = physicalId,
-                                  .hwcDisplayId = *mHwcDisplayId,
-                                  .activeMode = activeMode->get()};
-
-                mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken,
-                                                                      physicalId, *mConnectionType,
-                                                                      std::move(modes),
-                                                                      ui::ColorModes(),
-                                                                      std::nullopt);
-
-                if (mFlinger.scheduler() && mRegisterDisplay) {
-                    mFlinger.scheduler()->registerDisplay(physicalId,
-                                                          display->holdRefreshRateSelector(),
-                                                          std::move(controller),
-                                                          std::move(tracker));
-                }
-
-                display->setActiveMode(activeModeId, fps, fps);
+            if (refreshRateOpt) {
+                display->setActiveMode(activeModeId, *refreshRateOpt, *refreshRateOpt);
             }
 
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
@@ -1112,7 +1116,8 @@
         sp<BBinder> mDisplayToken = sp<BBinder>::make();
         DisplayDeviceCreationArgs mCreationArgs;
         DisplayModes mDisplayModes;
-        bool mRegisterDisplay = true;
+        DisplayModeId mActiveModeId;
+        bool mSchedulerRegistration = true;
         const std::optional<ui::DisplayConnectionType> mConnectionType;
         const std::optional<hal::HWDisplayId> mHwcDisplayId;
     };