SurfaceFlinger: HWVsync when display is off

HWC expects that HWVsync will not be turned on if display if off.
To ensure that we move enabling/disabling of HWVsync to the main thread
and caching the latest request in case the display is off. When the
display is turned on again we apply the latest state of HWVsync.

Bug: 134050982
Test: Toggle screen on/off in a loop
Change-Id: Ib87f885f95195b8bb402d5a5b2c75f20e213d2aa
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e735e10..a543bc7 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1537,10 +1537,25 @@
 
 void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
     ATRACE_CALL();
-    Mutex::Autolock lock(mStateLock);
+
+    // Enable / Disable HWVsync from the main thread to avoid race conditions with
+    // display power state.
+    postMessageAsync(new LambdaMessage(
+            [=]() NO_THREAD_SAFETY_ANALYSIS { setPrimaryVsyncEnabledInternal(enabled); }));
+}
+
+void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
+    ATRACE_CALL();
+
     if (const auto displayId = getInternalDisplayIdLocked()) {
-        getHwComposer().setVsyncEnabled(*displayId,
-                                        enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+        sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
+        if (display && display->isPoweredOn()) {
+            getHwComposer().setVsyncEnabled(*displayId,
+                                            enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+        } else {
+            // Cache the latest vsync state and apply it when screen is on again
+            mEnableHWVsyncScreenOn = enabled;
+        }
     }
 }
 
@@ -4438,6 +4453,11 @@
         // Turn on the display
         getHwComposer().setPowerMode(*displayId, mode);
         if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
+            if (mEnableHWVsyncScreenOn) {
+                setPrimaryVsyncEnabledInternal(mEnableHWVsyncScreenOn);
+                mEnableHWVsyncScreenOn = false;
+            }
+
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
         }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ddfe88c..02ee46a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -293,6 +293,9 @@
     // TODO: this should be made accessible only to EventThread
     void setPrimaryVsyncEnabled(bool enabled);
 
+    // main thread function to enable/disable h/w composer event
+    void setPrimaryVsyncEnabledInternal(bool enabled);
+
     // called on the main thread by MessageQueue when an internal message
     // is received
     // TODO: this should be made accessible only to MessageQueue
@@ -1166,6 +1169,9 @@
     // The Layer pointer is removed from the set when the destructor is called so there shouldn't
     // be any issues with a raw pointer referencing an invalid object.
     std::unordered_set<Layer*> mOffscreenLayers;
+
+    // Flag to indicate whether to re-enable HWVsync when screen is on
+    bool mEnableHWVsyncScreenOn = false;
 };
 
 } // namespace android