SF: Implement leader display promotion/demotion

Remove Scheduler::mRefreshRateSelector by using mLeaderDisplayId to look
up the leader's RefreshRateSelector. Replace setRefreshRateSelector with
setLeaderDisplay. Add logic to demote/promote the leader display (always
the primary display for now) when displays are added/changed/removed.

This fixes a regression where Scheduler::applyPolicy no longer respected
the active display after Ifa1bf23bc991fe60e67dba1cb31077e42fd5396e.

Bug: 255635821
Bug: 241286431
Test: Fold/unfold
Test: dumpsys SurfaceFlinger --scheduler
Change-Id: Ib1e8eacd2cdbf0d798ff7e8bb9c25607972104f5
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2a18521..b4e16fc 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -195,8 +195,7 @@
         mDrawingState.color.b = -1.0_hf;
     }
 
-    mFrameTracker.setDisplayRefreshPeriod(
-            args.flinger->mScheduler->getVsyncPeriodFromRefreshRateSelector());
+    mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod());
 
     mOwnerUid = args.ownerUid;
     mOwnerPid = args.ownerPid;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 0e80817..65ee487 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -363,14 +363,16 @@
         }
     }
 
-    void resetIdleTimer(bool kernelOnly) {
-        if (!mIdleTimer) {
-            return;
+    void resetKernelIdleTimer() {
+        if (mIdleTimer && mConfig.kernelIdleTimerController) {
+            mIdleTimer->reset();
         }
-        if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) {
-            return;
+    }
+
+    void resetIdleTimer() {
+        if (mIdleTimer) {
+            mIdleTimer->reset();
         }
-        mIdleTimer->reset();
     }
 
     void dump(utils::Dumper&) const EXCLUDES(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6108d92..f1fcc88 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -70,7 +70,7 @@
     mTouchTimer.reset();
 
     // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
-    setRefreshRateSelector(nullptr);
+    demoteLeaderDisplay();
 }
 
 void Scheduler::startTimers() {
@@ -95,40 +95,29 @@
     }
 }
 
-void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr newSelectorPtr) {
-    // No need to lock for reads on kMainThreadContext.
-    if (const auto& selectorPtr = FTL_FAKE_GUARD(mRefreshRateSelectorLock, mRefreshRateSelector)) {
-        unbindIdleTimer(*selectorPtr);
-    }
+void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+    demoteLeaderDisplay();
 
-    {
-        // Clear state that depends on the current RefreshRateSelector.
-        std::scoped_lock lock(mPolicyLock);
-        mPolicy = {};
-    }
-
-    std::scoped_lock lock(mRefreshRateSelectorLock);
-    mRefreshRateSelector = std::move(newSelectorPtr);
-
-    if (mRefreshRateSelector) {
-        bindIdleTimer(*mRefreshRateSelector);
-    }
+    std::scoped_lock lock(mDisplayLock);
+    promoteLeaderDisplay(leaderIdOpt);
 }
 
 void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
-    if (!mLeaderDisplayId) {
-        mLeaderDisplayId = displayId;
-    }
+    demoteLeaderDisplay();
 
+    std::scoped_lock lock(mDisplayLock);
     mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
+
+    promoteLeaderDisplay();
 }
 
 void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
-    if (mLeaderDisplayId == displayId) {
-        mLeaderDisplayId.reset();
-    }
+    demoteLeaderDisplay();
 
+    std::scoped_lock lock(mDisplayLock);
     mRefreshRateSelectors.erase(displayId);
+
+    promoteLeaderDisplay();
 }
 
 void Scheduler::run() {
@@ -163,7 +152,7 @@
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
     const bool supportsFrameRateOverrideByContent =
-            holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
+            leaderSelectorPtr()->supportsFrameRateOverrideByContent();
     return mFrameRateOverrideMappings
             .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
 }
@@ -178,8 +167,6 @@
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
-    std::scoped_lock lock(mRefreshRateSelectorLock);
-
     return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
         return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
     };
@@ -187,7 +174,7 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = holdRefreshRateSelector()->getActiveModePtr()->getFps();
+        const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
         const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
 
         const auto frameRate = getFrameRateOverride(uid);
