SF: Introduce a configure stage

Decouple hotplug processing from commit of display transactions, as a
step toward per-display commit/composite. The configure stage will be
responsible for mode setting as well.

Bug: 241285876
Test: libsurfaceflinger_unittest
Change-Id: I72b7223760fa5896debb046f158845a0b4f4599a
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f7146c6..85c003a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -763,10 +763,10 @@
 
 // Do not call property_set on main thread which will be blocked by init
 // Use StartPropertySetThread instead.
-void SurfaceFlinger::init() {
+void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
-    Mutex::Autolock _l(mStateLock);
+    Mutex::Autolock lock(mStateLock);
 
     // Get a RenderEngine for the given display / config (can't fail)
     // TODO(b/77156734): We need to stop casting and use HAL types when possible.
@@ -806,12 +806,14 @@
     }
 
     // Process any initial hotplug and resulting display changes.
-    processDisplayHotplugEventsLocked();
+    LOG_ALWAYS_FATAL_IF(!configureLocked(),
+                        "Initial display configuration failed: HWC did not hotplug");
+    processDisplayChangesLocked();
+
     const auto display = getDefaultDisplayDeviceLocked();
-    LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback.");
-    const auto displayId = display->getPhysicalId();
-    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
-                        "Primary display is disconnected.");
+    LOG_ALWAYS_FATAL_IF(!display, "Failed to configure the primary display");
+    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(display->getPhysicalId()),
+                        "Primary display is disconnected");
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
@@ -1869,23 +1871,14 @@
 
 void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
                                           hal::Connection connection) {
-    const bool connected = connection == hal::Connection::CONNECTED;
-    ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId);
-
-    // Only lock if we're not on the main thread. This function is normally
-    // called on a hwbinder thread, but for the primary display it's called on
-    // the main thread with the state lock already held, so don't attempt to
-    // acquire it here.
-    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
-    mPendingHotplugEvents.emplace_back(HotplugEvent{hwcDisplayId, connection});
-
-    if (std::this_thread::get_id() == mMainThreadId) {
-        // Process all pending hot plug events immediately if we are on the main thread.
-        processDisplayHotplugEventsLocked();
+    {
+        std::lock_guard<std::mutex> lock(mHotplugMutex);
+        mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
     }
 
-    setTransactionFlags(eDisplayTransactionNeeded);
+    if (mScheduler) {
+        mScheduler->scheduleConfigure();
+    }
 }
 
 void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged(
@@ -1958,6 +1951,13 @@
     return vsyncDeadline + schedule.period();
 }
 
+void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
+    Mutex::Autolock lock(mStateLock);
+    if (configureLocked()) {
+        setTransactionFlags(eDisplayTransactionNeeded);
+    }
+}
+
 bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
         FTL_FAKE_GUARD(kMainThreadContext) {
     // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
@@ -2642,8 +2642,14 @@
     return {modes, activeMode};
 }
 
-void SurfaceFlinger::processDisplayHotplugEventsLocked() {
-    for (const auto& event : mPendingHotplugEvents) {
+bool SurfaceFlinger::configureLocked() {
+    std::vector<HotplugEvent> events;
+    {
+        std::lock_guard<std::mutex> lock(mHotplugMutex);
+        events = std::move(mPendingHotplugEvents);
+    }
+
+    for (const auto& event : events) {
         std::optional<DisplayIdentificationInfo> info =
                 getHwComposer().onHotplug(event.hwcDisplayId, event.connection);
 
@@ -2653,6 +2659,7 @@
 
         const auto displayId = info->id;
         const auto token = mPhysicalDisplayTokens.get(displayId);
+        const char* log;
 
         if (event.connection == hal::Connection::CONNECTED) {
             auto [supportedModes, activeMode] = loadDisplayModes(displayId);
@@ -2664,7 +2671,7 @@
             }
 
             if (!token) {
-                ALOGV("Creating display %s", to_string(displayId).c_str());
+                log = "Connecting";
 
                 DisplayDeviceState state;
                 state.physical = {.id = displayId,
@@ -2681,7 +2688,7 @@
                 mPhysicalDisplayTokens.try_emplace(displayId, std::move(token));
                 mInterceptor->saveDisplayCreation(state);
             } else {
-                ALOGV("Recreating display %s", to_string(displayId).c_str());
+                log = "Reconnecting";
 
                 auto& state = mCurrentState.displays.editValueFor(token->get());
                 state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
@@ -2692,7 +2699,7 @@
                 }
             }
         } else {
-            ALOGV("Removing display %s", to_string(displayId).c_str());
+            log = "Disconnecting";
 
             if (const ssize_t index = mCurrentState.displays.indexOfKey(token->get()); index >= 0) {
                 const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
@@ -2703,15 +2710,14 @@
             mPhysicalDisplayTokens.erase(displayId);
         }
 
-        processDisplayChangesLocked();
+        ALOGI("%s display %s (HAL ID %" PRIu64 ")", log, to_string(displayId).c_str(),
+              event.hwcDisplayId);
     }
 
-    mPendingHotplugEvents.clear();
+    return !events.empty();
 }
 
 void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    ALOGI("Dispatching display hotplug event displayId=%s, connected=%d",
-          to_string(displayId).c_str(), connected);
     mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected);
     mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
 }
@@ -3041,7 +3047,6 @@
     const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
     if (displayTransactionNeeded) {
         processDisplayChangesLocked();
-        processDisplayHotplugEventsLocked();
     }
     mForceTransactionDisplayChange = displayTransactionNeeded;