Merge "A bunch of improvements to recoverymap calculations."
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 4a0f2ec..b3b4fb1 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -41,6 +41,7 @@
           "include-filter": "android.view.cts.input",
           "include-filter": "android.view.cts.MotionEventTest",
           "include-filter": "android.view.cts.PointerCaptureTest",
+          "include-filter": "android.view.cts.TooltipTest",
           "include-filter": "android.view.cts.VerifyInputEventTest"
         }
       ]
@@ -128,6 +129,7 @@
         {
           "include-filter": "android.view.cts.MotionEventTest",
           "include-filter": "android.view.cts.PointerCaptureTest",
+          "include-filter": "android.view.cts.TooltipTest",
           "include-filter": "android.view.cts.VerifyInputEventTest"
         }
       ]
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 87a4ff4..eb938c8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1769,15 +1769,16 @@
 
 void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
     if (DEBUG_OUTBOUND_EVENT_DETAILS) {
-        ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+        ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%" PRId32
               ", policyFlags=0x%x, "
               "action=%s, actionButton=0x%x, flags=0x%x, "
               "metaState=0x%x, buttonState=0x%x,"
               "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
-              prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
-              entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
-              entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
-              entry.xPrecision, entry.yPrecision, entry.downTime);
+              prefix, entry.eventTime, entry.deviceId,
+              inputEventSourceToString(entry.source).c_str(), entry.displayId, entry.policyFlags,
+              MotionEvent::actionToString(entry.action).c_str(), entry.actionButton, entry.flags,
+              entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision,
+              entry.yPrecision, entry.downTime);
 
         for (uint32_t i = 0; i < entry.pointerCount; i++) {
             ALOGD("  Pointer %d: id=%d, toolType=%d, "
@@ -2502,6 +2503,10 @@
         }
     }
 
+    if (tempTouchState.windows.empty()) {
+        mTouchStatesByDisplay.erase(displayId);
+    }
+
     // Update hover state.
     mLastHoverWindowHandle = newHoverWindowHandle;
 
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index ee7da93..114e0bf 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -148,7 +148,8 @@
 
 std::string TouchState::dump() const {
     std::string out;
-    out += StringPrintf("deviceId=%d, source=0x%08x\n", deviceId, source);
+    out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
+                        inputEventSourceToString(source).c_str());
     if (!windows.empty()) {
         out += "  Windows:\n";
         for (size_t i = 0; i < windows.size(); i++) {
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index ccff353..b193dff 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -119,6 +119,18 @@
             if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
                 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
             }
+        } else if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS && !mStylusMtToolSeen) {
+            mStylusMtToolSeen = true;
+            // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically
+            // re-configure this input device so that we add SOURCE_STYLUS if we haven't already.
+            // This is to cover the case where we cannot reliably detect whether a multi-touch
+            // device will ever produce stylus events when it is initially being configured.
+            if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) {
+                // Add the stylus source immediately so that it is included in any events generated
+                // before we have a chance to re-configure the device.
+                mSource |= AINPUT_SOURCE_STYLUS;
+                bumpGeneration();
+            }
         }
         if (shouldSimulateStylusWithTouch() &&
             outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
@@ -200,7 +212,8 @@
 }
 
 bool MultiTouchInputMapper::hasStylus() const {
-    return mTouchButtonAccumulator.hasStylus() || shouldSimulateStylusWithTouch();
+    return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
+            shouldSimulateStylusWithTouch();
 }
 
 bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index ddf9e80..5f8bccf 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -50,6 +50,8 @@
     // Specifies the pointer id bits that are in use, and their associated tracking id.
     BitSet32 mPointerIdBits;
     int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
+
+    bool mStylusMtToolSeen{false};
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index bc55fe5..5be694f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -3763,15 +3763,12 @@
     PointerCoords pointerCoords[MAX_POINTERS];
     PointerProperties pointerProperties[MAX_POINTERS];
     uint32_t pointerCount = 0;
-    bool stylusToolFound = false;
     while (!idBits.isEmpty()) {
         uint32_t id = idBits.clearFirstMarkedBit();
         uint32_t index = idToIndex[id];
         pointerProperties[pointerCount].copyFrom(properties[index]);
         pointerCoords[pointerCount].copyFrom(coords[index]);
 
-        stylusToolFound |= isStylusToolType(pointerProperties[pointerCount].toolType);
-
         if (changedId >= 0 && id == uint32_t(changedId)) {
             action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
         }
@@ -3803,12 +3800,6 @@
     if (mDeviceMode == DeviceMode::POINTER) {
         mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
     }
-    if (stylusToolFound) {
-        // Dynamically add the stylus source when there's a stylus tool being used to cover the case
-        // where we cannot reliably detect whether a multi-touch device will ever produce stylus
-        // events when it is initially being configured.
-        source |= AINPUT_SOURCE_STYLUS;
-    }
     const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
     const int32_t deviceId = getDeviceId();
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index aaf50ce..689fe5c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -22,6 +22,7 @@
 #include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
 #include <fcntl.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <input/Input.h>
 #include <linux/input.h>
@@ -43,6 +44,7 @@
 namespace android::inputdispatcher {
 
 using namespace ftl::flag_operators;
+using testing::AllOf;
 
 // An arbitrary time value.
 static constexpr nsecs_t ARBITRARY_TIME = 1234;
@@ -102,6 +104,28 @@
             << MotionEvent::actionToString(receivedAction);
 }
 
+MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
+    bool matches = action == arg.getAction();
+    if (!matches) {
+        *result_listener << "expected action " << MotionEvent::actionToString(action)
+                         << ", but got " << MotionEvent::actionToString(arg.getAction());
+    }
+    if (action == AMOTION_EVENT_ACTION_CANCEL) {
+        if (!matches) {
+            *result_listener << "; ";
+        }
+        *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
+        matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+    }
+    return matches;
+}
+
+MATCHER_P(WithSource, source, "InputEvent with specified source") {
+    *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
+                     << inputEventSourceToString(arg.getSource());
+    return arg.getSource() == source;
+}
+
 // --- FakeInputDispatcherPolicy ---
 
 class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -1205,6 +1229,12 @@
         mInputReceiver->consumeCaptureEvent(hasCapture);
     }
 
+    void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+        MotionEvent* motionEvent = consumeMotion();
+        ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event";
+        ASSERT_THAT(*motionEvent, matcher);
+    }
+
     void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
                       std::optional<int32_t> expectedDisplayId,
                       std::optional<int32_t> expectedFlags) {
@@ -2207,6 +2237,48 @@
                          ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
 }
 
+/**
+ * Inject a mouse hover event followed by a tap from touchscreen.
+ * In the current implementation, the tap does not cause a HOVER_EXIT event.
+ */
+TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+    window->setFrame(Rect(0, 0, 100, 100));
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    // Inject a hover_move from mouse.
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE,
+                               ADISPLAY_ID_DEFAULT, {{50, 50}});
+    motionArgs.xCursorPosition = 50;
+    motionArgs.yCursorPosition = 50;
+    mDispatcher->notifyMotion(&motionArgs);
+    ASSERT_NO_FATAL_FAILURE(
+            window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+                                             WithSource(AINPUT_SOURCE_MOUSE))));
+    ASSERT_NO_FATAL_FAILURE(
+            window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                                             WithSource(AINPUT_SOURCE_MOUSE))));
+
+    // Tap on the window
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {{10, 10}});
+    mDispatcher->notifyMotion(&motionArgs);
+    ASSERT_NO_FATAL_FAILURE(
+            window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                                             WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {{10, 10}});
+    mDispatcher->notifyMotion(&motionArgs);
+    ASSERT_NO_FATAL_FAILURE(
+            window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                                             WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
+}
+
 TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 5c5fc77..6b6d0bb 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -10788,11 +10788,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
-TEST_F(MultiTouchInputMapperTest, ToolTypeSource) {
+TEST_F(MultiTouchInputMapperTest, StylusSourceIsAddedDynamicallyFromToolType) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
 
     // Even if the device supports reporting the ABS_MT_TOOL_TYPE axis, which could give it the
     // ability to report MT_TOOL_PEN, we do not report the device as coming from a stylus source.
@@ -10801,7 +10802,7 @@
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 
     // However, if the device ever ends up reporting an event with MT_TOOL_PEN, it should be
-    // reported with the stylus source, even through the device doesn't support the stylus source.
+    // reported with the stylus source.
     processId(mapper, FIRST_TRACKING_ID);
     processToolType(mapper, MT_TOOL_PEN);
     processPosition(mapper, 100, 200);
@@ -10812,15 +10813,27 @@
                   WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
                   WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
 
+    // Now that we know the device supports styluses, ensure that the device is re-configured with
+    // the stylus source.
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, mapper.getSources());
+    {
+        const auto& devices = mReader->getInputDevices();
+        auto deviceInfo =
+                std::find_if(devices.begin(), devices.end(),
+                             [](const InputDeviceInfo& info) { return info.getId() == DEVICE_ID; });
+        LOG_ALWAYS_FATAL_IF(deviceInfo == devices.end(), "Cannot find InputDevice");
+        ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, deviceInfo->getSources());
+    }
+
+    // Ensure the device was not reset to prevent interruptions of any ongoing gestures.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+
     processId(mapper, INVALID_TRACKING_ID);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
             AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
                   WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
                   WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
-
-    // The mapper should still report only a touchscreen source.
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 }
 
 // --- MultiTouchInputMapperTest_ExternalDevice ---
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 8721bd8..9db3422 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -46,7 +46,8 @@
 }
 
 MATCHER_P(WithSource, source, "InputEvent with specified source") {
-    *result_listener << "expected source " << source << ", but got " << arg.source;
+    *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
+                     << inputEventSourceToString(arg.source);
     return arg.source == source;
 }
 
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index f67c610..f5b360f 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -23,6 +23,7 @@
 #include <aidlcommonsupport/NativeHandle.h>
 #include <android-base/logging.h>
 #include <android/binder_manager.h>
+#include <aidl/sensors/convert.h>
 
 using ::aidl::android::hardware::sensors::AdditionalInfo;
 using ::aidl::android::hardware::sensors::DynamicSensorInfo;
@@ -34,469 +35,16 @@
 using ::android::AidlMessageQueue;
 using ::android::hardware::EventFlag;
 using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT;