@@ -281,7 +268,7 @@
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
     const bool supportsFrameRateOverrideByContent =
-            holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
+            leaderSelectorPtr()->supportsFrameRateOverrideByContent();
 
     std::vector<FrameRateOverride> overrides =
             mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -322,8 +309,7 @@
     // If the mode is not the current mode, this means that a
     // mode change is in progress. In that case we shouldn't dispatch an event
     // as it will be dispatched when the current mode changes.
-    if (std::scoped_lock lock(mRefreshRateSelectorLock);
-        mRefreshRateSelector->getActiveModePtr() != mPolicy.mode) {
+    if (leaderSelectorPtr()->getActiveModePtr() != mPolicy.mode) {
         return;
     }
 
@@ -416,10 +402,7 @@
     const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        const auto refreshRate = [&] {
-            std::scoped_lock lock(mRefreshRateSelectorLock);
-            return mRefreshRateSelector->getActiveModePtr()->getFps();
-        }();
+        const auto refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
         resyncToHardwareVsync(false, refreshRate);
     }
 }
@@ -478,12 +461,9 @@
 
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
-    {
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        if (!mRefreshRateSelector->canSwitch()) return;
+    if (leaderSelectorPtr()->canSwitch()) {
+        mLayerHistory.record(layer, presentTime, systemTime(), updateType);
     }
-
-    mLayerHistory.record(layer, presentTime, systemTime(), updateType);
 }
 
 void Scheduler::setModeChangePending(bool pending) {
@@ -496,7 +476,7 @@
 }
 
 void Scheduler::chooseRefreshRateForContent() {
-    const auto selectorPtr = holdRefreshRateSelector();
+    const auto selectorPtr = leaderSelectorPtr();
     if (!selectorPtr->canSwitch()) return;
 
     ATRACE_CALL();
@@ -506,16 +486,13 @@
 }
 
 void Scheduler::resetIdleTimer() {
-    std::scoped_lock lock(mRefreshRateSelectorLock);
-    mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ false);
+    leaderSelectorPtr()->resetIdleTimer();
 }
 
 void Scheduler::onTouchHint() {
     if (mTouchTimer) {
         mTouchTimer->reset();
-
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ true);
+        leaderSelectorPtr()->resetKernelIdleTimer();
     }
 }
 
@@ -535,30 +512,12 @@
     mLayerHistory.clear();
 }
 
-void Scheduler::bindIdleTimer(RefreshRateSelector& selector) {
-    selector.setIdleTimerCallbacks(
-            {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
-                          .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
-             .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
-                        .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
-
-    selector.startIdleTimer();
-}
-
-void Scheduler::unbindIdleTimer(RefreshRateSelector& selector) {
-    selector.stopIdleTimer();
-    selector.clearIdleTimerCallbacks();
-}
-
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
     ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const Fps refreshRate = [&] {
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        return mRefreshRateSelector->getActiveModePtr()->getFps();
-    }();
+    const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
     using namespace fps_approx_ops;
@@ -614,7 +573,11 @@
     }
     {
         utils::Dumper::Section section(dumper, "Policy"sv);
-
+        {
+            std::scoped_lock lock(mDisplayLock);
+            ftl::FakeGuard guard(kMainThreadContext);
+            dumper.dump("leaderDisplayId"sv, mLeaderDisplayId);
+        }
         dumper.dump("layerHistory"sv, mLayerHistory.dump());
         dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
         dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval));
@@ -638,17 +601,44 @@
 }
 
 bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
