Cleanup dumpsys for refresh configs for non-90hz devices.

Also add tests exercising RefreshRateConfigs behavior.

Bug: 127846986
Test: libsurfaceflinger_unittest
Test: dumpsys SurfaceFlinger
Change-Id: Idad91cf2a0a0b9661382d1b0011306fb4e36902b
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index cbcc031..9e95f95 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -57,16 +57,23 @@
     }
     ~RefreshRateConfigs() = default;
 
-    const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() {
+    const std::unordered_map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() {
         return mRefreshRates;
     }
-    const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; }
+    std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) {
+        const auto& refreshRate = mRefreshRates.find(type);
+        if (refreshRate != mRefreshRates.end()) {
+            return refreshRate->second;
+        }
+        return nullptr;
+    }
 
 private:
     void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
         // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
         mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
-                              RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
+                              std::make_shared<RefreshRate>(
+                                      RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}));
 
         if (configs.size() < 1) {
             ALOGE("Device does not have valid configs. Config size is 0.");
@@ -90,9 +97,10 @@
         if (vsyncPeriod != 0) {
             const float fps = 1e9 / vsyncPeriod;
             mRefreshRates.emplace(RefreshRateType::DEFAULT,
-                                  RefreshRate{configIdToVsyncPeriod[0].first,
-                                              base::StringPrintf("%2.ffps", fps),
-                                              static_cast<uint32_t>(fps)});
+                                  std::make_shared<RefreshRate>(
+                                          RefreshRate{configIdToVsyncPeriod[0].first,
+                                                      base::StringPrintf("%2.ffps", fps),
+                                                      static_cast<uint32_t>(fps)}));
         }
 
         if (configs.size() < 2) {
@@ -105,13 +113,14 @@
         if (vsyncPeriod != 0) {
             const float fps = 1e9 / vsyncPeriod;
             mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
-                                  RefreshRate{configIdToVsyncPeriod[1].first,
-                                              base::StringPrintf("%2.ffps", fps),
-                                              static_cast<uint32_t>(fps)});
+                                  std::make_shared<RefreshRate>(
+                                          RefreshRate{configIdToVsyncPeriod[1].first,
+                                                      base::StringPrintf("%2.ffps", fps),
+                                                      static_cast<uint32_t>(fps)}));
         }
     }
 
-    std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates;
+    std::unordered_map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 2491081..6b78cee 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -85,10 +85,13 @@
         std::unordered_map<std::string, int64_t> totalTime;
         for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) {
             int64_t totalTimeForConfig = 0;
-            if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
-                totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
+            if (!config) {
+                continue;
             }
-            totalTime[config.name] = totalTimeForConfig;
+            if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
+                totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
+            }
+            totalTime[config->name] = totalTimeForConfig;
         }
         return totalTime;
     }
@@ -124,8 +127,11 @@
 
         mConfigModesTotalTime[mode] += timeElapsedMs;
         for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) {
-            if (config.configId == mode) {
-                mTimeStats->recordRefreshRate(config.fps, timeElapsed);
+            if (!config) {
+                continue;
+            }
+            if (config->configId == mode) {
+                mTimeStats->recordRefreshRate(config->fps, timeElapsed);
             }
         }
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ca94e15..3c98349 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -570,10 +570,11 @@
         const auto displayId = getInternalDisplayIdLocked();
         LOG_ALWAYS_FATAL_IF(!displayId);
 
-        const auto performanceRefreshRate =
+        const auto& performanceRefreshRate =
                 mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
 
-        if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+        if (performanceRefreshRate &&
+            isConfigAllowed(*displayId, performanceRefreshRate->configId)) {
             setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
         } else {
             setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
@@ -1448,17 +1449,24 @@
     LOG_ALWAYS_FATAL_IF(!displayId);
     const auto displayToken = getInternalDisplayTokenLocked();
 
-    auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId;
-    const auto display = getDisplayDeviceLocked(displayToken);
-    if (desiredConfigId == display->getActiveConfig()) {
+    const auto& refreshRateConfig = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate);
+    if (!refreshRateConfig) {
+        ALOGV("Skipping refresh rate change request for unsupported rate.");
         return;
     }
 
+    const int desiredConfigId = refreshRateConfig->configId;
+
     if (!isConfigAllowed(*displayId, desiredConfigId)) {
         ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
         return;
     }
 
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (desiredConfigId == display->getActiveConfig()) {
+        return;
+    }
+
     mPhaseOffsets->setRefreshRateType(refreshRate);
     setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
 }
@@ -5745,12 +5753,12 @@
     int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId);
     if (!isConfigAllowed(*displayId, currentConfigIndex)) {
         for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) {
-            if (isConfigAllowed(*displayId, config.configId)) {
+            if (config && isConfigAllowed(*displayId, config->configId)) {
                 // TODO: we switch to the first allowed config. In the future
                 // we may want to enhance this logic to pick a similar config
                 // to the current one
-                ALOGV("Old config is not allowed - switching to config %d", config.configId);
-                setDesiredActiveConfig(displayToken, config.configId,
+                ALOGV("Old config is not allowed - switching to config %d", config->configId);
+                setDesiredActiveConfig(displayToken, config->configId,
                                        Scheduler::ConfigEvent::Changed);
                 break;
             }
@@ -5760,9 +5768,10 @@
     // If idle timer and fps detection are disabled and we are in RefreshRateType::DEFAULT,
     // there is no trigger to move to RefreshRateType::PERFORMANCE, even if it is an allowed.
     if (!mScheduler->isIdleTimerEnabled() && !mUseSmart90ForVideo) {
-        const auto performanceRefreshRate =
+        const auto& performanceRefreshRate =
                 mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
-        if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+        if (performanceRefreshRate &&
+            isConfigAllowed(*displayId, performanceRefreshRate->configId)) {
             setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
         }
     }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 25ce4ac..12b41fd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -47,6 +47,7 @@
         "LayerMetadataTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
+        "RefreshRateConfigsTest.cpp",
         "RefreshRateStatsTest.cpp",
         "TimeStatsTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
new file mode 100644
index 0000000..b218ad6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "DisplayHardware/HWC2.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+
+namespace android {
+namespace scheduler {
+
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+
+class RefreshRateConfigsTest : public testing::Test {
+protected:
+    static constexpr int CONFIG_ID_60 = 0;
+    static constexpr int CONFIG_ID_90 = 1;
+    static constexpr int64_t VSYNC_60 = 16666667;
+    static constexpr int64_t VSYNC_90 = 11111111;
+
+    RefreshRateConfigsTest();
+    ~RefreshRateConfigsTest();
+
+    void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
+        ASSERT_EQ(left.configId, right.configId);
+        ASSERT_EQ(left.name, right.name);
+        ASSERT_EQ(left.fps, right.fps);
+    }
+};
+
+RefreshRateConfigsTest::RefreshRateConfigsTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+RefreshRateConfigsTest::~RefreshRateConfigsTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+    RefreshRateConfigs configs(displayConfigs);
+
+    // We always store a configuration for screen off.
+    const auto& rates = configs.getRefreshRates();
+    ASSERT_EQ(1, rates.size());
+    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+    ASSERT_NE(rates.end(), powerSavingRate);
+    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
+
+    RefreshRate expectedConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+    assertRatesEqual(expectedConfig, *powerSavingRate->second);
+
+    ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+    assertRatesEqual(expectedConfig, *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+    ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+    ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+
+    // Sanity check that getRefreshRate() does not modify the underlying configs.
+    ASSERT_EQ(1, configs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
+    auto display = new Hwc2::mock::Display();
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+    config60.setVsyncPeriod(VSYNC_60);
+    displayConfigs.push_back(config60.build());
+    RefreshRateConfigs configs(displayConfigs);
+
+    const auto& rates = configs.getRefreshRates();
+    ASSERT_EQ(2, rates.size());
+    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+    const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+    ASSERT_NE(rates.end(), powerSavingRate);
+    ASSERT_NE(rates.end(), defaultRate);
+    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+
+    RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+    assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+    assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+
+    ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+    assertRatesEqual(expectedPowerSavingConfig,
+                     *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+    ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+    assertRatesEqual(expectedDefaultConfig, *configs.getRefreshRate(RefreshRateType::DEFAULT));
+    ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+
+    // Sanity check that getRefreshRate() does not modify the underlying configs.
+    ASSERT_EQ(2, configs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
+    auto display = new Hwc2::mock::Display();
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+    config60.setVsyncPeriod(VSYNC_60);
+    displayConfigs.push_back(config60.build());
+    auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+    config90.setVsyncPeriod(VSYNC_90);
+    displayConfigs.push_back(config90.build());
+    RefreshRateConfigs configs(displayConfigs);
+
+    const auto& rates = configs.getRefreshRates();
+    ASSERT_EQ(3, rates.size());
+    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+    const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+    const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
+    ASSERT_NE(rates.end(), powerSavingRate);
+    ASSERT_NE(rates.end(), defaultRate);
+    ASSERT_NE(rates.end(), performanceRate);
+
+    RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+    assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+    assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+    RefreshRate expectedPerformanceConfig = RefreshRate{CONFIG_ID_90, "90fps", 90};
+    assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+
+    ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+    assertRatesEqual(expectedPowerSavingConfig,
+                     *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+    ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+    assertRatesEqual(expectedDefaultConfig, *configs.getRefreshRate(RefreshRateType::DEFAULT));
+    ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+    assertRatesEqual(expectedPerformanceConfig,
+                     *configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+}
+} // namespace
+} // namespace scheduler
+} // namespace android