+using ::android::hardware::sensors::implementation::convertToStatus;
+using ::android::hardware::sensors::implementation::convertToSensor;
+using ::android::hardware::sensors::implementation::convertToSensorEvent;
+using ::android::hardware::sensors::implementation::convertFromSensorEvent;
+
 
 namespace android {
 
 namespace {
 
-status_t convertToStatus(ndk::ScopedAStatus status) {
-    if (status.isOk()) {
-        return OK;
-    } else {
-        switch (status.getExceptionCode()) {
-            case EX_ILLEGAL_ARGUMENT: {
-                return BAD_VALUE;
-            }
-            case EX_SECURITY: {
-                return PERMISSION_DENIED;
-            }
-            case EX_UNSUPPORTED_OPERATION: {
-                return INVALID_OPERATION;
-            }
-            case EX_SERVICE_SPECIFIC: {
-                switch (status.getServiceSpecificError()) {
-                    case ISensors::ERROR_BAD_VALUE: {
-                        return BAD_VALUE;
-                    }
-                    case ISensors::ERROR_NO_MEMORY: {
-                        return NO_MEMORY;
-                    }
-                    default: {
-                        return UNKNOWN_ERROR;
-                    }
-                }
-            }
-            default: {
-                return UNKNOWN_ERROR;
-            }
-        }
-    }
-}
-
-void convertToSensor(const SensorInfo &src, sensor_t *dst) {
-    dst->name = strdup(src.name.c_str());
-    dst->vendor = strdup(src.vendor.c_str());
-    dst->version = src.version;
-    dst->handle = src.sensorHandle;
-    dst->type = (int)src.type;
-    dst->maxRange = src.maxRange;
-    dst->resolution = src.resolution;
-    dst->power = src.power;
-    dst->minDelay = src.minDelayUs;
-    dst->fifoReservedEventCount = src.fifoReservedEventCount;
-    dst->fifoMaxEventCount = src.fifoMaxEventCount;
-    dst->stringType = strdup(src.typeAsString.c_str());
-    dst->requiredPermission = strdup(src.requiredPermission.c_str());
-    dst->maxDelay = src.maxDelayUs;
-    dst->flags = src.flags;
-    dst->reserved[0] = dst->reserved[1] = 0;
-}
-
-void convertToSensorEvent(const Event &src, sensors_event_t *dst) {
-    *dst = {.version = sizeof(sensors_event_t),
-            .sensor = src.sensorHandle,
-            .type = (int32_t)src.sensorType,
-            .reserved0 = 0,
-            .timestamp = src.timestamp};
-
-    switch (src.sensorType) {
-        case SensorType::META_DATA: {
-            // Legacy HALs expect the handle reference in the meta data field.
-            // Copy it over from the handle of the event.
-            dst->meta_data.what = (int32_t)src.payload.get<Event::EventPayload::meta>().what;
-            dst->meta_data.sensor = src.sensorHandle;
-            // Set the sensor handle to 0 to maintain compatibility.
-            dst->sensor = 0;
-            break;
-        }
-
-        case SensorType::ACCELEROMETER:
-        case SensorType::MAGNETIC_FIELD:
-        case SensorType::ORIENTATION:
-        case SensorType::GYROSCOPE:
-        case SensorType::GRAVITY:
-        case SensorType::LINEAR_ACCELERATION: {
-            dst->acceleration.x = src.payload.get<Event::EventPayload::vec3>().x;
-            dst->acceleration.y = src.payload.get<Event::EventPayload::vec3>().y;
-            dst->acceleration.z = src.payload.get<Event::EventPayload::vec3>().z;
-            dst->acceleration.status = (int32_t)src.payload.get<Event::EventPayload::vec3>().status;
-            break;
-        }
-
-        case SensorType::GAME_ROTATION_VECTOR: {
-            dst->data[0] = src.payload.get<Event::EventPayload::vec4>().x;
-            dst->data[1] = src.payload.get<Event::EventPayload::vec4>().y;
-            dst->data[2] = src.payload.get<Event::EventPayload::vec4>().z;
-            dst->data[3] = src.payload.get<Event::EventPayload::vec4>().w;
-            break;
-        }
-
-        case SensorType::ROTATION_VECTOR:
-        case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
-            dst->data[0] = src.payload.get<Event::EventPayload::data>().values[0];
-            dst->data[1] = src.payload.get<Event::EventPayload::data>().values[1];
-            dst->data[2] = src.payload.get<Event::EventPayload::data>().values[2];
-            dst->data[3] = src.payload.get<Event::EventPayload::data>().values[3];
-            dst->data[4] = src.payload.get<Event::EventPayload::data>().values[4];
-            break;
-        }
-
-        case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
-        case SensorType::GYROSCOPE_UNCALIBRATED:
-        case SensorType::ACCELEROMETER_UNCALIBRATED: {
-            dst->uncalibrated_gyro.x_uncalib = src.payload.get<Event::EventPayload::uncal>().x;
-            dst->uncalibrated_gyro.y_uncalib = src.payload.get<Event::EventPayload::uncal>().y;
-            dst->uncalibrated_gyro.z_uncalib = src.payload.get<Event::EventPayload::uncal>().z;
-            dst->uncalibrated_gyro.x_bias = src.payload.get<Event::EventPayload::uncal>().xBias;
-            dst->uncalibrated_gyro.y_bias = src.payload.get<Event::EventPayload::uncal>().yBias;
-            dst->uncalibrated_gyro.z_bias = src.payload.get<Event::EventPayload::uncal>().zBias;
-            break;
-        }
-
-        case SensorType::HINGE_ANGLE:
-        case SensorType::DEVICE_ORIENTATION:
-        case SensorType::LIGHT:
-        case SensorType::PRESSURE:
-        case SensorType::PROXIMITY:
-        case SensorType::RELATIVE_HUMIDITY:
-        case SensorType::AMBIENT_TEMPERATURE:
-        case SensorType::SIGNIFICANT_MOTION:
-        case SensorType::STEP_DETECTOR:
-        case SensorType::TILT_DETECTOR:
-        case SensorType::WAKE_GESTURE:
-        case SensorType::GLANCE_GESTURE:
-        case SensorType::PICK_UP_GESTURE:
-        case SensorType::WRIST_TILT_GESTURE:
-        case SensorType::STATIONARY_DETECT:
-        case SensorType::MOTION_DETECT:
-        case SensorType::HEART_BEAT:
-        case SensorType::LOW_LATENCY_OFFBODY_DETECT: {
-            dst->data[0] = src.payload.get<Event::EventPayload::scalar>();
-            break;
-        }
-
-        case SensorType::STEP_COUNTER: {
-            dst->u64.step_counter = src.payload.get<Event::EventPayload::stepCount>();
-            break;
-        }
-
-        case SensorType::HEART_RATE: {
-            dst->heart_rate.bpm = src.payload.get<Event::EventPayload::heartRate>().bpm;
-            dst->heart_rate.status =
-                    (int8_t)src.payload.get<Event::EventPayload::heartRate>().status;
-            break;
-        }
-
-        case SensorType::POSE_6DOF: { // 15 floats
-            for (size_t i = 0; i < 15; ++i) {
-                dst->data[i] = src.payload.get<Event::EventPayload::pose6DOF>().values[i];
-            }
-            break;
-        }
-
-        case SensorType::DYNAMIC_SENSOR_META: {
-            dst->dynamic_sensor_meta.connected =
-                    src.payload.get<Event::EventPayload::dynamic>().connected;
-            dst->dynamic_sensor_meta.handle =
-                    src.payload.get<Event::EventPayload::dynamic>().sensorHandle;
-            dst->dynamic_sensor_meta.sensor = NULL; // to be filled in later
-
-            memcpy(dst->dynamic_sensor_meta.uuid,
-                   src.payload.get<Event::EventPayload::dynamic>().uuid.values.data(), 16);
-
-            break;
-        }
-
-        case SensorType::ADDITIONAL_INFO: {
-            const AdditionalInfo &srcInfo = src.payload.get<Event::EventPayload::additional>();
-
-            additional_info_event_t *dstInfo = &dst->additional_info;
-            dstInfo->type = (int32_t)srcInfo.type;
-            dstInfo->serial = srcInfo.serial;
-
-            switch (srcInfo.payload.getTag()) {
-                case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: {
-                    const auto &values =
-                            srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>()
-                                    .values;
-                    CHECK_EQ(values.size() * sizeof(int32_t), sizeof(dstInfo->data_int32));
-                    memcpy(dstInfo->data_int32, values.data(), sizeof(dstInfo->data_int32));
-                    break;
-                }
-                case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: {
-                    const auto &values =
-                            srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>()
-                                    .values;
-                    CHECK_EQ(values.size() * sizeof(float), sizeof(dstInfo->data_float));
-                    memcpy(dstInfo->data_float, values.data(), sizeof(dstInfo->data_float));
-                    break;
-                }
-                default: {
-                    ALOGE("Invalid sensor additional info tag: %d", (int)srcInfo.payload.getTag());
-                }
-            }
-            break;
-        }
-
-        case SensorType::HEAD_TRACKER: {
-            const auto &ht = src.payload.get<Event::EventPayload::headTracker>();
-            dst->head_tracker.rx = ht.rx;
-            dst->head_tracker.ry = ht.ry;
-            dst->head_tracker.rz = ht.rz;
-            dst->head_tracker.vx = ht.vx;
-            dst->head_tracker.vy = ht.vy;
-            dst->head_tracker.vz = ht.vz;
-            dst->head_tracker.discontinuity_count = ht.discontinuityCount;
-            break;
-        }
-
-        case SensorType::ACCELEROMETER_LIMITED_AXES:
-        case SensorType::GYROSCOPE_LIMITED_AXES:
-            dst->limited_axes_imu.x = src.payload.get<Event::EventPayload::limitedAxesImu>().x;
-            dst->limited_axes_imu.y = src.payload.get<Event::EventPayload::limitedAxesImu>().y;
-            dst->limited_axes_imu.z = src.payload.get<Event::EventPayload::limitedAxesImu>().z;
-            dst->limited_axes_imu.x_supported =
-                    src.payload.get<Event::EventPayload::limitedAxesImu>().xSupported;
-            dst->limited_axes_imu.y_supported =
-                    src.payload.get<Event::EventPayload::limitedAxesImu>().ySupported;
-            dst->limited_axes_imu.z_supported =
-                    src.payload.get<Event::EventPayload::limitedAxesImu>().zSupported;
-            break;
-
-        case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
-        case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED:
-            dst->limited_axes_imu_uncalibrated.x_uncalib =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().x;
-            dst->limited_axes_imu_uncalibrated.y_uncalib =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().y;
-            dst->limited_axes_imu_uncalibrated.z_uncalib =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().z;
-            dst->limited_axes_imu_uncalibrated.x_bias =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xBias;
-            dst->limited_axes_imu_uncalibrated.y_bias =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().yBias;
-            dst->limited_axes_imu_uncalibrated.z_bias =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zBias;
-            dst->limited_axes_imu_uncalibrated.x_supported =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xSupported;
-            dst->limited_axes_imu_uncalibrated.y_supported =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().ySupported;
-            dst->limited_axes_imu_uncalibrated.z_supported =
-                    src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zSupported;
-            break;
-
-        case SensorType::HEADING:
-            dst->heading.heading = src.payload.get<Event::EventPayload::heading>().heading;
-            dst->heading.accuracy = src.payload.get<Event::EventPayload::heading>().accuracy;
-            break;
-
-        default: {
-            CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
-
-            memcpy(dst->data, src.payload.get<Event::EventPayload::data>().values.data(),
-                   16 * sizeof(float));
-            break;
-        }
-    }
-}
-
-void convertFromSensorEvent(const sensors_event_t &src, Event *dst) {
-    *dst = {
-            .timestamp = src.timestamp,
-            .sensorHandle = src.sensor,
-            .sensorType = (SensorType)src.type,
-    };
-
-    switch (dst->sensorType) {
-        case SensorType::META_DATA: {
-            Event::EventPayload::MetaData meta;
-            meta.what = (Event::EventPayload::MetaData::MetaDataEventType)src.meta_data.what;
-            // Legacy HALs contain the handle reference in the meta data field.
-            // Copy that over to the handle of the event. In legacy HALs this
-            // field was expected to be 0.
-            dst->sensorHandle = src.meta_data.sensor;
-            dst->payload.set<Event::EventPayload::Tag::meta>(meta);
-            break;
-        }
-
-        case SensorType::ACCELEROMETER:
-        case SensorType::MAGNETIC_FIELD:
-        case SensorType::ORIENTATION:
-        case SensorType::GYROSCOPE:
-        case SensorType::GRAVITY:
-        case SensorType::LINEAR_ACCELERATION: {
-            Event::EventPayload::Vec3 vec3;
-            vec3.x = src.acceleration.x;
-            vec3.y = src.acceleration.y;
-            vec3.z = src.acceleration.z;
-            vec3.status = (SensorStatus)src.acceleration.status;
-            dst->payload.set<Event::EventPayload::Tag::vec3>(vec3);
-            break;
-        }
-
-        case SensorType::GAME_ROTATION_VECTOR: {
-            Event::EventPayload::Vec4 vec4;
-            vec4.x = src.data[0];
-            vec4.y = src.data[1];
-            vec4.z = src.data[2];
-            vec4.w = src.data[3];
-            dst->payload.set<Event::EventPayload::Tag::vec4>(vec4);
-            break;
-        }
-
-        case SensorType::ROTATION_VECTOR:
-        case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
-            Event::EventPayload::Data data;
-            memcpy(data.values.data(), src.data, 5 * sizeof(float));
-            dst->payload.set<Event::EventPayload::Tag::data>(data);
-            break;
-        }
-
-        case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
-        case SensorType::GYROSCOPE_UNCALIBRATED:
-        case SensorType::ACCELEROMETER_UNCALIBRATED: {
-            Event::EventPayload::Uncal uncal;
-            uncal.x = src.uncalibrated_gyro.x_uncalib;
-            uncal.y = src.uncalibrated_gyro.y_uncalib;
-            uncal.z = src.uncalibrated_gyro.z_uncalib;
-            uncal.xBias = src.uncalibrated_gyro.x_bias;
-            uncal.yBias = src.uncalibrated_gyro.y_bias;
-            uncal.zBias = src.uncalibrated_gyro.z_bias;
-            dst->payload.set<Event::EventPayload::Tag::uncal>(uncal);
-            break;
-        }
-
-        case SensorType::DEVICE_ORIENTATION:
-        case SensorType::LIGHT:
-        case SensorType::PRESSURE:
-        case SensorType::PROXIMITY:
-        case SensorType::RELATIVE_HUMIDITY:
-        case SensorType::AMBIENT_TEMPERATURE:
-        case SensorType::SIGNIFICANT_MOTION:
-        case SensorType::STEP_DETECTOR:
-        case SensorType::TILT_DETECTOR:
-        case SensorType::WAKE_GESTURE:
-        case SensorType::GLANCE_GESTURE:
-        case SensorType::PICK_UP_GESTURE:
-        case SensorType::WRIST_TILT_GESTURE:
-        case SensorType::STATIONARY_DETECT:
-        case SensorType::MOTION_DETECT:
-        case SensorType::HEART_BEAT:
-        case SensorType::LOW_LATENCY_OFFBODY_DETECT:
-        case SensorType::HINGE_ANGLE: {
-            dst->payload.set<Event::EventPayload::Tag::scalar>((float)src.data[0]);
-            break;
-        }
-
-        case SensorType::STEP_COUNTER: {
-            dst->payload.set<Event::EventPayload::Tag::stepCount>(src.u64.step_counter);
-            break;
-        }
-
-        case SensorType::HEART_RATE: {
-            Event::EventPayload::HeartRate heartRate;
-            heartRate.bpm = src.heart_rate.bpm;
-            heartRate.status = (SensorStatus)src.heart_rate.status;
-            dst->payload.set<Event::EventPayload::Tag::heartRate>(heartRate);
-            break;
-        }
-
-        case SensorType::POSE_6DOF: { // 15 floats
-            Event::EventPayload::Pose6Dof pose6DOF;
-            for (size_t i = 0; i < 15; ++i) {
-                pose6DOF.values[i] = src.data[i];
-            }
-            dst->payload.set<Event::EventPayload::Tag::pose6DOF>(pose6DOF);
-            break;
-        }
-
-        case SensorType::DYNAMIC_SENSOR_META: {
-            DynamicSensorInfo dynamic;
-            dynamic.connected = src.dynamic_sensor_meta.connected;
-            dynamic.sensorHandle = src.dynamic_sensor_meta.handle;
-
-            memcpy(dynamic.uuid.values.data(), src.dynamic_sensor_meta.uuid, 16);
-            dst->payload.set<Event::EventPayload::Tag::dynamic>(dynamic);
-            break;
-        }
-
-        case SensorType::ADDITIONAL_INFO: {
-            AdditionalInfo info;
-            const additional_info_event_t &srcInfo = src.additional_info;
-            info.type = (AdditionalInfo::AdditionalInfoType)srcInfo.type;
-            info.serial = srcInfo.serial;
-
-            AdditionalInfo::AdditionalInfoPayload::Int32Values data;
-            CHECK_EQ(data.values.size() * sizeof(int32_t), sizeof(srcInfo.data_int32));
-            memcpy(data.values.data(), srcInfo.data_int32, sizeof(srcInfo.data_int32));
-            info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(data);
-
-            dst->payload.set<Event::EventPayload::Tag::additional>(info);
-            break;
-        }
-
-        case SensorType::HEAD_TRACKER: {
-            Event::EventPayload::HeadTracker headTracker;
-            headTracker.rx = src.head_tracker.rx;
-            headTracker.ry = src.head_tracker.ry;
-            headTracker.rz = src.head_tracker.rz;
-            headTracker.vx = src.head_tracker.vx;
-            headTracker.vy = src.head_tracker.vy;
-            headTracker.vz = src.head_tracker.vz;
-            headTracker.discontinuityCount = src.head_tracker.discontinuity_count;
-
-            dst->payload.set<Event::EventPayload::Tag::headTracker>(headTracker);
-            break;
-        }
-
-        case SensorType::ACCELEROMETER_LIMITED_AXES:
-        case SensorType::GYROSCOPE_LIMITED_AXES: {
-            Event::EventPayload::LimitedAxesImu limitedAxesImu;
-            limitedAxesImu.x = src.limited_axes_imu.x;
-            limitedAxesImu.y = src.limited_axes_imu.y;
-            limitedAxesImu.z = src.limited_axes_imu.z;
-            limitedAxesImu.xSupported = src.limited_axes_imu.x_supported;
-            limitedAxesImu.ySupported = src.limited_axes_imu.y_supported;
-            limitedAxesImu.zSupported = src.limited_axes_imu.z_supported;
-            dst->payload.set<Event::EventPayload::Tag::limitedAxesImu>(limitedAxesImu);
-            break;
-        }
-
-        case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
-        case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: {
-            Event::EventPayload::LimitedAxesImuUncal limitedAxesImuUncal;
-            limitedAxesImuUncal.x = src.limited_axes_imu_uncalibrated.x_uncalib;
-            limitedAxesImuUncal.y = src.limited_axes_imu_uncalibrated.y_uncalib;
-            limitedAxesImuUncal.z = src.limited_axes_imu_uncalibrated.z_uncalib;
-            limitedAxesImuUncal.xBias = src.limited_axes_imu_uncalibrated.x_bias;
-            limitedAxesImuUncal.yBias = src.limited_axes_imu_uncalibrated.y_bias;
-            limitedAxesImuUncal.zBias = src.limited_axes_imu_uncalibrated.z_bias;
-            limitedAxesImuUncal.xSupported = src.limited_axes_imu_uncalibrated.x_supported;
-            limitedAxesImuUncal.ySupported = src.limited_axes_imu_uncalibrated.y_supported;
-            limitedAxesImuUncal.zSupported = src.limited_axes_imu_uncalibrated.z_supported;
-            dst->payload.set<Event::EventPayload::Tag::limitedAxesImuUncal>(limitedAxesImuUncal);
-            break;
-        }
-
-        case SensorType::HEADING: {
-            Event::EventPayload::Heading heading;
-            heading.heading = src.heading.heading;
-            heading.accuracy = src.heading.accuracy;
-            dst->payload.set<Event::EventPayload::heading>(heading);
-            break;
-        }
-
-        default: {
-            CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
-
-            Event::EventPayload::Data data;
-            memcpy(data.values.data(), src.data, 16 * sizeof(float));
-            dst->payload.set<Event::EventPayload::Tag::data>(data);
-            break;
-        }
-    }
-}
-
 void serviceDied(void *cookie) {
     ALOGW("Sensors HAL died, attempting to reconnect.");
     ((AidlSensorHalWrapper *)cookie)->prepareForReconnect();
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 5ad4815..0eeb820 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -75,6 +75,7 @@
     static_libs: [
         "libaidlcommonsupport",
         "android.hardware.sensors@1.0-convert",
+        "android.hardware.sensors-V1-convert",
         "android.hardware.sensors-V1-ndk",
     ],
 
diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp
new file mode 100644
index 0000000..bbf49da
--- /dev/null
+++ b/services/sensorservice/aidl/Android.bp
@@ -0,0 +1,43 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_shared {
+    name: "libsensorserviceaidl",
+    srcs: [
+        "EventQueue.cpp",
+        "DirectReportChannel.cpp",
+        "SensorManager.cpp",
+        "utils.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    header_libs: ["jni_headers"],
+    shared_libs: [
+        "libbase",
+        "libutils",
+        "libcutils",
+        "libbinder_ndk",
+        "libsensor",
+        "android.frameworks.sensorservice-V1-ndk",
+        "android.hardware.sensors-V1-ndk",
+    ],
+    export_include_dirs: [
+        "include/",
+    ],
+    static_libs: [
+        "android.hardware.sensors-V1-convert",
+    ],
+
+    export_header_lib_headers: ["jni_headers"],
+    local_include_dirs: [
+        "include/sensorserviceaidl/",
+    ],
+}
diff --git a/services/sensorservice/aidl/DirectReportChannel.cpp b/services/sensorservice/aidl/DirectReportChannel.cpp
new file mode 100644
index 0000000..cab53c1
--- /dev/null
+++ b/services/sensorservice/aidl/DirectReportChannel.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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 "DirectReportChannel.h"
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+DirectReportChannel::DirectReportChannel(::android::SensorManager& manager, int channelId)
+      : mManager(manager), mId(channelId) {}
+
+DirectReportChannel::~DirectReportChannel() {
+    mManager.destroyDirectChannel(mId);
+}
+
+ndk::ScopedAStatus DirectReportChannel::configure(
+        int32_t sensorHandle, ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
+        int32_t* _aidl_return) {
+    int token = mManager.configureDirectChannel(mId, sensorHandle, static_cast<int>(rate));
+    if (token <= 0) {
+        return ndk::ScopedAStatus::fromServiceSpecificError(token);
+    }
+    *_aidl_return = token;
+    return ndk::ScopedAStatus::ok();
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/DirectReportChannel.h b/services/sensorservice/aidl/DirectReportChannel.h
new file mode 100644
index 0000000..d9ea73d
--- /dev/null
+++ b/services/sensorservice/aidl/DirectReportChannel.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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 <aidl/android/frameworks/sensorservice/BnDirectReportChannel.h>
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+class DirectReportChannel final
+      : public ::aidl::android::frameworks::sensorservice::BnDirectReportChannel {
+public:
+    DirectReportChannel(::android::SensorManager& manager, int channelId);
+    ~DirectReportChannel();
+
+    ndk::ScopedAStatus configure(int32_t sensorHandle,
+                                 ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
+                                 int32_t* _aidl_return) override;
+
+private:
+    ::android::SensorManager& mManager;
+    const int mId;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/EventQueue.cpp b/services/sensorservice/aidl/EventQueue.cpp
new file mode 100644
index 0000000..d4e8906
--- /dev/null
+++ b/services/sensorservice/aidl/EventQueue.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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 "EventQueue.h"
+#include "utils.h"
+
+#include <android-base/logging.h>
+#include <utils/Looper.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+using ::aidl::android::frameworks::sensorservice::IEventQueueCallback;
+using ::aidl::android::hardware::sensors::Event;
+
+class EventQueueLooperCallback : public ::android::LooperCallback {
+public:
+    EventQueueLooperCallback(sp<::android::SensorEventQueue> queue,
+                             std::shared_ptr<IEventQueueCallback> callback)
+          : mQueue(queue), mCallback(callback) {}
+
+    int handleEvent(__unused int fd, __unused int events, __unused void* data) {
+        ASensorEvent event;
+        ssize_t actual;
+
+        auto internalQueue = mQueue.promote();
+        if (internalQueue == nullptr) {
+            return 1;
+        }
+
+        while ((actual = internalQueue->read(&event, 1)) > 0) {
+            internalQueue->sendAck(&event, actual);
+            ndk::ScopedAStatus ret = mCallback->onEvent(convertEvent(event));
+            if (!ret.isOk()) {
+                LOG(ERROR) << "Failed to envoke EventQueueCallback: " << ret;
+            }
+        }
+
+        return 1; // continue to receive callbacks
+    }
+
+private:
+    wp<::android::SensorEventQueue> mQueue;
+    std::shared_ptr<IEventQueueCallback> mCallback;
+};
+
+EventQueue::EventQueue(std::shared_ptr<IEventQueueCallback> callback, sp<::android::Looper> looper,
+                       sp<::android::SensorEventQueue> internalQueue)
+      : mLooper(looper), mInternalQueue(internalQueue) {
+    mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT,
+                   new EventQueueLooperCallback(internalQueue, callback), nullptr);
+}
+
+// FIXME why was this on onLastStrongRef instead of dtor?
+EventQueue::~EventQueue() {
+    mLooper->removeFd(mInternalQueue->getFd());
+}
+
+ndk::ScopedAStatus EventQueue::enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs,
+                                            int64_t in_maxBatchReportLatencyUs) {
+    return convertResult(mInternalQueue->enableSensor(in_sensorHandle, in_samplingPeriodUs,
+                                                      in_maxBatchReportLatencyUs, 0));
+}
+
+ndk::ScopedAStatus EventQueue::disableSensor(int32_t in_sensorHandle) {
+    return convertResult(mInternalQueue->disableSensor(in_sensorHandle));
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/EventQueue.h b/services/sensorservice/aidl/EventQueue.h
new file mode 100644
index 0000000..0ae1eba
--- /dev/null
+++ b/services/sensorservice/aidl/EventQueue.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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 "SensorManagerAidl.h"
+
+#include <aidl/android/frameworks/sensorservice/BnEventQueue.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+struct EventQueue final : public aidl::android::frameworks::sensorservice::BnEventQueue {
+    EventQueue(
+            std::shared_ptr<aidl::android::frameworks::sensorservice::IEventQueueCallback> callback,
+            sp<::android::Looper> looper, sp<::android::SensorEventQueue> internalQueue);
+    ~EventQueue();
+
+    ndk::ScopedAStatus enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs,
+                                    int64_t in_maxBatchReportLatencyUs) override;
+    ndk::ScopedAStatus disableSensor(int32_t sensorHandle) override;
+
+private:
+    friend class EventQueueLooperCallback;
+    sp<::android::Looper> mLooper;
+    sp<::android::SensorEventQueue> mInternalQueue;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
new file mode 100644
index 0000000..6d8d574
--- /dev/null
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// LOG_TAG defined via build flag.
+#ifndef LOG_TAG
+#define LOG_TAG "AidlSensorManager"
+#endif
+
+#include "DirectReportChannel.h"
+#include "EventQueue.h"
+#include "SensorManagerAidl.h"
+#include "utils.h"
+
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder.h>
+#include <sched.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+using ::aidl::android::frameworks::sensorservice::IDirectReportChannel;
+using ::aidl::android::frameworks::sensorservice::IEventQueue;
+using ::aidl::android::frameworks::sensorservice::IEventQueueCallback;
+using ::aidl::android::frameworks::sensorservice::ISensorManager;
+using ::aidl::android::hardware::common::Ashmem;
+using ::aidl::android::hardware::sensors::ISensors;
+using ::aidl::android::hardware::sensors::SensorInfo;
+using ::aidl::android::hardware::sensors::SensorType;
+using ::android::frameworks::sensorservice::implementation::SensorManagerAidl;
+
+static const char* POLL_THREAD_NAME = "aidl_ssvc_poll";
+
+SensorManagerAidl::SensorManagerAidl(JavaVM* vm)
+      : mLooper(new Looper(false)), mStopThread(true), mJavaVm(vm) {}
+SensorManagerAidl::~SensorManagerAidl() {
+    // Stops pollAll inside the thread.
+    std::lock_guard<std::mutex> lock(mThreadMutex);
+
+    mStopThread = true;
+    if (mLooper != nullptr) {
+        mLooper->wake();
+    }
+    if (mPollThread.joinable()) {
+        mPollThread.join();
+    }
+}
+
+ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type,
+                                       const native_handle_t* handle,
+                                       std::shared_ptr<IDirectReportChannel>* chan) {
+    int channelId = manager.createDirectChannel(size, type, handle);
+    if (channelId < 0) {
+        return convertResult(channelId);
+    }
+    if (channelId == 0) {
+        return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+    }
+    *chan = ndk::SharedRefBase::make<DirectReportChannel>(manager, channelId);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createAshmemDirectChannel(
+        const Ashmem& in_mem, int64_t in_size,
+        std::shared_ptr<IDirectReportChannel>* _aidl_return) {
+    if (in_size > in_mem.size || in_size < ISensors::DIRECT_REPORT_SENSOR_EVENT_TOTAL_LENGTH) {
+        return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE);
+    }
+    native_handle_t* handle = native_handle_create(1, 0);
+    handle->data[0] = dup(in_mem.fd.get());
+
+    auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_ASHMEM,
+                                      handle, _aidl_return);
+    int result = native_handle_close(handle);
+    CHECK(result == 0) << "Failed to close the native_handle_t: " << result;
+    result = native_handle_delete(handle);
+    CHECK(result == 0) << "Failed to delete the native_handle_t: " << result;
+
+    return status;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createGrallocDirectChannel(
+        const ndk::ScopedFileDescriptor& in_mem, int64_t in_size,
+        std::shared_ptr<IDirectReportChannel>* _aidl_return) {
+    native_handle_t* handle = native_handle_create(1, 0);
+    handle->data[0] = dup(in_mem.get());
+
+    auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_GRALLOC,
+                                      handle, _aidl_return);
+    int result = native_handle_close(handle);
+    CHECK(result == 0) << "Failed to close the native_handle_t: " << result;
+    result = native_handle_delete(handle);
+    CHECK(result == 0) << "Failed to delete the native_handle_t: " << result;
+
+    return status;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createEventQueue(
+        const std::shared_ptr<IEventQueueCallback>& in_callback,
+        std::shared_ptr<IEventQueue>* _aidl_return) {
+    if (in_callback == nullptr) {
+        return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE);
+    }
+
+    sp<::android::Looper> looper = getLooper();
+    if (looper == nullptr) {
+        LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue cannot initialize looper";
+        return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+    }
+
+    String8 package(String8::format("aidl_client_pid_%d", AIBinder_getCallingPid()));
+    sp<::android::SensorEventQueue> internalQueue = getInternalManager().createEventQueue(package);
+    if (internalQueue == nullptr) {
+        LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue returns nullptr.";
+        return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+    }
+
+    *_aidl_return = ndk::SharedRefBase::make<EventQueue>(in_callback, looper, internalQueue);
+
+    return ndk::ScopedAStatus::ok();
+}
+
+SensorInfo convertSensor(Sensor src) {
+    SensorInfo dst;
+    dst.sensorHandle = src.getHandle();
+    dst.name = src.getName();
+    dst.vendor = src.getVendor();
+    dst.version = src.getVersion();
+    dst.type = static_cast<SensorType>(src.getType());
+    dst.typeAsString = src.getStringType();
+    // maxRange uses maxValue because ::android::Sensor wraps the
+    // internal sensor_t in this way.
+    dst.maxRange = src.getMaxValue();
+    dst.resolution = src.getResolution();
+    dst.power = src.getPowerUsage();
+    dst.minDelayUs = src.getMinDelay();
+    dst.fifoReservedEventCount = src.getFifoReservedEventCount();
+    dst.fifoMaxEventCount = src.getFifoMaxEventCount();
+    dst.requiredPermission = src.getRequiredPermission();
+    dst.maxDelayUs = src.getMaxDelay();
+    dst.flags = src.getFlags();
+    return dst;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::getDefaultSensor(SensorType in_type,
+                                                       SensorInfo* _aidl_return) {
+    ::android::Sensor const* sensor =
+            getInternalManager().getDefaultSensor(static_cast<int>(in_type));
+    if (!sensor) {
+        return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_NOT_EXIST);
+    }
+    *_aidl_return = convertSensor(*sensor);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SensorManagerAidl::getSensorList(std::vector<SensorInfo>* _aidl_return) {
+    Sensor const* const* list;
+    _aidl_return->clear();
+    ssize_t count = getInternalManager().getSensorList(&list);
+    if (count < 0 || list == nullptr) {
+        LOG(ERROR) << "SensorMAanger::getSensorList failed with count: " << count;
+        return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+    }
+    _aidl_return->reserve(static_cast<size_t>(count));
+    for (ssize_t i = 0; i < count; ++i) {
+        _aidl_return->push_back(convertSensor(*list[i]));
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+::android::SensorManager& SensorManagerAidl::getInternalManager() {
+    std::lock_guard<std::mutex> lock(mInternalManagerMutex);
+    if (mInternalManager == nullptr) {
+        mInternalManager = &::android::SensorManager::getInstanceForPackage(
+                String16(ISensorManager::descriptor));
+    }
+    return *mInternalManager;
+}
+
+/* One global looper for all event queues created from this SensorManager. */
+sp<Looper> SensorManagerAidl::getLooper() {
+    std::lock_guard<std::mutex> lock(mThreadMutex);
+
+    if (!mPollThread.joinable()) {
+        // if thread not initialized, start thread
+        mStopThread = false;
+        std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] {
+            struct sched_param p = {0};
+            p.sched_priority = 10;
+            if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) {
+                LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno);
+            }
+
+            // set looper
+            Looper::setForThread(looper);
+
+            // Attach the thread to JavaVM so that pollAll do not crash if the thread
+            // eventually calls into Java.
+            JavaVMAttachArgs args{.version = JNI_VERSION_1_2,
+                                  .name = POLL_THREAD_NAME,
+                                  .group = nullptr};
+            JNIEnv* env;
+            if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) {
+                LOG(FATAL) << "Cannot attach SensorManager looper thread to Java VM.";
+            }
+
+            LOG(INFO) << POLL_THREAD_NAME << " started.";
+            for (;;) {
+                int pollResult = looper->pollAll(-1 /* timeout */);
+                if (pollResult == Looper::POLL_WAKE) {
+                    if (stopThread == true) {
+                        LOG(INFO) << POLL_THREAD_NAME << ": requested to stop";
+                        break;
+                    } else {
+                        LOG(INFO) << POLL_THREAD_NAME << ": spurious wake up, back to work";
+                    }
+                } else {
+                    LOG(ERROR) << POLL_THREAD_NAME << ": Looper::pollAll returns unexpected "
+                               << pollResult;
+                    break;
+                }
+            }
+
+            if (javaVm->DetachCurrentThread() != JNI_OK) {
+                LOG(ERROR) << "Cannot detach SensorManager looper thread from Java VM.";
+            }
+
+            LOG(INFO) << POLL_THREAD_NAME << " is terminated.";
+        }};
+        mPollThread = std::move(pollThread);
+    }
+    return mLooper;
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
new file mode 100644
index 0000000..c77ee88
--- /dev/null
+++ b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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 <aidl/android/frameworks/sensorservice/BnSensorManager.h>
+#include <jni.h>
+#include <sensor/SensorManager.h>
+#include <utils/Looper.h>
+#include <mutex>
+#include <thread>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+class SensorManagerAidl : public ::aidl::android::frameworks::sensorservice::BnSensorManager {
+public:
+    explicit SensorManagerAidl(JavaVM* vm);
+    ~SensorManagerAidl();
+
+    ::ndk::ScopedAStatus createAshmemDirectChannel(
+            const ::aidl::android::hardware::common::Ashmem& in_mem, int64_t in_size,
+            std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>*
+                    _aidl_return) override;
+    ::ndk::ScopedAStatus createEventQueue(
+            const std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueueCallback>&
+                    in_callback,
+            std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueue>* _aidl_return)
+            override;
+    ::ndk::ScopedAStatus createGrallocDirectChannel(
+            const ::ndk::ScopedFileDescriptor& in_buffer, int64_t in_size,
+            std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>*
+                    _aidl_return) override;
+    ::ndk::ScopedAStatus getDefaultSensor(
+            ::aidl::android::hardware::sensors::SensorType in_type,
+            ::aidl::android::hardware::sensors::SensorInfo* _aidl_return) override;
+    ::ndk::ScopedAStatus getSensorList(
+            std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override;
+
+private:
+    // Block until ::android::SensorManager is initialized.
+    ::android::SensorManager& getInternalManager();
+    sp<Looper> getLooper();
+
+    std::mutex mInternalManagerMutex;
+    ::android::SensorManager* mInternalManager = nullptr; // does not own
+    sp<Looper> mLooper;
+
+    volatile bool mStopThread;
+    std::mutex mThreadMutex; // protects mPollThread
+    std::thread mPollThread;
+
+    JavaVM* mJavaVm;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp
new file mode 100644
index 0000000..26bcdc5
--- /dev/null
+++ b/services/sensorservice/aidl/utils.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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 "utils.h"
+
+#include <aidl/android/frameworks/sensorservice/ISensorManager.h>
+#include <aidl/sensors/convert.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+ndk::ScopedAStatus convertResult(status_t src) {
+    using ::aidl::android::frameworks::sensorservice::ISensorManager;
+
+    int err = 0;
+    switch (src) {
+        case OK:
+            return ndk::ScopedAStatus::ok();
+        case NAME_NOT_FOUND:
+            err = ISensorManager::RESULT_NOT_EXIST;
+            break;
+        case NO_MEMORY:
+            err = ISensorManager::RESULT_NO_MEMORY;
+            break;
+        case NO_INIT:
+            err = ISensorManager::RESULT_NO_INIT;
+            break;
+        case PERMISSION_DENIED:
+            err = ISensorManager::RESULT_PERMISSION_DENIED;
+            break;
+        case BAD_VALUE:
+            err = ISensorManager::RESULT_BAD_VALUE;
+            break;
+        case INVALID_OPERATION:
+            err = ISensorManager::RESULT_INVALID_OPERATION;
+            break;
+        default:
+            err = ISensorManager::RESULT_UNKNOWN_ERROR;
+    }
+    return ndk::ScopedAStatus::fromServiceSpecificError(err);
+}
+
+::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) {
+    ::aidl::android::hardware::sensors::Event dst;
+    ::android::hardware::sensors::implementation::
+            convertFromSensorEvent(reinterpret_cast<const sensors_event_t&>(src), &dst);
+    return dst;
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/utils.h b/services/sensorservice/aidl/utils.h
new file mode 100644
index 0000000..06ba59e
--- /dev/null
+++ b/services/sensorservice/aidl/utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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 <aidl/android/hardware/sensors/Event.h>
+#include <sensor/Sensor.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+::ndk::ScopedAStatus convertResult(status_t status);
+::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& event);
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index c5f5372..a5195f4 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -41,7 +41,7 @@
 
 #include "Display/DisplaySnapshot.h"
 #include "DisplayDevice.h"
