VTS for the Refresh rate callback debug enabled

BUG: 202734676
Test: atest VtsHalGraphicsComposer3_TargetTest
with the patch of
Ibc80d66eae6b21c3cf84d35fa819e97ccc509ede

Change-Id: I02bdf061420fe12ef4548008d66bb73e8cd416e8
diff --git a/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp b/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
index 4fb5d01..7b3a2b4 100644
--- a/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
+++ b/graphics/composer/aidl/vts/GraphicsComposerCallback.cpp
@@ -29,6 +29,11 @@
     mVsyncAllowed = allowed;
 }
 
+void GraphicsComposerCallback::setRefreshRateChangedDebugDataEnabledCallbackAllowed(bool allowed) {
+    std::scoped_lock lock(mMutex);
+    mRefreshRateChangedDebugDataEnabledCallbackAllowed = allowed;
+}
+
 std::vector<int64_t> GraphicsComposerCallback::getDisplays() const {
     std::scoped_lock lock(mMutex);
     return mDisplays;
@@ -79,6 +84,21 @@
     return ret;
 }
 
+std::vector<RefreshRateChangedDebugData>
+GraphicsComposerCallback::takeListOfRefreshRateChangedDebugData() {
+    std::scoped_lock lock(mMutex);
+
+    std::vector<RefreshRateChangedDebugData> ret;
+    ret.swap(mRefreshRateChangedDebugData);
+
+    return ret;
+}
+
+int32_t GraphicsComposerCallback::getInvalidRefreshRateDebugEnabledCallbackCount() const {
+    std::scoped_lock lock(mMutex);
+    return mInvalidRefreshRateDebugEnabledCallbackCount;
+}
+
 ::ndk::ScopedAStatus GraphicsComposerCallback::onHotplug(int64_t in_display, bool in_connected) {
     std::scoped_lock lock(mMutex);
 
@@ -125,9 +145,16 @@
 }
 
 ::ndk::ScopedAStatus GraphicsComposerCallback::onRefreshRateChangedDebug(
-        const RefreshRateChangedDebugData&) {
-    // TODO(b/202734676) Add implementation for Vts tests
-    return ::ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+        const RefreshRateChangedDebugData& data) {
+    std::scoped_lock lock(mMutex);
+
+    const auto it = std::find(mDisplays.begin(), mDisplays.end(), data.display);
+    if (mRefreshRateChangedDebugDataEnabledCallbackAllowed && it != mDisplays.end()) {
+        mRefreshRateChangedDebugData.push_back(data);
+    } else {
+        mInvalidRefreshRateDebugEnabledCallbackCount++;
+    }
+    return ::ndk::ScopedAStatus::ok();
 }
 
 ::ndk::ScopedAStatus GraphicsComposerCallback::onVsyncPeriodTimingChanged(
diff --git a/graphics/composer/aidl/vts/GraphicsComposerCallback.h b/graphics/composer/aidl/vts/GraphicsComposerCallback.h
index 9c3bc70..13e992a 100644
--- a/graphics/composer/aidl/vts/GraphicsComposerCallback.h
+++ b/graphics/composer/aidl/vts/GraphicsComposerCallback.h
@@ -26,6 +26,8 @@
   public:
     void setVsyncAllowed(bool allowed);
 
+    void setRefreshRateChangedDebugDataEnabledCallbackAllowed(bool allowed);
+
     std::vector<int64_t> getDisplays() const;
 
     int32_t getInvalidHotplugCount() const;
@@ -44,6 +46,10 @@
 
     std::optional<VsyncPeriodChangeTimeline> takeLastVsyncPeriodChangeTimeline();
 
+    std::vector<RefreshRateChangedDebugData> takeListOfRefreshRateChangedDebugData();
+
+    int32_t getInvalidRefreshRateDebugEnabledCallbackCount() const;
+
   private:
     virtual ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override;
     virtual ::ndk::ScopedAStatus onRefresh(int64_t in_display) override;
@@ -63,9 +69,13 @@
     std::vector<int64_t> mDisplays GUARDED_BY(mMutex);
     // true only when vsync is enabled
     bool mVsyncAllowed GUARDED_BY(mMutex) = true;
+    // true only when RefreshRateChangedCallbackDebugEnabled is set to true.
+    bool mRefreshRateChangedDebugDataEnabledCallbackAllowed GUARDED_BY(mMutex) = false;
 
     std::optional<VsyncPeriodChangeTimeline> mTimeline GUARDED_BY(mMutex);
 
+    std::vector<RefreshRateChangedDebugData> mRefreshRateChangedDebugData GUARDED_BY(mMutex);
+
     int32_t mVsyncIdleCount GUARDED_BY(mMutex) = 0;
     int64_t mVsyncIdleTime GUARDED_BY(mMutex) = 0;
 
@@ -75,6 +85,7 @@
     int32_t mInvalidVsyncCount GUARDED_BY(mMutex) = 0;
     int32_t mInvalidVsyncPeriodChangeCount GUARDED_BY(mMutex) = 0;
     int32_t mInvalidSeamlessPossibleCount GUARDED_BY(mMutex) = 0;
+    int32_t mInvalidRefreshRateDebugEnabledCallbackCount GUARDED_BY(mMutex) = 0;
 };
 
 }  // namespace aidl::android::hardware::graphics::composer3::vts
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.cpp b/graphics/composer/aidl/vts/VtsComposerClient.cpp
index e03ef25..25b0ca0 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.cpp
+++ b/graphics/composer/aidl/vts/VtsComposerClient.cpp
@@ -119,6 +119,24 @@
     return updateDisplayProperties(vtsDisplay, config);
 }
 
