SF: Propagate Display Mode Rejection from SF to DM

Create a new DisplayEventReceiver callback for onModeRejected
to signal mode rejection from SF to DM so that DM can trigger
a retry.
Add the plumbing through DisplayEventReceiver and EventThread

Bug: 374184110
Test: manual
Flag: com.android.graphics.surfaceflinger.flags.display_config_error_hal

Change-Id: I78515d413bdf2e6e07d6649b7b60df8ced199b19
Signed-off-by: Manasi Navare <navaremanasi@google.com>
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index fff4284..c6d7160 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -43,6 +43,7 @@
 #include <utils/Errors.h>
 
 #include <common/FlagManager.h>
+#include <scheduler/FrameRateMode.h>
 #include <scheduler/VsyncConfig.h>
 #include "FrameTimeline.h"
 #include "VSyncDispatch.h"
@@ -102,6 +103,10 @@
                                 to_string(event.header.displayId).c_str(),
                                 event.hdcpLevelsChange.connectedLevel,
                                 event.hdcpLevelsChange.maxLevel);
+        case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+            return StringPrintf("ModeRejected{displayId=%s, modeId=%u}",
+                                to_string(event.header.displayId).c_str(),
+                                event.modeRejection.modeId);
         default:
             return "Event{}";
     }
@@ -186,6 +191,18 @@
     };
 }
 
+DisplayEventReceiver::Event makeModeRejection(PhysicalDisplayId displayId, DisplayModeId modeId) {
+    return DisplayEventReceiver::Event{
+            .header =
+                    DisplayEventReceiver::Event::Header{
+                            .type = DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION,
+                            .displayId = displayId,
+                            .timestamp = systemTime(),
+                    },
+            .modeRejection.modeId = ftl::to_underlying(modeId),
+    };
+}
+
 } // namespace
 
 EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
@@ -480,6 +497,13 @@
     mCondition.notify_all();
 }
 
+void EventThread::onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    mPendingEvents.push_back(makeModeRejection(displayId, modeId));
+    mCondition.notify_all();
+}
+
 // Merge lists of buffer stuffed Uids
 void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
     std::lock_guard<std::mutex> lock(mMutex);
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 95632c7..18bf416 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -21,6 +21,7 @@
 #include <gui/DisplayEventReceiver.h>
 #include <private/gui/BitTube.h>
 #include <sys/types.h>
+#include <ui/DisplayId.h>
 #include <utils/Errors.h>
 
 #include <scheduler/FrameRateMode.h>
@@ -32,6 +33,7 @@
 #include <thread>
 #include <vector>
 
+#include "DisplayHardware/DisplayMode.h"
 #include "TracedOrdinal.h"
 #include "VSyncDispatch.h"
 #include "VsyncSchedule.h"
@@ -115,6 +117,9 @@
     // called when SF changes the active mode and apps needs to be notified about the change
     virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
 
+    // called when SF rejects the mode change request
+    virtual void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) = 0;
+
     // called when SF updates the Frame Rate Override list
     virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
                                              std::vector<FrameRateOverride> overrides) = 0;
@@ -179,6 +184,8 @@
 
     void onModeChanged(const scheduler::FrameRateMode&) override;
 
+    void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) override;
+
     void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
                                      std::vector<FrameRateOverride> overrides) override;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 2875650..86d91d9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -465,6 +465,12 @@
 }
 #pragma clang diagnostic pop
 
+void Scheduler::onDisplayModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) {
+    if (hasEventThreads()) {
+        eventThreadFor(Cycle::Render).onModeRejected(displayId, modeId);
+    }
+}
+
 void Scheduler::emitModeChangeIfNeeded() {
     if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
         ALOGW("No mode change to emit");
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 61c68a9..3fdddac 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,12 +36,14 @@
 #include <ftl/non_null.h>
 #include <ftl/optional.h>
 #include <scheduler/Features.h>
+#include <scheduler/FrameRateMode.h>
 #include <scheduler/FrameTargeter.h>
 #include <scheduler/Time.h>
 #include <scheduler/VsyncConfig.h>
 #include <ui/DisplayId.h>
 #include <ui/DisplayMap.h>
 
+#include "DisplayHardware/DisplayMode.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
 #include "ISchedulerCallback.h"
@@ -153,6 +155,8 @@
     bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&,
                               bool clearContentRequirements) EXCLUDES(mPolicyLock);
 
+    void onDisplayModeRejected(PhysicalDisplayId, DisplayModeId);
+
     void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
     void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 97c8623..0c2922d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1532,9 +1532,15 @@
         constraints.seamlessRequired = false;
         hal::VsyncPeriodChangeTimeline outTimeline;
 
-        if (mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
-                                                      constraints, outTimeline) !=
-            display::DisplayModeController::ModeChangeResult::Changed) {
+        const auto error =
+                mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
+                                                          constraints, outTimeline);
+        if (error != display::DisplayModeController::ModeChangeResult::Changed) {
+            dropModeRequest(displayId);
+            if (FlagManager::getInstance().display_config_error_hal() &&
+                error == display::DisplayModeController::ModeChangeResult::Rejected) {
+                mScheduler->onDisplayModeRejected(displayId, desiredModeId);
+            }
             continue;
         }
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index c976697..3036fec 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -34,6 +34,8 @@
     MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
     MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
     MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override));
+    MOCK_METHOD(void, onModeRejected, (PhysicalDisplayId displayId, DisplayModeId modeId),
+                (override));
     MOCK_METHOD(void, onFrameRateOverridesChanged,
                 (PhysicalDisplayId, std::vector<FrameRateOverride>), (override));
     MOCK_METHOD(void, dump, (std::string&), (const, override));