Merge "Update uniqueId association native methods"
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 0712c0a..28e5ee2 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -240,11 +240,11 @@
     EXPECT_GE(st.st_size, 1000000 /* 1MB */);
 }
 
-TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
-    EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
-                             << duration.count() << " s.";
+TEST_F(ZippedBugreportGenerationTest, TakesBetween20And300Seconds) {
+    EXPECT_GE(duration, 20s) << "Expected completion in more than 20s. Actual time "
+                             << duration.count() << " ms.";
     EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
-                              << duration.count() << " s.";
+                              << duration.count() << " ms.";
 }
 
 /**
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index b6f42ad..c796da6 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -244,7 +244,7 @@
 // The location is the profile name for primary apks or the dex path for secondary dex files.
 bool clear_primary_current_profiles(const std::string& package_name, const std::string& location) {
     bool success = true;
-    // For secondary dex files, we don't really need the user but we use it for sanity checks.
+    // For secondary dex files, we don't really need the user but we use it for validity checks.
     std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
     for (auto user : users) {
         success &= clear_current_profile(package_name, location, user, /*is_secondary_dex*/false);
@@ -468,7 +468,7 @@
     *reference_profile_fd = open_reference_profile(uid, package_name, location,
             /*read_write*/ true, is_secondary_dex);
 
-    // For secondary dex files, we don't really need the user but we use it for sanity checks.
+    // For secondary dex files, we don't really need the user but we use it for validity checks.
     // Note: the user owning the dex file should be the current user.
     std::vector<userid_t> users;
     if (is_secondary_dex){
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index c4ecd07..0f8a732 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -829,7 +829,7 @@
  * to top level directories (i.e. have "..").
  */
 static int validate_path(const std::string& dir, const std::string& path, int maxSubdirs) {
-    // Argument sanity checking
+    // Argument check
     if (dir.find('/') != 0 || dir.rfind('/') != dir.size() - 1
             || dir.find("..") != std::string::npos) {
         LOG(ERROR) << "Invalid directory " << dir;
diff --git a/cmds/ip-up-vpn/Android.bp b/cmds/ip-up-vpn/Android.bp
new file mode 100644
index 0000000..c746f7f
--- /dev/null
+++ b/cmds/ip-up-vpn/Android.bp
@@ -0,0 +1,31 @@
+// Copyright 2011 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+    name: "ip-up-vpn",
+
+    srcs: ["ip-up-vpn.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+}
diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk
deleted file mode 100644
index 396ae9d..0000000
--- a/cmds/ip-up-vpn/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# Copyright (C) 2011 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := ip-up-vpn.c
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_MODULE := ip-up-vpn
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index fe417a3..d5ca725 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -21,6 +21,7 @@
 #include <cutils/ashmem.h>
 
 #include <getopt.h>
+#include <libgen.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
diff --git a/headers/Android.bp b/headers/Android.bp
index 7481a23..cb18837 100644
--- a/headers/Android.bp
+++ b/headers/Android.bp
@@ -28,6 +28,11 @@
         "libstagefright_foundation_headers",
     ],
     min_sdk_version: "29",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+    ],
 
     host_supported: true,
     target: {
diff --git a/include/android/input.h b/include/android/input.h
index fbd61b5..e6ad943f 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -877,6 +877,7 @@
  * Keyboard types.
  *
  * Refer to the documentation on android.view.InputDevice for more details.
+ * Note: When adding a new keyboard type here InputDeviceInfo::setKeyboardType needs to be updated.
  */
 enum {
     /** none */
diff --git a/include/input/Input.h b/include/input/Input.h
index f4147a0..55ebb90 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -164,7 +164,7 @@
  * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
  * will occasionally emit 11.  There is not much harm making this constant bigger.)
  */
-#define MAX_POINTERS 16
+static constexpr size_t MAX_POINTERS = 16;
 
 /*
  * Maximum number of samples supported per motion event.
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 22aae19..c4f03c9 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -235,7 +235,7 @@
     void addBatteryInfo(const InputDeviceBatteryInfo& info);
     void addLightInfo(const InputDeviceLightInfo& info);
 
-    inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
+    void setKeyboardType(int32_t keyboardType);
     inline int32_t getKeyboardType() const { return mKeyboardType; }
 
     inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index edcb615..5f9a37d 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -500,24 +500,6 @@
     status_t sendTimeline(int32_t inputEventId,
                           std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
 
-    /* Returns true if there is a deferred event waiting.
-     *
-     * Should be called after calling consume() to determine whether the consumer
-     * has a deferred event to be processed.  Deferred events are somewhat special in
-     * that they have already been removed from the input channel.  If the input channel
-     * becomes empty, the client may need to do extra work to ensure that it processes
-     * the deferred event despite the fact that the input channel's file descriptor
-     * is not readable.
-     *
-     * One option is simply to call consume() in a loop until it returns WOULD_BLOCK.
-     * This guarantees that all deferred events will be processed.
-     *
-     * Alternately, the caller can call hasDeferredEvent() to determine whether there is
-     * a deferred event waiting and then ensure that its event loop wakes up at least
-     * one more time to consume the deferred event.
-     */
-    bool hasDeferredEvent() const;
-
     /* Returns true if there is a pending batch.
      *
      * Should be called after calling consume() with consumeBatches == false to determine
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index bb40f51..41b3460 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -57,4 +57,11 @@
         },
     },
     min_sdk_version: "29",
+    // static link, so it won't straddle a module boundary at runtime.
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media",
+        "com.android.media.swcodec",
+    ],
+
 }
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 90cbf9d..355b3b4 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -44,6 +44,7 @@
         "libtokio",
     ],
     host_supported: true,
+    vendor_available: true,
     target: {
         darwin: {
             enabled: false,
@@ -52,8 +53,10 @@
     apex_available: [
         "//apex_available:platform",
         "com.android.compos",
+        "com.android.uwb",
         "com.android.virt",
     ],
+    min_sdk_version: "Tiramisu",
 }
 
 rust_library {
diff --git a/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder
new file mode 100644
index 0000000..ae081e6
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/corpus/transact_on_binder
Binary files differ
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 0a82463..4860613 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -31,7 +31,7 @@
     target: {
         darwin: {
             enabled: false,
-        }
+        },
     },
 
     shared_libs: [
@@ -46,6 +46,11 @@
         "-Werror",
     ],
     min_sdk_version: "29",
+    // static link, so it won't straddle a module boundary at runtime.
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+    ],
 }
 
 hidl_package_root {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index e73c3b8..930d819 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -35,6 +35,7 @@
 
 cc_library {
     name: "libinput",
+    cpp_std: "c++20",
     host_supported: true,
     cflags: [
         "-Wall",
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index ac84627..0bee1b6 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -252,6 +252,13 @@
     mLights.insert_or_assign(info.id, info);
 }
 
+void InputDeviceInfo::setKeyboardType(int32_t keyboardType) {
+    static_assert(AINPUT_KEYBOARD_TYPE_NONE < AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+    static_assert(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC < AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // There can be multiple subdevices with different keyboard types, set it to the highest type
+    mKeyboardType = std::max(mKeyboardType, keyboardType);
+}
+
 std::vector<InputDeviceSensorInfo> InputDeviceInfo::getSensors() {
     std::vector<InputDeviceSensorInfo> infos;
     infos.reserve(mSensors.size());
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index a065ce2..6195052 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -1318,10 +1318,6 @@
     return result;
 }
 
-bool InputConsumer::hasDeferredEvent() const {
-    return mMsgDeferred;
-}
-
 bool InputConsumer::hasPendingBatch() const {
     return !mBatches.empty();
 }
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index b6a9476..1c8658b 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -115,12 +115,9 @@
     static_assert(sizeof(InputMessage::Header) == 8);
 }
 
-/**
- * We cannot use the Body::size() method here because it is not static for
- * the Motion type, where "pointerCount" variable affects the size and can change at runtime.
- */
 void TestBodySize() {
     static_assert(sizeof(InputMessage::Body::Key) == 96);
+    static_assert(sizeof(InputMessage::Body::Motion::Pointer) == 136);
     static_assert(sizeof(InputMessage::Body::Motion) ==
                   offsetof(InputMessage::Body::Motion, pointers) +
                           sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
@@ -132,6 +129,38 @@
     // Timeline
     static_assert(GraphicsTimeline::SIZE == 2);
     static_assert(sizeof(InputMessage::Body::Timeline) == 24);
+
+    /**
+     * We cannot use the Body::size() method here because it is not static for
+     * the Motion type, where "pointerCount" variable affects the size and can change at runtime.
+     */
+    static_assert(sizeof(InputMessage::Body) ==
+                  offsetof(InputMessage::Body::Motion, pointers) +
+                          sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
+    static_assert(sizeof(InputMessage::Body) == 160 + 136 * 16);
+    static_assert(sizeof(InputMessage::Body) == 2336);
+}
+
+/**
+ * In general, we are sending a variable-length message across the socket, because the number of
+ * pointers varies. When we receive the message, we still need to allocate enough memory for the
+ * entire InputMessage struct. This size is, therefore, the worst case scenario. However, it is
+ * still helpful to compute to get an idea of the sizes that are involved.
+ */
+void TestWorstCaseInputMessageSize() {
+    static_assert(sizeof(InputMessage) == /*header*/ 8 + /*body*/ 2336);
+    static_assert(sizeof(InputMessage) == 2344);
+}
+
+/**
+ * Assuming a single pointer, how big is the message that we are sending across the socket?
+ */
+void CalculateSinglePointerInputMessageSize() {
+    constexpr size_t pointerCount = 1;
+    constexpr size_t bodySize = offsetof(InputMessage::Body::Motion, pointers) +
+            sizeof(InputMessage::Body::Motion::Pointer) * pointerCount;
+    static_assert(bodySize == 160 + 136);
+    static_assert(bodySize == 296); // For the total message size, add the small header
 }
 
 // --- VerifiedInputEvent ---
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 063ce67..763b82d 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1104,6 +1104,15 @@
                 paint.setDither(true);
             }
             paint.setAlphaf(layer.alpha);
+
+            if (imageTextureRef->colorType() == kAlpha_8_SkColorType) {
+                LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8");
+                float matrix[] = { 0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0,
+                                   0, 0, 0, -1, 1 };
+                paint.setColorFilter(SkColorFilters::Matrix(matrix));
+            }
         } else {
             ATRACE_NAME("DrawColor");
             const auto color = layer.source.solidColor;
@@ -1125,7 +1134,11 @@
             paint.setBlendMode(SkBlendMode::kSrc);
         }
 
-        paint.setColorFilter(displayColorTransform);
+        // A color filter will have been set for an A8 buffer. Do not replace
+        // it with the displayColorTransform, which shouldn't affect A8.
+        if (!paint.getColorFilter()) {
+            paint.setColorFilter(displayColorTransform);
+        }
 
         if (!roundRectClip.isEmpty()) {
             canvas->clipRRect(roundRectClip, true);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 2a25b0b..612a0aa 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -218,14 +218,35 @@
         uint8_t* pixels;
         buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                                   reinterpret_cast<void**>(&pixels));
-        pixels[0] = color.r;
-        pixels[1] = color.g;
-        pixels[2] = color.b;
-        pixels[3] = color.a;
+        for (uint32_t j = 0; j < height; j++) {
+            uint8_t* dst = pixels + (buffer->getBuffer()->getStride() * j * 4);
+            for (uint32_t i = 0; i < width; i++) {
+                dst[0] = color.r;
+                dst[1] = color.g;
+                dst[2] = color.b;
+                dst[3] = color.a;
+                dst += 4;
+            }
+        }
         buffer->getBuffer()->unlock();
         return buffer;
     }
 
+    std::shared_ptr<renderengine::ExternalTexture> allocateR8Buffer(int width, int height) {
+        auto buffer = new GraphicBuffer(width, height, android::PIXEL_FORMAT_R_8, 1,
+                                        GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                                GRALLOC_USAGE_HW_TEXTURE,
+                                        "r8");
+        if (buffer->initCheck() != 0) {
+            // Devices are not required to support R8.
+            return nullptr;
+        }
+        return std::make_shared<
+                renderengine::impl::ExternalTexture>(std::move(buffer), *mRE,
+                                                     renderengine::impl::ExternalTexture::Usage::
+                                                             READABLE);
+    }
+
     RenderEngineTest() {
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
@@ -2541,6 +2562,66 @@
 
     expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
 }
+
+TEST_P(RenderEngineTest, r8_behaves_as_mask) {
+    if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+        return;
+    }
+
+    initializeRenderEngine();
+
+    const auto r8Buffer = allocateR8Buffer(2, 1);
+    if (!r8Buffer) {
+        return;
+    }
+    {
+        uint8_t* pixels;
+        r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                    reinterpret_cast<void**>(&pixels));
+        // This will be drawn on top of a green buffer. We'll verify that 255
+        // results in keeping the original green and 0 results in black.
+        pixels[0] = 0;
+        pixels[1] = 255;
+        r8Buffer->getBuffer()->unlock();
+    }
+
+    const auto rect = Rect(0, 0, 2, 1);
+    const renderengine::DisplaySettings display{
+            .physicalDisplay = rect,
+            .clip = rect,
+            .outputDataspace = ui::Dataspace::SRGB,
+    };
+
+    const auto greenBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(0, 255, 0, 255));
+    const renderengine::LayerSettings greenLayer{
+            .geometry.boundaries = rect.toFloatRect(),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = greenBuffer,
+                                    },
+                    },
+            .alpha = 1.0f,
+    };
+    const renderengine::LayerSettings r8Layer{
+            .geometry.boundaries = rect.toFloatRect(),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = r8Buffer,
+                                    },
+                    },
+            .alpha = 1.0f,
+    };
+
+    std::vector<renderengine::LayerSettings> layers{greenLayer, r8Layer};
+    invokeDraw(display, layers);
+
+    expectBufferColor(Rect(0, 0, 1, 1), 0,   0, 0, 255);
+    expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255);
+}
 } // namespace renderengine
 } // namespace android
 
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 1f8a2f0..e026324 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -92,17 +92,6 @@
     outRect.height = rect.height();
     return outRect;
 }
-static inline void sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height,
-                                         PixelFormat format, uint32_t layerCount, uint64_t usage,
-                                         IMapper::BufferDescriptorInfo* outDescriptorInfo) {
-    outDescriptorInfo->name = name;
-    outDescriptorInfo->width = width;
-    outDescriptorInfo->height = height;
-    outDescriptorInfo->layerCount = layerCount;
-    outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format);
-    outDescriptorInfo->usage = usage;
-    outDescriptorInfo->reservedSize = 0;
-}
 
 // See if gralloc "4.1" is available.
 static bool hasIAllocatorAidl() {
@@ -119,6 +108,36 @@
     return sHasIAllocatorAidl;
 }
 
+// Determines whether the passed info is compatible with the mapper.
+static status_t validateBufferDescriptorInfo(IMapper::BufferDescriptorInfo* descriptorInfo) {
+    uint64_t validUsageBits = getValidUsageBits();
+    if (hasIAllocatorAidl()) {
+        validUsageBits |= getValidUsageBits41();
+    }
+
+    if (descriptorInfo->usage & ~validUsageBits) {
+        ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
+              descriptorInfo->usage & ~validUsageBits);
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+static inline status_t sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height,
+                                             PixelFormat format, uint32_t layerCount,
+                                             uint64_t usage,
+                                             IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+    outDescriptorInfo->name = name;
+    outDescriptorInfo->width = width;
+    outDescriptorInfo->height = height;
+    outDescriptorInfo->layerCount = layerCount;
+    outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format);
+    outDescriptorInfo->usage = usage;
+    outDescriptorInfo->reservedSize = 0;
+
+    return validateBufferDescriptorInfo(outDescriptorInfo);
+}
+
 } // anonymous namespace
 
 void Gralloc4Mapper::preload() {
@@ -140,21 +159,6 @@
     return mMapper != nullptr;
 }
 
-status_t Gralloc4Mapper::validateBufferDescriptorInfo(
-        IMapper::BufferDescriptorInfo* descriptorInfo) const {
-    uint64_t validUsageBits = getValidUsageBits();
-    if (hasIAllocatorAidl()) {
-        validUsageBits |= getValidUsageBits41();
-    }
-
-    if (descriptorInfo->usage & ~validUsageBits) {
-        ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
-              descriptorInfo->usage & ~validUsageBits);
-        return BAD_VALUE;
-    }
-    return NO_ERROR;
-}
-
 status_t Gralloc4Mapper::createDescriptor(void* bufferDescriptorInfo,
                                           void* outBufferDescriptor) const {
     IMapper::BufferDescriptorInfo* descriptorInfo =
@@ -207,8 +211,10 @@
                                             uint32_t layerCount, uint64_t usage,
                                             uint32_t stride) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
-    sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount, usage,
-                          &descriptorInfo);
+    if (auto error = sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount,
+                                           usage, &descriptorInfo) != OK) {
+        return error;
+    }
 
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
     auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
@@ -427,7 +433,7 @@
             if (fd >= 0) {
                 releaseFence = fd;
             } else {
-                ALOGD("failed to dup unlock release fence");
+                ALOGW("failed to dup unlock release fence");
                 sync_wait(fenceHandle->data[0], -1);
             }
         }
@@ -448,7 +454,12 @@
                                      uint32_t layerCount, uint64_t usage,
                                      bool* outSupported) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
-    sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage, &descriptorInfo);
+    if (auto error = sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage,
+                                           &descriptorInfo) != OK) {
+        // Usage isn't known to the HAL or otherwise failed validation.
+        *outSupported = false;
+        return OK;
+    }
 
     Error error;
     auto ret = mMapper->isSupported(descriptorInfo,
@@ -691,7 +702,10 @@
     }
 
     IMapper::BufferDescriptorInfo descriptorInfo;
-    sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage, &descriptorInfo);
+    if (auto error = sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage,
+                                           &descriptorInfo) != OK) {
+        return error;
+    }
 
     hidl_vec<uint8_t> vec;
     Error error;
@@ -1133,7 +1147,10 @@
                                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
                                      buffer_handle_t* outBufferHandles, bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