+ScopedAStatus VtsComposerClient::setPeakRefreshRateConfig(VtsDisplay* vtsDisplay) {
+    const auto displayId = vtsDisplay->getDisplayId();
+    auto [activeStatus, activeConfig] = getActiveConfig(displayId);
+    EXPECT_TRUE(activeStatus.isOk());
+    auto peakDisplayConfig = vtsDisplay->getDisplayConfig(activeConfig);
+    auto peakConfig = activeConfig;
+
+    const auto displayConfigs = vtsDisplay->getDisplayConfigs();
+    for (const auto [config, displayConfig] : displayConfigs) {
+        if (displayConfig.configGroup == peakDisplayConfig.configGroup &&
+            displayConfig.vsyncPeriod < peakDisplayConfig.vsyncPeriod) {
+            peakDisplayConfig = displayConfig;
+            peakConfig = config;
+        }
+    }
+    return setActiveConfig(vtsDisplay, peakConfig);
+}
+
 std::pair<ScopedAStatus, int32_t> VtsComposerClient::getDisplayAttribute(
         int64_t display, int32_t config, DisplayAttribute displayAttribute) {
     int32_t outDisplayAttribute;
@@ -375,10 +393,15 @@
     return mComposerCallback->getVsyncIdleTime();
 }
 
-ndk::ScopedAStatus VtsComposerClient::setRefreshRateChangedCallbackDebugEnabled(
-        int64_t /* display */, bool /* enabled */) {
-    // TODO(b/202734676) Add implementation for VTS tests
-    return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ndk::ScopedAStatus VtsComposerClient::setRefreshRateChangedCallbackDebugEnabled(int64_t display,
+                                                                                bool enabled) {
+    mComposerCallback->setRefreshRateChangedDebugDataEnabledCallbackAllowed(enabled);
+    return mComposerClient->setRefreshRateChangedCallbackDebugEnabled(display, enabled);
+}
+
+std::vector<RefreshRateChangedDebugData>
+VtsComposerClient::takeListOfRefreshRateChangedDebugData() {
+    return mComposerCallback->takeListOfRefreshRateChangedDebugData();
 }
 
 int64_t VtsComposerClient::getInvalidDisplayId() {
@@ -545,6 +568,10 @@
             ALOGE("Invalid seamless possible count");
             isValid = false;
         }
+        if (mComposerCallback->getInvalidRefreshRateDebugEnabledCallbackCount() != 0) {
+            ALOGE("Invalid refresh rate debug enabled callback count");
+            isValid = false;
+        }
     }
     return isValid;
 }
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.h b/graphics/composer/aidl/vts/VtsComposerClient.h
index 81a62b3..ea3318c 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.h
+++ b/graphics/composer/aidl/vts/VtsComposerClient.h
@@ -77,6 +77,8 @@
 
     ScopedAStatus setActiveConfig(VtsDisplay* vtsDisplay, int32_t config);
 