-    // we always update mFrameRateOverridesByContent here
-    // supportsFrameRateOverridesByContent will be checked
-    // when getting FrameRateOverrides from mFrameRateOverrideMappings
-    if (!consideredSignals.idle) {
-        const auto frameRateOverrides =
-                holdRefreshRateSelector()->getFrameRateOverrides(mPolicy.contentRequirements,
-                                                                 displayRefreshRate,
-                                                                 consideredSignals);
-        return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+    if (consideredSignals.idle) return false;
+
+    const auto frameRateOverrides =
+            leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+                                                       displayRefreshRate, consideredSignals);
+
+    // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
+    // the FrameRateOverrideMappings rather than here.
+    return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+}
+
+void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+    // TODO(b/241286431): Choose the leader display.
+    mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+    ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str());
+
+    if (const auto leaderPtr = leaderSelectorPtrLocked()) {
+        leaderPtr->setIdleTimerCallbacks(
+                {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+                              .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+                 .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+                            .onExpired =
+                                    [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+        leaderPtr->startIdleTimer();
     }
-    return false;
+}
+
+void Scheduler::demoteLeaderDisplay() {
+    // No need to lock for reads on kMainThreadContext.
+    if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) {
+        leaderPtr->stopIdleTimer();
+        leaderPtr->clearIdleTimerCallbacks();
+    }
+
+    // Clear state that depends on the leader's RefreshRateSelector.
+    std::scoped_lock lock(mPolicyLock);
+    mPolicy = {};
 }
 
 template <typename S, typename T>
@@ -660,23 +650,29 @@
     bool frameRateOverridesChanged;
 
     {
-        std::lock_guard<std::mutex> lock(mPolicyLock);
+        std::scoped_lock lock(mPolicyLock);
 
         auto& currentState = mPolicy.*statePtr;
         if (currentState == newState) return {};
         currentState = std::forward<T>(newState);
 
-        auto modeChoices = chooseDisplayModes();
-
-        // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest to go
-        // through. Fix this by tracking per-display Scheduler::Policy and timers.
+        DisplayModeChoiceMap modeChoices;
         DisplayModePtr modePtr;
-        std::tie(modePtr, consideredSignals) =
-                modeChoices.get(*mLeaderDisplayId)
-                        .transform([](const DisplayModeChoice& choice) {
-                            return std::make_pair(choice.modePtr, choice.consideredSignals);
-                        })
-                        .value();
+        {
+            std::scoped_lock lock(mDisplayLock);
+            ftl::FakeGuard guard(kMainThreadContext);
+
+            modeChoices = chooseDisplayModes();
+
+            // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
+            // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
+            std::tie(modePtr, consideredSignals) =
+                    modeChoices.get(*mLeaderDisplayId)
+                            .transform([](const DisplayModeChoice& choice) {
+                                return std::make_pair(choice.modePtr, choice.consideredSignals);
+                            })
+                            .value();
+        }
 
         modeRequests.reserve(modeChoices.size());
         for (auto& [id, choice] : modeChoices) {
@@ -807,7 +803,7 @@
     // Make sure the stored mode is up to date.
     if (mPolicy.mode) {
         const auto ranking =
-                holdRefreshRateSelector()
+                leaderSelectorPtr()
                         ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
                         .ranking;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 04f3b69..fb23071 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -22,7 +22,6 @@
 #include <future>
 #include <memory>
 #include <mutex>
-#include <optional>
 #include <unordered_map>
 #include <utility>
 
@@ -33,6 +32,8 @@
 #include <ui/GraphicTypes.h>
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
+#include <ftl/fake_guard.h>
+#include <ftl/optional.h>
 #include <scheduler/Features.h>
 #include <scheduler/Time.h>
 #include <ui/DisplayId.h>
@@ -108,12 +109,15 @@
 
     void startTimers();
 
-    using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
-    void setRefreshRateSelector(RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
-            EXCLUDES(mRefreshRateSelectorLock);
+    // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}.
+    void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
 
-    void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr);
-    void unregisterDisplay(PhysicalDisplayId);
+    using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
+
+    void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
+    void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
 
     void run();
 
@@ -165,7 +169,7 @@
     // Otherwise, if hardware vsync is not already enabled then this method will
     // no-op.
     void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate);
-    void resync() EXCLUDES(mRefreshRateSelectorLock);
+    void resync() EXCLUDES(mDisplayLock);
     void forceNextResync() { mLastResyncTime = 0; }
 
     // Passes a vsync sample to VsyncController. periodFlushed will be true if
@@ -176,14 +180,14 @@
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
-    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType)
-            EXCLUDES(mRefreshRateSelectorLock);
+    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType)
+            EXCLUDES(mDisplayLock);
     void setModeChangePending(bool pending);
     void setDefaultFrameRateCompatibility(Layer*);
     void deregisterLayer(Layer*);
 
     // Detects content using layer history, and selects a matching refresh rate.
-    void chooseRefreshRateForContent() EXCLUDES(mRefreshRateSelectorLock);
+    void chooseRefreshRateForContent() EXCLUDES(mDisplayLock);
 
     void resetIdleTimer();
 
@@ -228,11 +232,10 @@
     void setGameModeRefreshRateForUid(FrameRateOverride);
 
     // Retrieves the overridden refresh rate for a given uid.
-    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateSelectorLock);
+    std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
 