-    sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, &descriptorInfo);
+    if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage,
+                                           &descriptorInfo) != OK) {
+        return error;
+    }
 
     BufferDescriptor descriptor;
     status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 6bafcd6..fe38709 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -155,10 +155,6 @@
 private:
     friend class GraphicBufferAllocator;
 
-    // Determines whether the passed info is compatible with the mapper.
-    status_t validateBufferDescriptorInfo(
-            hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
-
     template <class T>
     using DecodeFunction = status_t (*)(const hardware::hidl_vec<uint8_t>& input, T* output);
 
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 3d85bef..9b72ff4 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -15,6 +15,9 @@
       "name": "inputflinger_tests"
     },
     {
+      "name": "libpalmrejection_test"
+    },
+    {
       "name": "InputTests"
     },
     {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 441a1de..7a3d6c5 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -215,7 +215,7 @@
         return false;
     }
     if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
-        ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.",
+        ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %zu.",
               pointerCount, MAX_POINTERS);
         return false;
     }
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 4bd1cd8..ff3a592 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -282,7 +282,7 @@
 
         if (outCount >= MAX_POINTERS) {
             if (DEBUG_POINTERS) {
-                ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
+                ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
                       "ignoring the rest.",
                       getDeviceName().c_str(), MAX_POINTERS);
             }
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index e686924..d19dbaf 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -57,7 +57,8 @@
         ],
     },
     static_libs: [
-        "libc++fs"
+        "libc++fs",
+        "libinput",
     ],
     require_root: true,
     test_suites: ["device-tests"],
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 0814bc2..aa2f832 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -558,7 +558,7 @@
     MotionEvent event;
     PointerProperties pointerProperties[MAX_POINTERS + 1];
     PointerCoords pointerCoords[MAX_POINTERS + 1];
-    for (int i = 0; i <= MAX_POINTERS; i++) {
+    for (size_t i = 0; i <= MAX_POINTERS; i++) {
         pointerProperties[i].clear();
         pointerProperties[i].id = i;
         pointerCoords[i].clear();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 0918510..8d26747 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -401,6 +401,19 @@
     return true;
 }
 
+namespace {
+bool isDisplayDecoration(const CachedSet& cachedSet) {
+    return cachedSet.getLayerCount() == 1 &&
+            cachedSet.getFirstLayer()
+                    .getState()
+                    ->getOutputLayer()
+                    ->getLayerFE()
+                    .getCompositionState()
+                    ->compositionType ==
+            aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+}
+} // namespace
+
 std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const {
     ATRACE_CALL();
     std::vector<Run> runs;
@@ -424,7 +437,7 @@
         }
 
         if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
-            !currentSet->hasUnsupportedDataspace()) {
+            !currentSet->hasUnsupportedDataspace() && !isDisplayDecoration(*currentSet)) {
             if (isPartOfRun) {
                 builder.increment();
             } else {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index bd3022b..6443c2b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -39,7 +39,7 @@
     HWComposer();
     ~HWComposer() override;
 
-    MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback*));
+    MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback&));
     MOCK_CONST_METHOD3(getDisplayIdentificationData,
                        bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
     MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 58dc244..656ef9a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -1337,5 +1337,56 @@
     EXPECT_NE(nullptr, overrideBuffer4);
 }
 
+TEST_F(FlattenerTest, flattenLayers_skips_DISPLAY_DECORATION) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    // The third layer uses DISPLAY_DECORATION, which should never be cached.
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+    mTestLayers[2]->layerFECompositionState.compositionType =
+            aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+    mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    mTime += 200ms;
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(
+                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    // This time we merge the CachedSet in, so we have a new hash, and we should
+    // only have two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 1448e56..b1057c3 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -30,6 +30,8 @@
 #include <algorithm>
 #include <cinttypes>
 
+#include "HWC2.h"
+
 namespace android {
 
 using hardware::hidl_handle;
@@ -169,40 +171,47 @@
 
 class AidlIComposerCallbackWrapper : public BnComposerCallback {
 public:
-    AidlIComposerCallbackWrapper(sp<V2_4::IComposerCallback> callback)
-          : mCallback(std::move(callback)) {}
+    AidlIComposerCallbackWrapper(HWC2::ComposerCallback& callback) : mCallback(callback) {}
 
     ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override {
         const auto connection = in_connected ? V2_4::IComposerCallback::Connection::CONNECTED
                                              : V2_4::IComposerCallback::Connection::DISCONNECTED;
-        mCallback->onHotplug(translate<Display>(in_display), connection);
+        mCallback.onComposerHalHotplug(translate<Display>(in_display), connection);
         return ::ndk::ScopedAStatus::ok();
     }
 
     ::ndk::ScopedAStatus onRefresh(int64_t in_display) override {
-        mCallback->onRefresh(translate<Display>(in_display));
+        mCallback.onComposerHalRefresh(translate<Display>(in_display));
         return ::ndk::ScopedAStatus::ok();
     }
+
     ::ndk::ScopedAStatus onSeamlessPossible(int64_t in_display) override {
-        mCallback->onSeamlessPossible(translate<Display>(in_display));
+        mCallback.onComposerHalSeamlessPossible(translate<Display>(in_display));
         return ::ndk::ScopedAStatus::ok();
     }
+
     ::ndk::ScopedAStatus onVsync(int64_t in_display, int64_t in_timestamp,
                                  int32_t in_vsyncPeriodNanos) override {
-        mCallback->onVsync_2_4(translate<Display>(in_display), in_timestamp,
-                               static_cast<uint32_t>(in_vsyncPeriodNanos));
+        mCallback.onComposerHalVsync(translate<Display>(in_display), in_timestamp,
+                                     static_cast<uint32_t>(in_vsyncPeriodNanos));
         return ::ndk::ScopedAStatus::ok();
     }
+
     ::ndk::ScopedAStatus onVsyncPeriodTimingChanged(
             int64_t in_display, const AidlVsyncPeriodChangeTimeline& in_updatedTimeline) override {
-        mCallback->onVsyncPeriodTimingChanged(translate<Display>(in_display),
-                                              translate<V2_4::VsyncPeriodChangeTimeline>(
-                                                      in_updatedTimeline));
+        mCallback.onComposerHalVsyncPeriodTimingChanged(translate<Display>(in_display),
+                                                        translate<V2_4::VsyncPeriodChangeTimeline>(
+                                                                in_updatedTimeline));
+        return ::ndk::ScopedAStatus::ok();
+    }
+
+    ::ndk::ScopedAStatus onVsyncIdle(int64_t in_display) override {
+        mCallback.onComposerHalVsyncIdle(translate<Display>(in_display));
         return ::ndk::ScopedAStatus::ok();
     }
 
 private:
-    sp<V2_4::IComposerCallback> mCallback;
+    HWC2::ComposerCallback& mCallback;
 };
 
 std::string AidlComposer::instance(const std::string& serviceName) {
@@ -262,10 +271,11 @@
     return info;
 }
 
-void AidlComposer::registerCallback(const sp<IComposerCallback>& callback) {
+void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) {
     if (mAidlComposerCallback) {
         ALOGE("Callback already registered");
     }
+
     mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback);
     AIBinder_setMinSchedulerPolicy(mAidlComposerCallback->asBinder().get(), SCHED_FIFO, 2);
 
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 6770017..374a436 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -63,7 +63,7 @@
     std::vector<IComposer::Capability> getCapabilities() override;
     std::string dumpDebugInfo() override;
 
-    void registerCallback(const sp<IComposerCallback>& callback) override;
+    void registerCallback(HWC2::ComposerCallback& callback) override;
 
     // Reset all pending commands in the command buffer. Useful if you want to
     // skip a frame but have already queued some commands.
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 22f424f..fe55e6b 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -34,11 +34,17 @@
 #include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
-namespace android::Hwc2 {
+namespace android {
+namespace HWC2 {
+struct ComposerCallback;
+} // namespace HWC2
+
+namespace Hwc2 {
 
 namespace types = hardware::graphics::common;
 
@@ -46,6 +52,7 @@
 namespace V2_2 = hardware::graphics::composer::V2_2;
 namespace V2_3 = hardware::graphics::composer::V2_3;
 namespace V2_4 = hardware::graphics::composer::V2_4;
+namespace V3_0 = ::aidl::android::hardware::graphics::composer3;
 
 using types::V1_0::ColorTransform;
 using types::V1_0::Transform;
@@ -89,7 +96,7 @@
     virtual std::vector<IComposer::Capability> getCapabilities() = 0;
     virtual std::string dumpDebugInfo() = 0;
 
-    virtual void registerCallback(const sp<IComposerCallback>& callback) = 0;
+    virtual void registerCallback(HWC2::ComposerCallback& callback) = 0;
 
     // Reset all pending commands in the command buffer. Useful if you want to
     // skip a frame but have already queued some commands.
@@ -109,9 +116,8 @@
     virtual Error destroyLayer(Display display, Layer layer) = 0;
 
     virtual Error getActiveConfig(Display display, Config* outConfig) = 0;
-    virtual Error getChangedCompositionTypes(
-            Display display, std::vector<Layer>* outLayers,
-            std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) = 0;
+    virtual Error getChangedCompositionTypes(Display display, std::vector<Layer>* outLayers,
+                                             std::vector<V3_0::Composition>* outTypes) = 0;
     virtual Error getColorModes(Display display, std::vector<ColorMode>* outModes) = 0;
     virtual Error getDisplayAttribute(Display display, Config config,
                                       IComposerClient::Attribute attribute, int32_t* outValue) = 0;
@@ -225,10 +231,8 @@
                                        const DisplayBrightnessOptions& options) = 0;
 
     // Composer HAL 2.4
-    virtual Error getDisplayCapabilities(
-            Display display,
-            std::vector<aidl::android::hardware::graphics::composer3::DisplayCapability>*
-                    outCapabilities) = 0;
+    virtual Error getDisplayCapabilities(Display display,
+                                         std::vector<V3_0::DisplayCapability>* outCapabilities) = 0;
     virtual V2_4::Error getDisplayConnectionType(
             Display display, IComposerClient::DisplayConnectionType* outType) = 0;
     virtual V2_4::Error getDisplayVsyncPeriod(Display display,
@@ -263,4 +267,5 @@
     virtual Error getPreferredBootDisplayConfig(Display displayId, Config*) = 0;
 };
 
-} // namespace android::Hwc2
+} // namespace Hwc2
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 57eb128..0a605a8 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -72,6 +72,7 @@
     virtual void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
                                                        const hal::VsyncPeriodChangeTimeline&) = 0;
     virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0;
+    virtual void onComposerHalVsyncIdle(hal::HWDisplayId) = 0;
 
 protected:
     ~ComposerCallback() = default;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 44e4597..9174ec7 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -74,63 +74,6 @@
 namespace hal = android::hardware::graphics::composer::hal;
 
 namespace android {
-namespace {
-
-using android::hardware::Return;
-using android::hardware::Void;
-using android::HWC2::ComposerCallback;
-
-class ComposerCallbackBridge : public hal::IComposerCallback {
-public:
-    ComposerCallbackBridge(ComposerCallback* callback, bool vsyncSwitchingSupported)
-          : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
-
-    Return<void> onHotplug(hal::HWDisplayId display, hal::Connection connection) override {
-        mCallback->onComposerHalHotplug(display, connection);
-        return Void();
-    }
-
-    Return<void> onRefresh(hal::HWDisplayId display) override {
-        mCallback->onComposerHalRefresh(display);
-        return Void();
-    }
-
-    Return<void> onVsync(hal::HWDisplayId display, int64_t timestamp) override {
-        if (!mVsyncSwitchingSupported) {
-            mCallback->onComposerHalVsync(display, timestamp, std::nullopt);
-        } else {
-            ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
-        }
-        return Void();
-    }
-
-    Return<void> onVsync_2_4(hal::HWDisplayId display, int64_t timestamp,
-                             hal::VsyncPeriodNanos vsyncPeriodNanos) override {
-        if (mVsyncSwitchingSupported) {
-            mCallback->onComposerHalVsync(display, timestamp, vsyncPeriodNanos);
-        } else {
-            ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
-        }
-        return Void();
-    }
-
-    Return<void> onVsyncPeriodTimingChanged(
-            hal::HWDisplayId display, const hal::VsyncPeriodChangeTimeline& timeline) override {
-        mCallback->onComposerHalVsyncPeriodTimingChanged(display, timeline);
-        return Void();
-    }
-
-    Return<void> onSeamlessPossible(hal::HWDisplayId display) override {
-        mCallback->onComposerHalSeamlessPossible(display);
-        return Void();
-    }
-
-private:
-    ComposerCallback* const mCallback;
-    const bool mVsyncSwitchingSupported;
-};
-
-} // namespace
 
 HWComposer::~HWComposer() = default;
 
@@ -149,7 +92,7 @@
     mDisplayData.clear();
 }
 
-void HWComposer::setCallback(HWC2::ComposerCallback* callback) {
+void HWComposer::setCallback(HWC2::ComposerCallback& callback) {
     loadCapabilities();
     loadLayerMetadataSupport();
 
@@ -159,10 +102,7 @@
     }
     mRegisteredCallback = true;
 
-    const bool vsyncSwitchingSupported =
-            mComposer->isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
-    mComposer->registerCallback(
-            sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported));
+    mComposer->registerCallback(callback);
 }
 
 bool HWComposer::getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 93773fa..3e68028 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -105,7 +105,7 @@
 
     virtual ~HWComposer();
 
-    virtual void setCallback(HWC2::ComposerCallback*) = 0;
+    virtual void setCallback(HWC2::ComposerCallback&) = 0;
 
     virtual bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
                                               DisplayIdentificationData* outData) const = 0;
@@ -272,7 +272,7 @@
 
     ~HWComposer() override;
 
-    void setCallback(HWC2::ComposerCallback*) override;
+    void setCallback(HWC2::ComposerCallback&) override;
 
     bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort,
                                       DisplayIdentificationData* outData) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index d3acecb..746ac64 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -30,6 +30,7 @@
 #include <hidl/HidlTransportUtils.h>
 #include <log/log.h>
 #include <utils/Trace.h>
+#include "HWC2.h"
 #include "Hal.h"
 
 #include <algorithm>
@@ -44,6 +45,63 @@
 using hardware::Return;
 
 namespace Hwc2 {
+namespace {
+
+using android::hardware::Return;
+using android::hardware::Void;
+using android::HWC2::ComposerCallback;
+
+class ComposerCallbackBridge : public IComposerCallback {
+public:
+    ComposerCallbackBridge(ComposerCallback& callback, bool vsyncSwitchingSupported)
+          : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
+
+    Return<void> onHotplug(Display display, Connection connection) override {
+        mCallback.onComposerHalHotplug(display, connection);
+        return Void();
+    }
+
+    Return<void> onRefresh(Display display) override {
+        mCallback.onComposerHalRefresh(display);
+        return Void();
+    }
+
+    Return<void> onVsync(Display display, int64_t timestamp) override {
+        if (!mVsyncSwitchingSupported) {
+            mCallback.onComposerHalVsync(display, timestamp, std::nullopt);
+        } else {
+            ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
+        }
+        return Void();
+    }
+
+    Return<void> onVsync_2_4(Display display, int64_t timestamp,
+                             VsyncPeriodNanos vsyncPeriodNanos) override {
+        if (mVsyncSwitchingSupported) {
+            mCallback.onComposerHalVsync(display, timestamp, vsyncPeriodNanos);
+        } else {
+            ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
+        }
+        return Void();
+    }
+
+    Return<void> onVsyncPeriodTimingChanged(Display display,
+                                            const VsyncPeriodChangeTimeline& timeline) override {
+        mCallback.onComposerHalVsyncPeriodTimingChanged(display, timeline);
+        return Void();
+    }
+
+    Return<void> onSeamlessPossible(Display display) override {
+        mCallback.onComposerHalSeamlessPossible(display);
+        return Void();
+    }
+
+private:
+    ComposerCallback& mCallback;
+    const bool mVsyncSwitchingSupported;
+};
+
+} // namespace
 
 HidlComposer::~HidlComposer() = default;
 
@@ -1246,6 +1304,13 @@
     return Error::NONE;
 }
 
+void HidlComposer::registerCallback(ComposerCallback& callback) {
+    const bool vsyncSwitchingSupported =
+            isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
+
+    registerCallback(sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported));
+}
+
 CommandReader::~CommandReader() {
     resetData();
 }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index c8c7800..1ffca6e 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -172,7 +172,7 @@
     std::vector<IComposer::Capability> getCapabilities() override;
     std::string dumpDebugInfo() override;
 
-    void registerCallback(const sp<IComposerCallback>& callback) override;
+    void registerCallback(HWC2::ComposerCallback& callback) override;
 
     // Reset all pending commands in the command buffer. Useful if you want to
     // skip a frame but have already queued some commands.
@@ -334,6 +334,8 @@
         ~CommandWriter() override {}
     };
 
