SF: Fix getting vsync period in SF and Scheduler

SF and Scheduler rely on primary refresh rate configuration
from init. If non primary display becomes vsync source,
make call to getVsyncPeriodFromHWC to get correct vsync
source and to get the correct vsync period. In addition,
cache the vsync period in DisplayDevice until there is a
mode switch or power mode change.

CRs-Fixed: 3004697
Change-Id: I4810b4df8cede54c80ea3b03a32e294eecf4b07c
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9d20af9..bee0c8c 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -137,6 +137,7 @@
 // ----------------------------------------------------------------------------
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
     mPowerMode = mode;
+    resetVsyncPeriod();
     getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
 }
 
@@ -156,6 +157,7 @@
     const auto mode = getMode(id);
     LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported.");
     mActiveMode = mode;
+    resetVsyncPeriod();
 }
 
 status_t DisplayDevice::initiateModeChange(DisplayModeId modeId,
@@ -188,15 +190,34 @@
     return nullptr;
 }
 
+void DisplayDevice::resetVsyncPeriod() {
+    std::scoped_lock<std::mutex> lock(mModeLock);
+    mVsyncPeriodUpdated = true;
+    mVsyncPeriod = 0;
+}
+
 nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
+    std::scoped_lock<std::mutex> lock(mModeLock);
     const auto physicalId = getPhysicalId();
     if (!mHwComposer.isConnected(physicalId)) {
         return 0;
     }
 
+    if (!mVsyncPeriodUpdated && mVsyncPeriod) {
+        ALOGD("%s: value is cached. return %lu", __func__, (unsigned long)mVsyncPeriod);
+        return mVsyncPeriod;
+    }
+
     nsecs_t vsyncPeriod;
     const auto status = mHwComposer.getDisplayVsyncPeriod(physicalId, &vsyncPeriod);
     if (status == NO_ERROR) {
+        ALOGD("%s: Called HWC getDisplayVsyncPeriod. No error. period=%lu", __func__,
+          (unsigned long)vsyncPeriod);
+        if (mVsyncPeriod == vsyncPeriod) {
+            mVsyncPeriodUpdated = false;
+        } else {
+            mVsyncPeriod = vsyncPeriod;
+        }
         return vsyncPeriod;
     }
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 7f7eea3..43d8a2a 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -20,6 +20,7 @@
 #include <optional>
 #include <string>
 #include <unordered_map>
+#include <mutex>
 
 #include <android/native_window.h>
 #include <binder/IBinder.h>
@@ -190,6 +191,7 @@
     void onVsync(nsecs_t timestamp);
     nsecs_t getVsyncPeriodFromHWC() const;
     nsecs_t getRefreshTimestamp() const;
+    void resetVsyncPeriod();
 
     void setPowerModeOverrideConfig(bool supported);
     bool getPowerModeOverrideConfig() const;
@@ -226,6 +228,10 @@
 
     std::atomic<nsecs_t> mLastHwVsync = 0;
 
+    mutable std::mutex mModeLock;
+    mutable bool mVsyncPeriodUpdated = true;
+    mutable nsecs_t mVsyncPeriod = 0;
+
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index d5bb9c3..7195b6c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -560,7 +560,7 @@
     const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod());
+        resyncToHardwareVsync(false, mSchedulerCallback.getVsyncPeriodFromHWCcb());
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 35cf7b7..3b3f5f1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -63,6 +63,7 @@
     virtual void kernelTimerChanged(bool expired) = 0;
     virtual void triggerOnFrameRateOverridesChanged() = 0;
     virtual void getModeFromFps(float, DisplayModePtr&) = 0;
+    virtual nsecs_t getVsyncPeriodFromHWCcb() = 0;
 
 protected:
     ~ISchedulerCallback() = default;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 28e7607..01a8660 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1537,6 +1537,7 @@
         // Initiate a mode change.
         mDesiredActiveModeChanged = true;
         mDesiredActiveMode = info;
+        display->resetVsyncPeriod();
 
         // This will trigger HWC refresh without resetting the idle timer.
         repaintEverythingForHWC();
@@ -1659,7 +1660,8 @@
     clearDesiredActiveModeState();
 
     const auto refreshRate = getDefaultDisplayDeviceLocked()->getMode(modeId)->getFps();
-    mScheduler->resyncToHardwareVsync(true, refreshRate.getPeriodNsecs());
+    mScheduler->resyncToHardwareVsync(true, getCurrentVsyncSource()->isPrimary() ?
+        refreshRate.getPeriodNsecs() : getVsyncPeriodFromHWC());
     updatePhaseConfiguration(refreshRate);
 }
 
@@ -2261,6 +2263,33 @@
     return 0;
 }
 
+sp<DisplayDevice> SurfaceFlinger::getCurrentVsyncSource() {
+    Mutex::Autolock lock(mStateLock);
+    if (mNextVsyncSource) {
+        return mNextVsyncSource;
+    } else if (mActiveVsyncSource) {
+        return mActiveVsyncSource;
+    }
+
+    return getDefaultDisplayDeviceLocked();
+}
+
+nsecs_t SurfaceFlinger::getVsyncPeriodFromHWCcb() {
+    Mutex::Autolock lock(mStateLock);
+    auto display = getDefaultDisplayDeviceLocked();
+    if (mNextVsyncSource) {
+        display = mNextVsyncSource;
+    } else if (mActiveVsyncSource) {
+        display = mActiveVsyncSource;
+    }
+
+    if (display && !display->isPrimary()) {
+        return display->getVsyncPeriodFromHWC();
+    }
+
+    return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+}
+
 void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                         std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
     ATRACE_CALL();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8bb3a30..8db2463 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1176,6 +1176,8 @@
      * VSYNC
      */
     nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
+    nsecs_t getVsyncPeriodFromHWCcb();
+    sp<DisplayDevice> getCurrentVsyncSource();
 
     // Sets the refresh rate by switching active configs, if they are available for
     // the desired refresh rate.
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 1ebee36..c5f7c57 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -767,6 +767,7 @@
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() {}
     void getModeFromFps(float, DisplayModePtr&) override {}
+    nsecs_t getVsyncPeriodFromHWCcb() { return 0; }
 
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 07650b7..eecf2b4 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -31,6 +31,7 @@
     MOCK_METHOD1(kernelTimerChanged, void(bool));
     MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
     MOCK_METHOD2(getModeFromFps, void(float, DisplayModePtr&));
+    MOCK_METHOD0(getVsyncPeriodFromHWCcb, nsecs_t());
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -41,6 +42,7 @@
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() {}
     void getModeFromFps(float, DisplayModePtr&) override {}
+    nsecs_t getVsyncPeriodFromHWCcb() { return 0; }
 };
 
 } // namespace android::mock