+    ScopedAStatus setPeakRefreshRateConfig(VtsDisplay* vtsDisplay);
+
     std::pair<ScopedAStatus, int32_t> getDisplayAttribute(int64_t display, int32_t config,
                                                           DisplayAttribute displayAttribute);
 
@@ -183,6 +185,10 @@
 
     std::pair<ScopedAStatus, OverlayProperties> getOverlaySupport();
 
+    ndk::ScopedAStatus setRefreshRateChangedCallbackDebugEnabled(int64_t display, bool enabled);
+
+    std::vector<RefreshRateChangedDebugData> takeListOfRefreshRateChangedDebugData();
+
   private:
     ScopedAStatus addDisplayConfig(VtsDisplay* vtsDisplay, int32_t config);
     ScopedAStatus updateDisplayProperties(VtsDisplay* vtsDisplay, int32_t config);
@@ -197,9 +203,6 @@
 
     bool verifyComposerCallbackParams();
 
-    ndk::ScopedAStatus setRefreshRateChangedCallbackDebugEnabled(int64_t /* display */,
-                                                                 bool /* enabled */);
-
     // Keep track of displays and layers. When a test fails/ends,
     // the VtsComposerClient::tearDown should be called from the
     // test tearDown to clean up the resources for the test.
@@ -245,15 +248,17 @@
     };
 
     void addDisplayConfig(int32_t config, DisplayConfig displayConfig) {
-        displayConfigs.insert({config, displayConfig});
+        mDisplayConfigs.insert({config, displayConfig});
     }
 
-    DisplayConfig getDisplayConfig(int32_t config) { return displayConfigs.find(config)->second; }
+    DisplayConfig getDisplayConfig(int32_t config) { return mDisplayConfigs.find(config)->second; }
+
+    std::unordered_map<int32_t, DisplayConfig> getDisplayConfigs() { return mDisplayConfigs; }
 
   private:
     int64_t mDisplayId;
     int32_t mDisplayWidth;
     int32_t mDisplayHeight;
-    std::unordered_map<int32_t, DisplayConfig> displayConfigs;
+    std::unordered_map<int32_t, DisplayConfig> mDisplayConfigs;
 };
 }  // namespace aidl::android::hardware::graphics::composer3::vts
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index 4974e71..37576b9 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -1217,6 +1217,14 @@
         }
     }
 
