Round up virtual display refresh rate

This semantics guarantees the virtual display will at least have the
specified refresh rate, e.g., if 60hz is requested on a 90hz display,
the virtual dislay will get a 90hz.

Bug: 266965278
Test: atest libsurfaceflinger_unittest
Change-Id: Ie7b30c5766454d0ad25cfd437f0498594c690a2e
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 01db0cd..20f4de1 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -535,8 +535,8 @@
 }
 
 void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
-    using fps_approx_ops::operator==;
-    if (mRequestedRefreshRate == 0_Hz) {
+    using fps_approx_ops::operator<=;
+    if (mRequestedRefreshRate <= 0_Hz) {
         return;
     }
 
@@ -547,7 +547,12 @@
     }
 
     unsigned divisor = static_cast<unsigned>(
-            std::round(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+            std::floor(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+    if (divisor == 0) {
+        mAdjustedRefreshRate = 0_Hz;
+        return;
+    }
+
     mAdjustedRefreshRate = pacesetterDisplayRefreshRate / divisor;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 834c8b6..7c5b0ce 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -492,7 +492,7 @@
 
     // Implements ISurfaceComposer
     sp<IBinder> createDisplay(const String8& displayName, bool secure,
-                              float requestedRefreshRate = 0);
+                              float requestedRefreshRate = 0.0f);
     void destroyDisplay(const sp<IBinder>& displayToken);
     std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const EXCLUDES(mStateLock) {
         Mutex::Autolock lock(mStateLock);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 6a9c970..1cc9ba4 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -17,12 +17,60 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <scheduler/Fps.h>
+
 #include "DisplayTransactionTestHelpers.h"
+#include "FpsOps.h"
 
 namespace android {
 namespace {
 
-class CreateDisplayTest : public DisplayTransactionTest {};
+class CreateDisplayTest : public DisplayTransactionTest {
+public:
+    void createDisplayWithRequestedRefreshRate(const String8& name, uint64_t displayId,
+                                               float pacesetterDisplayRefreshRate,
+                                               float requestedRefreshRate,
+                                               float expectedAdjustedRefreshRate) {
+        // --------------------------------------------------------------------
+        // Call Expectations
+
+        // --------------------------------------------------------------------
+        // Invocation
+
+        sp<IBinder> displayToken = mFlinger.createDisplay(name, false, requestedRefreshRate);
+
+        // --------------------------------------------------------------------
+        // Postconditions
+
+        // The display should have been added to the current state
+        ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+        const auto& display = getCurrentDisplayState(displayToken);
+        EXPECT_TRUE(display.isVirtual());
+        EXPECT_EQ(display.requestedRefreshRate, Fps::fromValue(requestedRefreshRate));
+        EXPECT_EQ(name.string(), display.displayName);
+
+        std::optional<VirtualDisplayId> vid =
+                DisplayId::fromValue<VirtualDisplayId>(displayId | DisplayId::FLAG_VIRTUAL);
+        ASSERT_TRUE(vid.has_value());
+
+        sp<DisplayDevice> device =
+                mFlinger.createVirtualDisplayDevice(displayToken, *vid, requestedRefreshRate);
+        EXPECT_TRUE(device->isVirtual());
+        device->adjustRefreshRate(Fps::fromValue(pacesetterDisplayRefreshRate));
+        // verifying desired value
+        EXPECT_EQ(device->getAdjustedRefreshRate(), Fps::fromValue(expectedAdjustedRefreshRate));
+        // verifying rounding up
+        if (requestedRefreshRate < pacesetterDisplayRefreshRate) {
+            EXPECT_GE(device->getAdjustedRefreshRate(), Fps::fromValue(requestedRefreshRate));
+        } else {
+            EXPECT_EQ(device->getAdjustedRefreshRate(),
+                      Fps::fromValue(pacesetterDisplayRefreshRate));
+        }
+
+        // --------------------------------------------------------------------
+        // Cleanup conditions
+    }
+};
 
 TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
     const String8 name("virtual.test");
@@ -84,5 +132,82 @@
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 }
 
+// Requesting 0 tells SF not to do anything, i.e., default to refresh as physical displays
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRate0) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 0.f;
+    const float kExpectedAdjustedRefreshRate = 0.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting negative refresh rate, will be ignored, same as requesting 0
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNegative) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = -60.f;
+    const float kExpectedAdjustedRefreshRate = 0.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a higher refresh rate than the pacesetter
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateHigh) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 90.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting the same refresh rate as the pacesetter
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateSame) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 60.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a divisor (30) of the pacesetter (60) should be honored
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateDivisor) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 30.f;
+    const float kExpectedAdjustedRefreshRate = 30.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a non divisor (45) of the pacesetter (120) should round up to a divisor (60)
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNoneDivisor) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 120.f;
+    const float kRequestedRefreshRate = 45.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a non divisor (75) of the pacesetter (120) should round up to pacesetter (120)
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNoneDivisorMax) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 120.f;
+    const float kRequestedRefreshRate = 75.f;
+    const float kExpectedAdjustedRefreshRate = 120.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index fc9e653..862a8de 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -48,10 +48,12 @@
 #include "TestableScheduler.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockFrameTimeline.h"
 #include "mock/MockFrameTracer.h"
 #include "mock/MockSchedulerCallback.h"
+#include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 namespace renderengine {
@@ -379,8 +381,8 @@
 
     void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
 
-    auto createDisplay(const String8& displayName, bool secure) {
-        return mFlinger->createDisplay(displayName, secure);
+    auto createDisplay(const String8& displayName, bool secure, float requestedRefreshRate = 0.0f) {
+        return mFlinger->createDisplay(displayName, secure, requestedRefreshRate);
     }
 
     auto destroyDisplay(const sp<IBinder>& displayToken) {
@@ -527,6 +529,24 @@
         mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
     }
 
+    sp<DisplayDevice> createVirtualDisplayDevice(const sp<IBinder> displayToken,
+                                                 VirtualDisplayId displayId,
+                                                 float requestedRefreshRate) {
+        constexpr ui::Size kResolution = {1080, 1920};
+        auto compositionDisplay = compositionengine::impl::
+                createDisplay(mFlinger->getCompositionEngine(),
+                              compositionengine::DisplayCreationArgsBuilder()
+                                      .setId(displayId)
+                                      .setPixels(kResolution)
+                                      .setPowerAdvisor(&mPowerAdvisor)
+                                      .build());
+        DisplayDeviceCreationArgs creationArgs(mFlinger, mFlinger->getHwComposer(), displayToken,
+                                               compositionDisplay);
+        creationArgs.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
+        creationArgs.nativeWindow = sp<mock::NativeWindow>::make();
+        return sp<DisplayDevice>::make(creationArgs);
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -964,6 +984,7 @@
     scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
     std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
     scheduler::TestableScheduler* mScheduler = nullptr;
+    Hwc2::mock::PowerAdvisor mPowerAdvisor;
 };
 
 } // namespace android