-    nsecs_t getVsyncPeriodFromRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) {
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        return mRefreshRateSelector->getActiveModePtr()->getFps().getPeriodNsecs();
+    nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
+        return leaderSelectorPtr()->getActiveModePtr()->getFps().getPeriodNsecs();
     }
 
     // Returns the framerate of the layer with the given sequence ID
@@ -255,21 +258,23 @@
     sp<EventThreadConnection> createConnectionInternal(
             EventThread*, EventRegistrationFlags eventRegistration = {});
 
-    void bindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext, mRefreshRateSelectorLock);
-
-    // Blocks until the timer thread exits. `mRefreshRateSelectorLock` must not be locked by the
-    // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
-    static void unbindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext)
-            EXCLUDES(mRefreshRateSelectorLock);
-
     // Update feature state machine to given state when corresponding timer resets or expires.
-    void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateSelectorLock);
+    void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
     void idleTimerCallback(TimerState);
     void touchTimerCallback(TimerState);
     void displayPowerTimerCallback(TimerState);
 
     void setVsyncPeriod(nsecs_t period);
 
+    // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
+    // `mLeaderDisplayId` is never `std::nullopt`.
+    void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt)
+            REQUIRES(kMainThreadContext, mDisplayLock);
+
+    // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the
+    // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+    void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+
     struct Policy;
 
     // Sets the S state of the policy to the T value under mPolicyLock, and chooses a display mode
@@ -296,23 +301,20 @@
     };
 
     using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>;
-    DisplayModeChoiceMap chooseDisplayModes() const REQUIRES(mPolicyLock);
+
+    // See mDisplayLock for thread safety.
+    DisplayModeChoiceMap chooseDisplayModes() const
+            REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext);
 
     GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
 
     bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
 
-    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateSelectorLock);
+    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
 
-    android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
-            EXCLUDES(mRefreshRateSelectorLock);
+    android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
     android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
-    RefreshRateSelectorPtr holdRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) {
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        return mRefreshRateSelector;
-    }
-
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
         sp<EventThreadConnection> connection;
@@ -342,10 +344,34 @@
 
     ISchedulerCallback& mSchedulerCallback;
 
+    // mDisplayLock may be locked while under mPolicyLock.
     mutable std::mutex mPolicyLock;
 
-    display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors;
-    std::optional<PhysicalDisplayId> mLeaderDisplayId;
+    // Only required for reads outside kMainThreadContext. kMainThreadContext is the only writer, so
+    // must lock for writes but not reads. See also mPolicyLock for locking order.
+    mutable std::mutex mDisplayLock;
+
+    display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors
+            GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+
+    ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock)
+            GUARDED_BY(kMainThreadContext);
+
+    RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) {
+        std::scoped_lock lock(mDisplayLock);
+        return leaderSelectorPtrLocked();
+    }
+
+    RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        const RefreshRateSelectorPtr noLeader;
+        return mLeaderDisplayId
+                .and_then([this](PhysicalDisplayId leaderId)
+                                  REQUIRES(mDisplayLock, kMainThreadContext) {
+                                      return mRefreshRateSelectors.get(leaderId);
+                                  })
+                .value_or(std::cref(noLeader));
+    }
 
     struct Policy {
         // Policy for choosing the display mode.
@@ -367,10 +393,6 @@
         std::optional<ModeChangedParams> cachedModeChangedParams;
     } mPolicy GUARDED_BY(mPolicyLock);
 