+    bool checkIfCallbackRefreshRateChangedDebugEnabledReceived(
+            std::function<bool(RefreshRateChangedDebugData)> filter) {
+        const auto list = mComposerClient->takeListOfRefreshRateChangedDebugData();
+        return std::any_of(list.begin(), list.end(), [&](auto refreshRateChangedDebugData) {
+            return filter(refreshRateChangedDebugData);
+        });
+    }
+
     sp<GraphicBuffer> allocate(::android::PixelFormat pixelFormat) {
         return sp<GraphicBuffer>::make(
                 static_cast<uint32_t>(getPrimaryDisplay().getDisplayWidth()),
@@ -1316,7 +1324,7 @@
         return vsyncPeriod;
     }
 
-    int64_t createOnScreenLayer() {
+    int64_t createOnScreenLayer(Composition composition = Composition::DEVICE) {
         const auto& [status, layer] =
                 mComposerClient->createLayer(getPrimaryDisplayId(), kBufferSlotCount);
         EXPECT_TRUE(status.isOk());
@@ -1324,12 +1332,25 @@
                           getPrimaryDisplay().getDisplayHeight()};
         FRect cropRect{0, 0, (float)getPrimaryDisplay().getDisplayWidth(),
                        (float)getPrimaryDisplay().getDisplayHeight()};
-        configureLayer(getPrimaryDisplay(), layer, Composition::DEVICE, displayFrame, cropRect);
+        configureLayer(getPrimaryDisplay(), layer, composition, displayFrame, cropRect);
         auto& writer = getWriter(getPrimaryDisplayId());
         writer.setLayerDataspace(getPrimaryDisplayId(), layer, common::Dataspace::UNKNOWN);
         return layer;
     }
 
+    void sendBufferUpdate(int64_t layer) {
+        const auto buffer = allocate(::android::PIXEL_FORMAT_RGBA_8888);
+        ASSERT_NE(nullptr, buffer->handle);
+
+        auto& writer = getWriter(getPrimaryDisplayId());
+        writer.setLayerBuffer(getPrimaryDisplayId(), layer, /*slot*/ 0, buffer->handle,
+                              /*acquireFence*/ -1);
+
+        const sp<::android::Fence> presentFence =
+                presentAndGetFence(ComposerClientWriter::kNoTimestamp);
+        presentFence->waitForever(LOG_TAG);
+    }
+
     bool hasDisplayCapability(int64_t display, DisplayCapability cap) {
         const auto& [status, capabilities] = mComposerClient->getDisplayCapabilities(display);
         EXPECT_TRUE(status.isOk());
@@ -2268,6 +2289,179 @@
     EXPECT_TRUE(mComposerClient->setPowerMode(getPrimaryDisplayId(), PowerMode::OFF).isOk());
 }
 
+TEST_P(GraphicsComposerAidlCommandTest, SetRefreshRateChangedCallbackDebug_Unsupported) {
+    if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
+        auto status = mComposerClient->setRefreshRateChangedCallbackDebugEnabled(
+                getPrimaryDisplayId(), /*enabled*/ true);
+        EXPECT_FALSE(status.isOk());
+        EXPECT_NO_FATAL_FAILURE(
+                assertServiceSpecificError(status, IComposerClient::EX_UNSUPPORTED));
+
+        status = mComposerClient->setRefreshRateChangedCallbackDebugEnabled(getPrimaryDisplayId(),
+                                                                            /*enabled*/ false);
+        EXPECT_FALSE(status.isOk());
+        EXPECT_NO_FATAL_FAILURE(
+                assertServiceSpecificError(status, IComposerClient::EX_UNSUPPORTED));
+    }
+}
+
+TEST_P(GraphicsComposerAidlCommandTest, SetRefreshRateChangedCallbackDebug_Enabled) {
+    if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
+        GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
+        return;
+    }
+
+    const auto displayId = getPrimaryDisplayId();
+    EXPECT_TRUE(mComposerClient->setPowerMode(displayId, PowerMode::ON).isOk());
+    // Enable the callback
+    ASSERT_TRUE(mComposerClient
+                        ->setRefreshRateChangedCallbackDebugEnabled(displayId,
+                                                                    /*enabled*/ true)
+                        .isOk());
+    std::this_thread::sleep_for(100ms);
+
+    const bool isCallbackReceived = checkIfCallbackRefreshRateChangedDebugEnabledReceived(
+            [&](auto refreshRateChangedDebugData) {
+                return displayId == refreshRateChangedDebugData.display;
+            });
+
+    // Check that we immediately got a callback
+    EXPECT_TRUE(isCallbackReceived);
+
+    ASSERT_TRUE(mComposerClient
+                        ->setRefreshRateChangedCallbackDebugEnabled(displayId,
+                                                                    /*enabled*/ false)
+                        .isOk());
+}
+
+TEST_P(GraphicsComposerAidlCommandTest,
+       SetRefreshRateChangedCallbackDebugEnabled_noCallbackWhenIdle) {
+    if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
+        GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
+        return;
+    }
+
+    auto display = getEditablePrimaryDisplay();
+    const auto displayId = display.getDisplayId();
+
+    if (!hasDisplayCapability(displayId, DisplayCapability::DISPLAY_IDLE_TIMER)) {
+        GTEST_SUCCEED() << "DisplayCapability::DISPLAY_IDLE_TIMER is not supported";
+        return;
+    }
+
+    EXPECT_TRUE(mComposerClient->setPowerMode(displayId, PowerMode::ON).isOk());
+    EXPECT_TRUE(mComposerClient->setPeakRefreshRateConfig(&display).isOk());
+
+    ASSERT_TRUE(mComposerClient->setIdleTimerEnabled(displayId, /*timeoutMs*/ 500).isOk());
+    // Enable the callback
+    ASSERT_TRUE(mComposerClient
+                        ->setRefreshRateChangedCallbackDebugEnabled(displayId,
+                                                                    /*enabled*/ true)
+                        .isOk());
+
+    const bool isCallbackReceived = checkIfCallbackRefreshRateChangedDebugEnabledReceived(
+            [&](auto refreshRateChangedDebugData) {
+                return displayId == refreshRateChangedDebugData.display;
+            });
+
+    int retryCount = 3;
+    do {
+        // Wait for 1s so that we enter the idle state
+        std::this_thread::sleep_for(1s);
+        if (!isCallbackReceived) {
+            // DID NOT receive a callback, we are in the idle state.
+            break;
+        }
+    } while (--retryCount > 0);
+
+    if (retryCount == 0) {
+        GTEST_SUCCEED() << "Unable to enter the idle mode";
+        return;
+    }
+
+    // Send the REFRESH_RATE_INDICATOR update
+    ASSERT_NO_FATAL_FAILURE(
+            sendBufferUpdate(createOnScreenLayer(Composition::REFRESH_RATE_INDICATOR)));
+    std::this_thread::sleep_for(1s);
+    EXPECT_FALSE(isCallbackReceived)
+            << "A callback should not be received for REFRESH_RATE_INDICATOR";
+
+    EXPECT_TRUE(mComposerClient
+                        ->setRefreshRateChangedCallbackDebugEnabled(displayId,
+                                                                    /*enabled*/ false)
+                        .isOk());
+}
+
+TEST_P(GraphicsComposerAidlCommandTest,
+       SetRefreshRateChangedCallbackDebugEnabled_SetActiveConfigWithConstraints) {
+    if (!hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG)) {
+        GTEST_SUCCEED() << "Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG is not supported";
+        return;
+    }
+
+    VsyncPeriodChangeConstraints constraints;
+    constraints.seamlessRequired = false;
+    constraints.desiredTimeNanos = systemTime();
+
+    for (VtsDisplay& display : mDisplays) {
+        const auto displayId = display.getDisplayId();
+        EXPECT_TRUE(mComposerClient->setPowerMode(displayId, PowerMode::ON).isOk());
+
+        // Enable the callback
+        ASSERT_TRUE(mComposerClient
+                            ->setRefreshRateChangedCallbackDebugEnabled(displayId, /*enabled*/ true)
+                            .isOk());
+
+        forEachTwoConfigs(displayId, [&](int32_t config1, int32_t config2) {
+            const int32_t vsyncPeriod1 = display.getDisplayConfig(config1).vsyncPeriod;
+            const int32_t vsyncPeriod2 = display.getDisplayConfig(config2).vsyncPeriod;
+
+            if (vsyncPeriod1 == vsyncPeriod2) {
+                return;  // continue
+            }
+
+            EXPECT_TRUE(mComposerClient->setActiveConfig(&display, config1).isOk());
+            sendRefreshFrame(display, nullptr);
+
+            const auto& [status, timeline] =
+                    mComposerClient->setActiveConfigWithConstraints(&display, config2, constraints);
+            EXPECT_TRUE(status.isOk());
+
+            if (timeline.refreshRequired) {
+                sendRefreshFrame(display, &timeline);
+            }
+
+            const auto isCallbackReceived = checkIfCallbackRefreshRateChangedDebugEnabledReceived(
+                    [&](auto refreshRateChangedDebugData) {
+                        constexpr int kVsyncThreshold = 1000;
+                        return displayId == refreshRateChangedDebugData.display &&
+                               std::abs(vsyncPeriod2 -
+                                        refreshRateChangedDebugData.vsyncPeriodNanos) <=
+                                       kVsyncThreshold;
+                    });
+
+            int retryCount = 3;
+            do {
+                std::this_thread::sleep_for(100ms);
+                if (isCallbackReceived) {
+                    GTEST_SUCCEED() << "Received a callback successfully";
+                    break;
+                }
+            } while (--retryCount > 0);
+
+            if (retryCount == 0) {
+                GTEST_FAIL() << "failed to get a callback for the display " << displayId
+                             << " with config " << config2;
+            }
+        });
+
+        EXPECT_TRUE(
+                mComposerClient
+                        ->setRefreshRateChangedCallbackDebugEnabled(displayId, /*enabled*/ false)
+                        .isOk());
+    }
+}
+
 /*
  * Test that no two display configs are exactly the same.
  */