SurfaceFlinger: add a test for unknown offset

Add a unit test that tries to get phase offset for a
refresh rate that is not part of the boot list.
The scenarios simulates an external display hotplug
with different refresh rate than the internal one.

Bug: 151859083
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Change-Id: I3adee92b91d0c1a337c9a6ffd7431e7b854bbe0d
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 7941cda..43883fb 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -36,6 +36,19 @@
     return std::abs(fpsA - fpsB) <= MARGIN;
 }
 
+std::vector<float> getRefreshRatesFromConfigs(
+        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+    std::vector<float> refreshRates;
+    refreshRates.reserve(allRefreshRates.size());
+
+    for (const auto& [ignored, refreshRate] : allRefreshRates) {
+        refreshRates.emplace_back(refreshRate->fps);
+    }
+
+    return refreshRates;
+}
+
 } // namespace
 
 namespace android::scheduler {
@@ -45,14 +58,21 @@
 namespace impl {
 
 PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
-        // for the N+2 vsync instead of N+1. This means that:
-        // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
-        // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
-        mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
-                                       .value_or(std::numeric_limits<nsecs_t>::max())),
-        mOffsets(initializeOffsets(refreshRateConfigs)),
-        mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
+      : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+                     refreshRateConfigs.getCurrentRefreshRate().fps,
+                     // Below defines the threshold when an offset is considered to be negative,
+                     // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+                     // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+                     // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+                     // vsync.
+                     getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                             .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+                           nsecs_t thresholdForNextVsync)
+      : mThresholdForNextVsync(thresholdForNextVsync),
+        mOffsets(initializeOffsets(refreshRates)),
+        mRefreshRateFps(currentFps) {}
 
 void PhaseOffsets::dump(std::string& result) const {
     const auto [early, earlyGl, late] = getCurrentOffsets();
@@ -67,12 +87,12 @@
 }
 
 std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
-        const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+        const std::vector<float>& refreshRates) const {
     std::unordered_map<float, Offsets> offsets;
 
-    for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
-        const float fps = refreshRate->fps;
-        offsets.emplace(fps, getPhaseOffsets(fps, refreshRate->vsyncPeriod));
+    for (const auto& refreshRate : refreshRates) {
+        offsets.emplace(refreshRate,
+                        getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
     }
     return offsets;
 }
@@ -243,19 +263,6 @@
     };
 }
 
-static std::vector<float> getRefreshRatesFromConfigs(
-        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
-    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
-    std::vector<float> refreshRates;
-    refreshRates.reserve(allRefreshRates.size());
-
-    for (const auto& [ignored, refreshRate] : allRefreshRates) {
-        refreshRates.emplace_back(refreshRate->fps);
-    }
-
-    return refreshRates;
-}
-
 std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
         const std::vector<float>& refreshRates) const {
     std::unordered_map<float, Offsets> offsets;