-    // TODO(b/255635821): Remove this by instead looking up the `mLeaderDisplayId` selector.
-    mutable std::mutex mRefreshRateSelectorLock;
-    RefreshRateSelectorPtr mRefreshRateSelector GUARDED_BY(mRefreshRateSelectorLock);
-
     std::mutex mVsyncTimelineLock;
     std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
             GUARDED_BY(mVsyncTimelineLock);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f0d6aeb..3e12a77 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2954,18 +2954,16 @@
                                                  displaySurface, producer);
 
     if (mScheduler && !display->isVirtual()) {
-        auto selectorPtr = display->holdRefreshRateSelector();
-
-        // Display modes are reloaded on hotplug reconnect.
-        if (display->isPrimary()) {
+        const auto displayId = display->getPhysicalId();
+        {
             // TODO(b/241285876): Annotate `processDisplayAdded` instead.
             ftl::FakeGuard guard(kMainThreadContext);
-            mScheduler->setRefreshRateSelector(selectorPtr);
+
+            // For hotplug reconnect, renew the registration since display modes have been reloaded.
+            mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector());
         }
 
-        const auto displayId = display->getPhysicalId();
-        mScheduler->registerDisplay(displayId, std::move(selectorPtr));
-        dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
+        dispatchDisplayHotplugEvent(displayId, true);
     }
 
     mDisplays.try_emplace(displayToken, std::move(display));
@@ -3424,9 +3422,7 @@
         !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
         features |= Feature::kPresentFences;
     }
-
-    auto selectorPtr = display->holdRefreshRateSelector();
-    if (selectorPtr->kernelIdleTimerController()) {
+    if (display->refreshRateSelector().kernelIdleTimerController()) {
         features |= Feature::kKernelIdleTimer;
     }
 
@@ -3434,8 +3430,7 @@
                                                         static_cast<ISchedulerCallback&>(*this),
                                                         features);
     mScheduler->createVsyncSchedule(features);
-    mScheduler->setRefreshRateSelector(selectorPtr);
-    mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr));
+    mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
 
     setVsyncEnabled(false);
     mScheduler->startTimers();
@@ -6980,9 +6975,11 @@
     }
     mActiveDisplayId = activeDisplay->getPhysicalId();
     activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
+
     updateInternalDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
-    mScheduler->setRefreshRateSelector(activeDisplay->holdRefreshRateSelector());
+    mScheduler->setLeaderDisplay(mActiveDisplayId);
+
     onActiveDisplaySizeChanged(activeDisplay);
     mActiveDisplayTransformHint = activeDisplay->getTransformHint();
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1918913..b51210d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -464,8 +464,7 @@
               typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
     void modulateVsync(Handler handler, Args... args) {
         if (const auto config = (*mVsyncModulator.*handler)(args...)) {
-            const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateSelector();
-            setVsyncConfig(*config, vsyncPeriod);
+            setVsyncConfig(*config, mScheduler->getLeaderVsyncPeriod());
         }
     }
 
@@ -928,7 +927,8 @@
             const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
     void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext);
-    void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
+    void processDisplayRemoved(const wp<IBinder>& displayToken)
+            REQUIRES(mStateLock, kMainThreadContext);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
                                const DisplayDeviceState& drawingState)
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 9ba9b90..ef79039 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -230,7 +230,10 @@
                       ISchedulerCallback& callback)
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-        setRefreshRateSelector(std::move(selectorPtr));
+
+        const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
+                                              selectorPtr->getActiveMode().getPhysicalDisplayId());
+        registerDisplay(displayId, std::move(selectorPtr));
     }
 
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -242,7 +245,7 @@
 
     auto &mutableLayerHistory() { return mLayerHistory; }
 
-    auto refreshRateSelector() { return holdRefreshRateSelector(); }
+    auto refreshRateSelector() { return leaderSelectorPtr(); }
 
     void replaceTouchTimer(int64_t millis) {
         if (mTouchTimer) {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ea4666e..8e333a3 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -190,8 +190,10 @@
     sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     ASSERT_EQ(1u, mScheduler->layerHistorySize());
 
-    mScheduler->setRefreshRateSelector(
-            std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId()));
+    // Replace `mSelector` with a new `RefreshRateSelector` that has different display modes.
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -234,11 +236,9 @@
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
-    const auto selectorPtr =
-            std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId());
-
-    mScheduler->registerDisplay(kDisplayId1, selectorPtr);
-    mScheduler->setRefreshRateSelector(selectorPtr);
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
 
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ba214d5..3f8fe0d 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -43,7 +43,10 @@
                       ISchedulerCallback& callback)
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-        setRefreshRateSelector(std::move(selectorPtr));
+
+        const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
+                                              selectorPtr->getActiveMode().getPhysicalDisplayId());
+        registerDisplay(displayId, std::move(selectorPtr));
 
         ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
             // Execute task to prevent broken promise exception on destruction.
@@ -67,12 +70,27 @@
     auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
     auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
 
-    auto refreshRateSelector() { return holdRefreshRateSelector(); }
-    bool hasRefreshRateSelectors() const { return !mRefreshRateSelectors.empty(); }
+    auto refreshRateSelector() { return leaderSelectorPtr(); }
 
-    void setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
+    const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
+        return mRefreshRateSelectors;
+    }
+
+    bool hasRefreshRateSelectors() const { return !refreshRateSelectors().empty(); }
+
+    void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
         ftl::FakeGuard guard(kMainThreadContext);
-        return Scheduler::setRefreshRateSelector(std::move(selectorPtr));
+        Scheduler::registerDisplay(displayId, std::move(selectorPtr));
+    }
+
+    void unregisterDisplay(PhysicalDisplayId displayId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        Scheduler::unregisterDisplay(displayId);
+    }
+
+    void setLeaderDisplay(PhysicalDisplayId displayId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        Scheduler::setLeaderDisplay(displayId);
     }
 
     auto& mutableLayerHistory() { return mLayerHistory; }
@@ -115,14 +133,13 @@
     using Scheduler::DisplayModeChoice;
     using Scheduler::DisplayModeChoiceMap;
 
-    DisplayModeChoiceMap chooseDisplayModes() {
-        std::lock_guard<std::mutex> lock(mPolicyLock);
+    DisplayModeChoiceMap chooseDisplayModes() NO_THREAD_SAFETY_ANALYSIS {
         return Scheduler::chooseDisplayModes();
     }
 
     void dispatchCachedReportedMode() {
         std::lock_guard<std::mutex> lock(mPolicyLock);
-        return Scheduler::dispatchCachedReportedMode();
+        Scheduler::dispatchCachedReportedMode();
     }
 
     void clearCachedReportedMode() {
@@ -131,7 +148,7 @@
     }
 
     void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
-        return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
+        Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
 private:
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index df53f15..2d5eb92 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -837,33 +837,36 @@
 
             sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
             mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
-            if (mFlinger.scheduler()) {
-                mFlinger.scheduler()->registerDisplay(display->getPhysicalId(),
-                                                      display->holdRefreshRateSelector());
-            }
 
             DisplayDeviceState state;
             state.isSecure = mCreationArgs.isSecure;
 
             if (mConnectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
-                const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
-                LOG_ALWAYS_FATAL_IF(!physicalId);
+                const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId);
+                LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
+                const auto physicalId = *physicalIdOpt;
+
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
 
                 const auto activeMode = modes.get(activeModeId);
                 LOG_ALWAYS_FATAL_IF(!activeMode);
 
-                state.physical = {.id = *physicalId,
+                state.physical = {.id = physicalId,
                                   .hwcDisplayId = *mHwcDisplayId,
                                   .activeMode = activeMode->get()};
 
                 const auto it = mFlinger.mutablePhysicalDisplays()
-                                        .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
+                                        .emplace_or_replace(physicalId, mDisplayToken, physicalId,
                                                             *mConnectionType, std::move(modes),
                                                             ui::ColorModes(), std::nullopt)
                                         .first;
 
+                if (mFlinger.scheduler()) {
+                    mFlinger.scheduler()->registerDisplay(physicalId,
+                                                          display->holdRefreshRateSelector());
+                }
+
                 display->setActiveMode(activeModeId, it->second.snapshot());
             }