+    void registerCallback(const sp<IComposerCallback>& callback);
+
     // Many public functions above simply write a command into the command
     // queue to batch the calls.  validateDisplay and presentDisplay will call
     // this function to execute the command queue.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 3becb5c..3b9cfa6 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -38,6 +38,8 @@
 namespace android::scheduler {
 namespace {
 
+constexpr RefreshRateConfigs::GlobalSignals kNoSignals;
+
 std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
     return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(),
                               ftl::enum_string(layer.vote).c_str(), weight,
@@ -235,63 +237,26 @@
     float score;
 };
 
-RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                                   GlobalSignals globalSignals,
-                                                   GlobalSignals* outSignalsConsidered) const {
+auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+                                            GlobalSignals signals) const
+        -> std::pair<RefreshRate, GlobalSignals> {
     std::lock_guard lock(mLock);
 
-    if (auto cached = getCachedBestRefreshRate(layers, globalSignals, outSignalsConsidered)) {
-        return *cached;
+    if (mGetBestRefreshRateCache &&
+        mGetBestRefreshRateCache->arguments == std::make_pair(layers, signals)) {
+        return mGetBestRefreshRateCache->result;
     }
 
-    GlobalSignals signalsConsidered;
-    RefreshRate result = getBestRefreshRateLocked(layers, globalSignals, &signalsConsidered);
-    lastBestRefreshRateInvocation.emplace(
-            GetBestRefreshRateInvocation{.layerRequirements = layers,
-                                         .globalSignals = globalSignals,
-                                         .outSignalsConsidered = signalsConsidered,
-                                         .resultingBestRefreshRate = result});
-    if (outSignalsConsidered) {
-        *outSignalsConsidered = signalsConsidered;
-    }
+    const auto result = getBestRefreshRateLocked(layers, signals);
+    mGetBestRefreshRateCache = GetBestRefreshRateCache{{layers, signals}, result};
     return result;
 }
 
-std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
-        const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
-        GlobalSignals* outSignalsConsidered) const {
-    const bool sameAsLastCall = lastBestRefreshRateInvocation &&
-            lastBestRefreshRateInvocation->layerRequirements == layers &&
-            lastBestRefreshRateInvocation->globalSignals == globalSignals;
-
-    if (sameAsLastCall) {
-        if (outSignalsConsidered) {
-            *outSignalsConsidered = lastBestRefreshRateInvocation->outSignalsConsidered;
-        }
-        return lastBestRefreshRateInvocation->resultingBestRefreshRate;
-    }
-
-    return {};
-}
-
-RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
-        const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
-        GlobalSignals* outSignalsConsidered) const {
+auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
+                                                  GlobalSignals signals) const
+        -> std::pair<RefreshRate, GlobalSignals> {
     ATRACE_CALL();
-    ALOGV("getBestRefreshRate %zu layers", layers.size());
-
-    if (outSignalsConsidered) *outSignalsConsidered = {};
-    const auto setTouchConsidered = [&] {
-        if (outSignalsConsidered) {
-            outSignalsConsidered->touch = true;
-        }
-    };
-
-    const auto setIdleConsidered = [&] {
-        if (outSignalsConsidered) {
-            outSignalsConsidered->idle = true;
-        }
-    };
+    ALOGV("%s: %zu layers", __func__, layers.size());
 
     int noVoteLayers = 0;
     int minVoteLayers = 0;
@@ -301,6 +266,7 @@
     int explicitExact = 0;
     float maxExplicitWeight = 0;
     int seamedFocusedLayers = 0;
+
     for (const auto& layer : layers) {
         switch (layer.vote) {
             case LayerVoteType::NoVote:
@@ -349,10 +315,9 @@
 
     // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
     // selected a refresh rate to see if we should apply touch boost.
-    if (globalSignals.touch && !hasExplicitVoteLayers) {
+    if (signals.touch && !hasExplicitVoteLayers) {
         ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
-        setTouchConsidered();
-        return getMaxRefreshRateByPolicyLocked(anchorGroup);
+        return {getMaxRefreshRateByPolicyLocked(anchorGroup), GlobalSignals{.touch = true}};
     }
 
     // If the primary range consists of a single refresh rate then we can only
@@ -361,23 +326,21 @@
     const bool primaryRangeIsSingleRate =
             isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
 
-    if (!globalSignals.touch && globalSignals.idle &&
-        !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+    if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
         ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
-        setIdleConsidered();
-        return getMinRefreshRateByPolicyLocked();
+        return {getMinRefreshRateByPolicyLocked(), GlobalSignals{.idle = true}};
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
         const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
         ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str());
-        return refreshRate;
+        return {refreshRate, kNoSignals};
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
         ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
-        return getMinRefreshRateByPolicyLocked();
+        return {getMinRefreshRateByPolicyLocked(), kNoSignals};
     }
 
     // Find the best refresh rate based on score
@@ -466,9 +429,9 @@
                         [](RefreshRateScore score) { return score.score == 0; })) {
             const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
             ALOGV("layers not scored - choose %s", refreshRate.getName().c_str());
-            return refreshRate;
+            return {refreshRate, kNoSignals};
         } else {
-            return *bestRefreshRate;
+            return {*bestRefreshRate, kNoSignals};
         }
     }
 
@@ -490,14 +453,13 @@
 
     using fps_approx_ops::operator<;
 
-    if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
+    if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
         bestRefreshRate->getFps() < touchRefreshRate.getFps()) {
-        setTouchConsidered();
         ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
-        return touchRefreshRate;
+        return {touchRefreshRate, GlobalSignals{.touch = true}};
     }
 
-    return *bestRefreshRate;
+    return {*bestRefreshRate, kNoSignals};
 }
 
 std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
@@ -699,7 +661,7 @@
 
     // Invalidate the cached invocation to getBestRefreshRate. This forces
     // the refresh rate to be recomputed on the next call to getBestRefreshRate.
-    lastBestRefreshRateInvocation.reset();
+    mGetBestRefreshRateCache.reset();
 
     mCurrentRefreshRate = mRefreshRates.at(modeId).get();
 }
@@ -741,7 +703,7 @@
 
     // Invalidate the cached invocation to getBestRefreshRate. This forces
     // the refresh rate to be recomputed on the next call to getBestRefreshRate.
-    lastBestRefreshRateInvocation.reset();
+    mGetBestRefreshRateCache.reset();
 
     mRefreshRates.clear();
     for (const auto& mode : modes) {
@@ -800,7 +762,7 @@
         ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
         return BAD_VALUE;
     }
-    lastBestRefreshRateInvocation.reset();
+    mGetBestRefreshRateCache.reset();
     Policy previousPolicy = *getCurrentPolicyLocked();
     mDisplayManagerPolicy = policy;
     if (*getCurrentPolicyLocked() == previousPolicy) {
@@ -815,7 +777,7 @@
     if (policy && !isPolicyValidLocked(*policy)) {
         return BAD_VALUE;
     }
-    lastBestRefreshRateInvocation.reset();
+    mGetBestRefreshRateCache.reset();
     Policy previousPolicy = *getCurrentPolicyLocked();
     mOverridePolicy = policy;
     if (*getCurrentPolicyLocked() == previousPolicy) {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 849d297..ade1787 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -20,6 +20,7 @@
 #include <numeric>
 #include <optional>
 #include <type_traits>
+#include <utility>
 
 #include <android-base/stringprintf.h>
 #include <gui/DisplayEventReceiver.h>
@@ -249,11 +250,10 @@
         }
     };
 
-    // Returns the refresh rate that best fits the given layers. outSignalsConsidered returns
-    // whether the refresh rate was chosen based on touch boost and/or idle timer.
-    RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>&, GlobalSignals,
-                                   GlobalSignals* outSignalsConsidered = nullptr) const
-            EXCLUDES(mLock);
+    // Returns the refresh rate that best fits the given layers, and whether the refresh rate was
+    // chosen based on touch boost and/or idle timer.
+    std::pair<RefreshRate, GlobalSignals> getBestRefreshRate(const std::vector<LayerRequirement>&,
+                                                             GlobalSignals) const EXCLUDES(mLock);
 
     FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
         std::lock_guard lock(mLock);
@@ -310,6 +310,9 @@
                                         .idleTimerTimeoutMs = 0,
                                         .supportKernelIdleTimer = false});
 
+    RefreshRateConfigs(const RefreshRateConfigs&) = delete;
+    RefreshRateConfigs& operator=(const RefreshRateConfigs&) = delete;
+
     // Returns whether switching modes (refresh rate or resolution) is possible.
     // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
     // differ in resolution.
@@ -391,11 +394,8 @@
 
     void dump(std::string& result) const EXCLUDES(mLock);
 
-    RefreshRateConfigs(const RefreshRateConfigs&) = delete;
-    void operator=(const RefreshRateConfigs&) = delete;
-
 private:
-    friend class RefreshRateConfigsTest;
+    friend struct TestableRefreshRateConfigs;
 
     void constructAvailableRefreshRates() REQUIRES(mLock);
 
@@ -403,13 +403,8 @@
             const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
             std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
 
-    std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>&,
-                                                        GlobalSignals,
-                                                        GlobalSignals* outSignalsConsidered) const
-            REQUIRES(mLock);
-
-    RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>&, GlobalSignals,
-                                         GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);
+    std::pair<RefreshRate, GlobalSignals> getBestRefreshRateLocked(
+            const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock);
 
     // Returns the refresh rate with the highest score in the collection specified from begin
     // to end. If there are more than one with the same highest refresh rate, the first one is
@@ -497,14 +492,11 @@
     const Config mConfig;
     bool mSupportsFrameRateOverrideByContent;
 