-#include "FrontEnd/FrontEndDisplayInfo.h"
+#include "FrontEnd/DisplayInfo.h"
 #include "Layer.h"
 #include "RefreshRateOverlay.h"
 #include "SurfaceFlinger.h"
@@ -132,7 +132,7 @@
     }
 }
 
-auto DisplayDevice::getFrontEndInfo() const -> FrontEndDisplayInfo {
+auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo {
     gui::DisplayInfo info;
     info.displayId = getLayerStack().id;
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index afa13e5..4b64aab 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -45,7 +45,7 @@
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/Hal.h"
 #include "DisplayHardware/PowerAdvisor.h"
-#include "FrontEnd/FrontEndDisplayInfo.h"
+#include "FrontEnd/DisplayInfo.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "ThreadContext.h"
 #include "TracedOrdinal.h"
@@ -167,7 +167,7 @@
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
-    surfaceflinger::FrontEndDisplayInfo getFrontEndInfo() const;
+    surfaceflinger::frontend::DisplayInfo getFrontEndInfo() const;
 
     /* ------------------------------------------------------------------------
      * Display power mode management.
diff --git a/services/surfaceflinger/FrontEnd/FrontEndDisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
similarity index 84%
rename from services/surfaceflinger/FrontEnd/FrontEndDisplayInfo.h
rename to services/surfaceflinger/FrontEnd/DisplayInfo.h
index 95e69b3..0c7b24a 100644
--- a/services/surfaceflinger/FrontEnd/FrontEndDisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -18,11 +18,10 @@
 
 #include <gui/DisplayInfo.h>
 
-// TODO (b/259553365) fix namespace to be consistent with other components
-namespace android::surfaceflinger {
+namespace android::surfaceflinger::frontend {
 
 // Display information needed to populate input and calculate layer geometry.
-struct FrontEndDisplayInfo {
+struct DisplayInfo {
     gui::DisplayInfo info;
     ui::Transform transform;
     bool receivesInput;
@@ -32,4 +31,4 @@
     ui::Transform::RotationFlags rotationFlags;
 };
 
-} // namespace android::surfaceflinger
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 95961fe..8629671 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -24,7 +24,7 @@
 
 #include "TransactionHandler.h"
 
-namespace android {
+namespace android::surfaceflinger::frontend {
 
 void TransactionHandler::queueTransaction(TransactionState&& state) {
     mLocklessTransactionQueue.push(std::move(state));
@@ -186,4 +186,4 @@
         mStalledTransactions.erase(it);
     }
 }
-} // namespace android
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 2b6f07d..a06b870 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -18,9 +18,6 @@
 
 #include <semaphore.h>
 #include <cstdint>
-#include <mutex>
-#include <queue>
-#include <thread>
 #include <vector>
 
 #include <LocklessQueue.h>
@@ -30,6 +27,10 @@
 #include <ftl/small_vector.h>
 
 namespace android {
+
+class TestableSurfaceFlinger;
+namespace surfaceflinger::frontend {
+
 class TransactionHandler {
 public:
     struct TransactionFlushState {
@@ -60,7 +61,7 @@
 
 private:
     // For unit tests
-    friend class TestableSurfaceFlinger;
+    friend class ::android::TestableSurfaceFlinger;
 
     int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
     TransactionReadiness applyFilters(TransactionFlushState&);
@@ -71,5 +72,5 @@
     ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
     std::vector<uint64_t> mStalledTransactions;
 };
-
+} // namespace surfaceflinger::frontend
 } // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8a3e784..d53f0b1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1123,7 +1123,7 @@
 
     // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
     // the same reason we are allowing touch boost for those layers. See
-    // RefreshRateSelector::rankRefreshRates for details.
+    // RefreshRateSelector::rankFrameRates for details.
     const auto layerVotedWithDefaultCompatibility =
             frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default;
     const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 8d4ea05..fd1a733 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -24,6 +24,7 @@
 #include <chrono>
 #include <cmath>
 #include <deque>
+#include <map>
 
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -31,6 +32,7 @@
 #include <ftl/fake_guard.h>
 #include <ftl/match.h>
 #include <ftl/unit.h>
+#include <scheduler/FrameRateMode.h>
 #include <utils/Trace.h>
 
 #include "../SurfaceFlingerProperties.h"
@@ -43,7 +45,7 @@
 namespace {
 
 struct RefreshRateScore {
-    DisplayModeIterator modeIt;
+    FrameRateMode frameRateMode;
     float overallScore;
     struct {
         float modeBelowThreshold;
@@ -77,19 +79,11 @@
     return knownFrameRates;
 }
 
-// The Filter is a `bool(const DisplayMode&)` predicate.
-template <typename Filter>
-std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) {
+std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes) {
     std::vector<DisplayModeIterator> sortedModes;
     sortedModes.reserve(modes.size());
-
     for (auto it = modes.begin(); it != modes.end(); ++it) {
-        const auto& [id, mode] = *it;
-
-        if (filter(*mode)) {
-            ALOGV("%s: including mode %d", __func__, id.value());
-            sortedModes.push_back(it);
-        }
+        sortedModes.push_back(it);
     }
 
     std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
@@ -106,6 +100,21 @@
     return sortedModes;
 }
 
+std::pair<unsigned, unsigned> divisorRange(Fps fps, FpsRange range,
+                                           RefreshRateSelector::Config::FrameRateOverride config) {
+    if (config != RefreshRateSelector::Config::FrameRateOverride::Enabled) {
+        return {1, 1};
+    }
+
+    using fps_approx_ops::operator/;
+    const auto start = std::max(1u, fps / range.max - 1);
+    const auto end = fps /
+            std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate,
+                     fps_approx_ops::operator<);
+
+    return {start, end};
+}
+
 bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
     for (const auto it1 : sortedModes) {
         const auto& mode1 = it1->second;
@@ -136,27 +145,109 @@
 
 } // namespace
 
+auto RefreshRateSelector::createFrameRateModes(
+        std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const
+        -> std::vector<FrameRateMode> {
+    struct Key {
+        Fps fps;
+        int32_t group;
+    };
+
+    struct KeyLess {
+        bool operator()(const Key& a, const Key& b) const {
+            using namespace fps_approx_ops;
+            if (a.fps != b.fps) {
+                return a.fps < b.fps;
+            }
+
+            // For the same fps the order doesn't really matter, but we still
+            // want the behaviour of a strictly less operator.
+            // We use the group id as the secondary ordering for that.
+            return a.group < b.group;
+        }
+    };
+
+    std::map<Key, DisplayModeIterator, KeyLess> ratesMap;
+    for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
+        const auto& [id, mode] = *it;
+
+        if (!filterModes(*mode)) {
+            continue;
+        }
+        const auto [start, end] =
+                divisorRange(mode->getFps(), renderRange, mConfig.enableFrameRateOverride);
+        for (auto divisor = start; divisor <= end; divisor++) {
+            const auto fps = mode->getFps() / divisor;
+            using fps_approx_ops::operator<;
+            if (fps < kMinSupportedFrameRate) {
+                break;
+            }
+
+            if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Enabled &&
+                !renderRange.includes(fps)) {
+                continue;
+            }
+
+            if (mConfig.enableFrameRateOverride ==
+                        Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+                !isNativeRefreshRate(fps)) {
+                continue;
+            }
+
+            const auto [existingIter, emplaceHappened] =
+                    ratesMap.try_emplace(Key{fps, mode->getGroup()}, it);
+            if (emplaceHappened) {
+                ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(),
+                      to_string(mode->getFps()).c_str());
+            } else {
+                // We might need to update the map as we found a lower refresh rate
+                if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) {
+                    existingIter->second = it;
+                    ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(),
+                          to_string(mode->getFps()).c_str());
+                }
+            }
+        }
+    }
+
+    std::vector<FrameRateMode> frameRateModes;
+    frameRateModes.reserve(ratesMap.size());
+    for (const auto& [key, mode] : ratesMap) {
+        frameRateModes.emplace_back(FrameRateMode{key.fps, mode->second});
+    }
+
+    // We always want that the lowest frame rate will be corresponding to the
+    // lowest mode for power saving.
+    const auto lowestRefreshRateIt =
+            std::min_element(frameRateModes.begin(), frameRateModes.end(),
+                             [](const FrameRateMode& lhs, const FrameRateMode& rhs) {
+                                 return isStrictlyLess(lhs.modePtr->getFps(),
+                                                       rhs.modePtr->getFps());
+                             });
+    frameRateModes.erase(frameRateModes.begin(), lowestRefreshRateIt);
+
+    return frameRateModes;
+}
+
 struct RefreshRateSelector::RefreshRateScoreComparator {
     bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
-        const auto& [modeIt, overallScore, _] = lhs;
+        const auto& [frameRateMode, overallScore, _] = lhs;
 
-        std::string name = to_string(modeIt->second->getFps());
+        std::string name = to_string(frameRateMode);
+
         ALOGV("%s sorting scores %.2f", name.c_str(), overallScore);
-
         ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
 
-        if (!ScoredRefreshRate::scoresEqual(overallScore, rhs.overallScore)) {
+        if (!ScoredFrameRate::scoresEqual(overallScore, rhs.overallScore)) {
             return overallScore > rhs.overallScore;
         }
 
-        // If overallScore tie we will pick the higher refresh rate if
-        // high refresh rate is the priority else the lower refresh rate.
         if (refreshRateOrder == RefreshRateOrder::Descending) {
             using fps_approx_ops::operator>;
-            return modeIt->second->getFps() > rhs.modeIt->second->getFps();
+            return frameRateMode.fps > rhs.frameRateMode.fps;
         } else {
             using fps_approx_ops::operator<;
-            return modeIt->second->getFps() < rhs.modeIt->second->getFps();
+            return frameRateMode.fps < rhs.frameRateMode.fps;
         }
     }
 
@@ -210,7 +301,15 @@
 
     if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
         layer.vote == LayerVoteType::Heuristic) {
-        if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
+        const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue();
+
+        // We only want to score this layer as a fractional pair if the content is not
+        // significantly faster than the display rate, at it would cause a significant frame drop.
+        // It is more appropriate to choose a higher display rate even if
+        // a pull-down will be required.
+        constexpr float kMinMultiplier = 0.25f;
+        if (multiplier >= kMinMultiplier &&
+            isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
             return kScoreForFractionalPairs;
         }
 
@@ -245,9 +344,9 @@
     return 0;
 }
 
-float RefreshRateSelector::calculateRefreshRateScoreForFps(Fps refreshRate) const {
-    const float ratio =
-            refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue();
+float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const {
+    const auto& maxFps = mAppRequestFrameRates.back().fps;
+    const float ratio = refreshRate.getValue() / maxFps.getValue();
     // Use ratio^2 to get a lower score the more we get further from peak
     return ratio * ratio;
 }
@@ -260,12 +359,12 @@
 
     // If the layer wants Max, give higher score to the higher refresh rate
     if (layer.vote == LayerVoteType::Max) {
-        return calculateRefreshRateScoreForFps(refreshRate);
+        return calculateDistanceScoreFromMax(refreshRate);
     }
 
     if (layer.vote == LayerVoteType::ExplicitExact) {
         const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
-        if (supportsFrameRateOverrideByContent()) {
+        if (supportsAppFrameRateOverrideByContent()) {
             // Since we support frame rate override, allow refresh rates which are
             // multiples of the layer's request, as those apps would be throttled
             // down to run at the desired refresh rate.
@@ -289,33 +388,33 @@
             kNonExactMatchingPenalty;
 }
 
-auto RefreshRateSelector::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
-                                                GlobalSignals signals) const -> RankedRefreshRates {
+auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+                                              GlobalSignals signals) const -> RankedFrameRates {
     std::lock_guard lock(mLock);
 
-    if (mGetRankedRefreshRatesCache &&
-        mGetRankedRefreshRatesCache->arguments == std::make_pair(layers, signals)) {
-        return mGetRankedRefreshRatesCache->result;
+    if (mGetRankedFrameRatesCache &&
+        mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) {
+        return mGetRankedFrameRatesCache->result;
     }
 
-    const auto result = getRankedRefreshRatesLocked(layers, signals);
-    mGetRankedRefreshRatesCache = GetRankedRefreshRatesCache{{layers, signals}, result};
+    const auto result = getRankedFrameRatesLocked(layers, signals);
+    mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result};
     return result;
 }
 
-auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
-                                                      GlobalSignals signals) const
-        -> RankedRefreshRates {
+auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+                                                    GlobalSignals signals) const
+        -> RankedFrameRates {
     using namespace fps_approx_ops;
     ATRACE_CALL();
     ALOGV("%s: %zu layers", __func__, layers.size());
 
     const auto& activeMode = *getActiveModeItLocked()->second;
 
-    // Keep the display at max refresh rate for the duration of powering on the display.
+    // Keep the display at max frame rate for the duration of powering on the display.
     if (signals.powerOnImminent) {
         ALOGV("Power On Imminent");
-        return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Descending),
+        return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending),
                 GlobalSignals{.powerOnImminent = true}};
     }
 
@@ -375,7 +474,7 @@
     // selected a refresh rate to see if we should apply touch boost.
     if (signals.touch && !hasExplicitVoteLayers) {
         ALOGV("Touch Boost");
-        return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending),
+        return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending),
                 GlobalSignals{.touch = true}};
     }
 
@@ -387,27 +486,27 @@
 
     if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
         ALOGV("Idle");
-        return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending),
+        return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending),
                 GlobalSignals{.idle = true}};
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
         ALOGV("No layers with votes");
-        return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
+        return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
         ALOGV("All layers Min");
-        return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals};
+        return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals};
     }
 
     // Find the best refresh rate based on score
     std::vector<RefreshRateScore> scores;
-    scores.reserve(mAppRequestRefreshRates.size());
+    scores.reserve(mAppRequestFrameRates.size());
 
-    for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) {
-        scores.emplace_back(RefreshRateScore{modeIt, 0.0f});
+    for (const FrameRateMode& it : mAppRequestFrameRates) {
+        scores.emplace_back(RefreshRateScore{it, 0.0f});
     }
 
     for (const auto& layer : layers) {
@@ -420,13 +519,13 @@
 
         const auto weight = layer.weight;
 
-        for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
-            const auto& [id, mode] = *modeIt;
-            const bool isSeamlessSwitch = mode->getGroup() == activeMode.getGroup();
+        for (auto& [mode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+            const auto& [fps, modePtr] = mode;
+            const bool isSeamlessSwitch = modePtr->getGroup() == activeMode.getGroup();
 
             if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
                 ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
-                      formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+                      formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
                       to_string(activeMode).c_str());
                 continue;
             }
@@ -435,7 +534,7 @@
                 !layer.focused) {
                 ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
                       " Current mode = %s",
-                      formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+                      formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
                       to_string(activeMode).c_str());
                 continue;
             }
@@ -445,14 +544,14 @@
             // mode group otherwise. In second case, if the current mode group is different
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
-            const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
+            const bool isInPolicyForDefault = modePtr->getGroup() == anchorGroup;
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
                 ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
-                      to_string(*mode).c_str(), to_string(activeMode).c_str());
+                      to_string(*modePtr).c_str(), to_string(activeMode).c_str());
                 continue;
             }
 
-            const bool inPrimaryRange = policy->primaryRanges.physical.includes(mode->getFps());
+            const bool inPrimaryRange = policy->primaryRanges.physical.includes(modePtr->getFps());
             if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
                 !(layer.focused &&
                   (layer.vote == LayerVoteType::ExplicitDefault ||
@@ -462,8 +561,7 @@
                 continue;
             }
 
-            const float layerScore =
-                    calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
+            const float layerScore = calculateLayerScoreLocked(layer, fps, isSeamlessSwitch);
             const float weightedLayerScore = weight * layerScore;
 
             // Layer with fixed source has a special consideration which depends on the
@@ -491,21 +589,21 @@
                             Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
             if (fixedSourceLayer && layerBelowThreshold) {
                 const bool modeAboveThreshold =
-                        mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+                        modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
                 if (modeAboveThreshold) {
-                    ALOGV("%s gives %s fixed source (above threshold) score of %.4f",
-                          formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
-                          layerScore);
+                    ALOGV("%s gives %s (%s) fixed source (above threshold) score of %.4f",
+                          formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+                          to_string(modePtr->getFps()).c_str(), layerScore);
                     fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
                 } else {
-                    ALOGV("%s gives %s fixed source (below threshold) score of %.4f",
-                          formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
-                          layerScore);
+                    ALOGV("%s gives %s (%s) fixed source (below threshold) score of %.4f",
+                          formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+                          to_string(modePtr->getFps()).c_str(), layerScore);
                     fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
                 }
             } else {
-                ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
-                      to_string(mode->getFps()).c_str(), layerScore);
+                ALOGV("%s gives %s (%s) score of %.4f", formatLayerInfo(layer, weight).c_str(),
+                      to_string(fps).c_str(), to_string(modePtr->getFps()).c_str(), layerScore);
                 overallScore += weightedLayerScore;
             }
         }
@@ -524,25 +622,26 @@
         const auto maxScoreIt =
                 std::max_element(scores.begin(), scores.end(),
                                  [](RefreshRateScore max, RefreshRateScore current) {
-                                     const auto& [modeIt, overallScore, _] = current;
-                                     return overallScore > max.overallScore;
+                                     return current.overallScore > max.overallScore;
                                  });
-        ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for "
+        ALOGV("%s (%s) is the best refresh rate without fixed source layers. It is %s the "
+              "threshold for "
               "refresh rate multiples",
-              to_string(maxScoreIt->modeIt->second->getFps()).c_str(),
+              to_string(maxScoreIt->frameRateMode.fps).c_str(),
+              to_string(maxScoreIt->frameRateMode.modePtr->getFps()).c_str(),
               maxScoreAboveThreshold ? "above" : "below");
-        return maxScoreIt->modeIt->second->getFps() >=
+        return maxScoreIt->frameRateMode.modePtr->getFps() >=
                 Fps::fromValue(mConfig.frameRateMultipleThreshold);
     }();
 
     // Now we can add the fixed rate layers score
-    for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+    for (auto& [frameRateMode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
         overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
         if (maxScoreAboveThreshold) {
             overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
         }
-        ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(),
-              overallScore);
+        ALOGV("%s (%s) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(),
+              to_string(frameRateMode.modePtr->getFps()).c_str(), overallScore);
     }
 
     // Now that we scored all the refresh rates we need to pick the one that got the highest
@@ -552,12 +651,12 @@
     std::sort(scores.begin(), scores.end(),
               RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
 
-    RefreshRateRanking ranking;
+    FrameRateRanking ranking;
     ranking.reserve(scores.size());
 
     std::transform(scores.begin(), scores.end(), back_inserter(ranking),
                    [](const RefreshRateScore& score) {
-                       return ScoredRefreshRate{score.modeIt->second, score.overallScore};
+                       return ScoredFrameRate{score.frameRateMode, score.overallScore};
                    });
 
     const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
@@ -569,7 +668,7 @@
         // range instead of picking a random score from the app range.
         if (noLayerScore) {
             ALOGV("Layers not scored");
-            return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
+            return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
         } else {
             return {ranking, kNoSignals};
         }
@@ -580,7 +679,7 @@
     // vote we should not change it if we get a touch event. Only apply touch boost if it will
     // actually increase the refresh rate over the normal selection.
     const bool touchBoostForExplicitExact = [&] {
-        if (supportsFrameRateOverrideByContent()) {
+        if (supportsAppFrameRateOverrideByContent()) {
             // Enable touch boost if there are other layers besides exact
             return explicitExact + noVoteLayers != layers.size();
         } else {
@@ -589,12 +688,11 @@
         }
     }();
 
-    const auto touchRefreshRates = rankRefreshRates(anchorGroup, RefreshRateOrder::Descending);
-
+    const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
     using fps_approx_ops::operator<;
 
     if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
-        scores.front().modeIt->second->getFps() < touchRefreshRates.front().modePtr->getFps()) {
+        scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
         ALOGV("Touch Boost");
         return {touchRefreshRates, GlobalSignals{.touch = true}};
     }
@@ -603,7 +701,7 @@
     // current config
     if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
         const auto preferredDisplayMode = activeMode.getId();
-        return {rankRefreshRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode),
+        return {rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode),
                 kNoSignals};
     }
 
@@ -649,20 +747,13 @@
                                                 GlobalSignals globalSignals) const
         -> UidToFrameRateOverride {
     ATRACE_CALL();
-    ALOGV("%s: %zu layers", __func__, layers.size());
-
-    std::lock_guard lock(mLock);
-
-    // Prepare a set of supported display refresh rates for easy lookup
-    constexpr size_t kStaticCapacity = 8;
-    ftl::SmallMap<Fps, ftl::Unit, kStaticCapacity, FpsApproxEqual> supportedDisplayRefreshRates;
-    if (mConfig.enableFrameRateOverride ==
-        Config::FrameRateOverride::EnabledForNativeRefreshRates) {
-        for (const auto& [_, modePtr] : mDisplayModes) {
-            supportedDisplayRefreshRates.try_emplace(modePtr->getFps(), ftl::unit);
-        }
+    if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) {
+        return {};
     }
 
+    ALOGV("%s: %zu layers", __func__, layers.size());
+    std::lock_guard lock(mLock);
+
     const auto* policyPtr = getCurrentPolicyLocked();
     // We don't want to run lower than 30fps
     const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess);
@@ -676,8 +767,8 @@
     for (unsigned n = numMultiples; n > 0; n--) {
         const Fps divisor = displayRefreshRate / n;
         if (mConfig.enableFrameRateOverride ==
-                    Config::FrameRateOverride::EnabledForNativeRefreshRates &&
-            !supportedDisplayRefreshRates.contains(divisor)) {
+                    Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+            !isNativeRefreshRate(divisor)) {
             continue;
         }
 
@@ -736,7 +827,7 @@
                                   [](const auto& lhsPair, const auto& rhsPair) {
                                       const float lhs = lhsPair.second;
                                       const float rhs = rhsPair.second;
-                                      return lhs < rhs && !ScoredRefreshRate::scoresEqual(lhs, rhs);
+                                      return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
                                   });
         ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
         frameRateOverrides.emplace(uid, overrideFps);
@@ -765,10 +856,9 @@
 const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
     const auto& activeMode = *getActiveModeItLocked()->second;
 
-    for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
-        const auto& mode = modeIt->second;
-        if (activeMode.getGroup() == mode->getGroup()) {
-            return mode;
+    for (const FrameRateMode& mode : mPrimaryFrameRates) {
+        if (activeMode.getGroup() == mode.modePtr->getGroup()) {
+            return mode.modePtr;
         }
     }
 
@@ -776,55 +866,72 @@
           to_string(activeMode).c_str());
 
     // Default to the lowest refresh rate.
-    return mPrimaryRefreshRates.front()->second;
+    return mPrimaryFrameRates.front().modePtr;
 }
 
 const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
-    for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
-        const auto& mode = (*it)->second;
-        if (anchorGroup == mode->getGroup()) {
-            return mode;
+    const DisplayModePtr* maxByAnchor = &mPrimaryFrameRates.back().modePtr;
+    const DisplayModePtr* max = &mPrimaryFrameRates.back().modePtr;
+
+    bool maxByAnchorFound = false;
+    for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) {
+        using namespace fps_approx_ops;
+        if (it->modePtr->getFps() > (*max)->getFps()) {
+            max = &it->modePtr;
         }
+
+        if (anchorGroup == it->modePtr->getGroup() &&
+            it->modePtr->getFps() >= (*maxByAnchor)->getFps()) {
+            maxByAnchorFound = true;
+            maxByAnchor = &it->modePtr;
+        }
+    }
+
+    if (maxByAnchorFound) {
+        return *maxByAnchor;
     }
 
     ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
 
     // Default to the highest refresh rate.
-    return mPrimaryRefreshRates.back()->second;
+    return *max;
 }
 
-auto RefreshRateSelector::rankRefreshRates(
-        std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
-        std::optional<DisplayModeId> preferredDisplayModeOpt) const -> RefreshRateRanking {
-    std::deque<ScoredRefreshRate> ranking;
-
-    const auto rankRefreshRate = [&](DisplayModeIterator it) REQUIRES(mLock) {
-        const auto& mode = it->second;
-        if (anchorGroupOpt && mode->getGroup() != anchorGroupOpt) {
+auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt,
+                                         RefreshRateOrder refreshRateOrder,
+                                         std::optional<DisplayModeId> preferredDisplayModeOpt) const
+        -> FrameRateRanking {
+    const char* const whence = __func__;
+    std::deque<ScoredFrameRate> ranking;
+    const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
+        const auto& modePtr = frameRateMode.modePtr;
+        if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
             return;
         }
 
-        float score = calculateRefreshRateScoreForFps(mode->getFps());
+        float score = calculateDistanceScoreFromMax(frameRateMode.fps);
         const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
         if (inverseScore) {
             score = 1.0f / score;
         }
         if (preferredDisplayModeOpt) {
-            if (*preferredDisplayModeOpt == mode->getId()) {
+            if (*preferredDisplayModeOpt == modePtr->getId()) {
                 constexpr float kScore = std::numeric_limits<float>::max();
-                ranking.push_front(ScoredRefreshRate{mode, kScore});
+                ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
                 return;
             }
             constexpr float kNonPreferredModePenalty = 0.95f;
             score *= kNonPreferredModePenalty;
         }
-        ranking.push_back(ScoredRefreshRate{mode, score});
+        ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
+              to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score);
+        ranking.emplace_back(ScoredFrameRate{frameRateMode, score});
     };
 
     if (refreshRateOrder == RefreshRateOrder::Ascending) {
-        std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), rankRefreshRate);
+        std::for_each(mPrimaryFrameRates.begin(), mPrimaryFrameRates.end(), rankFrameRate);
     } else {
-        std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), rankRefreshRate);
+        std::for_each(mPrimaryFrameRates.rbegin(), mPrimaryFrameRates.rend(), rankFrameRate);
     }
 
     if (!ranking.empty() || !anchorGroupOpt) {
@@ -836,7 +943,7 @@
           refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
 
     constexpr std::optional<int> kNoAnchorGroup = std::nullopt;
-    return rankRefreshRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
+    return rankFrameRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
 }
 
 DisplayModePtr RefreshRateSelector::getActiveModePtr() const {
@@ -858,9 +965,9 @@
 void RefreshRateSelector::setActiveModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
 
-    // Invalidate the cached invocation to getRankedRefreshRates. This forces
-    // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
-    mGetRankedRefreshRatesCache.reset();
+    // Invalidate the cached invocation to getRankedFrameRates. This forces
+    // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+    mGetRankedFrameRatesCache.reset();
 
     mActiveModeIt = mDisplayModes.find(modeId);
     LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
@@ -895,16 +1002,15 @@
 void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
     std::lock_guard lock(mLock);
 
-    // Invalidate the cached invocation to getRankedRefreshRates. This forces
-    // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
-    mGetRankedRefreshRatesCache.reset();
+    // Invalidate the cached invocation to getRankedFrameRates. This forces
+    // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+    mGetRankedFrameRatesCache.reset();
 
     mDisplayModes = std::move(modes);
     mActiveModeIt = mDisplayModes.find(activeModeId);
     LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
 
-    const auto sortedModes =
-            sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; });
+    const auto sortedModes = sortByRefreshRate(mDisplayModes);
     mMinRefreshRateModeIt = sortedModes.front();
     mMaxRefreshRateModeIt = sortedModes.back();
 
@@ -915,15 +1021,23 @@
     mFrameRateOverrideConfig = [&] {
         switch (mConfig.enableFrameRateOverride) {
             case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverride:
             case Config::FrameRateOverride::Enabled:
                 return mConfig.enableFrameRateOverride;
-            case Config::FrameRateOverride::EnabledForNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
                 return shouldEnableFrameRateOverride(sortedModes)
-                        ? Config::FrameRateOverride::EnabledForNativeRefreshRates
+                        ? Config::FrameRateOverride::AppOverrideNativeRefreshRates
                         : Config::FrameRateOverride::Disabled;
         }
     }();
 
+    if (mConfig.enableFrameRateOverride ==
+        Config::FrameRateOverride::AppOverrideNativeRefreshRates) {
+        for (const auto& [_, mode] : mDisplayModes) {
+            mAppOverrideNativeRefreshRates.try_emplace(mode->getFps(), ftl::unit);
+        }
+    }
+
     constructAvailableRefreshRates();
 }
 
@@ -939,9 +1053,13 @@
         return false;
     }
 
-    using namespace fps_approx_ops;
-    return policy.appRequestRanges.physical.min <= policy.primaryRanges.physical.min &&
-            policy.appRequestRanges.physical.max >= policy.primaryRanges.physical.max;
+    const auto& primaryRanges = policy.primaryRanges;
+    const auto& appRequestRanges = policy.appRequestRanges;
+    ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical),
+             "Physical range is invalid");
+    ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render), "Render range is invalid");
+
+    return primaryRanges.valid() && appRequestRanges.valid();
 }
 
 auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
@@ -979,7 +1097,7 @@
             return SetPolicyResult::Invalid;
         }
 
-        mGetRankedRefreshRatesCache.reset();
+        mGetRankedFrameRatesCache.reset();
 
         if (*getCurrentPolicyLocked() == oldPolicy) {
             return SetPolicyResult::Unchanged;
@@ -1016,9 +1134,9 @@
 
 bool RefreshRateSelector::isModeAllowed(DisplayModeId modeId) const {
     std::lock_guard lock(mLock);
-    return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
-                       [modeId](DisplayModeIterator modeIt) {
-                           return modeIt->second->getId() == modeId;
+    return std::any_of(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(),
+                       [modeId](const FrameRateMode& frameRateMode) {
+                           return frameRateMode.modePtr->getId() == modeId;
                        });
 }
 
@@ -1029,33 +1147,35 @@
 
     const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
 
-    const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) {
-        const auto filter = [&](const DisplayMode& mode) {
+    const auto filterRefreshRates = [&](const FpsRanges& ranges,
+                                        const char* rangeName) REQUIRES(mLock) {
+        const auto filterModes = [&](const DisplayMode& mode) {
             return mode.getResolution() == defaultMode->getResolution() &&
                     mode.getDpi() == defaultMode->getDpi() &&
                     (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
-                    range.includes(mode.getFps());
+                    ranges.physical.includes(mode.getFps()) &&
+                    (supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
         };
 
-        const auto modes = sortByRefreshRate(mDisplayModes, filter);
-        LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName,
-                            to_string(range).c_str());
+        const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+        LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
+                            "No matching frame rate modes for %s physicalRange %s", rangeName,
+                            to_string(ranges.physical).c_str());
 
         const auto stringifyModes = [&] {
             std::string str;
-            for (const auto modeIt : modes) {
-                str += to_string(modeIt->second->getFps());
-                str.push_back(' ');
+            for (const auto& frameRateMode : frameRateModes) {
+                str += to_string(frameRateMode) + " ";
             }
             return str;
         };
-        ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str());
+        ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str());
 
-        return modes;
+        return frameRateModes;
     };
 
-    mPrimaryRefreshRates = filterRefreshRates(policy->primaryRanges.physical, "primary");
-    mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRanges.physical, "app request");
+    mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary");
+    mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request");
 }
 
 Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 65ee487..89ebeea 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -24,9 +24,11 @@
 
 #include <ftl/concat.h>
 #include <ftl/optional.h>
+#include <ftl/unit.h>
 #include <gui/DisplayEventReceiver.h>
 
 #include <scheduler/Fps.h>
+#include <scheduler/FrameRateMode.h>
 #include <scheduler/Seamlessness.h>
 
 #include "DisplayHardware/DisplayMode.h"
@@ -58,6 +60,9 @@
     static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
             std::chrono::nanoseconds(800us).count();
 
+    // The lowest Render Frame Rate that will ever be selected
+    static constexpr Fps kMinSupportedFrameRate = 20_Hz;
+
     class Policy {
         static constexpr int kAllowGroupSwitchingDefault = false;
 
@@ -196,12 +201,12 @@
         }
     };
 
-    struct ScoredRefreshRate {
-        DisplayModePtr modePtr;
+    struct ScoredFrameRate {
+        FrameRateMode frameRateMode;
         float score = 0.0f;
 
-        bool operator==(const ScoredRefreshRate& other) const {
-            return modePtr == other.modePtr && score == other.score;
+        bool operator==(const ScoredFrameRate& other) const {
+            return frameRateMode == other.frameRateMode && score == other.score;
         }
 
         static bool scoresEqual(float lhs, float rhs) {
@@ -210,25 +215,25 @@
         }
 
         struct DescendingScore {
-            bool operator()(const ScoredRefreshRate& lhs, const ScoredRefreshRate& rhs) const {
+            bool operator()(const ScoredFrameRate& lhs, const ScoredFrameRate& rhs) const {
                 return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score);
             }
         };
     };
 
-    using RefreshRateRanking = std::vector<ScoredRefreshRate>;
+    using FrameRateRanking = std::vector<ScoredFrameRate>;
 
-    struct RankedRefreshRates {
-        RefreshRateRanking ranking; // Ordered by descending score.
+    struct RankedFrameRates {
+        FrameRateRanking ranking; // Ordered by descending score.
         GlobalSignals consideredSignals;
 
-        bool operator==(const RankedRefreshRates& other) const {
+        bool operator==(const RankedFrameRates& other) const {
             return ranking == other.ranking && consideredSignals == other.consideredSignals;
         }
     };
 
-    RankedRefreshRates getRankedRefreshRates(const std::vector<LayerRequirement>&,
-                                             GlobalSignals) const EXCLUDES(mLock);
+    RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
+            EXCLUDES(mLock);
 
     FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
         std::lock_guard lock(mLock);
@@ -257,9 +262,12 @@
 
             // Override the frame rate for an app to a value which is also
             // a display refresh rate
-            EnabledForNativeRefreshRates,
+            AppOverrideNativeRefreshRates,
 
             // Override the frame rate for an app to any value
+            AppOverride,
+
+            // Override the frame rate for all apps and all values.
             Enabled,
 
             ftl_last = Enabled
@@ -291,10 +299,13 @@
 
     // 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.
+    //  differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default,
+    //  we can probably remove canSwitch altogether since all devices will be able
+    //  to switch to a frame rate divisor.
     bool canSwitch() const EXCLUDES(mLock) {
         std::lock_guard lock(mLock);
-        return mDisplayModes.size() > 1;
+        return mDisplayModes.size() > 1 ||
+                mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
     }
 
     // Class to enumerate options around toggling the kernel timer on and off.
@@ -307,10 +318,14 @@
     // refresh rates.
     KernelIdleTimerAction getIdleTimerAction() const;
 
-    bool supportsFrameRateOverrideByContent() const {
+    bool supportsAppFrameRateOverrideByContent() const {
         return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled;
     }
 
+    bool supportsFrameRateOverride() const {
+        return mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
+    }
+
     // Return the display refresh rate divisor to match the layer
     // frame rate, or 0 if the display refresh rate is not a multiple of the
     // layer refresh rate.
@@ -387,8 +402,8 @@
     // See mActiveModeIt for thread safety.
     DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
 
-    RankedRefreshRates getRankedRefreshRatesLocked(const std::vector<LayerRequirement>&,
-                                                   GlobalSignals) const REQUIRES(mLock);
+    RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+                                               GlobalSignals signals) const REQUIRES(mLock);
 
     // Returns number of display frames and remainder when dividing the layer refresh period by
     // display refresh period.
@@ -404,18 +419,24 @@
 
     struct RefreshRateScoreComparator;
 
-    enum class RefreshRateOrder { Ascending, Descending };
+    enum class RefreshRateOrder {
+        Ascending,
+        Descending,
+
+        ftl_last = Descending
+    };
 
     // Only uses the primary range, not the app request range.
-    RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, RefreshRateOrder,
-                                        std::optional<DisplayModeId> preferredDisplayModeOpt =
-                                                std::nullopt) const REQUIRES(mLock);
+    FrameRateRanking rankFrameRates(
+            std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
+            std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const
+            REQUIRES(mLock);
 
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
     // Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1.
-    float calculateRefreshRateScoreForFps(Fps refreshRate) const REQUIRES(mLock);
+    float calculateDistanceScoreFromMax(Fps refreshRate) const REQUIRES(mLock);
     // calculates a score for a layer. Used to determine the display refresh rate
     // and the frame rate override for certains applications.
     float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
@@ -436,11 +457,27 @@
                                                              : mIdleTimerCallbacks->platform;
     }
 
+    bool isNativeRefreshRate(Fps fps) const REQUIRES(mLock) {
+        LOG_ALWAYS_FATAL_IF(mConfig.enableFrameRateOverride !=
+                                    Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+                            "should only be called when "
+                            "Config::FrameRateOverride::AppOverrideNativeRefreshRates is used");
+        return mAppOverrideNativeRefreshRates.contains(fps);
+    }
+
+    std::vector<FrameRateMode> createFrameRateModes(
+            std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const
+            REQUIRES(mLock);
+
     // The display modes of the active display. The DisplayModeIterators below are pointers into
     // this container, so must be invalidated whenever the DisplayModes change. The Policy below
     // is also dependent, so must be reset as well.
     DisplayModes mDisplayModes GUARDED_BY(mLock);
 
+    // Set of supported display refresh rates for easy lookup
+    // when FrameRateOverride::AppOverrideNativeRefreshRates is in use.
+    ftl::SmallMap<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates;
+
     // Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext
     // need not be under mLock.
     DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext);
@@ -449,8 +486,8 @@
     DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
 
     // Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate.
-    std::vector<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock);
-    std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock);
+    std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock);
+    std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
 
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
@@ -466,11 +503,11 @@
     const Config mConfig;
     Config::FrameRateOverride mFrameRateOverrideConfig;
 
-    struct GetRankedRefreshRatesCache {
+    struct GetRankedFrameRatesCache {
         std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
-        RankedRefreshRates result;
+        RankedFrameRates result;
     };
-    mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock);
+    mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache 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 f1fcc88..0c541f9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -152,7 +152,7 @@
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
     const bool supportsFrameRateOverrideByContent =
-            leaderSelectorPtr()->supportsFrameRateOverrideByContent();
+            leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
     return mFrameRateOverrideMappings
             .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
 }
@@ -268,7 +268,7 @@
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
     const bool supportsFrameRateOverrideByContent =
-            leaderSelectorPtr()->supportsFrameRateOverrideByContent();
+            leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
 
     std::vector<FrameRateOverride> overrides =
             mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -707,7 +707,7 @@
 auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
     ATRACE_CALL();
 
-    using RankedRefreshRates = RefreshRateSelector::RankedRefreshRates;
+    using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
     display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
 
     // Tallies the score of a refresh rate across `displayCount` displays.
@@ -726,9 +726,10 @@
 
     for (const auto& [id, selectorPtr] : mRefreshRateSelectors) {
         auto rankedRefreshRates =
-                selectorPtr->getRankedRefreshRates(mPolicy.contentRequirements, globalSignals);
+                selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals);
 
-        for (const auto& [modePtr, score] : rankedRefreshRates.ranking) {
+        for (const auto& [frameRateMode, score] : rankedRefreshRates.ranking) {
+            const auto& modePtr = frameRateMode.modePtr;
             const auto [it, inserted] = refreshRateTallies.try_emplace(modePtr->getFps(), score);
 
             if (!inserted) {
@@ -771,16 +772,18 @@
 
     for (auto& [ranking, signals] : perDisplayRanking) {
         if (!chosenFps) {
-            auto& [modePtr, _] = ranking.front();
+            const auto& [frameRateMode, _] = ranking.front();
+            const auto& modePtr = frameRateMode.modePtr;
             modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
-                                    DisplayModeChoice{std::move(modePtr), signals});
+                                    DisplayModeChoice{modePtr, signals});
             continue;
         }
 
-        for (auto& [modePtr, _] : ranking) {
+        for (auto& [frameRateMode, _] : ranking) {
+            const auto& modePtr = frameRateMode.modePtr;
             if (modePtr->getFps() == *chosenFps) {
                 modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
-                                        DisplayModeChoice{std::move(modePtr), signals});
+                                        DisplayModeChoice{modePtr, signals});
                 break;
             }
         }
@@ -804,10 +807,10 @@
     if (mPolicy.mode) {
         const auto ranking =
                 leaderSelectorPtr()
-                        ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
+                        ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
                         .ranking;
 
-        mPolicy.mode = ranking.front().modePtr;
+        mPolicy.mode = ranking.front().frameRateMode.modePtr;
     }
     return mPolicy.mode;
 }
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 31b1d69..5522ff8 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -66,6 +66,7 @@
     Fps max = Fps::fromValue(std::numeric_limits<float>::max());
 
     bool includes(Fps) const;
+    bool includes(FpsRange) const;
 };
 
 struct FpsRanges {
@@ -75,6 +76,8 @@
     // the range of frame rates that refers to the render rate, which is
     // the rate that frames are swapped.
     FpsRange render;
+
+    bool valid() const;
 };
 
 static_assert(std::is_trivially_copyable_v<Fps>);
@@ -159,6 +162,16 @@
     return min <= fps && fps <= max;
 }
 
+inline bool FpsRange::includes(FpsRange range) const {
+    using namespace fps_approx_ops;
+    return min <= range.min && max >= range.max;
+}
+
+inline bool FpsRanges::valid() const {
+    using fps_approx_ops::operator>=;
+    return physical.max >= render.max;
+}
+
 struct FpsApproxEqual {
     bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
 };
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
new file mode 100644
index 0000000..670ab45
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 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 <scheduler/Fps.h>
+
+// TODO(b/241285191): Pull this to <ui/DisplayMode.h>
+#include "DisplayHardware/DisplayMode.h"
+
+namespace android::scheduler {
+
+struct FrameRateMode {
+    Fps fps; // The render frame rate, which is a divisor of modePtr->getFps().
+    DisplayModePtr modePtr;
+
+    bool operator==(const FrameRateMode& other) const {
+        return isApproxEqual(fps, other.fps) && modePtr == other.modePtr;
+    }
+
+    bool operator!=(const FrameRateMode& other) const { return !(*this == other); }
+};
+
+inline std::string to_string(const FrameRateMode& mode) {
+    if (mode.modePtr) {
+        return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")";
+    }
+    return "{invalid}";
+}
+
+} // namespace android::scheduler
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2c510d7..032541c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -180,6 +180,7 @@
 using base::StringAppendF;
 using display::PhysicalDisplay;
 using display::PhysicalDisplays;
+using frontend::TransactionHandler;
 using gui::DisplayInfo;
 using gui::GameMode;
 using gui::IDisplayEventConnection;
@@ -2792,7 +2793,11 @@
             }
 
             if (sysprop::frame_rate_override_for_native_rates(true)) {
-                return Config::FrameRateOverride::EnabledForNativeRefreshRates;
+                return Config::FrameRateOverride::AppOverrideNativeRefreshRates;
+            }
+
+            if (!base::GetBoolProperty("debug.sf.frame_rate_override_global"s, false)) {
+                return Config::FrameRateOverride::AppOverride;
             }
 
             return Config::FrameRateOverride::Enabled;
@@ -3304,7 +3309,7 @@
         if (!layer->needsInputInfo()) return;
 
         const auto opt = mFrontEndDisplayInfos.get(layer->getLayerStack())
-                                 .transform([](const FrontEndDisplayInfo& info) {
+                                 .transform([](const frontend::DisplayInfo& info) {
                                      return Layer::InputDisplayArgs{&info.transform, info.isSecure};
                                  });
 
@@ -6386,8 +6391,10 @@
                 std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
                 if (!renderArea) {
                     ALOGW("Skipping screen capture because of invalid render area.");
-                    captureResults.fenceResult = base::unexpected(NO_MEMORY);
-                    captureListener->onScreenCaptureCompleted(captureResults);
+                    if (captureListener) {
+                        captureResults.fenceResult = base::unexpected(NO_MEMORY);
+                        captureListener->onScreenCaptureCompleted(captureResults);
+                    }
                     return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
                 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7c0926a..50e8e73 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -66,7 +66,7 @@
 #include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
 #include "FlagManager.h"
-#include "FrontEnd/FrontEndDisplayInfo.h"
+#include "FrontEnd/DisplayInfo.h"
 #include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/TransactionHandler.h"
 #include "LayerVector.h"
@@ -121,6 +121,7 @@
 class ScreenCapturer;
 class WindowInfosListenerInvoker;
 
+using frontend::TransactionHandler;
 using gui::CaptureArgs;
 using gui::DisplayCaptureArgs;
 using gui::IRegionSamplingListener;
@@ -1367,7 +1368,7 @@
     } mPowerHintSessionMode;
 
     TransactionHandler mTransactionHandler;
-    display::DisplayMap<ui::LayerStack, FrontEndDisplayInfo> mFrontEndDisplayInfos;
+    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
 };
 
 class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index bd11a37..950e6d3 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -353,7 +353,7 @@
     const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
     std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
 
-    refreshRateSelector.getRankedRefreshRates(layers, globalSignals);
+    refreshRateSelector.getRankedFrameRates(layers, globalSignals);
 
     layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
     layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index cedb7eb..8757e63 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -26,6 +26,7 @@
 #include <log/log.h>
 #include <ui/Size.h>
 
+#include <scheduler/FrameRateMode.h>
 #include "DisplayHardware/HWC2.h"
 #include "FpsOps.h"
 #include "Scheduler/RefreshRateSelector.h"
@@ -44,9 +45,13 @@
 
 using mock::createDisplayMode;
 
+// Use a C style macro to keep the line numbers printed in gtest
+#define EXPECT_SCORED_FRAME_RATE(modePtr, fps, scored) \
+    EXPECT_EQ((FrameRateMode{(fps), (modePtr)}), (scored).frameRateMode)
+
 struct TestableRefreshRateSelector : RefreshRateSelector {
+    using RefreshRateSelector::FrameRateRanking;
     using RefreshRateSelector::RefreshRateOrder;
-    using RefreshRateSelector::RefreshRateRanking;
 
     using RefreshRateSelector::RefreshRateSelector;
 
@@ -80,36 +85,41 @@
         return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
     }
 
-    RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
-                                        RefreshRateOrder refreshRateOrder) const {
+    FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
+                                      RefreshRateOrder refreshRateOrder) const {
         std::lock_guard lock(mLock);
-        return RefreshRateSelector::rankRefreshRates(anchorGroupOpt, refreshRateOrder);
+        return RefreshRateSelector::rankFrameRates(anchorGroupOpt, refreshRateOrder);
     }
 
     const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
 
-    using RefreshRateSelector::GetRankedRefreshRatesCache;
-    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; }
+    using RefreshRateSelector::GetRankedFrameRatesCache;
+    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
 
-    auto getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
-                               GlobalSignals signals) const {
-        const auto result = RefreshRateSelector::getRankedRefreshRates(layers, signals);
+    auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+                             GlobalSignals signals) const {
+        const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals);
 
         EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
-                                   ScoredRefreshRate::DescendingScore{}));
+                                   ScoredFrameRate::DescendingScore{}));
 
         return result;
     }
 
     auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
                                      GlobalSignals signals) const {
-        const auto [ranking, consideredSignals] = getRankedRefreshRates(layers, signals);
+        const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals);
         return std::make_pair(ranking, consideredSignals);
     }
 
-    DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
-                                      GlobalSignals signals = {}) const {
-        return getRankedRefreshRates(layers, signals).ranking.front().modePtr;
+    DisplayModePtr getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
+                                        GlobalSignals signals = {}) const {
+        return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr;
+    }
+
+    ScoredFrameRate getBestScoredFrameRate(const std::vector<LayerRequirement>& layers = {},
+                                           GlobalSignals signals = {}) const {
+        return getRankedFrameRates(layers, signals).ranking.front();
     }
 
     SetPolicyResult setPolicy(const PolicyVariant& policy) {
@@ -120,9 +130,11 @@
     SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) {
         return setPolicy(policy);
     }
+
+    const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
 };
 
-class RefreshRateSelectorTest : public testing::Test {
+class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
 protected:
     using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder;
 
@@ -140,6 +152,7 @@
     static constexpr DisplayModeId kModeId24Frac{8};
     static constexpr DisplayModeId kModeId30Frac{9};
     static constexpr DisplayModeId kModeId60Frac{10};
+    static constexpr DisplayModeId kModeId35{11};
 
     static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
     static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz);
@@ -156,12 +169,14 @@
     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 kMode35 = createDisplayMode(kModeId35, 35_Hz);
     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);
 
     // Test configurations.
     static inline const DisplayModes kModes_60 = makeModes(kMode60);
+    static inline const DisplayModes kModes_35_60_90 = makeModes(kMode35, kMode60, kMode90);
     static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90);
     static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1);
     static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K);
@@ -185,6 +200,13 @@
     static inline const DisplayModes kModes_24_25_30_50_60_Frac =
             makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
                       kMode60Frac);
+
+    static TestableRefreshRateSelector createSelector(DisplayModes modes,
+                                                      DisplayModeId activeModeId,
+                                                      Config config = {}) {
+        config.enableFrameRateOverride = GetParam();
+        return TestableRefreshRateSelector(modes, activeModeId, config);
+    }
 };
 
 RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -201,13 +223,23 @@
 
 namespace {
 
-TEST_F(RefreshRateSelectorTest, oneMode_canSwitch) {
-    RefreshRateSelector selector(kModes_60, kModeId60);
-    EXPECT_FALSE(selector.canSwitch());
+INSTANTIATE_TEST_SUITE_P(PerOverrideConfig, RefreshRateSelectorTest,
+                         testing::Values(Config::FrameRateOverride::Disabled,
+                                         Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+                                         Config::FrameRateOverride::AppOverride,
+                                         Config::FrameRateOverride::Enabled));
+
+TEST_P(RefreshRateSelectorTest, oneMode_canSwitch) {
+    auto selector = createSelector(kModes_60, kModeId60);
+    if (GetParam() == Config::FrameRateOverride::Enabled) {
+        EXPECT_TRUE(selector.canSwitch());
+    } else {
+        EXPECT_FALSE(selector.canSwitch());
+    }
 }
 
-TEST_F(RefreshRateSelectorTest, invalidPolicy) {
-    TestableRefreshRateSelector selector(kModes_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, invalidPolicy) {
+    auto selector = createSelector(kModes_60, kModeId60);
 
     EXPECT_EQ(SetPolicyResult::Invalid,
               selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
@@ -215,8 +247,8 @@
               selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
 }
 
-TEST_F(RefreshRateSelectorTest, unchangedPolicy) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, unchangedPolicy) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
@@ -236,8 +268,8 @@
               selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
 }
 
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     const auto minRate = selector.getMinSupportedRefreshRate();
     const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -252,8 +284,8 @@
     EXPECT_EQ(performanceRateByPolicy, performanceRate);
 }
 
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     const auto minRate = selector.getMinRefreshRateByPolicy();
     const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -276,8 +308,8 @@
     EXPECT_EQ(kMode90_G1, performanceRate90);
 }
 
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
-    TestableRefreshRateSelector selector(kModes_60_90_4K, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
+    auto selector = createSelector(kModes_60_90_4K, kModeId60);
 
     const auto minRate = selector.getMinRefreshRateByPolicy();
     const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -300,8 +332,8 @@
     EXPECT_EQ(kMode90_4K, performanceRate90);
 }
 
-TEST_F(RefreshRateSelectorTest, twoModes_policyChange) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_policyChange) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     const auto minRate = selector.getMinRefreshRateByPolicy();
     const auto performanceRate = selector.getMaxRefreshRateByPolicy();
@@ -319,8 +351,8 @@
     EXPECT_EQ(kMode60, performanceRate60);
 }
 
-TEST_F(RefreshRateSelectorTest, twoModes_getActiveMode) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_getActiveMode) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
     {
         const auto& mode = selector.getActiveMode();
         EXPECT_EQ(mode.getId(), kModeId60);
@@ -340,31 +372,31 @@
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_noLayers) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_noLayers) {
     {
-        TestableRefreshRateSelector selector(kModes_60_72_90, kModeId72);
+        auto selector = createSelector(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(kMode90, selector.getBestRefreshRate());
+        EXPECT_EQ(kMode90, selector.getBestFrameRateMode());
 
         EXPECT_EQ(SetPolicyResult::Changed,
                   selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
-        EXPECT_EQ(kMode60, selector.getBestRefreshRate());
+        EXPECT_EQ(kMode60, selector.getBestFrameRateMode());
     }
     {
         // We select max even when this will cause a non-seamless switch.
-        TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+        auto selector = createSelector(kModes_60_90_G1, kModeId60);
         constexpr bool kAllowGroupSwitching = true;
         EXPECT_EQ(SetPolicyResult::Changed,
                   selector.setDisplayManagerPolicy(
                           {kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching}));
-        EXPECT_EQ(kMode90_G1, selector.getBestRefreshRate());
+        EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode());
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId72);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_exactDontChangeRefreshRateWhenNotInPolicy) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId72);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].vote = LayerVoteType::ExplicitExact;
@@ -372,188 +404,187 @@
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_90) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_90) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.name = "";
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_multipleThreshold_60_90) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60,
-                                         {.frameRateMultipleThreshold = 90});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multipleThreshold_60_90) {
+    auto selector = createSelector(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(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_72_90) {
-    TestableRefreshRateSelector selector(kModes_60_72_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_72_90) {
+    auto selector = createSelector(kModes_60_72_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90_120) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90_120) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -563,23 +594,23 @@
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_90_120_DifferentTypes) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -591,7 +622,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -599,7 +630,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -607,7 +638,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -615,7 +646,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -623,7 +654,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -631,7 +662,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
@@ -639,7 +670,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -647,7 +678,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -655,12 +686,13 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60,
-                                         {.frameRateMultipleThreshold = 120});
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_30_60_90_120_DifferentTypes_multipleThreshold) {
+    auto selector =
+            createSelector(kModes_30_60_72_90_120, kModeId60, {.frameRateMultipleThreshold = 120});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -673,7 +705,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -681,7 +713,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -689,7 +721,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -697,7 +729,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -705,7 +737,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -713,7 +745,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
@@ -721,7 +753,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -729,7 +761,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -737,14 +769,14 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -752,7 +784,7 @@
     lr2.desiredRefreshRate = 120_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "120Hz ExplicitDefault";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -760,7 +792,7 @@
     lr2.desiredRefreshRate = 120_Hz;
     lr2.vote = LayerVoteType::ExplicitExact;
     lr2.name = "120Hz ExplicitExact";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 10_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -768,7 +800,7 @@
     lr2.desiredRefreshRate = 120_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "120Hz ExplicitExact";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 
     lr1.desiredRefreshRate = 30_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -779,86 +811,86 @@
     lr3.vote = LayerVoteType::Heuristic;
     lr3.desiredRefreshRate = 120_Hz;
     lr3.name = "120Hz Heuristic";
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60) {
-    TestableRefreshRateSelector selector(kModes_30_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60) {
+    auto selector = createSelector(kModes_30_60, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90) {
+    auto selector = createSelector(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(kMode30, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.name = "24Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_PriorityTest) {
-    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_PriorityTest) {
+    auto selector = createSelector(kModes_30_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -866,43 +898,43 @@
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 15_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 30_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -910,15 +942,14 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = selector.getBestRefreshRate(layers);
+        const auto mode = selector.getBestFrameRateMode(layers);
         EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
                                  << to_string(mode->getFps());
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
-    TestableRefreshRateSelector selector(kModes_60_120, kModeId60,
-                                         {.frameRateMultipleThreshold = 120});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo_multipleThreshold_60_120) {
+    auto selector = createSelector(kModes_60_120, kModeId60, {.frameRateMultipleThreshold = 120});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -926,14 +957,14 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = selector.getBestRefreshRate(layers);
+        const auto mode = selector.getBestFrameRateMode(layers);
         EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
                                  << to_string(mode->getFps());
     }
 }
 
-TEST_F(RefreshRateSelectorTest, twoModes_getBestRefreshRate_Explicit) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_getBestFrameRateMode_Explicit) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -943,23 +974,23 @@
     lr1.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_75HzContent) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_75HzContent) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -967,14 +998,14 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = selector.getBestRefreshRate(layers, {});
+        const auto mode = selector.getBestFrameRateMode(layers, {});
         EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
                                  << to_string(mode->getFps());
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_Multiples) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -986,7 +1017,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
@@ -994,14 +1025,14 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30_Hz;
@@ -1009,18 +1040,18 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30_Hz;
     lr1.name = "30Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -1031,28 +1062,28 @@
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     // The other layer starts to provide buffers
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1061,49 +1092,70 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
     // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
     // different group.
-    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
-
+    auto selector = createSelector(kModes_30_60_90, kModeId60);
     const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
                                                         RefreshRateOrder::Descending);
 
-    const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
+    const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+        }
+    }();
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
 
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
-        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
-                << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
-                << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
     // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
     // different group.
-    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+    auto selector = createSelector(kModes_30_60_90, kModeId60);
 
     const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
                                                         RefreshRateOrder::Ascending);
 
-    const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
+    const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+            case Config::FrameRateOverride::Enabled:
+                return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+        }
+    }();
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
 
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
-        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
-                << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
-                << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
     // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
     // different group.
-    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
+    auto selector = createSelector(kModes_30_60_90, kModeId72);
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
@@ -1111,20 +1163,31 @@
     const auto refreshRates =
             selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
 
-    const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
+    const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+            case Config::FrameRateOverride::Enabled:
+                return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+        }
+    }();
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
 
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
-        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
-                << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
-                << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
     // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
     // different group.
-    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
+    auto selector = createSelector(kModes_30_60_90, kModeId72);
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
@@ -1132,29 +1195,52 @@
     const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt,
                                                         RefreshRateOrder::Descending);
 
-    const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
+    const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+        }
+    }();
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
 
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
-        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
-                << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
-                << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 }
 
-TEST_F(RefreshRateSelectorTest, powerOnImminentConsidered) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
-    auto [refreshRates, signals] = selector.getRankedRefreshRates({}, {});
+    auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
     EXPECT_FALSE(signals.powerOnImminent);
 
-    std::array expectedRefreshRates = {kMode90, kMode60};
+    auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}};
+            case Config::FrameRateOverride::Enabled:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60},   {45_Hz, kMode90},
+                        {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+        }
+    }();
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
 
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
-        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
-                << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
-                << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 
     std::tie(refreshRates, signals) =
@@ -1164,9 +1250,11 @@
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
 
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
-        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
-                << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
-                << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1182,29 +1270,43 @@
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
 
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
-        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
-                << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
-                << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 
     std::tie(refreshRates, signals) =
             selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
     EXPECT_FALSE(signals.powerOnImminent);
 
-    expectedRefreshRates = {kMode60, kMode90};
+    expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{60_Hz, kMode60}, {90_Hz, kMode90}};
+            case Config::FrameRateOverride::Enabled:
+                return {{60_Hz, kMode60}, {90_Hz, kMode90},   {45_Hz, kMode90},
+                        {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+        }
+    }();
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
 
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
-        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
-                << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
-                << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 }
 
-TEST_F(RefreshRateSelectorTest, touchConsidered) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, touchConsidered) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
-    auto [_, signals] = selector.getRankedRefreshRates({}, {});
+    auto [_, signals] = selector.getRankedFrameRates({}, {});
     EXPECT_FALSE(signals.touch);
 
     std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
@@ -1251,8 +1353,8 @@
     EXPECT_FALSE(signals.touch);
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitDefault) {
-    TestableRefreshRateSelector selector(kModes_60_90_72_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) {
+    auto selector = createSelector(kModes_60_90_72_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1284,57 +1386,57 @@
         ss << "ExplicitDefault " << desired;
         lr.name = ss.str();
 
-        EXPECT_EQ(expected, selector.getBestRefreshRate(layers)->getFps());
+        EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps());
     }
 }
 
-TEST_F(RefreshRateSelectorTest,
-       getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     // Test that 23.976 will choose 24 if 23.976 is not supported
     {
-        TestableRefreshRateSelector selector(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
-                                                       kMode60, kMode60Frac),
-                                             kModeId60);
+        auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60,
+                                                 kMode60Frac),
+                                       kModeId60);
 
         lr.vote = LayerVoteType::ExplicitExactOrMultiple;
         lr.desiredRefreshRate = 23.976_Hz;
         lr.name = "ExplicitExactOrMultiple 23.976 Hz";
-        EXPECT_EQ(kModeId24, selector.getBestRefreshRate(layers)->getId());
+        EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId());
     }
 
     // Test that 24 will choose 23.976 if 24 is not supported
     {
-        TestableRefreshRateSelector selector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
-                                                       kMode60, kMode60Frac),
-                                             kModeId60);
+        auto selector = createSelector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
+                                                 kMode60, kMode60Frac),
+                                       kModeId60);
 
         lr.desiredRefreshRate = 24_Hz;
         lr.name = "ExplicitExactOrMultiple 24 Hz";
-        EXPECT_EQ(kModeId24Frac, selector.getBestRefreshRate(layers)->getId());
+        EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers)->getId());
     }
 
     // Test that 29.97 will prefer 59.94 over 60 and 30
     {
-        TestableRefreshRateSelector selector(makeModes(kMode24, kMode24Frac, kMode25, kMode30,
-                                                       kMode60, kMode60Frac),
-                                             kModeId60);
+        auto selector = createSelector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode60,
+                                                 kMode60Frac),
+                                       kModeId60);
 
         lr.desiredRefreshRate = 29.97_Hz;
         lr.name = "ExplicitExactOrMultiple 29.97 Hz";
-        EXPECT_EQ(kModeId60Frac, selector.getBestRefreshRate(layers)->getId());
+        EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) {
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     // Test that voting for supported refresh rate will select this refresh rate
     {
-        TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60);
+        auto selector = createSelector(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;
@@ -1343,14 +1445,14 @@
             ss << "ExplicitExact " << desired;
             lr.name = ss.str();
 
-            EXPECT_EQ(lr.desiredRefreshRate, selector.getBestRefreshRate(layers)->getFps());
+            EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getFps());
         }
     }
 }
 
-TEST_F(RefreshRateSelectorTest,
-       getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+    auto selector = createSelector(kModes_60_90, kModeId90);
 
     constexpr FpsRange k90 = {90_Hz, 90_Hz};
     constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1365,16 +1467,16 @@
     lr.name = "60Hz ExplicitDefault";
     lr.focused = true;
 
-    const auto [mode, signals] =
-            selector.getRankedRefreshRates(layers, {.touch = true, .idle = true});
+    const auto [rankedFrameRate, signals] =
+            selector.getRankedFrameRates(layers, {.touch = true, .idle = true});
 
-    EXPECT_EQ(mode.begin()->modePtr, kMode60);
+    EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
     EXPECT_FALSE(signals.touch);
 }
 
-TEST_F(RefreshRateSelectorTest,
-       getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     constexpr FpsRange k60 = {60_Hz, 60_Hz};
     constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1389,11 +1491,11 @@
     lr.desiredRefreshRate = 90_Hz;
     lr.name = "90Hz ExplicitDefault";
     lr.focused = true;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.idle = true}));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true}));
 }
 
-TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f},
                                             {.weight = 1.f},
@@ -1426,15 +1528,31 @@
     lr5.name = "30Hz";
     lr5.focused = true;
 
-    std::array expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
-    auto actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+    auto expectedRanking = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{120_Hz, kMode120},
+                        {90_Hz, kMode90},
+                        {72_Hz, kMode72},
+                        {60_Hz, kMode60},
+                        {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{120_Hz, kMode120}, {90_Hz, kMode90},  {72_Hz, kMode72}, {60_Hz, kMode60},
+                        {45_Hz, kMode90},   {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+        }
+    }();
 
+    auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
     ASSERT_EQ(expectedRanking.size(), actualRanking.size());
 
     for (size_t i = 0; i < expectedRanking.size(); ++i) {
-        EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
-                << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
-                << actualRanking[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+                << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 
     lr1.vote = LayerVoteType::Max;
@@ -1452,15 +1570,31 @@
     lr5.desiredRefreshRate = 120_Hz;
     lr5.name = "120Hz";
 
-    expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
-    actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+    expectedRanking = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{120_Hz, kMode120},
+                        {90_Hz, kMode90},
+                        {72_Hz, kMode72},
+                        {60_Hz, kMode60},
+                        {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{120_Hz, kMode120}, {90_Hz, kMode90},  {72_Hz, kMode72}, {60_Hz, kMode60},
+                        {45_Hz, kMode90},   {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+        }
+    }();
+    actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
 
     ASSERT_EQ(expectedRanking.size(), actualRanking.size());
 
     for (size_t i = 0; i < expectedRanking.size(); ++i) {
-        EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
-                << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
-                << actualRanking[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+                << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 
     lr1.vote = LayerVoteType::Heuristic;
@@ -1476,15 +1610,31 @@
     lr5.desiredRefreshRate = 72_Hz;
     lr5.name = "72Hz";
 
-    expectedRanking = {kMode30, kMode60, kMode90, kMode120, kMode72};
-    actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+    expectedRanking = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{30_Hz, kMode30},
+                        {60_Hz, kMode60},
+                        {90_Hz, kMode90},
+                        {120_Hz, kMode120},
+                        {72_Hz, kMode72}};
+            case Config::FrameRateOverride::Enabled:
+                return {{30_Hz, kMode30}, {60_Hz, kMode60},  {90_Hz, kMode90}, {120_Hz, kMode120},
+                        {45_Hz, kMode90}, {40_Hz, kMode120}, {72_Hz, kMode72}, {36_Hz, kMode72}};
+        }
+    }();
+    actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
 
     ASSERT_EQ(expectedRanking.size(), actualRanking.size());
 
     for (size_t i = 0; i < expectedRanking.size(); ++i) {
-        EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
-                << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
-                << actualRanking[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+                << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 
     lr1.desiredRefreshRate = 120_Hz;
@@ -1503,21 +1653,37 @@
     lr5.desiredRefreshRate = 120_Hz;
     lr5.name = "120Hz-2";
 
-    expectedRanking = {kMode90, kMode60, kMode120, kMode72, kMode30};
-    actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+    expectedRanking = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{90_Hz, kMode90},
+                        {60_Hz, kMode60},
+                        {120_Hz, kMode120},
+                        {72_Hz, kMode72},
+                        {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60},  {120_Hz, kMode120}, {72_Hz, kMode72},
+                        {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72},   {30_Hz, kMode30}};
+        }
+    }();
+    actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
 
     ASSERT_EQ(expectedRanking.size(), actualRanking.size());
 
     for (size_t i = 0; i < expectedRanking.size(); ++i) {
-        EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
-                << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
-                << actualRanking[i].modePtr->getFps().getIntValue();
+        EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+                << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
     }
 }
 
-TEST_F(RefreshRateSelectorTest,
-       getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
+    auto selector = createSelector(kModes_60_90, kModeId90);
 
     constexpr FpsRange k90 = {90_Hz, 90_Hz};
     constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1525,8 +1691,8 @@
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
 
-    const auto [ranking, signals] = selector.getRankedRefreshRates({}, {});
-    EXPECT_EQ(ranking.front().modePtr, kMode90);
+    const auto [ranking, signals] = selector.getRankedFrameRates({}, {});
+    EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
     EXPECT_FALSE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1536,50 +1702,50 @@
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitExactOrMultiple";
     lr.focused = false;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::ExplicitDefault;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitDefault";
     lr.focused = false;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Heuristic;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
     lr.focused = false;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Max";
     lr.focused = false;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.vote = LayerVoteType::Min;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Min";
     lr.focused = false;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     // The default policy doesn't allow group switching. Verify that no
     // group switches are performed.
@@ -1591,11 +1757,11 @@
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     RefreshRateSelector::DisplayManagerPolicy policy;
     policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1609,11 +1775,11 @@
     layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     RefreshRateSelector::DisplayManagerPolicy policy;
     policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1628,11 +1794,11 @@
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     RefreshRateSelector::DisplayManagerPolicy policy;
     policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1649,11 +1815,11 @@
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     RefreshRateSelector::DisplayManagerPolicy policy;
     policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1673,11 +1839,11 @@
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     RefreshRateSelector::DisplayManagerPolicy policy;
     policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1702,11 +1868,11 @@
     layers[1].name = "90Hz ExplicitDefault";
     layers[1].focused = false;
 
-    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     RefreshRateSelector::DisplayManagerPolicy policy;
     policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1735,11 +1901,11 @@
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
 
     RefreshRateSelector::DisplayManagerPolicy policy;
     policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1765,11 +1931,11 @@
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
-    TestableRefreshRateSelector selector(kModes_30_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
+    auto selector = createSelector(kModes_30_60, kModeId60);
 
     // Allow group switching.
     RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1785,14 +1951,14 @@
     layer.name = "60Hz ExplicitExactOrMultiple";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
 
     selector.setActiveModeId(kModeId120);
-    EXPECT_EQ(kModeId120, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
-    TestableRefreshRateSelector selector(kModes_25_30_50_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+    auto selector = createSelector(kModes_25_30_50_60, kModeId60);
 
     // Allow group switching.
     RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1813,18 +1979,18 @@
                                              .weight = 1.f,
                                              .focused = true}};
 
-    EXPECT_EQ(kModeId50, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers)->getId());
 
     auto& seamedLayer = layers[0];
     seamedLayer.desiredRefreshRate = 30_Hz;
     seamedLayer.name = "30Hz ExplicitDefault";
     selector.setActiveModeId(kModeId30);
 
-    EXPECT_EQ(kModeId25, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
-    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId90);
+TEST_P(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId90);
 
     // Allow group switching.
     RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1835,11 +2001,11 @@
     std::vector<LayerRequirement> layers = {
             {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
 
-    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
-    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
+    auto selector = createSelector(kModes_30_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
@@ -1849,13 +2015,14 @@
         bool focused = true;
     };
 
-    // Returns the mode selected by getBestRefreshRate for a single layer with the given arguments.
+    // Returns the mode selected by getBestFrameRateMode for a single layer with the given
+    // arguments.
     const auto getFrameRate = [&](LayerVoteType voteType, Fps fps,
                                   Args args = {}) -> DisplayModeId {
         layers[0].vote = voteType;
         layers[0].desiredRefreshRate = fps;
         layers[0].focused = args.focused;
-        return selector.getBestRefreshRate(layers, {.touch = args.touch})->getId();
+        return selector.getBestFrameRateMode(layers, {.touch = args.touch})->getId();
     };
 
     constexpr FpsRange k30_60 = {30_Hz, 60_Hz};
@@ -1864,7 +2031,7 @@
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}}));
 
-    EXPECT_EQ(kModeId60, selector.getBestRefreshRate()->getId());
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode()->getId());
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
     EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
@@ -1897,22 +2064,23 @@
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
 }
 
-TEST_F(RefreshRateSelectorTest, idle) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, idle) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
 
-    const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
+    const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+                                          bool touchActive) -> DisplayModeId {
         layers[0].vote = voteType;
         layers[0].desiredRefreshRate = 90_Hz;
 
         const auto [ranking, signals] =
-                selector.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true});
+                selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
 
         // Refresh rate will be chosen by either touch state or idle state.
         EXPECT_EQ(!touchActive, signals.idle);
-        return ranking.front().modePtr->getId();
+        return ranking.front().frameRateMode.modePtr->getId();
     };
 
     EXPECT_EQ(SetPolicyResult::Changed,
@@ -1921,38 +2089,38 @@
     // Idle should be lower priority than touch boost.
     {
         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, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
         EXPECT_EQ(kModeId90,
-                  getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+                  getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
     }
 
     // With no layers, idle should still be lower priority than touch boost.
-    EXPECT_EQ(kModeId90, selector.getBestRefreshRate({}, {.touch = true, .idle = true})->getId());
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
     selector.setActiveModeId(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, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
         EXPECT_EQ(kModeId60,
-                  getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+                  getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
     }
 
     // Idle should be applied rather than the active mode when there are no layers.
-    EXPECT_EQ(kModeId60, selector.getBestRefreshRate({}, {.idle = true})->getId());
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true})->getId());
 }
 
-TEST_F(RefreshRateSelectorTest, findClosestKnownFrameRate) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, findClosestKnownFrameRate) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
         const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps));
@@ -1969,8 +2137,8 @@
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_KnownFrameRate) {
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_KnownFrameRate) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
 
     struct Expectation {
         Fps fps;
@@ -1997,101 +2165,80 @@
 
     for (const auto& [fps, mode] : knownFrameRatesExpectations) {
         layer.desiredRefreshRate = fps;
-        EXPECT_EQ(mode, selector.getBestRefreshRate(layers));
+        EXPECT_EQ(mode, selector.getBestFrameRateMode(layers));
     }
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
     auto& explicitExactOrMultipleLayer = layers[1];
 
-    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
-    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
     explicitExactLayer.vote = LayerVoteType::ExplicitExact;
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers, {.touch = true}));
-
-    explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
-    explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60,
-                                         {.enableFrameRateOverride =
-                                                  Config::FrameRateOverride::Enabled});
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
-    auto& explicitExactLayer = layers[0];
-    auto& explicitExactOrMultipleLayer = layers[1];
-
     explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
     explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
     explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
 
-    explicitExactLayer.vote = LayerVoteType::ExplicitExact;
-    explicitExactLayer.name = "ExplicitExact";
-    explicitExactLayer.desiredRefreshRate = 30_Hz;
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        EXPECT_SCORED_FRAME_RATE(kMode30, 30_Hz, selector.getBestScoredFrameRate(layers));
+        EXPECT_SCORED_FRAME_RATE(kMode30, 30_Hz,
+                                 selector.getBestScoredFrameRate(layers, {.touch = true}));
 
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true}));
+    } else {
+        EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers));
+        EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz,
+                                 selector.getBestScoredFrameRate(layers, {.touch = true}));
+    }
 
     explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
     explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers));
+    } else {
+        EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers));
+    }
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_SCORED_FRAME_RATE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_SCORED_FRAME_RATE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ReadsCache) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
 
     using GlobalSignals = RefreshRateSelector::GlobalSignals;
     const auto args = std::make_pair(std::vector<LayerRequirement>{},
                                      GlobalSignals{.touch = true, .idle = true});
 
-    const RefreshRateSelector::RankedRefreshRates result = {{RefreshRateSelector::ScoredRefreshRate{
-                                                                    kMode90}},
-                                                            {.touch = true}};
+    const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
+                                                                  {90_Hz, kMode90}}},
+                                                          GlobalSignals{.touch = true}};
 
     selector.mutableGetRankedRefreshRatesCache() = {args, result};
 
-    EXPECT_EQ(result, selector.getRankedRefreshRates(args.first, args.second));
+    EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_WritesCache) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
 
     EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
 
-    const auto result = selector.getRankedRefreshRates(layers, globalSignals);
+    const auto result = selector.getRankedFrameRates(layers, globalSignals);
 
     const auto& cache = selector.mutableGetRankedRefreshRatesCache();
     ASSERT_TRUE(cache);
@@ -2100,10 +2247,8 @@
     EXPECT_EQ(cache->result, result);
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactTouchBoost) {
-    TestableRefreshRateSelector selector(kModes_60_120, kModeId60,
-                                         {.enableFrameRateOverride =
-                                                  Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExactTouchBoost) {
+    auto selector = createSelector(kModes_60_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
@@ -2117,19 +2262,21 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+    } else {
+        EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true}));
+    }
 
     explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
 
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
 }
 
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
-    TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60,
-                                         {.enableFrameRateOverride =
-                                                  Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_ExactAndDefault) {
+    auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
     auto& explicitDefaultLayer = layers[0];
@@ -2143,12 +2290,12 @@
     explicitDefaultLayer.name = "ExplicitDefault";
     explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
 
-    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
 }
 
 // b/190578904
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withCloseRefreshRates) {
-    constexpr int kMinRefreshRate = 10;
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) {
+    const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
     constexpr int kMaxRefreshRate = 240;
 
     DisplayModes displayModes;
@@ -2159,14 +2306,13 @@
                                                    Fps::fromValue(static_cast<float>(fps))));
     }
 
-    const TestableRefreshRateSelector selector(std::move(displayModes),
-                                               DisplayModeId(kMinRefreshRate));
+    const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate));
 
     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(), selector.getBestRefreshRate(layers)->getFps().getIntValue())
+        EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
                 << "Failed for " << ftl::enum_string(vote);
     };
 
@@ -2180,7 +2326,7 @@
 }
 
 // b/190578904
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_conflictingVotes) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_conflictingVotes) {
     constexpr DisplayModeId kActiveModeId{0};
     DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz),
                                           createDisplayMode(DisplayModeId(1), 53_Hz),
@@ -2188,7 +2334,7 @@
                                           createDisplayMode(DisplayModeId(3), 60_Hz));
 
     const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
-    const TestableRefreshRateSelector selector(std::move(displayModes), kActiveModeId);
+    const auto selector = createSelector(std::move(displayModes), kActiveModeId);
 
     const std::vector<LayerRequirement> layers = {
             {
@@ -2205,19 +2351,19 @@
             },
     };
 
-    EXPECT_EQ(53_Hz, selector.getBestRefreshRate(layers, globalSignals)->getFps());
+    EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getFps());
 }
 
-TEST_F(RefreshRateSelectorTest, modeComparison) {
+TEST_P(RefreshRateSelectorTest, modeComparison) {
     EXPECT_LT(kMode60->getFps(), kMode90->getFps());
     EXPECT_GE(kMode60->getFps(), kMode60->getFps());
     EXPECT_GE(kMode90->getFps(), kMode90->getFps());
 }
 
-TEST_F(RefreshRateSelectorTest, testKernelIdleTimerAction) {
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerAction) {
     using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
 
-    TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+    auto selector = createSelector(kModes_60_90, kModeId90);
 
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
 
@@ -2234,10 +2380,10 @@
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
 }
 
-TEST_F(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
     using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
 
-    TestableRefreshRateSelector selector(kModes_60_120, kModeId120);
+    auto selector = createSelector(kModes_60_120, kModeId120);
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
@@ -2256,8 +2402,8 @@
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
 }
 
-TEST_F(RefreshRateSelectorTest, getFrameRateDivisor) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId30);
+TEST_P(RefreshRateSelectorTest, getFrameRateDivisor) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId30);
 
     const auto frameRate = 30_Hz;
     Fps displayRefreshRate = selector.getActiveMode().getFps();
@@ -2289,7 +2435,7 @@
     EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz));
 }
 
-TEST_F(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
+TEST_P(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
     EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
     EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
 
@@ -2315,22 +2461,72 @@
     EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
 }
 
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
-    RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120);
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
 
     EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty());
 }
 
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
-    RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
-                                 {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_NonExplicit) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
 
     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;
 
+    layers[0].vote = LayerVoteType::NoVote;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::Min;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::Max;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::Heuristic;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_Disabled) {
+    if (GetParam() != Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    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;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::ExplicitExact;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+                GetParam() == Config::FrameRateOverride::AppOverride ||
+                GetParam() == Config::FrameRateOverride::Enabled);
+
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    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 = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_EQ(1u, frameRateOverrides.size());
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
@@ -2342,26 +2538,23 @@
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
     EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    layers[0].vote = LayerVoteType::NoVote;
+    layers[0].vote = LayerVoteType::ExplicitExact;
     frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Min;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Max;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 }
 
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
-    RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
-                                 {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+                GetParam() == Config::FrameRateOverride::AppOverride ||
+                GetParam() == Config::FrameRateOverride::Enabled);
+
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
 
     std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
                                             {.ownerUid = 5678, .weight = 1.f}};
@@ -2392,9 +2585,16 @@
     EXPECT_TRUE(frameRateOverrides.empty());
 }
 
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
-    RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
-                                 {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+                GetParam() == Config::FrameRateOverride::AppOverride ||
+                GetParam() == Config::FrameRateOverride::Enabled);
+
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
 
     std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
     layers[0].name = "Test layer";
@@ -2432,88 +2632,87 @@
     EXPECT_TRUE(frameRateOverrides.empty());
 }
 
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_Enabled) {
-    RefreshRateSelector selector(kModes_60_120, kModeId120,
-                                 {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) {
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+                GetParam() == Config::FrameRateOverride::AppOverride ||
+                GetParam() == Config::FrameRateOverride::Enabled);
+
+    auto selector = createSelector(kModes_60_120, kModeId120);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
     layers[0].ownerUid = 1234;
     layers[0].desiredRefreshRate = 30_Hz;
-    layers[0].vote = LayerVoteType::ExplicitDefault;
 
+    const auto expetedFps =
+            GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ? 60_Hz : 30_Hz;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
     auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_EQ(1u, frameRateOverrides.size());
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+    EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
     frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_EQ(1u, frameRateOverrides.size());
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+    EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
 
-    layers[0].vote = LayerVoteType::NoVote;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Min;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Max;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-TEST_F(RefreshRateSelectorTest,
-       getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_EnabledForNativeRefreshRates) {
-    RefreshRateSelector selector(kModes_60_120, kModeId120,
-                                 {.enableFrameRateOverride =
-                                          Config::FrameRateOverride::EnabledForNativeRefreshRates});
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    layers[0].name = "Test layer";
-    layers[0].ownerUid = 1234;
-    layers[0].desiredRefreshRate = 30_Hz;
-    layers[0].vote = LayerVoteType::ExplicitDefault;
-
-    auto frameRateOverrides = selector.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;
+    layers[0].vote = LayerVoteType::ExplicitExact;
     frameRateOverrides = selector.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 = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Min;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Max;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
+    EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
 }
 
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) {
-    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
-                                         {.enableFrameRateOverride =
-                                                  Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, renderFrameRateInvalidPolicy) {
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    // The render frame rate cannot be greater than the physical refresh rate
+    {
+        const FpsRange physical = {60_Hz, 60_Hz};
+        const FpsRange render = {60_Hz, 120_Hz};
+        EXPECT_EQ(SetPolicyResult::Invalid,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId60, {physical, render}, {physical, render}}));
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRateRestrictsPhysicalRefreshRate) {
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    {
+        const FpsRange physical = {0_Hz, 120_Hz};
+        const FpsRange render = {0_Hz, 60_Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId60, {physical, render}, {physical, render}}));
+        const auto expectedMaxMode =
+                GetParam() == Config::FrameRateOverride::Enabled ? kMode120 : kMode60;
+        EXPECT_EQ(expectedMaxMode, selector.getMaxRefreshRateByPolicy());
+        EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+    }
+
+    {
+        const FpsRange physical = {0_Hz, 120_Hz};
+        const FpsRange render = {120_Hz, 120_Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId60, {physical, render}, {physical, render}}));
+        EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+        EXPECT_EQ(kMode120, selector.getMinRefreshRateByPolicy());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     {
@@ -2566,5 +2765,153 @@
     EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
 }
 
+TEST_P(RefreshRateSelectorTest, renderFrameRates) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    // [renderRate, refreshRate]
+    const auto expected = []() -> std::vector<std::pair<Fps, Fps>> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{30_Hz, 30_Hz},
+                        {60_Hz, 60_Hz},
+                        {72_Hz, 72_Hz},
+                        {90_Hz, 90_Hz},
+                        {120_Hz, 120_Hz}};
+            case Config::FrameRateOverride::Enabled:
+                return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz},
+                        {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz},  {120_Hz, 120_Hz}};
+        }
+    }();
+
+    const auto& primaryRefreshRates = selector.getPrimaryFrameRates();
+    ASSERT_EQ(expected.size(), primaryRefreshRates.size());
+
+    for (size_t i = 0; i < expected.size(); i++) {
+        const auto [expectedRenderRate, expectedRefreshRate] = expected[i];
+        EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps);
+        EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, refreshRateIsCappedWithRenderFrameRate) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    auto selector = createSelector(kModes_60_120, kModeId60);
+
+    constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
+    constexpr FpsRange k0_60Hz = {0_Hz, 60_Hz};
+
+    constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz,
+                                       /*render*/ k0_120Hz};
+
+    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate());
+    {
+        constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+                                        /*render*/ k0_120Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+                                                    /*primaryRanges*/
+                                                    kPrimary,
+                                                    /*appRequestRanges*/
+                                                    kAppRequest}));
+    }
+    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate());
+
+    {
+        constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz,
+                                        /*render*/ k0_60Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+                                                    /*primaryRanges*/
+                                                    kPrimary,
+                                                    /*appRequestRanges*/
+                                                    kAppRequest}));
+    }
+    EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate());
+
+    {
+        constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+                                        /*render*/ k0_60Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+                                                    /*primaryRanges*/
+                                                    kPrimary,
+                                                    /*appRequestRanges*/
+                                                    kAppRequest}));
+    }
+    EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate());
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) {
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+
+    const auto expectedRenderRate =
+            GetParam() == Config::FrameRateOverride::Enabled ? 30_Hz : 60_Hz;
+
+    layer.name = "30Hz ExplicitDefault";
+    layer.desiredRefreshRate = 30_Hz;
+    layer.vote = LayerVoteType::ExplicitDefault;
+    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+
+    layer.name = "30Hz Heuristic";
+    layer.desiredRefreshRate = 30_Hz;
+    layer.vote = LayerVoteType::Heuristic;
+    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+
+    layer.name = "30Hz ExplicitExactOrMultiple";
+    layer.desiredRefreshRate = 30_Hz;
+    layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) {
+    auto selector = createSelector(kModes_35_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+
+    const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+                                          bool touchActive) -> DisplayModeId {
+        layers[0].vote = voteType;
+        layers[0].desiredRefreshRate = 90_Hz;
+
+        const auto [ranking, signals] =
+                selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
+
+        // Refresh rate will be chosen by either touch state or idle state.
+        EXPECT_EQ(!touchActive, signals.idle);
+        return ranking.front().frameRateMode.modePtr->getId();
+    };
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 90_Hz}}));
+
+    // With no layers, idle should still be lower priority than touch boost.
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+
+    // Idle should be higher precedence than other layer frame rate considerations.
+    selector.setActiveModeId(kModeId90);
+    {
+        constexpr bool kTouchActive = false;
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+        EXPECT_EQ(kModeId35,
+                  getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+    }
+
+    // Idle should be applied rather than the active mode when there are no layers.
+    EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+}
+
 } // namespace
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 488d4a9..d84698f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -42,6 +42,8 @@
 using testing::Return;
 
 using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using frontend::TransactionHandler;
+
 constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5);
 class TransactionApplicationTest : public testing::Test {
 public: