SF: Isolate modesetting in DisplayModeController

Move the per-display state machine for modesetting from DisplayDevice to
DMC. In lieu of mStateLock, protect display lookup from multiple threads
using a mutex internal to DMC, which fixes the following deadlock:

OneShotTimer::loop
    SF::requestDisplayModes
    mStateLock

SF::commit
    mStateLock
    SF::processDisplayChangesLocked (hotplug or resolution change)
    Scheduler::demotePacesetterDisplay
    OneShotTimer::stop

A notable change is that {initiate,finalize}DisplayModeChange(s) are no
longer called under mStateLock, thanks to DMC's granular, internal lock.
finalizeDisplayModeChange still locks mStateLock for resolution changes.

Add an ActiveModeListener to DMC and register a callback in SF to update
the refresh rate overlay, which still lives in DisplayDevice for now.

Fixes: 329450361
Bug: 241285876
Test: DisplayModeControllerTest
Test: libsurfaceflinger_unittest
Change-Id: I30ec756f134d2d67a70ac8797008dc792eac035e
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index f093384..a6a9bec 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -20,30 +20,221 @@
 
 #include "Display/DisplayModeController.h"
 #include "Display/DisplaySnapshot.h"
+#include "DisplayHardware/HWComposer.h"
 
+#include <common/FlagManager.h>
+#include <ftl/concat.h>
+#include <ftl/expected.h>
 #include <log/log.h>
 
 namespace android::display {
 
+template <size_t N>
+inline std::string DisplayModeController::Display::concatId(const char (&str)[N]) const {
+    return std::string(ftl::Concat(str, ' ', snapshot.get().displayId().value).str());
+}
+
+DisplayModeController::Display::Display(DisplaySnapshotRef snapshot,
+                                        RefreshRateSelectorPtr selectorPtr)
+      : snapshot(snapshot),
+        selectorPtr(std::move(selectorPtr)),
+        pendingModeFpsTrace(concatId("PendingModeFps")),
+        activeModeFpsTrace(concatId("ActiveModeFps")),
+        renderRateFpsTrace(concatId("RenderRateFps")),
+        hasDesiredModeTrace(concatId("HasDesiredMode"), false) {}
+
+void DisplayModeController::registerDisplay(PhysicalDisplayId displayId,
+                                            DisplaySnapshotRef snapshotRef,
+                                            RefreshRateSelectorPtr selectorPtr) {
+    std::lock_guard lock(mDisplayLock);
+    mDisplays.emplace_or_replace(displayId, std::make_unique<Display>(snapshotRef, selectorPtr));
+}
+
 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);
+    std::lock_guard lock(mDisplayLock);
+    mDisplays.emplace_or_replace(displayId,
+                                 std::make_unique<Display>(snapshotRef, snapshot.displayModes(),
+                                                           activeModeId, config));
 }
 
 void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) {
+    std::lock_guard lock(mDisplayLock);
     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 {
+auto DisplayModeController::selectorPtrFor(PhysicalDisplayId displayId) const
+        -> RefreshRateSelectorPtr {
+    std::lock_guard lock(mDisplayLock);
     return mDisplays.get(displayId)
-            .transform([](const Display& display) { return display.selectorPtr; })
+            .transform([](const DisplayPtr& displayPtr) { return displayPtr->selectorPtr; })
             .value_or(nullptr);
 }
 
+auto DisplayModeController::setDesiredMode(PhysicalDisplayId displayId,
+                                           DisplayModeRequest&& desiredMode) -> DesiredModeAction {
+    std::lock_guard lock(mDisplayLock);
+    const auto& displayPtr =
+            FTL_EXPECT(mDisplays.get(displayId).ok_or(DesiredModeAction::None)).get();
+
+    {
+        ATRACE_NAME(displayPtr->concatId(__func__).c_str());
+        ALOGD("%s %s", displayPtr->concatId(__func__).c_str(), to_string(desiredMode).c_str());
+
+        std::scoped_lock lock(displayPtr->desiredModeLock);
+
+        if (auto& desiredModeOpt = displayPtr->desiredModeOpt) {
+            // A mode transition was already scheduled, so just override the desired mode.
+            const bool emitEvent = desiredModeOpt->emitEvent;
+            const bool force = desiredModeOpt->force;
+            desiredModeOpt = std::move(desiredMode);
+            desiredModeOpt->emitEvent |= emitEvent;
+            if (FlagManager::getInstance().connected_display()) {
+                desiredModeOpt->force |= force;
+            }
+            return DesiredModeAction::None;
+        }
+
+        // If the desired mode is already active...
+        const auto activeMode = displayPtr->selectorPtr->getActiveMode();
+        if (const auto& desiredModePtr = desiredMode.mode.modePtr;
+            !desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) {
+            if (activeMode == desiredMode.mode) {
+                return DesiredModeAction::None;
+            }
+
+            // ...but the render rate changed:
+            setActiveModeLocked(displayId, desiredModePtr->getId(), desiredModePtr->getVsyncRate(),
+                                desiredMode.mode.fps);
+            return DesiredModeAction::InitiateRenderRateSwitch;
+        }
+
+        // Restore peak render rate to schedule the next frame as soon as possible.
+        setActiveModeLocked(displayId, activeMode.modePtr->getId(),
+                            activeMode.modePtr->getVsyncRate(), activeMode.modePtr->getPeakFps());
+
+        // Initiate a mode change.
+        displayPtr->desiredModeOpt = std::move(desiredMode);
+        displayPtr->hasDesiredModeTrace = true;
+    }
+
+    return DesiredModeAction::InitiateDisplayModeSwitch;
+}
+
+auto DisplayModeController::getDesiredMode(PhysicalDisplayId displayId) const
+        -> DisplayModeRequestOpt {
+    std::lock_guard lock(mDisplayLock);
+    const auto& displayPtr =
+            FTL_EXPECT(mDisplays.get(displayId).ok_or(DisplayModeRequestOpt())).get();
+
+    {
+        std::scoped_lock lock(displayPtr->desiredModeLock);
+        return displayPtr->desiredModeOpt;
+    }
+}
+
+auto DisplayModeController::getPendingMode(PhysicalDisplayId displayId) const
+        -> DisplayModeRequestOpt {
+    std::lock_guard lock(mDisplayLock);
+    const auto& displayPtr =
+            FTL_EXPECT(mDisplays.get(displayId).ok_or(DisplayModeRequestOpt())).get();
+
+    {
+        std::scoped_lock lock(displayPtr->desiredModeLock);
+        return displayPtr->pendingModeOpt;
+    }
+}
+
+bool DisplayModeController::isModeSetPending(PhysicalDisplayId displayId) const {
+    std::lock_guard lock(mDisplayLock);
+    const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+
+    {
+        std::scoped_lock lock(displayPtr->desiredModeLock);
+        return displayPtr->isModeSetPending;
+    }
+}
+
+scheduler::FrameRateMode DisplayModeController::getActiveMode(PhysicalDisplayId displayId) const {
+    return selectorPtrFor(displayId)->getActiveMode();
+}
+
+void DisplayModeController::clearDesiredMode(PhysicalDisplayId displayId) {
+    std::lock_guard lock(mDisplayLock);
+    const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+    {
+        std::scoped_lock lock(displayPtr->desiredModeLock);
+        displayPtr->desiredModeOpt.reset();
+        displayPtr->hasDesiredModeTrace = false;
+    }
+}
+
+bool DisplayModeController::initiateModeChange(PhysicalDisplayId displayId,
+                                               DisplayModeRequest&& desiredMode,
+                                               const hal::VsyncPeriodChangeConstraints& constraints,
+                                               hal::VsyncPeriodChangeTimeline& outTimeline) {
+    std::lock_guard lock(mDisplayLock);
+    const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+
+    // TODO: b/255635711 - Flow the DisplayModeRequest through the desired/pending/active states.
+    // For now, `desiredMode` and `desiredModeOpt` are one and the same, but the latter is not
+    // cleared until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been
+    // consumed at this point, so clear the `force` flag to prevent an endless loop of
+    // `initiateModeChange`.
+    if (FlagManager::getInstance().connected_display()) {
+        std::scoped_lock lock(displayPtr->desiredModeLock);
+        if (displayPtr->desiredModeOpt) {
+            displayPtr->desiredModeOpt->force = false;
+        }
+    }
+
+    displayPtr->pendingModeOpt = std::move(desiredMode);
+    displayPtr->isModeSetPending = true;
+
+    const auto& mode = *displayPtr->pendingModeOpt->mode.modePtr;
+
+    if (mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(), constraints,
+                                                   &outTimeline) != OK) {
+        return false;
+    }
+
+    ATRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+    return true;
+}
+
+void DisplayModeController::finalizeModeChange(PhysicalDisplayId displayId, DisplayModeId modeId,
+                                               Fps vsyncRate, Fps renderFps) {
+    std::lock_guard lock(mDisplayLock);
+    setActiveModeLocked(displayId, modeId, vsyncRate, renderFps);
+
+    const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+    displayPtr->isModeSetPending = false;
+}
+
+void DisplayModeController::setActiveMode(PhysicalDisplayId displayId, DisplayModeId modeId,
+                                          Fps vsyncRate, Fps renderFps) {
+    std::lock_guard lock(mDisplayLock);
+    setActiveModeLocked(displayId, modeId, vsyncRate, renderFps);
+}
+
+void DisplayModeController::setActiveModeLocked(PhysicalDisplayId displayId, DisplayModeId modeId,
+                                                Fps vsyncRate, Fps renderFps) {
+    const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+    ATRACE_INT(displayPtr->activeModeFpsTrace.c_str(), vsyncRate.getIntValue());
+    ATRACE_INT(displayPtr->renderRateFpsTrace.c_str(), renderFps.getIntValue());
+
+    displayPtr->selectorPtr->setActiveMode(modeId, renderFps);
+
+    if (mActiveModeListener) {
+        mActiveModeListener(displayId, vsyncRate, renderFps);
+    }
+}
+
 } // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index b6a6bee..258b04b 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -17,16 +17,26 @@
 #pragma once
 
 #include <memory>
+#include <mutex>
+#include <string>
 #include <utility>
 
 #include <android-base/thread_annotations.h>
+#include <ftl/function.h>
+#include <ftl/optional.h>
 #include <ui/DisplayId.h>
 #include <ui/DisplayMap.h>
 
+#include "Display/DisplayModeRequest.h"
 #include "Display/DisplaySnapshotRef.h"
 #include "DisplayHardware/DisplayMode.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "ThreadContext.h"
+#include "TracedOrdinal.h"
+
+namespace android {
+class HWComposer;
+} // namespace android
 
 namespace android::display {
 
@@ -34,40 +44,97 @@
 // 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);
+    using ActiveModeListener = ftl::Function<void(PhysicalDisplayId, Fps vsyncRate, Fps renderFps)>;
 
-    // TODO(b/241285876): Remove once ownership is no longer shared with DisplayDevice.
+    DisplayModeController() = default;
+
+    void setHwComposer(HWComposer* composerPtr) { mComposerPtr = composerPtr; }
+    void setActiveModeListener(const ActiveModeListener& listener) {
+        mActiveModeListener = listener;
+    }
+
+    // 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);
-    }
+    // TODO: b/241285876 - Remove this.
+    void registerDisplay(PhysicalDisplayId, DisplaySnapshotRef, RefreshRateSelectorPtr)
+            EXCLUDES(mDisplayLock);
+
+    // The referenced DisplaySnapshot must outlive the registration.
+    void registerDisplay(DisplaySnapshotRef, DisplayModeId, scheduler::RefreshRateSelector::Config)
+            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+    void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+    // Returns `nullptr` if the display is no longer registered (or never was).
+    RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+
+    enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
+
+    DesiredModeAction setDesiredMode(PhysicalDisplayId, DisplayModeRequest&&)
+            EXCLUDES(mDisplayLock);
+
+    using DisplayModeRequestOpt = ftl::Optional<DisplayModeRequest>;
+
+    DisplayModeRequestOpt getDesiredMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+    void clearDesiredMode(PhysicalDisplayId) EXCLUDES(mDisplayLock);
+
+    DisplayModeRequestOpt getPendingMode(PhysicalDisplayId) const REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
+    bool isModeSetPending(PhysicalDisplayId) const REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
+
+    scheduler::FrameRateMode getActiveMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+
+    bool initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
+                            const hal::VsyncPeriodChangeConstraints&,
+                            hal::VsyncPeriodChangeTimeline& outTimeline)
+            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+    void finalizeModeChange(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+    void setActiveMode(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+            EXCLUDES(mDisplayLock);
 
 private:
     struct Display {
-        Display(DisplaySnapshotRef snapshot, RefreshRateSelectorPtr selectorPtr)
-              : snapshot(snapshot), selectorPtr(std::move(selectorPtr)) {}
+        template <size_t N>
+        std::string concatId(const char (&)[N]) const;
 
+        Display(DisplaySnapshotRef, RefreshRateSelectorPtr);
         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;
+
+        const std::string pendingModeFpsTrace;
+        const std::string activeModeFpsTrace;
+        const std::string renderRateFpsTrace;
+
+        std::mutex desiredModeLock;
+        DisplayModeRequestOpt desiredModeOpt GUARDED_BY(desiredModeLock);
+        TracedOrdinal<bool> hasDesiredModeTrace GUARDED_BY(desiredModeLock);
+
+        DisplayModeRequestOpt pendingModeOpt GUARDED_BY(kMainThreadContext);
+        bool isModeSetPending GUARDED_BY(kMainThreadContext) = false;
     };
 
-    ui::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays;
+    using DisplayPtr = std::unique_ptr<Display>;
+
+    void setActiveModeLocked(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+            REQUIRES(mDisplayLock);
+
+    // Set once when initializing the DisplayModeController, which the HWComposer must outlive.
+    HWComposer* mComposerPtr = nullptr;
+
+    ActiveModeListener mActiveModeListener;
+
+    mutable std::mutex mDisplayLock;
+    ui::PhysicalDisplayMap<PhysicalDisplayId, DisplayPtr> mDisplays GUARDED_BY(mDisplayLock);
 };
 
 } // namespace android::display