-    struct GetBestRefreshRateInvocation {
-        std::vector<LayerRequirement> layerRequirements;
-        GlobalSignals globalSignals;
-        GlobalSignals outSignalsConsidered;
-        RefreshRate resultingBestRefreshRate;
+    struct GetBestRefreshRateCache {
+        std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
+        std::pair<RefreshRate, GlobalSignals> result;
     };
-    mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
-            GUARDED_BY(mLock);
+    mutable std::optional<GetBestRefreshRateCache> mGetBestRefreshRateCache GUARDED_BY(mLock);
 
     // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
     std::mutex mIdleTimerCallbacksMutex;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index a85e748..665d36982 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -537,18 +537,19 @@
 
     ATRACE_CALL();
 
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
-    scheduler::LayerHistory::Summary summary =
-            mLayerHistory.summarize(*refreshRateConfigs, systemTime());
-    scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     DisplayModePtr newMode;
+    GlobalSignals consideredSignals;
+
     bool frameRateChanged;
     bool frameRateOverridesChanged;
+
+    const auto refreshRateConfigs = holdRefreshRateConfigs();
+    LayerHistory::Summary summary = mLayerHistory.summarize(*refreshRateConfigs, systemTime());
     {
         std::lock_guard<std::mutex> lock(mPolicyLock);
-        mPolicy.contentRequirements = summary;
+        mPolicy.contentRequirements = std::move(summary);
 
-        newMode = calculateRefreshRateModeId(&consideredSignals);
+        std::tie(newMode, consideredSignals) = chooseDisplayMode();
         frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
 
         if (mPolicy.mode == newMode) {
@@ -678,8 +679,7 @@
     mVsyncSchedule->dump(out);
 }
 
-bool Scheduler::updateFrameRateOverrides(
-        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
+bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
     const auto refreshRateConfigs = holdRefreshRateConfigs();
     if (!refreshRateConfigs->supportsFrameRateOverrideByContent()) {
         return false;
@@ -697,9 +697,11 @@
 template <class T>
 bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
     DisplayModePtr newMode;
+    GlobalSignals consideredSignals;
+
     bool refreshRateChanged = false;
     bool frameRateOverridesChanged;
-    scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
+
     const auto refreshRateConfigs = holdRefreshRateConfigs();
     {
         std::lock_guard<std::mutex> lock(mPolicyLock);
@@ -707,7 +709,7 @@
             return false;
         }
         *currentState = newState;
-        newMode = calculateRefreshRateModeId(&consideredSignals);
+        std::tie(newMode, consideredSignals) = chooseDisplayMode();
         frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
         if (mPolicy.mode == newMode) {
             // We don't need to change the display mode, but we might need to send an event
@@ -733,33 +735,33 @@
     return consideredSignals.touch;
 }
 
-DisplayModePtr Scheduler::calculateRefreshRateModeId(
-        scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
+auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> {
     ATRACE_CALL();
-    if (consideredSignals) *consideredSignals = {};
 
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
+    const auto configs = holdRefreshRateConfigs();
+
     // If Display Power is not in normal operation we want to be in performance mode. When coming
     // back to normal mode, a grace period is given with DisplayPowerTimer.
     if (mDisplayPowerTimer &&
         (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
-        return refreshRateConfigs->getMaxRefreshRateByPolicy().getMode();
+        constexpr GlobalSignals kNoSignals;
+        return {configs->getMaxRefreshRateByPolicy().getMode(), kNoSignals};
     }
 
-    const bool touchActive = mTouchTimer && mPolicy.touch == TouchState::Active;
-    const bool idle = mPolicy.idleTimer == TimerState::Expired;
+    const GlobalSignals signals{.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
+                                .idle = mPolicy.idleTimer == TimerState::Expired};
 
-    return refreshRateConfigs
-            ->getBestRefreshRate(mPolicy.contentRequirements, {.touch = touchActive, .idle = idle},
-                                 consideredSignals)
-            .getMode();
+    const auto [refreshRate, consideredSignals] =
+            configs->getBestRefreshRate(mPolicy.contentRequirements, signals);
+
+    return {refreshRate.getMode(), consideredSignals};
 }
 
 DisplayModePtr Scheduler::getPreferredDisplayMode() {
     std::lock_guard<std::mutex> lock(mPolicyLock);
-    // Make sure that the default mode ID is first updated, before returned.
+    // Make sure the stored mode is up to date.
     if (mPolicy.mode) {
-        mPolicy.mode = calculateRefreshRateModeId();
+        mPolicy.mode = chooseDisplayMode().first;
     }
     return mPolicy.mode;
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index bc9024a..468c4cc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -269,15 +269,14 @@
 
     void setVsyncPeriod(nsecs_t period);
 
-    // This function checks whether individual features that are affecting the refresh rate
-    // selection were initialized, prioritizes them, and calculates the DisplayModeId
-    // for the suggested refresh rate.
-    DisplayModePtr calculateRefreshRateModeId(
-            RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mPolicyLock);
+    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+    // Returns the display mode that fulfills the policy, and the signals that were considered.
+    std::pair<DisplayModePtr, GlobalSignals> chooseDisplayMode() REQUIRES(mPolicyLock);
+
+    bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
 
     void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
-    bool updateFrameRateOverrides(RefreshRateConfigs::GlobalSignals, Fps displayRefreshRate)
-            REQUIRES(mPolicyLock);
 
     impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
             EXCLUDES(mRefreshRateConfigsLock);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 74f733e..73a5f58 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -849,7 +849,7 @@
 
     mCompositionEngine->setTimeStats(mTimeStats);
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
-    mCompositionEngine->getHwComposer().setCallback(this);
+    mCompositionEngine->getHwComposer().setCallback(*this);
     ClientCache::getInstance().setRenderEngine(&getRenderEngine());
 
     if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) {
@@ -1962,6 +1962,11 @@
     scheduleComposite(FrameHint::kNone);
 }
 
+void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) {
+    // TODO(b/198106220): force enable HWVsync to avoid drift problem during
+    // idle.
+}
+
 void SurfaceFlinger::setVsyncEnabled(bool enabled) {
     ATRACE_CALL();
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 97a32d4..0c990d8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -631,6 +631,7 @@
     void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
                                                const hal::VsyncPeriodChangeTimeline&) override;
     void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
+    void onComposerHalVsyncIdle(hal::HWDisplayId) override;
 
     // ICompositor overrides:
 
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
new file mode 100644
index 0000000..7eebd9b
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 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.
+ *
+ */
+
+cc_defaults {
+    name: "surfaceflinger_fuzz_defaults",
+    include_dirs: [
+        "frameworks/native/services/surfaceflinger/tests/unittests",
+    ],
+    static_libs: [
+        "android.hardware.graphics.composer@2.1-resources",
+        "libgmock",
+        "libgui_mocks",
+        "libgmock_ndk",
+        "libgmock_main",
+        "libgtest_ndk_c++",
+        "libgmock_main_ndk",
+        "librenderengine_mocks",
+        "perfetto_trace_protos",
+        "libcompositionengine_mocks",
+        "perfetto_trace_protos",
+    ],
+    shared_libs: [
+        "libprotoutil",
+        "libstatssocket",
+        "libstatspull",
+        "libtimestats",
+        "libtimestats_proto",
+        "libprotobuf-cpp-full",
+        "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@3.0",
+        "android.hardware.graphics.mapper@4.0",
+    ],
+    srcs: [
+        ":libsurfaceflinger_sources",
+        ":libsurfaceflinger_mock_sources",
+    ],
+    defaults: [
+        "libsurfaceflinger_defaults",
+    ],
+    header_libs: [
+        "libui_fuzzableDataspaces_headers",
+        "libsurfaceflinger_headers",
+        "libui_headers",
+    ],
+    cflags: [
+        "-Wno-unused-result",
+        "-Wno-conversion",
+        "-Wno-sign-compare",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_fuzz {
+    name: "surfaceflinger_fuzzer",
+    defaults: [
+        "surfaceflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "surfaceflinger_fuzzer.cpp",
+    ],
+}
diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md
new file mode 100644
index 0000000..7b244fc
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/README.md
@@ -0,0 +1,24 @@
+# Fuzzers for SurfaceFlinger
+## Table of contents
++ [SurfaceFlinger](#SurfaceFlinger)
+
+# <a name="SurfaceFlinger"></a> Fuzzer for SurfaceFlinger
+
+SurfaceFlinger supports the following data sources:
+1. Pixel Formats (parameter name: `defaultCompositionPixelFormat`)
+2. Data Spaces (parameter name: `defaultCompositionDataspace`)
+3. Rotations (parameter name: `internalDisplayOrientation`)
+3. Surface composer tags (parameter name: `onTransact`)
+
+You can find the possible values in the fuzzer's source code.
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) surfaceflinger_fuzzer
+```
+2. To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/surfaceflinger_fuzzer/surfaceflinger_fuzzer
+```
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
new file mode 100644
index 0000000..4f89cd9
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2021 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.
+ *
+ */
+
+#include <FuzzableDataspaces.h>
+#include <binder/IServiceManager.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <ui/DisplayStatInfo.h>
+#include "surfaceflinger_fuzzers_utils.h"
+
+namespace android::fuzz {
+
+static constexpr LatchUnsignaledConfig kLatchUnsignaledConfig[] = {
+        LatchUnsignaledConfig::Always,
+        LatchUnsignaledConfig::Auto,
+        LatchUnsignaledConfig::Disabled,
+};
+
+static constexpr ui::PixelFormat kPixelFormats[] = {ui::PixelFormat::RGBA_8888,
+                                                    ui::PixelFormat::RGBX_8888,
+                                                    ui::PixelFormat::RGB_888,
+                                                    ui::PixelFormat::RGB_565,
+                                                    ui::PixelFormat::BGRA_8888,
+                                                    ui::PixelFormat::YCBCR_422_SP,
+                                                    ui::PixelFormat::YCRCB_420_SP,
+                                                    ui::PixelFormat::YCBCR_422_I,
+                                                    ui::PixelFormat::RGBA_FP16,
+                                                    ui::PixelFormat::RAW16,
+                                                    ui::PixelFormat::BLOB,
+                                                    ui::PixelFormat::IMPLEMENTATION_DEFINED,
+                                                    ui::PixelFormat::YCBCR_420_888,
+                                                    ui::PixelFormat::RAW_OPAQUE,
+                                                    ui::PixelFormat::RAW10,
+                                                    ui::PixelFormat::RAW12,
+                                                    ui::PixelFormat::RGBA_1010102,
+                                                    ui::PixelFormat::Y8,
+                                                    ui::PixelFormat::Y16,
+                                                    ui::PixelFormat::YV12,
+                                                    ui::PixelFormat::DEPTH_16,
+                                                    ui::PixelFormat::DEPTH_24,
+                                                    ui::PixelFormat::DEPTH_24_STENCIL_8,
+                                                    ui::PixelFormat::DEPTH_32F,
+                                                    ui::PixelFormat::DEPTH_32F_STENCIL_8,
+                                                    ui::PixelFormat::STENCIL_8,
+                                                    ui::PixelFormat::YCBCR_P010,
+                                                    ui::PixelFormat::HSV_888};
+
+static constexpr ui::Rotation kRotations[] = {ui::Rotation::Rotation0, ui::Rotation::Rotation90,
+                                              ui::Rotation::Rotation180, ui::Rotation::Rotation270};
+
+static constexpr BnSurfaceComposer::ISurfaceComposerTag kSurfaceComposerTags[]{
+        BnSurfaceComposer::BOOT_FINISHED,
+        BnSurfaceComposer::CREATE_CONNECTION,
+        BnSurfaceComposer::GET_STATIC_DISPLAY_INFO,
+        BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
+        BnSurfaceComposer::CREATE_DISPLAY,
+        BnSurfaceComposer::DESTROY_DISPLAY,
+        BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN,
+        BnSurfaceComposer::SET_TRANSACTION_STATE,
+        BnSurfaceComposer::AUTHENTICATE_SURFACE,
+        BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS,
+        BnSurfaceComposer::GET_DISPLAY_MODES,
+        BnSurfaceComposer::GET_ACTIVE_DISPLAY_MODE,
+        BnSurfaceComposer::GET_DISPLAY_STATE,
+        BnSurfaceComposer::CAPTURE_DISPLAY,
+        BnSurfaceComposer::CAPTURE_LAYERS,
+        BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS,
+        BnSurfaceComposer::GET_ANIMATION_FRAME_STATS,
+        BnSurfaceComposer::SET_POWER_MODE,
+        BnSurfaceComposer::GET_DISPLAY_STATS,
+        BnSurfaceComposer::GET_HDR_CAPABILITIES,
+        BnSurfaceComposer::GET_DISPLAY_COLOR_MODES,
+        BnSurfaceComposer::GET_ACTIVE_COLOR_MODE,
+        BnSurfaceComposer::SET_ACTIVE_COLOR_MODE,
+        BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS,
+        BnSurfaceComposer::INJECT_VSYNC,
+        BnSurfaceComposer::GET_LAYER_DEBUG_INFO,
+        BnSurfaceComposer::GET_COMPOSITION_PREFERENCE,
+        BnSurfaceComposer::GET_COLOR_MANAGEMENT,
+        BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
+        BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED,
+        BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE,
+        BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT,
+        BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY,
+        BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES,
+        BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS,
+        BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER,
+        BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER,
+        BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS,
+        BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS,
+        BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT,
+        BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS,
+        BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID,
+        BnSurfaceComposer::NOTIFY_POWER_BOOST,
+        BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS,
+        BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+        BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE,
+        BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT,
+        BnSurfaceComposer::SET_GAME_CONTENT_TYPE,
+        BnSurfaceComposer::SET_FRAME_RATE,
+        BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
+        BnSurfaceComposer::SET_FRAME_TIMELINE_INFO,
+        BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER,
+        BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY,
+        BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT,
+        BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO,
+        BnSurfaceComposer::ADD_FPS_LISTENER,
+        BnSurfaceComposer::REMOVE_FPS_LISTENER,
+        BnSurfaceComposer::OVERRIDE_HDR_TYPES,
+        BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER,
+        BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER,
+        BnSurfaceComposer::ON_PULL_ATOM,
+        BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER,
+        BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER,
+        BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER,
+        BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER,
+};
+
+static constexpr uint32_t kMinCode = 1000;
+static constexpr uint32_t kMaxCode = 1050;
+
+class SurfaceFlingerFuzzer {
+public:
+    SurfaceFlingerFuzzer(const uint8_t *data, size_t size) : mFdp(data, size) {
+        mFlinger = mTestableFlinger.flinger();
+    };
+    void process(const uint8_t *data, size_t size);
+
+private:
+    void setUp();
+    void invokeFlinger();
+    void setTransactionState();
+    void setInternalDisplayPrimaries();
+    void setDisplayStateLocked();
+    void onTransact(const uint8_t *data, size_t size);
+
+    FuzzedDataProvider mFdp;
+    TestableSurfaceFlinger mTestableFlinger;
+    sp<SurfaceFlinger> mFlinger = nullptr;
+};
+
+void SurfaceFlingerFuzzer::invokeFlinger() {
+    mFlinger->setSchedFifo(mFdp.ConsumeBool());
+    mFlinger->setSchedAttr(mFdp.ConsumeBool());
+    mFlinger->getServiceName();
+    mFlinger->hasSyncFramework = mFdp.ConsumeBool();
+    mFlinger->dispSyncPresentTimeOffset = mFdp.ConsumeIntegral<int64_t>();
+    mFlinger->useHwcForRgbToYuv = mFdp.ConsumeBool();
+    mFlinger->maxFrameBufferAcquiredBuffers = mFdp.ConsumeIntegral<int64_t>();
+    mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>();
+    mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>();
+    mFlinger->hasWideColorDisplay = mFdp.ConsumeBool();
+    mFlinger->internalDisplayOrientation = mFdp.PickValueInArray(kRotations);
+    mFlinger->useContextPriority = mFdp.ConsumeBool();
+
+    mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces);
+    mFlinger->defaultCompositionPixelFormat = mFdp.PickValueInArray(kPixelFormats);
+    mFlinger->wideColorGamutCompositionDataspace = mFdp.PickValueInArray(kDataspaces);
+    mFlinger->wideColorGamutCompositionPixelFormat = mFdp.PickValueInArray(kPixelFormats);
+
+    mFlinger->enableLatchUnsignaledConfig = mFdp.PickValueInArray(kLatchUnsignaledConfig);
+
+    mFlinger->scheduleComposite(mFdp.ConsumeBool()
+                                        ? scheduler::ISchedulerCallback::FrameHint::kActive
+                                        : scheduler::ISchedulerCallback::FrameHint::kNone);
+
+    mFlinger->scheduleRepaint();
+    mFlinger->scheduleSample();
+
+    uint32_t texture = mFlinger->getNewTexture();
+    mFlinger->deleteTextureAsync(texture);
+
+    sp<IBinder> handle = defaultServiceManager()->checkService(
+            String16(mFdp.ConsumeRandomLengthString().c_str()));
+    mFlinger->fromHandle(handle);
+    mFlinger->windowInfosReported();
+    mFlinger->disableExpensiveRendering();
+}
+
+void SurfaceFlingerFuzzer::setInternalDisplayPrimaries() {
+    ui::DisplayPrimaries primaries;
+    primaries.red.X = mFdp.ConsumeFloatingPoint<float>();
+    primaries.red.Y = mFdp.ConsumeFloatingPoint<float>();
+    primaries.red.Z = mFdp.ConsumeFloatingPoint<float>();
+    primaries.green.X = mFdp.ConsumeFloatingPoint<float>();
+    primaries.green.Y = mFdp.ConsumeFloatingPoint<float>();
+    primaries.green.Z = mFdp.ConsumeFloatingPoint<float>();
+    primaries.blue.X = mFdp.ConsumeFloatingPoint<float>();
+    primaries.blue.Y = mFdp.ConsumeFloatingPoint<float>();
+    primaries.blue.Z = mFdp.ConsumeFloatingPoint<float>();
+    primaries.white.X = mFdp.ConsumeFloatingPoint<float>();
+    primaries.white.Y = mFdp.ConsumeFloatingPoint<float>();
+    primaries.white.Z = mFdp.ConsumeFloatingPoint<float>();
+    mTestableFlinger.setInternalDisplayPrimaries(primaries);
+}
+
+void SurfaceFlingerFuzzer::setTransactionState() {
+    Vector<ComposerState> states;
+    Vector<DisplayState> displays;
+    ComposerState composerState;
+    composerState.state.what = layer_state_t::eLayerChanged;
+    composerState.state.surface = nullptr;
+    states.add(composerState);
+    uint32_t flags = mFdp.ConsumeIntegral<uint32_t>();
+    const sp<IBinder> applyToken = nullptr;
+    int64_t desiredPresentTime = mFdp.ConsumeIntegral<int64_t>();
+    bool isAutoTimestamp = mFdp.ConsumeBool();
+    bool hasListenerCallbacks = mFdp.ConsumeBool();
+    std::vector<ListenerCallbacks> listenerCallbacks{};
+    uint64_t transactionId = mFdp.ConsumeIntegral<uint64_t>();
+
+    mTestableFlinger.setTransactionState(FrameTimelineInfo{}, states, displays, flags, applyToken,
+                                         InputWindowCommands{}, desiredPresentTime, isAutoTimestamp,
+                                         {}, hasListenerCallbacks, listenerCallbacks,
+                                         transactionId);
+}
+
+void SurfaceFlingerFuzzer::setDisplayStateLocked() {
+    DisplayState state{};
+    mTestableFlinger.setDisplayStateLocked(state);
+}
+
+void SurfaceFlingerFuzzer::onTransact(const uint8_t *data, size_t size) {
+    Parcel fuzzedData, reply;
+    fuzzedData.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+    fuzzedData.setData(data, size);
+    fuzzedData.setDataPosition(0);
+    uint32_t code = mFdp.ConsumeBool() ? mFdp.PickValueInArray(kSurfaceComposerTags)
+                                       : mFdp.ConsumeIntegralInRange<uint32_t>(kMinCode, kMaxCode);
+    mTestableFlinger.onTransact(code, fuzzedData, &reply, 0);
+}
+
+void SurfaceFlingerFuzzer::setUp() {
+    mTestableFlinger.setupScheduler(std::make_unique<android::mock::VsyncController>(),
+                                    std::make_unique<android::mock::VSyncTracker>(),
+                                    std::make_unique<android::mock::EventThread>(),
+                                    std::make_unique<android::mock::EventThread>());
+
+    mTestableFlinger.setupTimeStats(std::make_unique<android::mock::TimeStats>());
+
+    std::unique_ptr<android::renderengine::RenderEngine> renderEngine =
+            std::make_unique<android::renderengine::mock::RenderEngine>();
+    mTestableFlinger.setupRenderEngine(std::move(renderEngine));
+    mTestableFlinger.setupComposer(std::make_unique<android::Hwc2::mock::Composer>());
+}
+
+void SurfaceFlingerFuzzer::process(const uint8_t *data, size_t size) {
+    setUp();
+
+    invokeFlinger();
+
+    mTestableFlinger.fuzzSurfaceFlinger(data, size);
+
+    mTestableFlinger.setCreateBufferQueueFunction(
+            surfaceflinger::test::Factory::CreateBufferQueueFunction());
+    mTestableFlinger.setCreateNativeWindowSurface(
+            surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction());
+
+    setInternalDisplayPrimaries();
+
+    mTestableFlinger.enableHalVirtualDisplays(mFdp.ConsumeBool());
+
+    mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>());
+
+    mTestableFlinger.notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>());
+
+    setDisplayStateLocked();
+
+    setTransactionState();
+    mTestableFlinger.flushTransactionQueues();
+
+    onTransact(data, size);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    android::fuzz::SurfaceFlingerFuzzer surfaceFlingerFuzzer(data, size);
+    surfaceFlingerFuzzer.process(data, size);
+    return 0;
+}
+
+} // namespace android::fuzz
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
new file mode 100644
index 0000000..0a458c2
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -0,0 +1,803 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <compositionengine/Display.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/ScreenCaptureResults.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/mock/GraphicBufferProducer.h>
+#include <ui/DisplayStatInfo.h>
+#include <ui/DynamicDisplayInfo.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "DisplayHardware/ComposerHal.h"
+#include "EffectLayer.h"
+#include "FrameTimeline/FrameTimeline.h"
+#include "FrameTracer/FrameTracer.h"
+#include "Layer.h"
+#include "NativeWindowSurface.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
+#include "Scheduler/VsyncModulator.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlinger.h"
+#include "SurfaceFlingerDefaultFactory.h"
+#include "SurfaceInterceptor.h"
+#include "TimeStats/TimeStats.h"
+
+#include "renderengine/mock/RenderEngine.h"
+#include "scheduler/TimeKeeper.h"
+#include "tests/unittests/mock/DisplayHardware/MockComposer.h"
+#include "tests/unittests/mock/DisplayHardware/MockHWC2.h"
+#include "tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h"
+#include "tests/unittests/mock/MockEventThread.h"
+#include "tests/unittests/mock/MockFrameTimeline.h"
+#include "tests/unittests/mock/MockFrameTracer.h"
+#include "tests/unittests/mock/MockNativeWindowSurface.h"
+#include "tests/unittests/mock/MockSurfaceInterceptor.h"
+#include "tests/unittests/mock/MockTimeStats.h"
+#include "tests/unittests/mock/MockVSyncTracker.h"
+#include "tests/unittests/mock/MockVsyncController.h"
+
+namespace android {
+namespace Hwc2 {
+
+class Composer;
+
+namespace types = hardware::graphics::common;
+
+namespace V2_1 = hardware::graphics::composer::V2_1;
+namespace V2_2 = hardware::graphics::composer::V2_2;
+namespace V2_3 = hardware::graphics::composer::V2_3;
+namespace V2_4 = hardware::graphics::composer::V2_4;
+
+using types::V1_0::ColorTransform;
+using types::V1_0::Transform;
+using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
+using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
+
+using V2_1::Config;
+using V2_1::Display;
+using V2_1::Error;
+using V2_1::Layer;
+using V2_4::CommandReaderBase;
+using V2_4::CommandWriterBase;
+using V2_4::IComposer;
+using V2_4::IComposerCallback;
+using V2_4::IComposerClient;
+using V2_4::VsyncPeriodChangeTimeline;
+using V2_4::VsyncPeriodNanos;
+using DisplayCapability = IComposerClient::DisplayCapability;
+using PerFrameMetadata = IComposerClient::PerFrameMetadata;
+using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
+using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
+}; // namespace Hwc2
+
+static constexpr hal::HWDisplayId kHwDisplayId = 1000;
+
+static constexpr ui::Hdr kHdrTypes[] = {ui::Hdr::DOLBY_VISION, ui::Hdr::HDR10, ui::Hdr::HLG,
+                                        ui::Hdr::HDR10_PLUS};
+
+static constexpr ui::ColorMode kColormodes[] = {ui::ColorMode::NATIVE,
+                                                ui::ColorMode::STANDARD_BT601_625,
+                                                ui::ColorMode::STANDARD_BT601_625_UNADJUSTED,
+                                                ui::ColorMode::STANDARD_BT601_525,
+                                                ui::ColorMode::STANDARD_BT601_525_UNADJUSTED,
+                                                ui::ColorMode::STANDARD_BT709,
+                                                ui::ColorMode::DCI_P3,
+                                                ui::ColorMode::SRGB,
+                                                ui::ColorMode::ADOBE_RGB,
+                                                ui::ColorMode::DISPLAY_P3,
+                                                ui::ColorMode::BT2020,
+                                                ui::ColorMode::BT2100_PQ,
+                                                ui::ColorMode::BT2100_HLG,
+                                                ui::ColorMode::DISPLAY_BT2020};
+
+FloatRect getFuzzedFloatRect(FuzzedDataProvider *fdp) {
+    return FloatRect(fdp->ConsumeFloatingPoint<float>() /*left*/,
+                     fdp->ConsumeFloatingPoint<float>() /*right*/,
+                     fdp->ConsumeFloatingPoint<float>() /*top*/,
+                     fdp->ConsumeFloatingPoint<float>() /*bottom*/);
+}
+
+HdrMetadata getFuzzedHdrMetadata(FuzzedDataProvider *fdp) {
+    HdrMetadata hdrMetadata;
+    if (fdp->ConsumeBool()) {
+        hdrMetadata.cta8613.maxContentLightLevel = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.cta8613.maxFrameAverageLightLevel = fdp->ConsumeFloatingPoint<float>();
+
+        hdrMetadata.validTypes |= HdrMetadata::CTA861_3;
+    } else {
+        hdrMetadata.smpte2086.displayPrimaryRed.x = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.displayPrimaryRed.y = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.displayPrimaryGreen.x = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.displayPrimaryGreen.y = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.displayPrimaryBlue.x = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.displayPrimaryBlue.y = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.whitePoint.x = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.whitePoint.y = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.minLuminance = fdp->ConsumeFloatingPoint<float>();
+        hdrMetadata.smpte2086.maxLuminance = fdp->ConsumeFloatingPoint<float>();
+
+        hdrMetadata.validTypes |= HdrMetadata::SMPTE2086;
+    }
+    return hdrMetadata;
+}
+
+class EventThread;
+
+namespace hal = android::hardware::graphics::composer::hal;
+
+struct FakePhaseOffsets : scheduler::VsyncConfiguration {
+    static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+    static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
+
+    VsyncConfigSet getConfigsForRefreshRate(Fps) const override { return getCurrentConfigs(); }
+
+    VsyncConfigSet getCurrentConfigs() const override {
+        return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+                 FAKE_DURATION_OFFSET_NS},
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+                 FAKE_DURATION_OFFSET_NS},
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
+                 FAKE_DURATION_OFFSET_NS},
+                FAKE_DURATION_OFFSET_NS};
+    }
+
+    void reset() override {}
+    void setRefreshRateFps(Fps) override {}
+    void dump(std::string &) const override {}
+};
+namespace scheduler {
+class TestableScheduler : public Scheduler, private ICompositor {
+public:
+    TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs> &refreshRateConfigs,
+                      ISchedulerCallback &callback)
+          : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
+                              std::make_unique<android::mock::VSyncTracker>(), refreshRateConfigs,
+                              callback) {}
+
+    void scheduleFrame(){};
+    void postMessage(sp<MessageHandler> &&){};
+
+    TestableScheduler(std::unique_ptr<VsyncController> controller,
+                      std::unique_ptr<VSyncTracker> tracker,
+                      std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback &callback)
+          : Scheduler(*this, callback, Feature::kContentDetection) {
+        mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
+        setRefreshRateConfigs(std::move(configs));
+    }
+
+    ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
+        return Scheduler::createConnection(std::move(eventThread));
+    }
+
+    auto &mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
+    auto &mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+
+    auto &mutableLayerHistory() { return mLayerHistory; }
+
+    auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
+
+    void replaceTouchTimer(int64_t millis) {
+        if (mTouchTimer) {
+            mTouchTimer.reset();
+        }
+        mTouchTimer.emplace(
+                "Testable Touch timer", std::chrono::milliseconds(millis),
+                [this] { touchTimerCallback(TimerState::Reset); },
+                [this] { touchTimerCallback(TimerState::Expired); });
+        mTouchTimer->start();
+    }
+
+    bool isTouchActive() {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        return mPolicy.touch == Scheduler::TouchState::Active;
+    }
+
+    void dispatchCachedReportedMode() {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        return Scheduler::dispatchCachedReportedMode();
+    }
+
+    void clearCachedReportedMode() {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.cachedModeChangedParams.reset();
+    }
+
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+        return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
+    }
+
+private:
+    // ICompositor overrides:
+    bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
+    void composite(nsecs_t) override {}
+    void sample() override {}
+};
+}; // namespace scheduler
+
+namespace surfaceflinger::test {
+
+class Factory final : public surfaceflinger::Factory {
+public:
+    ~Factory() = default;
+
+    std::unique_ptr<HWComposer> createHWComposer(const std::string &) override { return nullptr; }
+
+    std::unique_ptr<MessageQueue> createMessageQueue(ICompositor &compositor) {
+        return std::make_unique<android::impl::MessageQueue>(compositor);
+    }
+
+    std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
+            Fps /*currentRefreshRate*/) override {
+        return std::make_unique<FakePhaseOffsets>();
+    }
+
+    std::unique_ptr<scheduler::Scheduler> createScheduler(
+            const std::shared_ptr<scheduler::RefreshRateConfigs> &,
+            scheduler::ISchedulerCallback &) {
+        return nullptr;
+    }
+
+    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
+        return new android::impl::SurfaceInterceptor();
+    }
+
+    sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
+        return new StartPropertySetThread(timestampPropertyValue);
+    }
+
+    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs &creationArgs) override {
+        return new DisplayDevice(creationArgs);
+    }
+
+    sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
+                                          uint32_t layerCount, uint64_t usage,
+                                          std::string requestorName) override {
+        return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+    }
+
+    void createBufferQueue(sp<IGraphicBufferProducer> *outProducer,
+                           sp<IGraphicBufferConsumer> *outConsumer,
+                           bool consumerIsSurfaceFlinger) override {
+        if (!mCreateBufferQueue) {
+            BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+            return;
+        }
+        mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+    }
+
+    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer> &producer,
+                                                       const sp<SurfaceFlinger> &flinger,
+                                                       const wp<Layer> &layer) override {
+        return new MonitoredProducer(producer, flinger, layer);
+    }
+
+    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer> &consumer,
+                                                      renderengine::RenderEngine &renderEngine,
+                                                      uint32_t textureName, Layer *layer) override {
+        return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
+    }
+
+    std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+            const sp<IGraphicBufferProducer> &producer) override {
+        if (!mCreateNativeWindowSurface) return nullptr;
+        return mCreateNativeWindowSurface(producer);
+    }
+
+    std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
+        return compositionengine::impl::createCompositionEngine();
+    }
+
+    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs &) override {
+        return nullptr;
+    }
+
+    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs &) override {
+        return nullptr;
+    }
+
+    sp<EffectLayer> createEffectLayer(const LayerCreationArgs &args) override {
+        return new EffectLayer(args);
+    }
+
+    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs &args) override {
+        return new ContainerLayer(args);
+    }
+
+    std::unique_ptr<FrameTracer> createFrameTracer() override {
+        return std::make_unique<android::mock::FrameTracer>();
+    }
+
+    std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
+            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
+        return std::make_unique<android::mock::FrameTimeline>(timeStats, surfaceFlingerPid);
+    }
+
+    using CreateBufferQueueFunction =
+            std::function<void(sp<IGraphicBufferProducer> * /* outProducer */,
+                               sp<IGraphicBufferConsumer> * /* outConsumer */,
+                               bool /* consumerIsSurfaceFlinger */)>;
+    CreateBufferQueueFunction mCreateBufferQueue;
+
+    using CreateNativeWindowSurfaceFunction =
+            std::function<std::unique_ptr<surfaceflinger::NativeWindowSurface>(
+                    const sp<IGraphicBufferProducer> &)>;
+    CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface;
+
+    using CreateCompositionEngineFunction =
+            std::function<std::unique_ptr<compositionengine::CompositionEngine>()>;
+    CreateCompositionEngineFunction mCreateCompositionEngine;
+};
+
+} // namespace surfaceflinger::test
+
+// TODO(b/189053744) : Create a common test/mock library for surfaceflinger
+class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback {
+public:
+    using HotplugEvent = SurfaceFlinger::HotplugEvent;
+
+    SurfaceFlinger *flinger() { return mFlinger.get(); }
+    scheduler::TestableScheduler *scheduler() { return mScheduler; }
+
+    // Allow reading display state without locking, as if called on the SF main thread.
+    auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->onInitializeDisplays();
+    }
+
+    void scheduleComposite(FrameHint){};
+
+    void setGlobalShadowSettings(FuzzedDataProvider *fdp) {
+        const half4 ambientColor{fdp->ConsumeFloatingPoint<float>(),
+                                 fdp->ConsumeFloatingPoint<float>(),
+                                 fdp->ConsumeFloatingPoint<float>(),
+                                 fdp->ConsumeFloatingPoint<float>()};
+        const half4 spotColor{fdp->ConsumeFloatingPoint<float>(),
+                              fdp->ConsumeFloatingPoint<float>(),
+                              fdp->ConsumeFloatingPoint<float>(),
+                              fdp->ConsumeFloatingPoint<float>()};
+        float lightPosY = fdp->ConsumeFloatingPoint<float>();
+        float lightPosZ = fdp->ConsumeFloatingPoint<float>();
+        float lightRadius = fdp->ConsumeFloatingPoint<float>();
+        mFlinger->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
+                                          lightRadius);
+    }
+
+    void onPullAtom(FuzzedDataProvider *fdp) {
+        const int32_t atomId = fdp->ConsumeIntegral<uint8_t>();
+        std::string pulledData = fdp->ConsumeRandomLengthString().c_str();
+        bool success = fdp->ConsumeBool();
+        mFlinger->onPullAtom(atomId, &pulledData, &success);
+    }
+
+    void fuzzDumpsysAndDebug(FuzzedDataProvider *fdp) {
+        std::string result = fdp->ConsumeRandomLengthString().c_str();
+        mFlinger->appendSfConfigString(result);
+        result = fdp->ConsumeRandomLengthString().c_str();
+        mFlinger->listLayersLocked(result);
+
+        using DumpArgs = Vector<String16>;
+        DumpArgs dumpArgs;
+        dumpArgs.push_back(String16(fdp->ConsumeRandomLengthString().c_str()));
+        mFlinger->clearStatsLocked(dumpArgs, result);
+
+        mFlinger->dumpTimeStats(dumpArgs, fdp->ConsumeBool(), result);
+        mFlinger->logFrameStats();
+
+        result = fdp->ConsumeRandomLengthString().c_str();
+        mFlinger->dumpFrameTimeline(dumpArgs, result);
+
+        result = fdp->ConsumeRandomLengthString().c_str();
+        mFlinger->dumpStaticScreenStats(result);
+
+        result = fdp->ConsumeRandomLengthString().c_str();
+        mFlinger->dumpFrameEventsLocked(result);
+
+        result = fdp->ConsumeRandomLengthString().c_str();
+        mFlinger->dumpRawDisplayIdentificationData(dumpArgs, result);
+
+        LayersProto layersProto = mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>());
+        mFlinger->dumpOffscreenLayersProto(layersProto);
+        LayersTraceProto layersTraceProto{};
+        mFlinger->dumpDisplayProto(layersTraceProto);
+
+        result = fdp->ConsumeRandomLengthString().c_str();
+        mFlinger->dumpHwc(result);
+
+        mFlinger->calculateColorMatrix(fdp->ConsumeFloatingPoint<float>());
+        mFlinger->updateColorMatrixLocked();
+        mFlinger->CheckTransactCodeCredentials(fdp->ConsumeIntegral<uint32_t>());
+
+        const CountDownLatch transactionCommittedSignal(fdp->ConsumeIntegral<uint32_t>());
+        mFlinger->waitForSynchronousTransaction(transactionCommittedSignal);
+        mFlinger->signalSynchronousTransactions(fdp->ConsumeIntegral<uint32_t>());
+    }
+
+    void getCompositionPreference() {
+        ui::Dataspace outDataspace;
+        ui::PixelFormat outPixelFormat;
+        ui::Dataspace outWideColorGamutDataspace;
+        ui::PixelFormat outWideColorGamutPixelFormat;
+        mFlinger->getCompositionPreference(&outDataspace, &outPixelFormat,
+                                           &outWideColorGamutDataspace,
+                                           &outWideColorGamutPixelFormat);
+    }
+
+    void overrideHdrTypes(sp<IBinder> &display, FuzzedDataProvider *fdp) {
+        std::vector<ui::Hdr> hdrTypes;
+        hdrTypes.push_back(fdp->PickValueInArray(kHdrTypes));
+        mFlinger->overrideHdrTypes(display, hdrTypes);
+    }
+
+    void getDisplayedContentSample(sp<IBinder> &display, FuzzedDataProvider *fdp) {
+        DisplayedFrameStats outDisplayedFrameStats;
+        mFlinger->getDisplayedContentSample(display, fdp->ConsumeIntegral<uint64_t>(),
+                                            fdp->ConsumeIntegral<uint64_t>(),
+                                            &outDisplayedFrameStats);
+    }
+
+    void getDisplayStats(sp<IBinder> &display) {
+        android::DisplayStatInfo stats;
+        mFlinger->getDisplayStats(display, &stats);
+    }
+
+    void getDisplayState(sp<IBinder> &display) {
+        ui::DisplayState displayState;
+        mFlinger->getDisplayState(display, &displayState);
+    }
+
+    void getStaticDisplayInfo(sp<IBinder> &display) {
+        ui::StaticDisplayInfo staticDisplayInfo;
+        mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo);
+    }
+
+    void getDynamicDisplayInfo(sp<IBinder> &display) {
+        android::ui::DynamicDisplayInfo dynamicDisplayInfo;
+        mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo);
+    }
+    void getDisplayNativePrimaries(sp<IBinder> &display) {
+        android::ui::DisplayPrimaries displayPrimaries;
+        mFlinger->getDisplayNativePrimaries(display, displayPrimaries);
+    }
+
+    void getDesiredDisplayModeSpecs(sp<IBinder> &display) {
+        ui::DisplayModeId outDefaultMode;
+        bool outAllowGroupSwitching;
+        float outPrimaryRefreshRateMin;
+        float outPrimaryRefreshRateMax;
+        float outAppRequestRefreshRateMin;
+        float outAppRequestRefreshRateMax;
+        mFlinger->getDesiredDisplayModeSpecs(display, &outDefaultMode, &outAllowGroupSwitching,
+                                             &outPrimaryRefreshRateMin, &outPrimaryRefreshRateMax,
+                                             &outAppRequestRefreshRateMin,
+                                             &outAppRequestRefreshRateMax);
+    }
+
+    void setVsyncConfig(FuzzedDataProvider *fdp) {
+        const scheduler::VsyncModulator::VsyncConfig vsyncConfig{};
+        mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
+    }
+
+    void updateCompositorTiming(FuzzedDataProvider *fdp) {
+        std::shared_ptr<FenceTime> presentFenceTime = FenceTime::NO_FENCE;
+        mFlinger->updateCompositorTiming({}, fdp->ConsumeIntegral<nsecs_t>(), presentFenceTime);
+    }
+
+    void getCompositorTiming() {
+        CompositorTiming compositorTiming;
+        mFlinger->getCompositorTiming(&compositorTiming);
+    }
+
+    sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
+        mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
+        mFlinger->createConnection();
+
+        DisplayIdGenerator<HalVirtualDisplayId> kGenerator;
+        HalVirtualDisplayId halVirtualDisplayId = kGenerator.generateId().value();
+
+        ui::Size uiSize{fdp->ConsumeIntegral<int32_t>(), fdp->ConsumeIntegral<int32_t>()};
+        ui::PixelFormat pixelFormat{};
+        mFlinger->getHwComposer().allocateVirtualDisplay(halVirtualDisplayId, uiSize, &pixelFormat);
+
+        PhysicalDisplayId physicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value();
+        mFlinger->getHwComposer().allocatePhysicalDisplay(kHwDisplayId, physicalDisplayId);
+
+        sp<IBinder> display =
+                mFlinger->createDisplay(String8(fdp->ConsumeRandomLengthString().c_str()),
+                                        fdp->ConsumeBool());
+
+        onInitializeDisplays();
+        mFlinger->getPhysicalDisplayToken(physicalDisplayId);
+
+        mFlinger->mStartPropertySetThread =
+                mFlinger->getFactory().createStartPropertySetThread(fdp->ConsumeBool());
+
+        mFlinger->bootFinished();
+
+        return display;
+    }
+
+    void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
+        FuzzedDataProvider mFdp(data, size);
+
+        sp<IBinder> display = fuzzBoot(&mFdp);
+
+        sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
+        mFlinger->authenticateSurfaceTexture(bufferProducer.get());
+
+        mFlinger->createDisplayEventConnection();
+
+        getDisplayStats(display);
+        getDisplayState(display);
+        getStaticDisplayInfo(display);
+        getDynamicDisplayInfo(display);
+        getDisplayNativePrimaries(display);
+
+        mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
+        mFlinger->setGameContentType(display, mFdp.ConsumeBool());
+        mFlinger->setPowerMode(display, mFdp.ConsumeIntegral<int>());
+        mFlinger->clearAnimationFrameStats();
+
+        overrideHdrTypes(display, &mFdp);
+
+        onPullAtom(&mFdp);
+
+        mFlinger->injectVSync(mFdp.ConsumeIntegral<nsecs_t>());
+
+        getCompositionPreference();
+        getDisplayedContentSample(display, &mFdp);
+        getDesiredDisplayModeSpecs(display);
+
+        bool outSupport;
+        mFlinger->getDisplayBrightnessSupport(display, &outSupport);
+
+        mFlinger->notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>());
+
+        setGlobalShadowSettings(&mFdp);
+
+        mFlinger->binderDied(display);
+        mFlinger->onFirstRef();
+
+        mFlinger->commitTransactions();
+        mFlinger->updateInputFlinger();
+        mFlinger->updateCursorAsync();
+
+        setVsyncConfig(&mFdp);
+
+        mFlinger->flushTransactionQueues(0);
+
+        mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
+        mFlinger->clearTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
+        mFlinger->commitOffscreenLayers();
+
+        mFlinger->frameIsEarly(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeIntegral<int64_t>());
+        mFlinger->computeLayerBounds();
+        mFlinger->startBootAnim();
+
+        mFlinger->readPersistentProperties();
+
+        mFlinger->exceedsMaxRenderTargetSize(mFdp.ConsumeIntegral<uint32_t>(),
+                                             mFdp.ConsumeIntegral<uint32_t>());
+
+        mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>());
+
+        mFlinger->postComposition();
+
+        getCompositorTiming();
+
+        updateCompositorTiming(&mFdp);
+
+        mFlinger->setCompositorTimingSnapped({}, mFdp.ConsumeIntegral<nsecs_t>());
+        mFlinger->postFrame();
+        mFlinger->calculateExpectedPresentTime({});
+
+        mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool());
+
+        fuzzDumpsysAndDebug(&mFdp);
+
+        mFlinger->destroyDisplay(display);
+    }
+
+    void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
+        mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
+    }
+
+    void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
+        mFlinger->mCompositionEngine->setHwComposer(
+                std::make_unique<impl::HWComposer>(std::move(composer)));
+    }
+
+    void setupTimeStats(const std::shared_ptr<TimeStats> &timeStats) {
+        mFlinger->mCompositionEngine->setTimeStats(timeStats);
+    }
+
+    // The ISchedulerCallback argument can be nullptr for a no-op implementation.
+    void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+                        std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
+                        std::unique_ptr<EventThread> appEventThread,
+                        std::unique_ptr<EventThread> sfEventThread,
+                        scheduler::ISchedulerCallback *callback = nullptr,
+                        bool hasMultipleModes = false) {
+        DisplayModes modes{DisplayMode::Builder(0)
+                                   .setId(DisplayModeId(0))
+                                   .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+                                   .setVsyncPeriod(16'666'667)
+                                   .setGroup(0)
+                                   .build()};
+
+        if (hasMultipleModes) {
+            modes.emplace_back(DisplayMode::Builder(1)
+                                       .setId(DisplayModeId(1))
+                                       .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+                                       .setVsyncPeriod(11'111'111)
+                                       .setGroup(0)
+                                       .build());
+        }
+
+        const auto currMode = DisplayModeId(0);
+        mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, currMode);
+        const auto currFps = mRefreshRateConfigs->getCurrentRefreshRate().getFps();
+        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
+        mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
+                mFlinger->mVsyncConfiguration->getCurrentConfigs());
+        mFlinger->mRefreshRateStats =
+                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
+                                                              /*powerMode=*/hal::PowerMode::OFF);
+
+        mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
+                                                      std::move(vsyncTracker), mRefreshRateConfigs,
+                                                      *(callback ?: this));
+
+        mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
+        mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
+        resetScheduler(mScheduler);
+    }
+
+    void resetScheduler(scheduler::Scheduler *scheduler) { mFlinger->mScheduler.reset(scheduler); }
+
+    scheduler::TestableScheduler &mutableScheduler() const { return *mScheduler; }
+
+    using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
+    void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
+        mFactory.mCreateBufferQueue = f;
+    }
+
+    using CreateNativeWindowSurfaceFunction =
+            surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction;
+    void setCreateNativeWindowSurface(CreateNativeWindowSurfaceFunction f) {
+        mFactory.mCreateNativeWindowSurface = f;
+    }
+
+    void setInternalDisplayPrimaries(const ui::DisplayPrimaries &primaries) {
+        memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries));
+    }
+
+    static auto &mutableLayerDrawingState(const sp<Layer> &layer) { return layer->mDrawingState; }
+
+    auto &mutableStateLock() { return mFlinger->mStateLock; }
+
+    static auto findOutputLayerForDisplay(const sp<Layer> &layer,
+                                          const sp<const DisplayDevice> &display) {
+        return layer->findOutputLayerForDisplay(display.get());
+    }
+
+    /* ------------------------------------------------------------------------
+     * Forwarding for functions being tested
+     */
+
+    void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); }
+
+    auto commitTransactionsLocked(uint32_t transactionFlags) {
+        Mutex::Autolock lock(mFlinger->mStateLock);
+        return mFlinger->commitTransactionsLocked(transactionFlags);
+    }
+
+    auto setDisplayStateLocked(const DisplayState &s) {
+        Mutex::Autolock lock(mFlinger->mStateLock);
+        return mFlinger->setDisplayStateLocked(s);
+    }
+
+    auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
+
+    // Allow reading display state without locking, as if called on the SF main thread.
+    auto setPowerModeInternal(const sp<DisplayDevice> &display,
+                              hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->setPowerModeInternal(display, mode);
+    }
+
+    auto onMessageReceived(int32_t /*what*/) { return 0; }
+
+    auto &getTransactionQueue() { return mFlinger->mTransactionQueue; }
+    auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+
+    auto setTransactionState(
+            const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states,
+            const Vector<DisplayState> &displays, uint32_t flags, const sp<IBinder> &applyToken,
+            const InputWindowCommands &inputWindowCommands, int64_t desiredPresentTime,
+            bool isAutoTimestamp, const client_cache_t &uncacheBuffer, bool hasListenerCallbacks,
+            std::vector<ListenerCallbacks> &listenerCallbacks, uint64_t transactionId) {
+        return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
+                                             inputWindowCommands, desiredPresentTime,
+                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+                                             listenerCallbacks, transactionId);
+    }
+
+    auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); };
+
+    auto onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+        return mFlinger->onTransact(code, data, reply, flags);
+    }
+
+    auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); }
+
+    auto calculateMaxAcquiredBufferCount(Fps refreshRate,
+                                         std::chrono::nanoseconds presentLatency) const {
+        return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
+    }
+
+    /* Read-write access to private data to set up preconditions and assert
+     * post-conditions.
+     */
+
+    auto &mutableCurrentState() { return mFlinger->mCurrentState; }
+    auto &mutableDisplays() { return mFlinger->mDisplays; }
+    auto &mutableDrawingState() { return mFlinger->mDrawingState; }
+    auto &mutableInterceptor() { return mFlinger->mInterceptor; }
+
+    auto fromHandle(const sp<IBinder> &handle) { return mFlinger->fromHandle(handle); }
+
+    ~TestableSurfaceFlinger() {
+        mutableDisplays().clear();
+        mutableCurrentState().displays.clear();
+        mutableDrawingState().displays.clear();
+        mutableInterceptor().clear();
+        mFlinger->mScheduler.reset();
+        mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
+        mFlinger->mCompositionEngine->setRenderEngine(
+                std::unique_ptr<renderengine::RenderEngine>());
+    }
+
+private:
+    void scheduleRefresh(FrameHint) {}
+    void setVsyncEnabled(bool) override {}
+    void changeRefreshRate(const RefreshRate &, DisplayModeEvent) override {}
+    void kernelTimerChanged(bool) override {}
+    void triggerOnFrameRateOverridesChanged() {}
+
+    surfaceflinger::test::Factory mFactory;
+    sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
+    scheduler::TestableScheduler *mScheduler = nullptr;
+    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+};
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 2af0480..0069441 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -89,6 +89,7 @@
     MOCK_METHOD2(onComposerHalVsyncPeriodTimingChanged,
                  void(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&));
     MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId));
+    MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId));
 };
 
 struct HWComposerSetCallbackTest : testing::Test {
@@ -110,11 +111,9 @@
                             }),
                             Return(hardware::graphics::composer::V2_4::Error::NONE)));
     EXPECT_CALL(*mHal, registerCallback(_));
-    EXPECT_CALL(*mHal, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
-            .WillOnce(Return(false));
 
     impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
-    hwc.setCallback(&mCallback);
+    hwc.setCallback(mCallback);
 
     const auto& supported = hwc.getSupportedLayerGenericMetadata();
     EXPECT_EQ(2u, supported.size());
@@ -129,11 +128,9 @@
     EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
             .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
     EXPECT_CALL(*mHal, registerCallback(_));
-    EXPECT_CALL(*mHal, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
-            .WillOnce(Return(false));
 
     impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
-    hwc.setCallback(&mCallback);
+    hwc.setCallback(mCallback);
 
     const auto& supported = hwc.getSupportedLayerGenericMetadata();
     EXPECT_EQ(0u, supported.size());
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 2bafabd..4efcc05 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -33,6 +29,21 @@
 using namespace std::chrono_literals;
 
 namespace android::scheduler {
+namespace {
+
+DisplayModePtr createDisplayMode(DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
+                                 ui::Size resolution = ui::Size()) {
+    return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
+            .setId(modeId)
+            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+            .setVsyncPeriod(static_cast<int32_t>(refreshRate.getPeriodNsecs()))
+            .setGroup(group)
+            .setHeight(resolution.height)
+            .setWidth(resolution.width)
+            .build();
+}
+
+} // namespace
 
 namespace hal = android::hardware::graphics::composer::hal;
 
@@ -40,143 +51,107 @@
 using LayerVoteType = RefreshRateConfigs::LayerVoteType;
 using LayerRequirement = RefreshRateConfigs::LayerRequirement;
 
+struct TestableRefreshRateConfigs : RefreshRateConfigs {
+    using RefreshRateConfigs::RefreshRateConfigs;
+
+    RefreshRate getMinSupportedRefreshRate() const {
+        std::lock_guard lock(mLock);
+        return *mMinSupportedRefreshRate;
+    }
+
+    RefreshRate getMaxSupportedRefreshRate() const {
+        std::lock_guard lock(mLock);
+        return *mMaxSupportedRefreshRate;
+    }
+
+    RefreshRate getMinRefreshRateByPolicy() const {
+        std::lock_guard lock(mLock);
+        return getMinRefreshRateByPolicyLocked();
+    }
+
+    const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
+
+    using RefreshRateConfigs::GetBestRefreshRateCache;
+    auto& mutableGetBestRefreshRateCache() { return mGetBestRefreshRateCache; }
+
+    auto getBestRefreshRateAndSignals(const std::vector<LayerRequirement>& layers,
+                                      GlobalSignals signals) const {
+        return RefreshRateConfigs::getBestRefreshRate(layers, signals);
+    }
+
+    RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
+                                   GlobalSignals signals = {}) const {
+        return getBestRefreshRateAndSignals(layers, signals).first;
+    }
+};
+
 class RefreshRateConfigsTest : public testing::Test {
 protected:
-    using GetBestRefreshRateInvocation = RefreshRateConfigs::GetBestRefreshRateInvocation;
-
     RefreshRateConfigsTest();
     ~RefreshRateConfigsTest();
 
-    RefreshRate createRefreshRate(DisplayModePtr displayMode) {
+    static RefreshRate asRefreshRate(DisplayModePtr displayMode) {
         return {displayMode, RefreshRate::ConstructorTag(0)};
     }
 
-    Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
-        return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
-    }
+    static constexpr DisplayModeId kModeId60{0};
+    static constexpr DisplayModeId kModeId90{1};
+    static constexpr DisplayModeId kModeId72{2};
+    static constexpr DisplayModeId kModeId120{3};
+    static constexpr DisplayModeId kModeId30{4};
+    static constexpr DisplayModeId kModeId25{5};
+    static constexpr DisplayModeId kModeId50{6};
+    static constexpr DisplayModeId kModeId24{7};
+    static constexpr DisplayModeId kModeId24Frac{8};
+    static constexpr DisplayModeId kModeId30Frac{9};
+    static constexpr DisplayModeId kModeId60Frac{10};
 
-    std::vector<Fps> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) {
-        return refreshRateConfigs.mKnownFrameRates;
-    }
+    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
+    static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz);
+    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
+    static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1);
+    static inline const DisplayModePtr kMode90_4K =
+            createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160});
+    static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz);
+    static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1);
+    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
+    static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1);
+    static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz);
+    static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1);
+    static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz);
+    static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz);
+    static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1);
+    static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz);
+    static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz);
+    static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz);
 
-    RefreshRate getMinRefreshRateByPolicy(const RefreshRateConfigs& refreshRateConfigs) {
-        std::lock_guard lock(refreshRateConfigs.mLock);
-        return refreshRateConfigs.getMinRefreshRateByPolicyLocked();
-    }
+    // Test configurations.
+    static inline const DisplayModes kModes_60 = {kMode60};
+    static inline const DisplayModes kModes_60_90 = {kMode60, kMode90};
+    static inline const DisplayModes kModes_60_90_G1 = {kMode60, kMode90_G1};
+    static inline const DisplayModes kModes_60_90_4K = {kMode60, kMode90_4K};
+    static inline const DisplayModes kModes_60_72_90 = {kMode60, kMode90, kMode72};
+    static inline const DisplayModes kModes_60_90_72_120 = {kMode60, kMode90, kMode72, kMode120};
+    static inline const DisplayModes kModes_30_60_72_90_120 = {kMode60, kMode90, kMode72, kMode120,
+                                                               kMode30};
 
-    RefreshRate getMinSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
-        std::lock_guard lock(refreshRateConfigs.mLock);
-        return *refreshRateConfigs.mMinSupportedRefreshRate;
-    }
-
-    RefreshRate getMaxSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) {
-        std::lock_guard lock(refreshRateConfigs.mLock);
-        return *refreshRateConfigs.mMaxSupportedRefreshRate;
-    }
-
-    void setLastBestRefreshRateInvocation(RefreshRateConfigs& refreshRateConfigs,
-                                          const GetBestRefreshRateInvocation& invocation) {
-        std::lock_guard lock(refreshRateConfigs.mLock);
-        refreshRateConfigs.lastBestRefreshRateInvocation.emplace(
-                GetBestRefreshRateInvocation(invocation));
-    }
-
-    std::optional<GetBestRefreshRateInvocation> getLastBestRefreshRateInvocation(
-            const RefreshRateConfigs& refreshRateConfigs) {
-        std::lock_guard lock(refreshRateConfigs.mLock);
-        return refreshRateConfigs.lastBestRefreshRateInvocation;
-    }
-
-    // Test config IDs
-    static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0);
-    static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1);
-    static inline const DisplayModeId HWC_CONFIG_ID_72 = DisplayModeId(2);
-    static inline const DisplayModeId HWC_CONFIG_ID_120 = DisplayModeId(3);
-    static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4);
-    static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5);
-    static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
-    static inline const DisplayModeId HWC_CONFIG_ID_24 = DisplayModeId(7);
-    static inline const DisplayModeId HWC_CONFIG_ID_24_FRAC = DisplayModeId(8);
-    static inline const DisplayModeId HWC_CONFIG_ID_30_FRAC = DisplayModeId(9);
-    static inline const DisplayModeId HWC_CONFIG_ID_60_FRAC = DisplayModeId(10);
-
-    // Test configs
-    DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, (60_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig60Frac =
-            createDisplayMode(HWC_CONFIG_ID_60_FRAC, 0, (59.94_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, (90_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig90DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_90, 1, (90_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig90DifferentResolution =
-            createDisplayMode(HWC_CONFIG_ID_90, 0, (90_Hz).getPeriodNsecs(), ui::Size(111, 222));
-    DisplayModePtr mConfig72 = createDisplayMode(HWC_CONFIG_ID_72, 0, (72_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig72DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_72, 1, (72_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig120 = createDisplayMode(HWC_CONFIG_ID_120, 0, (120_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig120DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_120, 1, (120_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, (30_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig30DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_30, 1, (30_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig30Frac =
-            createDisplayMode(HWC_CONFIG_ID_30_FRAC, 0, (29.97_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig25 = createDisplayMode(HWC_CONFIG_ID_25, 0, (25_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig25DifferentGroup =
-            createDisplayMode(HWC_CONFIG_ID_25, 1, (25_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, (50_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig24 = createDisplayMode(HWC_CONFIG_ID_24, 0, (24_Hz).getPeriodNsecs());
-    DisplayModePtr mConfig24Frac =
-            createDisplayMode(HWC_CONFIG_ID_24_FRAC, 0, (23.976_Hz).getPeriodNsecs());
-
-    // Test device configurations
-    // The positions of the configs in the arrays below MUST match their IDs. For example,
-    // the first config should always be 60Hz, the second 90Hz etc.
-    DisplayModes m60OnlyConfigDevice = {mConfig60};
-    DisplayModes m60_90Device = {mConfig60, mConfig90};
-    DisplayModes m60_90DeviceWithDifferentGroups = {mConfig60, mConfig90DifferentGroup};
-    DisplayModes m60_90DeviceWithDifferentResolutions = {mConfig60, mConfig90DifferentResolution};
-    DisplayModes m60_72_90Device = {mConfig60, mConfig90, mConfig72};
-    DisplayModes m60_90_72_120Device = {mConfig60, mConfig90, mConfig72, mConfig120};
-    DisplayModes m30_60_72_90_120Device = {mConfig60, mConfig90, mConfig72, mConfig120, mConfig30};
-    DisplayModes m30_60Device = {mConfig60, mConfig90DifferentGroup, mConfig72DifferentGroup,
-                                 mConfig120DifferentGroup, mConfig30};
-    DisplayModes m30_60_72_90Device = {mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup,
-                                       mConfig30};
-    DisplayModes m30_60_90Device = {mConfig60, mConfig90, mConfig72DifferentGroup,
-                                    mConfig120DifferentGroup, mConfig30};
-    DisplayModes m25_30_50_60Device = {mConfig60,
-                                       mConfig90,
-                                       mConfig72DifferentGroup,
-                                       mConfig120DifferentGroup,
-                                       mConfig30DifferentGroup,
-                                       mConfig25DifferentGroup,
-                                       mConfig50};
-    DisplayModes m60_120Device = {mConfig60, mConfig120};
+    static inline const DisplayModes kModes_30_60 = {kMode60, kMode90_G1, kMode72_G1, kMode120_G1,
+                                                     kMode30};
+    static inline const DisplayModes kModes_30_60_72_90 = {kMode60, kMode90, kMode72, kMode120_G1,
+                                                           kMode30};
+    static inline const DisplayModes kModes_30_60_90 = {kMode60, kMode90, kMode72_G1, kMode120_G1,
+                                                        kMode30};
+    static inline const DisplayModes kModes_25_30_50_60 = {kMode60,     kMode90,    kMode72_G1,
+                                                           kMode120_G1, kMode30_G1, kMode25_G1,
+                                                           kMode50};
+    static inline const DisplayModes kModes_60_120 = {kMode60, kMode120};
 
     // This is a typical TV configuration.
-    DisplayModes m24_25_30_50_60WithFracDevice = {mConfig24, mConfig24Frac, mConfig25,
-                                                  mConfig30, mConfig30Frac, mConfig50,
-                                                  mConfig60, mConfig60Frac};
-
-    // Expected RefreshRate objects
-    RefreshRate mExpected60Config = {mConfig60, RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpectedAlmost60Config = {createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665),
-                                           RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected90Config = {mConfig90, RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected90DifferentGroupConfig = {mConfig90DifferentGroup,
-                                                   RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected90DifferentResolutionConfig = {mConfig90DifferentResolution,
-                                                        RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected72Config = {mConfig72, RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)};
-
-    DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
-                                     ui::Size resolution = ui::Size());
+    static inline const DisplayModes kModes_24_25_30_50_60_Frac = {kMode24, kMode24Frac, kMode25,
+                                                                   kMode30, kMode30Frac, kMode50,
+                                                                   kMode60, kMode60Frac};
 };
 
-using Builder = DisplayMode::Builder;
-
 RefreshRateConfigsTest::RefreshRateConfigsTest() {
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
@@ -189,348 +164,315 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-DisplayModePtr RefreshRateConfigsTest::createDisplayMode(DisplayModeId modeId, int32_t group,
-                                                         int64_t vsyncPeriod, ui::Size resolution) {
-    return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
-            .setId(modeId)
-            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-            .setVsyncPeriod(int32_t(vsyncPeriod))
-            .setGroup(group)
-            .setHeight(resolution.height)
-            .setWidth(resolution.width)
-            .build();
-}
-
 namespace {
 
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingSupported) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+TEST_F(RefreshRateConfigsTest, oneMode_canSwitch) {
+    RefreshRateConfigs configs(kModes_60, kModeId60);
+    EXPECT_FALSE(configs.canSwitch());
 }
 
 TEST_F(RefreshRateConfigsTest, invalidPolicy) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0);
-    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {20_Hz, 40_Hz}}), 0);
+    RefreshRateConfigs configs(kModes_60, kModeId60);
+    EXPECT_LT(configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0);
+    EXPECT_LT(configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}), 0);
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) {
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
-    const auto& minRate = getMinSupportedRefreshRate(*refreshRateConfigs);
-    const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
+    const auto minRate = configs.getMinSupportedRefreshRate();
+    const auto performanceRate = configs.getMaxSupportedRefreshRate();
 
-    ASSERT_EQ(mExpected60Config, minRate);
-    ASSERT_EQ(mExpected90Config, performanceRate);
+    EXPECT_EQ(asRefreshRate(kMode60), minRate);
+    EXPECT_EQ(asRefreshRate(kMode90), performanceRate);
 
-    const auto& minRateByPolicy = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
-    ASSERT_EQ(minRateByPolicy, minRate);
-    ASSERT_EQ(performanceRateByPolicy, performanceRate);
+    const auto minRateByPolicy = configs.getMinRefreshRateByPolicy();
+    const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(minRateByPolicy, minRate);
+    EXPECT_EQ(performanceRateByPolicy, performanceRate);
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentGroups) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentGroups) {
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
-    const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    const auto minRate = configs.getMinRefreshRateByPolicy();
+    const auto performanceRate = configs.getMaxSupportedRefreshRate();
+    const auto minRate60 = configs.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
 
-    ASSERT_EQ(mExpected60Config, minRate);
-    ASSERT_EQ(mExpected60Config, minRate60);
-    ASSERT_EQ(mExpected60Config, performanceRate60);
+    EXPECT_EQ(asRefreshRate(kMode60), minRate);
+    EXPECT_EQ(asRefreshRate(kMode60), minRate60);
+    EXPECT_EQ(asRefreshRate(kMode60), performanceRate60);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60_Hz, 90_Hz}}), 0);
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
+    configs.setCurrentModeId(kModeId90);
 
-    const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    const auto minRate90 = configs.getMinRefreshRateByPolicy();
+    const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
 
-    ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate);
-    ASSERT_EQ(mExpected90DifferentGroupConfig, minRate90);
-    ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate90);
+    EXPECT_EQ(asRefreshRate(kMode90_G1), performanceRate);
+    EXPECT_EQ(asRefreshRate(kMode90_G1), minRate90);
+    EXPECT_EQ(asRefreshRate(kMode90_G1), performanceRate90);
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentResolutions) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentResolutions,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
+    TestableRefreshRateConfigs configs(kModes_60_90_4K, kModeId60);
 
-    const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs);
-    const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    const auto minRate = configs.getMinRefreshRateByPolicy();
+    const auto performanceRate = configs.getMaxSupportedRefreshRate();
+    const auto minRate60 = configs.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
 
-    ASSERT_EQ(mExpected60Config, minRate);
-    ASSERT_EQ(mExpected60Config, minRate60);
-    ASSERT_EQ(mExpected60Config, performanceRate60);
+    EXPECT_EQ(asRefreshRate(kMode60), minRate);
+    EXPECT_EQ(asRefreshRate(kMode60), minRate60);
+    EXPECT_EQ(asRefreshRate(kMode60), performanceRate60);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60_Hz, 90_Hz}}), 0);
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
+    configs.setCurrentModeId(kModeId90);
 
-    const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    const auto minRate90 = configs.getMinRefreshRateByPolicy();
+    const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
 
-    ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate);
-    ASSERT_EQ(mExpected90DifferentResolutionConfig, minRate90);
-    ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate90);
+    EXPECT_EQ(asRefreshRate(kMode90_4K), performanceRate);
+    EXPECT_EQ(asRefreshRate(kMode90_4K), minRate90);
+    EXPECT_EQ(asRefreshRate(kMode90_4K), performanceRate90);
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_policyChange) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+TEST_F(RefreshRateConfigsTest, twoModes_policyChange) {
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
-    auto minRate = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    const auto minRate = configs.getMinRefreshRateByPolicy();
+    const auto performanceRate = configs.getMaxRefreshRateByPolicy();
 
-    ASSERT_EQ(mExpected60Config, minRate);
-    ASSERT_EQ(mExpected90Config, performanceRate);
+    EXPECT_EQ(asRefreshRate(kMode60), minRate);
+    EXPECT_EQ(asRefreshRate(kMode90), performanceRate);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
 
-    auto minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
-    auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
-    ASSERT_EQ(mExpected60Config, minRate60);
-    ASSERT_EQ(mExpected60Config, performanceRate60);
+    const auto minRate60 = configs.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(asRefreshRate(kMode60), minRate60);
+    EXPECT_EQ(asRefreshRate(kMode60), performanceRate60);
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getCurrentRefreshRate) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+TEST_F(RefreshRateConfigsTest, twoModes_getCurrentRefreshRate) {
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
     {
-        auto current = refreshRateConfigs->getCurrentRefreshRate();
-        EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_60);
+        const auto current = configs.getCurrentRefreshRate();
+        EXPECT_EQ(current.getModeId(), kModeId60);
     }
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    configs.setCurrentModeId(kModeId90);
     {
-        auto current = refreshRateConfigs->getCurrentRefreshRate();
-        EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
+        const auto current = configs.getCurrentRefreshRate();
+        EXPECT_EQ(current.getModeId(), kModeId90);
     }
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
     {
-        auto current = refreshRateConfigs->getCurrentRefreshRate();
-        EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
+        const auto current = configs.getCurrentRefreshRate();
+        EXPECT_EQ(current.getModeId(), kModeId90);
     }
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_72);
+    {
+        TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId72);
 
-    // If there are no layers we select the default frame rate, which is the max of the primary
-    // range.
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate({}, {}));
+        // If there are no layers we select the default frame rate, which is the max of the primary
+        // range.
+        EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate());
 
-    ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}),
-              NO_ERROR);
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate({}, {}));
-
-    // We select max even when this will cause a non-seamless switch.
-    refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                              /*currentConfigId=*/HWC_CONFIG_ID_60);
-    ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_90, /*allowGroupSwitching*/ true, {0_Hz, 90_Hz}}),
-              NO_ERROR);
-    EXPECT_EQ(mExpected90DifferentGroupConfig, refreshRateConfigs->getBestRefreshRate({}, {}));
+        EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), NO_ERROR);
+        EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate());
+    }
+    {
+        // We select max even when this will cause a non-seamless switch.
+        TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+        constexpr bool kAllowGroupSwitching = true;
+        EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}),
+                  NO_ERROR);
+        EXPECT_EQ(asRefreshRate(kMode90_G1), configs.getBestRefreshRate());
+    }
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.name = "";
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0_Hz, 120_Hz}}), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}), 0);
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
-    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 90};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_72_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -540,25 +482,23 @@
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -570,7 +510,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -578,7 +518,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -586,7 +526,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -594,7 +534,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -602,7 +542,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -610,7 +550,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
@@ -618,7 +558,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -626,7 +566,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -634,14 +574,12 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
-    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
+                                       {.frameRateMultipleThreshold = 120});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -653,7 +591,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -661,7 +599,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -669,7 +607,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -677,7 +615,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -685,7 +623,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -693,7 +631,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
@@ -701,7 +639,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -709,7 +647,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -717,92 +655,86 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.name = "24Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -810,45 +742,43 @@
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Max;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 15_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 30_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -856,17 +786,15 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
-        EXPECT_EQ(mExpected60Config, refreshRate)
+        const auto refreshRate = configs.getBestRefreshRate(layers);
+        EXPECT_EQ(asRefreshRate(kMode60), refreshRate)
                 << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
     }
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
-    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+    TestableRefreshRateConfigs configs(kModes_60_120, kModeId60,
+                                       {.frameRateMultipleThreshold = 120});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -874,16 +802,14 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
-        EXPECT_EQ(mExpected60Config, refreshRate)
+        const auto refreshRate = configs.getBestRefreshRate(layers);
+        EXPECT_EQ(asRefreshRate(kMode60), refreshRate)
                 << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
     }
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+TEST_F(RefreshRateConfigsTest, twoModes_getBestRefreshRate_Explicit) {
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -893,33 +819,34 @@
     lr1.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, testInPolicy) {
-    ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(60.000004_Hz, 60.000004_Hz));
-    ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(59_Hz, 60.1_Hz));
-    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(75_Hz, 90_Hz));
-    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(60.0011_Hz, 90_Hz));
-    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(50_Hz, 59.998_Hz));
+    const auto refreshRate =
+            asRefreshRate(createDisplayMode(kModeId60, Fps::fromPeriodNsecs(16'666'665)));
+
+    EXPECT_TRUE(refreshRate.inPolicy(60.000004_Hz, 60.000004_Hz));
+    EXPECT_TRUE(refreshRate.inPolicy(59_Hz, 60.1_Hz));
+    EXPECT_FALSE(refreshRate.inPolicy(75_Hz, 90_Hz));
+    EXPECT_FALSE(refreshRate.inPolicy(60.0011_Hz, 90_Hz));
+    EXPECT_FALSE(refreshRate.inPolicy(50_Hz, 59.998_Hz));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -927,16 +854,14 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
-        EXPECT_EQ(mExpected90Config, refreshRate)
+        const auto refreshRate = configs.getBestRefreshRate(layers, {});
+        EXPECT_EQ(asRefreshRate(kMode90), refreshRate)
                 << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
     }
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -948,7 +873,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
@@ -956,14 +881,14 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30_Hz;
@@ -971,20 +896,18 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30_Hz;
     lr1.name = "30Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -995,28 +918,28 @@
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     // The other layer starts to provide buffers
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1025,20 +948,17 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, touchConsidered) {
-    RefreshRateConfigs::GlobalSignals consideredSignals;
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    RefreshRateConfigs configs(kModes_60_90, kModeId60);
 
-    refreshRateConfigs->getBestRefreshRate({}, {}, &consideredSignals);
-    EXPECT_EQ(false, consideredSignals.touch);
+    auto [_, signals] = configs.getBestRefreshRate({}, {});
+    EXPECT_FALSE(signals.touch);
 
-    refreshRateConfigs->getBestRefreshRate({}, {.touch = true}, &consideredSignals);
-    EXPECT_EQ(true, consideredSignals.touch);
+    std::tie(std::ignore, signals) = configs.getBestRefreshRate({}, {.touch = true});
+    EXPECT_TRUE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -1050,8 +970,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
-    EXPECT_EQ(true, consideredSignals.touch);
+    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    EXPECT_TRUE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60_Hz;
@@ -1059,8 +979,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
-    EXPECT_EQ(false, consideredSignals.touch);
+    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    EXPECT_FALSE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
@@ -1068,8 +988,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
-    EXPECT_EQ(true, consideredSignals.touch);
+    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    EXPECT_TRUE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60_Hz;
@@ -1077,14 +997,12 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
-    EXPECT_EQ(false, consideredSignals.touch);
+    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
+    EXPECT_FALSE(signals.touch);
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90_72_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1116,7 +1034,7 @@
         ss << "ExplicitDefault " << desired;
         lr.name = ss.str();
 
-        const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+        const auto refreshRate = configs.getBestRefreshRate(layers);
         EXPECT_EQ(refreshRate.getFps(), expected);
     }
 }
@@ -1128,39 +1046,36 @@
 
     // Test that 23.976 will choose 24 if 23.976 is not supported
     {
-        android::DisplayModes modes = {mConfig24,     mConfig25, mConfig30,
-                                       mConfig30Frac, mConfig60, mConfig60Frac};
-        auto refreshRateConfigs =
-                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        TestableRefreshRateConfigs configs({kMode24, kMode25, kMode30, kMode30Frac, kMode60,
+                                            kMode60Frac},
+                                           kModeId60);
 
         lr.vote = LayerVoteType::ExplicitExactOrMultiple;
         lr.desiredRefreshRate = 23.976_Hz;
         lr.name = "ExplicitExactOrMultiple 23.976 Hz";
-        EXPECT_EQ(HWC_CONFIG_ID_24, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers).getModeId());
     }
 
     // Test that 24 will choose 23.976 if 24 is not supported
     {
-        android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
-                                       mConfig30Frac, mConfig60, mConfig60Frac};
-        auto refreshRateConfigs =
-                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        TestableRefreshRateConfigs configs({kMode24Frac, kMode25, kMode30, kMode30Frac, kMode60,
+                                            kMode60Frac},
+                                           kModeId60);
+
         lr.desiredRefreshRate = 24_Hz;
         lr.name = "ExplicitExactOrMultiple 24 Hz";
-        EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
-                  refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers).getModeId());
     }
 
     // Test that 29.97 will prefer 59.94 over 60 and 30
     {
-        android::DisplayModes modes = {mConfig24, mConfig24Frac, mConfig25,
-                                       mConfig30, mConfig60,     mConfig60Frac};
-        auto refreshRateConfigs =
-                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        TestableRefreshRateConfigs configs({kMode24, kMode24Frac, kMode25, kMode30, kMode60,
+                                            kMode60Frac},
+                                           kModeId60);
+
         lr.desiredRefreshRate = 29.97_Hz;
         lr.name = "ExplicitExactOrMultiple 29.97 Hz";
-        EXPECT_EQ(HWC_CONFIG_ID_60_FRAC,
-                  refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+        EXPECT_EQ(kModeId60Frac, configs.getBestRefreshRate(layers).getModeId());
     }
 }
 
@@ -1170,9 +1085,7 @@
 
     // Test that voting for supported refresh rate will select this refresh rate
     {
-        auto refreshRateConfigs =
-                std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
-                                                     /*currentConfigId=*/HWC_CONFIG_ID_60);
+        TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60);
 
         for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
             lr.vote = LayerVoteType::ExplicitExact;
@@ -1181,69 +1094,61 @@
             ss << "ExplicitExact " << desired;
             lr.name = ss.str();
 
-            auto selectedRefreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+            auto selectedRefreshRate = configs.getBestRefreshRate(layers);
             EXPECT_EQ(selectedRefreshRate.getFps(), lr.desiredRefreshRate);
         }
     }
 
     // Test that 23.976 will choose 24 if 23.976 is not supported
     {
-        android::DisplayModes modes = {mConfig24,     mConfig25, mConfig30,
-                                       mConfig30Frac, mConfig60, mConfig60Frac};
-        auto refreshRateConfigs =
-                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        TestableRefreshRateConfigs configs({kMode24, kMode25, kMode30, kMode30Frac, kMode60,
+                                            kMode60Frac},
+                                           kModeId60);
+
         lr.vote = LayerVoteType::ExplicitExact;
         lr.desiredRefreshRate = 23.976_Hz;
         lr.name = "ExplicitExact 23.976 Hz";
-        EXPECT_EQ(HWC_CONFIG_ID_24, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers).getModeId());
     }
 
     // Test that 24 will choose 23.976 if 24 is not supported
     {
-        android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
-                                       mConfig30Frac, mConfig60, mConfig60Frac};
-        auto refreshRateConfigs =
-                std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+        TestableRefreshRateConfigs configs({kMode24Frac, kMode25, kMode30, kMode30Frac, kMode60,
+                                            kMode60Frac},
+                                           kModeId60);
+
         lr.desiredRefreshRate = 24_Hz;
         lr.name = "ExplicitExact 24 Hz";
-        EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
-                  refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers).getModeId());
     }
 }
 
 TEST_F(RefreshRateConfigsTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+    RefreshRateConfigs configs(kModes_60_90, kModeId90);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
-              0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
-    RefreshRateConfigs::GlobalSignals consideredSignals;
     lr.vote = LayerVoteType::ExplicitDefault;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitDefault";
     lr.focused = true;
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = true},
-                                                     &consideredSignals));
-    EXPECT_EQ(false, consideredSignals.touch);
+
+    const auto [refreshRate, signals] =
+            configs.getBestRefreshRate(layers, {.touch = true, .idle = true});
+
+    EXPECT_EQ(refreshRate, asRefreshRate(kMode60));
+    EXPECT_FALSE(signals.touch);
 }
 
 TEST_F(RefreshRateConfigsTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}),
-              0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}), 0);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1252,23 +1157,18 @@
     lr.desiredRefreshRate = 90_Hz;
     lr.name = "90Hz ExplicitDefault";
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.idle = true}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.idle = true}));
 }
 
 TEST_F(RefreshRateConfigsTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
-              0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
 
-    RefreshRateConfigs::GlobalSignals consideredSignals;
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getBestRefreshRate({}, {}, &consideredSignals));
-    EXPECT_EQ(false, consideredSignals.touch);
+    const auto [refreshRate, signals] = configs.getBestRefreshRateAndSignals({}, {});
+    EXPECT_EQ(refreshRate, asRefreshRate(kMode90));
+    EXPECT_FALSE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1277,52 +1177,50 @@
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitExactOrMultiple";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::ExplicitDefault;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitDefault";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Heuristic;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Max";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Min;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Min";
     lr.focused = false;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
     // The default policy doesn't allow group switching. Verify that no
     // group switches are performed.
@@ -1334,17 +1232,16 @@
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
 
-    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+
     RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1353,17 +1250,16 @@
     layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+
     RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
     // Verify that we won't change the group if seamless switch is required.
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1373,19 +1269,18 @@
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+    configs.setCurrentModeId(kModeId90);
 
     // Verify that we won't do a seamless switch if we request the same mode as the default
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1395,19 +1290,18 @@
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
-    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+    configs.setCurrentModeId(kModeId90);
 
     // Verify that if the current config is in another group and there are no layers with
     // seamlessness=SeamedAndSeamless we'll go back to the default group.
@@ -1420,19 +1314,18 @@
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
 
-    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+    configs.setCurrentModeId(kModeId90);
 
     // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
     // seamlessness=OnlySeamless can't change the mode group.
@@ -1450,19 +1343,18 @@
     layers[1].name = "90Hz ExplicitDefault";
     layers[1].focused = false;
 
-    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+    configs.setCurrentModeId(kModeId90);
 
     // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
     // seamlessness=Default can't change the mode group back to the group of the default
@@ -1484,19 +1376,18 @@
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+    RefreshRateConfigs::Policy policy;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+
+    configs.setCurrentModeId(kModeId90);
 
     // Layer with seamlessness=Default can change the mode group if there's a not
     // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
@@ -1515,19 +1406,17 @@
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
 
     // Allow group switching.
     RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1537,22 +1426,20 @@
     layer.name = "60Hz ExplicitExactOrMultiple";
     layer.focused = true;
 
-    ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
-    ASSERT_EQ(HWC_CONFIG_ID_120, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    configs.setCurrentModeId(kModeId120);
+    EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m25_30_50_60Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60);
 
     // Allow group switching.
     RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
     std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
                                              .vote = LayerVoteType::ExplicitDefault,
@@ -1567,37 +1454,33 @@
                                              .weight = 1.f,
                                              .focused = true}};
 
-    ASSERT_EQ(HWC_CONFIG_ID_50, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers).getModeId());
 
     auto& seamedLayer = layers[0];
     seamedLayer.desiredRefreshRate = 30_Hz;
     seamedLayer.name = "30Hz ExplicitDefault";
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30);
+    configs.setCurrentModeId(kModeId30);
 
-    ASSERT_EQ(HWC_CONFIG_ID_25, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90);
 
     // Allow group switching.
     RefreshRateConfigs::Policy policy;
-    policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
     std::vector<LayerRequirement> layers = {
             {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
 
-    ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
@@ -1613,50 +1496,45 @@
         layers[0].vote = voteType;
         layers[0].desiredRefreshRate = fps;
         layers[0].focused = args.focused;
-        return refreshRateConfigs->getBestRefreshRate(layers, {.touch = args.touch}).getModeId();
+        return configs.getBestRefreshRate(layers, {.touch = args.touch}).getModeId();
     };
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}),
-              0);
-    EXPECT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate({}, {}).getModeId());
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), 0);
+
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate().getModeId());
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+    EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+    EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
 
     // Unfocused layers are not allowed to override primary config.
-    EXPECT_EQ(HWC_CONFIG_ID_60,
-              getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
-    EXPECT_EQ(HWC_CONFIG_ID_60,
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
+    EXPECT_EQ(kModeId60,
               getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false}));
 
     // Touch boost should be restricted to the primary range.
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true}));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true}));
+
     // When we're higher than the primary range max due to a layer frame rate setting, touch boost
     // shouldn't drag us back down to the primary range max.
-    EXPECT_EQ(HWC_CONFIG_ID_90,
-              getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true}));
-    EXPECT_EQ(HWC_CONFIG_ID_60,
+    EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true}));
+    EXPECT_EQ(kModeId60,
               getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}),
-              0);
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}), 0);
+
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
 }
 
 TEST_F(RefreshRateConfigsTest, idle) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
@@ -1664,82 +1542,70 @@
     const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
         layers[0].vote = voteType;
         layers[0].desiredRefreshRate = 90_Hz;
-        RefreshRateConfigs::GlobalSignals consideredSignals;
-        const auto configId =
-                refreshRateConfigs
-                        ->getBestRefreshRate(layers, {.touch = touchActive, .idle = true},
-                                             &consideredSignals)
-                        .getModeId();
-        // Refresh rate will be chosen by either touch state or idle state
-        EXPECT_EQ(!touchActive, consideredSignals.idle);
-        return configId;
+
+        const auto [refreshRate, signals] =
+                configs.getBestRefreshRateAndSignals(layers, {.touch = touchActive, .idle = true});
+
+        // Refresh rate will be chosen by either touch state or idle state.
+        EXPECT_EQ(!touchActive, signals.idle);
+        return refreshRate.getModeId();
     };
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
-                      {HWC_CONFIG_ID_60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
-              0);
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
 
     // Idle should be lower priority than touch boost.
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/true));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/true));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/true));
-    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/true));
-    EXPECT_EQ(HWC_CONFIG_ID_90,
-              getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/true));
-    EXPECT_EQ(HWC_CONFIG_ID_90,
-              getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/true));
+    {
+        constexpr bool kTouchActive = true;
+        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
+        EXPECT_EQ(kModeId90,
+                  getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+    }
 
     // With no layers, idle should still be lower priority than touch boost.
-    EXPECT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true})
-                      .getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true}).getModeId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, /*touchActive=*/false));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/false));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/false));
-    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/false));
-    EXPECT_EQ(HWC_CONFIG_ID_60,
-              getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/false));
-    EXPECT_EQ(HWC_CONFIG_ID_60,
-              getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/false));
+    configs.setCurrentModeId(kModeId90);
+
+    {
+        constexpr bool kTouchActive = false;
+        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
+        EXPECT_EQ(kModeId60,
+                  getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+    }
 
     // Idle should be applied rather than the current config when there are no layers.
-    EXPECT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getBestRefreshRate({}, {.idle = true}).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true}).getModeId());
 }
 
 TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
-        const auto knownFrameRate =
-                findClosestKnownFrameRate(*refreshRateConfigs, Fps::fromValue(fps));
-        Fps expectedFrameRate;
-        if (fps < 26.91f) {
-            expectedFrameRate = 24_Hz;
-        } else if (fps < 37.51f) {
-            expectedFrameRate = 30_Hz;
-        } else if (fps < 52.51f) {
-            expectedFrameRate = 45_Hz;
-        } else if (fps < 66.01f) {
-            expectedFrameRate = 60_Hz;
-        } else if (fps < 81.01f) {
-            expectedFrameRate = 72_Hz;
-        } else {
-            expectedFrameRate = 90_Hz;
-        }
+        const auto knownFrameRate = configs.findClosestKnownFrameRate(Fps::fromValue(fps));
+        const Fps expectedFrameRate = [fps] {
+            if (fps < 26.91f) return 24_Hz;
+            if (fps < 37.51f) return 30_Hz;
+            if (fps < 52.51f) return 45_Hz;
+            if (fps < 66.01f) return 60_Hz;
+            if (fps < 81.01f) return 72_Hz;
+            return 90_Hz;
+        }();
+
         EXPECT_EQ(expectedFrameRate, knownFrameRate);
     }
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
     struct Expectation {
         Fps fps;
@@ -1747,13 +1613,14 @@
     };
 
     const std::initializer_list<Expectation> knownFrameRatesExpectations = {
-            {24_Hz, mExpected60Config}, {30_Hz, mExpected60Config}, {45_Hz, mExpected90Config},
-            {60_Hz, mExpected60Config}, {72_Hz, mExpected90Config}, {90_Hz, mExpected90Config},
+            {24_Hz, asRefreshRate(kMode60)}, {30_Hz, asRefreshRate(kMode60)},
+            {45_Hz, asRefreshRate(kMode90)}, {60_Hz, asRefreshRate(kMode60)},
+            {72_Hz, asRefreshRate(kMode90)}, {90_Hz, asRefreshRate(kMode90)},
     };
 
     // Make sure the test tests all the known frame rate
-    const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs);
-    const bool equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
+    const auto& knownFrameRates = configs.knownFrameRates();
+    const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(),
                                   knownFrameRatesExpectations.begin(),
                                   [](Fps fps, const Expectation& expected) {
                                       return isApproxEqual(fps, expected.fps);
@@ -1766,14 +1633,12 @@
 
     for (const auto& [fps, refreshRate] : knownFrameRatesExpectations) {
         layer.desiredRefreshRate = fps;
-        EXPECT_EQ(refreshRate, refreshRateConfigs->getBestRefreshRate(layers, {}));
+        EXPECT_EQ(refreshRate, configs.getBestRefreshRate(layers));
     }
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
@@ -1787,28 +1652,26 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
     explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
-    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
+                                       {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
@@ -1822,92 +1685,55 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
     explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) {
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+
     using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+    const auto args = std::make_pair(std::vector<LayerRequirement>{},
+                                     GlobalSignals{.touch = true, .idle = true});
+    const auto result = std::make_pair(asRefreshRate(kMode90), GlobalSignals{.touch = true});
 
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    configs.mutableGetBestRefreshRateCache() = {args, result};
 
-    setLastBestRefreshRateInvocation(*refreshRateConfigs,
-                                     GetBestRefreshRateInvocation{.globalSignals = {.touch = true,
-                                                                                    .idle = true},
-                                                                  .outSignalsConsidered =
-                                                                          {.touch = true},
-                                                                  .resultingBestRefreshRate =
-                                                                          createRefreshRate(
-                                                                                  mConfig90)});
-
-    EXPECT_EQ(createRefreshRate(mConfig90),
-              refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true}));
-
-    const GlobalSignals cachedSignalsConsidered{.touch = true};
-    setLastBestRefreshRateInvocation(*refreshRateConfigs,
-                                     GetBestRefreshRateInvocation{.globalSignals = {.touch = true,
-                                                                                    .idle = true},
-                                                                  .outSignalsConsidered =
-                                                                          cachedSignalsConsidered,
-                                                                  .resultingBestRefreshRate =
-                                                                          createRefreshRate(
-                                                                                  mConfig30)});
-
-    GlobalSignals signalsConsidered;
-    EXPECT_EQ(createRefreshRate(mConfig30),
-              refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true},
-                                                     &signalsConsidered));
-
-    EXPECT_EQ(cachedSignalsConsidered, signalsConsidered);
+    EXPECT_EQ(result, configs.getBestRefreshRateAndSignals(args.first, args.second));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
-    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
 
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-    ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value());
+    EXPECT_FALSE(configs.mutableGetBestRefreshRateCache());
 
-    GlobalSignals globalSignals{.touch = true, .idle = true};
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
-    const auto lastResult =
-            refreshRateConfigs->getBestRefreshRate(layers, globalSignals,
-                                                   /* outSignalsConsidered */ nullptr);
+    RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true};
 
-    const auto lastInvocation = getLastBestRefreshRateInvocation(*refreshRateConfigs);
+    const auto result = configs.getBestRefreshRateAndSignals(layers, globalSignals);
 
-    ASSERT_TRUE(lastInvocation.has_value());
-    ASSERT_EQ(layers, lastInvocation->layerRequirements);
-    ASSERT_EQ(globalSignals, lastInvocation->globalSignals);
-    ASSERT_EQ(lastResult, lastInvocation->resultingBestRefreshRate);
+    const auto& cache = configs.mutableGetBestRefreshRateCache();
+    ASSERT_TRUE(cache);
 
-    // outSignalsConsidered needs to be populated even tho earlier we gave nullptr
-    // to getBestRefreshRate()
-    GlobalSignals detaultSignals;
-    ASSERT_FALSE(detaultSignals == lastInvocation->outSignalsConsidered);
+    EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
+    EXPECT_EQ(cache->result, result);
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) {
-    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+    TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
@@ -1921,20 +1747,18 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
 
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers, {.touch = true}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
-    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+    TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60,
+                                       {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
     auto& explicitDefaultLayer = layers[0];
@@ -1948,32 +1772,27 @@
     explicitDefaultLayer.name = "ExplicitDefault";
     explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
 
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
 }
 
 // b/190578904
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) {
     constexpr int kMinRefreshRate = 10;
     constexpr int kMaxRefreshRate = 240;
 
     DisplayModes displayModes;
     for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
-        constexpr int32_t kGroup = 0;
-        const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
         displayModes.push_back(
-                createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs()));
+                createDisplayMode(DisplayModeId(fps), Fps::fromValue(static_cast<float>(fps))));
     }
 
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(displayModes,
-                                                 /*currentConfigId=*/displayModes[0]->getId());
+    const TestableRefreshRateConfigs configs(displayModes, displayModes[0]->getId());
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
         layers[0].desiredRefreshRate = fps;
         layers[0].vote = vote;
-        EXPECT_EQ(fps.getIntValue(),
-                  refreshRateConfigs->getBestRefreshRate(layers, {}).getFps().getIntValue())
+        EXPECT_EQ(fps.getIntValue(), configs.getBestRefreshRate(layers).getFps().getIntValue())
                 << "Failed for " << ftl::enum_string(vote);
     };
 
@@ -1989,25 +1808,23 @@
 // b/190578904
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
     const DisplayModes displayModes = {
-            createDisplayMode(DisplayModeId(0), 0, (43_Hz).getPeriodNsecs()),
-            createDisplayMode(DisplayModeId(1), 0, (53_Hz).getPeriodNsecs()),
-            createDisplayMode(DisplayModeId(2), 0, (55_Hz).getPeriodNsecs()),
-            createDisplayMode(DisplayModeId(3), 0, (60_Hz).getPeriodNsecs()),
+            createDisplayMode(DisplayModeId(0), 43_Hz),
+            createDisplayMode(DisplayModeId(1), 53_Hz),
+            createDisplayMode(DisplayModeId(2), 55_Hz),
+            createDisplayMode(DisplayModeId(3), 60_Hz),
     };
 
     const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(displayModes,
-                                                 /*currentConfigId=*/displayModes[0]->getId());
+    const TestableRefreshRateConfigs configs(displayModes, displayModes[0]->getId());
 
-    const auto layers = std::vector<LayerRequirement>{
-            LayerRequirement{
+    const std::vector<LayerRequirement> layers = {
+            {
                     .vote = LayerVoteType::ExplicitDefault,
                     .desiredRefreshRate = 43_Hz,
                     .seamlessness = Seamlessness::SeamedAndSeamless,
                     .weight = 0.41f,
             },
-            LayerRequirement{
+            {
                     .vote = LayerVoteType::ExplicitExactOrMultiple,
                     .desiredRefreshRate = 53_Hz,
                     .seamlessness = Seamlessness::SeamedAndSeamless,
@@ -2015,89 +1832,83 @@
             },
     };
 
-    EXPECT_EQ(53_Hz, refreshRateConfigs->getBestRefreshRate(layers, globalSignals).getFps());
+    EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals).getFps());
 }
 
 TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
-    EXPECT_TRUE(mExpected60Config < mExpected90Config);
-    EXPECT_FALSE(mExpected60Config < mExpected60Config);
-    EXPECT_FALSE(mExpected90Config < mExpected90Config);
+    EXPECT_TRUE(asRefreshRate(kMode60) < asRefreshRate(kMode90));
+    EXPECT_FALSE(asRefreshRate(kMode60) < asRefreshRate(kMode60));
+    EXPECT_FALSE(asRefreshRate(kMode90) < asRefreshRate(kMode90));
 }
 
 TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
-    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+    using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
 
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+    RefreshRateConfigs configs(kModes_60_90, kModeId90);
+
     // SetPolicy(60, 90), current 90Hz => TurnOn.
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
     // SetPolicy(60, 90), current 60Hz => TurnOn.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 90_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
     // SetPolicy(60, 60), current 60Hz => TurnOff
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 
     // SetPolicy(90, 90), current 90Hz => TurnOff.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 }
 
 TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
-    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+    using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
 
-    // Tests with 120Hz
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_120);
+    RefreshRateConfigs configs(kModes_60_120, kModeId120);
+
     // SetPolicy(0, 60), current 60Hz => TurnOn.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0_Hz, 60_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
     // SetPolicy(60, 60), current 60Hz => TurnOff.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 
     // SetPolicy(60, 120), current 60Hz => TurnOn.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 120_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
     // SetPolicy(120, 120), current 120Hz => TurnOff.
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_120, {120_Hz, 120_Hz}}),
-              0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateDivider) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_30);
+    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
 
     const auto frameRate = 30_Hz;
-    Fps displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    Fps displayRefreshRate = configs.getCurrentRefreshRate().getFps();
     EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_60);
-    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    configs.setCurrentModeId(kModeId60);
+    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
     EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_72);
-    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    configs.setCurrentModeId(kModeId72);
+    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
-    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    configs.setCurrentModeId(kModeId90);
+    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
     EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
-    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    configs.setCurrentModeId(kModeId120);
+    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
 
-    refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
-    displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
+    configs.setCurrentModeId(kModeId90);
+    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, 22.5_Hz));
 
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(24_Hz, 25_Hz));
@@ -2133,57 +1944,52 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120);
+    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120);
 
-    ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides({}, 120_Hz, {}).empty());
+    EXPECT_TRUE(configs.getFrameRateOverrides({}, 120_Hz, {}).empty());
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
-    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120, config);
+    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
+                               {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
     layers[0].ownerUid = 1234;
     layers[0].desiredRefreshRate = 60_Hz;
     layers[0].vote = LayerVoteType::ExplicitDefault;
-    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_EQ(1, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_EQ(1, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::NoVote;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_TRUE(frameRateOverrides.empty());
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Min;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_TRUE(frameRateOverrides.empty());
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Max;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_TRUE(frameRateOverrides.empty());
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_TRUE(frameRateOverrides.empty());
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_TRUE(frameRateOverrides.empty());
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) {
-    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120, config);
+    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
+                               {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
                                             {.ownerUid = 5678, .weight = 1.f}};
@@ -2195,69 +2001,64 @@
     layers[1].name = "Test layer 5678";
     layers[1].desiredRefreshRate = 30_Hz;
     layers[1].vote = LayerVoteType::ExplicitDefault;
-    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
+    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
 
-    ASSERT_EQ(2, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
-    ASSERT_EQ(1, frameRateOverrides.count(5678));
-    ASSERT_EQ(30_Hz, frameRateOverrides.at(5678));
+    EXPECT_EQ(2u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+    ASSERT_EQ(1u, frameRateOverrides.count(5678));
+    EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
 
     layers[1].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_EQ(1, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[1].ownerUid = 1234;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_TRUE(frameRateOverrides.empty());
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_TRUE(frameRateOverrides.empty());
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) {
-    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120, config);
+    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
+                               {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
     layers[0].name = "Test layer";
     layers[0].desiredRefreshRate = 60_Hz;
     layers[0].vote = LayerVoteType::ExplicitDefault;
 
-    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_EQ(1, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
+    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
-    ASSERT_EQ(1, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExact;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_EQ(1, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
-    ASSERT_EQ(1, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
-    ASSERT_EQ(1, frameRateOverrides.size());
-    ASSERT_EQ(1, frameRateOverrides.count(1234));
-    ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
-    ASSERT_TRUE(frameRateOverrides.empty());
+    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    EXPECT_TRUE(frameRateOverrides.empty());
 }
 
 } // namespace
 } // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 4273401..261e106 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -19,6 +19,7 @@
 #include <gmock/gmock.h>
 
 #include "DisplayHardware/ComposerHal.h"
+#include "DisplayHardware/HWC2.h"
 
 namespace android {
 
@@ -51,7 +52,7 @@
     MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override));
     MOCK_METHOD0(getCapabilities, std::vector<IComposer::Capability>());
     MOCK_METHOD0(dumpDebugInfo, std::string());
-    MOCK_METHOD1(registerCallback, void(const sp<IComposerCallback>&));
+    MOCK_METHOD1(registerCallback, void(HWC2::ComposerCallback&));
     MOCK_METHOD0(resetCommands, void());
     MOCK_METHOD0(executeCommands, Error());
     MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());