Merge "graphics.common V3 -> V4"
diff --git a/include/android/font.h b/include/android/font.h
index 45eb81a..0225725 100644
--- a/include/android/font.h
+++ b/include/android/font.h
@@ -87,10 +87,11 @@
     AFONT_WEIGHT_MAX = 1000
 };
 
+struct AFont;
 /**
  * AFont provides information of the single font configuration.
  */
-struct AFont;
+typedef struct AFont AFont;
 
 /**
  * Close an AFont.
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
index 63b0328..60ff95e 100644
--- a/include/android/font_matcher.h
+++ b/include/android/font_matcher.h
@@ -117,11 +117,12 @@
     AFAMILY_VARIANT_ELEGANT = 2,
 };
 
+struct AFontMatcher;
 /**
  * AFontMatcher performs match operation on given parameters and available font files.
  * This matcher is not a thread-safe object. Do not pass this matcher to other threads.
  */
-struct AFontMatcher;
+typedef struct AFontMatcher AFontMatcher;
 
 /**
  * Select the best font from given parameters.
diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h
index b0bbb95..94484ea 100644
--- a/include/android/system_fonts.h
+++ b/include/android/system_fonts.h
@@ -87,13 +87,14 @@
 
 __BEGIN_DECLS
 
+struct ASystemFontIterator;
 /**
  * ASystemFontIterator provides access to the system font configuration.
  *
  * ASystemFontIterator is an iterator for all available system font settings.
  * This iterator is not a thread-safe object. Do not pass this iterator to other threads.
  */
-struct ASystemFontIterator;
+typedef struct ASystemFontIterator ASystemFontIterator;
 
 /**
  * Create a system font iterator.
diff --git a/include/input/Input.h b/include/input/Input.h
index 172e5b4..dd74a51 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -366,7 +366,7 @@
 
     // Values of axes that are stored in this structure packed in order by axis id
     // for each axis that is present in the structure according to 'bits'.
-    float values[MAX_AXES];
+    std::array<float, MAX_AXES> values;
 
     inline void clear() {
         BitSet64::clear(bits);
@@ -406,7 +406,8 @@
         return !(*this == other);
     }
 
-    void copyFrom(const PointerCoords& other);
+    inline void copyFrom(const PointerCoords& other) { *this = other; }
+    PointerCoords& operator=(const PointerCoords&) = default;
 
 private:
     void tooManyAxes(int axis);
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index ac9c5a5..e911734 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -58,6 +58,9 @@
     // reuse values that are not associated with an input anymore.
     uint16_t nonce;
 
+    // The bluetooth address of the device, if known.
+    std::optional<std::string> bluetoothAddress;
+
     /**
      * Return InputDeviceIdentifier.name that has been adjusted as follows:
      *     - all characters besides alphanumerics, dash,
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 55f730b..e24344b 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -24,16 +24,20 @@
 namespace android {
 
 template <typename T>
-std::string constToString(const T& v) {
+inline std::string constToString(const T& v) {
     return std::to_string(v);
 }
 
+inline std::string constToString(const std::string& s) {
+    return s;
+}
+
 /**
  * Convert an optional type to string.
  */
 template <typename T>
-std::string toString(const std::optional<T>& optional,
-                     std::string (*toString)(const T&) = constToString) {
+inline std::string toString(const std::optional<T>& optional,
+                            std::string (*toString)(const T&) = constToString) {
     return optional ? toString(*optional) : "<not set>";
 }
 
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 78bcb43..81975e7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -291,7 +291,10 @@
 binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args,
                                                      uint32_t numArgs) {
     std::shared_ptr<ICInterface> interface = getInterface(binder);
-    return interface->dump(fd, args, numArgs);
+    if (interface != nullptr) {
+        return interface->dump(fd, args, numArgs);
+    }
+    return STATUS_DEAD_OBJECT;
 }
 
 #ifdef HAS_BINDER_SHELL_COMMAND
@@ -299,7 +302,10 @@
                                                                  int err, const char** argv,
                                                                  uint32_t argc) {
     std::shared_ptr<ICInterface> interface = getInterface(binder);
-    return interface->handleShellCommand(in, out, err, argv, argc);
+    if (interface != nullptr) {
+        return interface->handleShellCommand(in, out, err, argv, argc);
+    }
+    return STATUS_DEAD_OBJECT;
 }
 #endif
 
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index a135796..738d16a 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -88,6 +88,7 @@
     min_sdk_version: "Tiramisu",
     lints: "none",
     clippy_lints: "none",
+    visibility: [":__subpackages__"],
 }
 
 rust_bindgen {
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 195d9ac..a0e61d9 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -148,4 +148,5 @@
     pub use crate::binder::AsNative;
     pub use crate::proxy::unstable_api::new_spibinder;
     pub use crate::sys::AIBinder;
+    pub use crate::sys::AParcel;
 }
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
new file mode 100644
index 0000000..6fe4fcd
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
@@ -0,0 +1,50 @@
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+rust_bindgen {
+    name: "libbinder_random_parcel_bindgen",
+    crate_name: "binder_random_parcel_bindgen",
+    host_supported: true,
+    wrapper_src: "wrappers/RandomParcelWrapper.hpp",
+    source_stem: "bindings",
+    visibility: [":__subpackages__"],
+    bindgen_flags: [
+        "--size_t-is-usize",
+        "--allowlist-function",
+        "createRandomParcel",
+    ],
+    shared_libs: [
+        "libc++",
+        "libbinder_ndk",
+    ],
+    rustlibs: [
+        "libbinder_rs",
+    ],
+}
+
+rust_library {
+    name: "libbinder_random_parcel_rs",
+    crate_name: "binder_random_parcel_rs",
+    host_supported: true,
+    srcs: [
+        "src/lib.rs",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "libcutils",
+        "libc++",
+    ],
+    static_libs: [
+        "libbinder_create_parcel",
+        "libbinder_random_parcel",
+    ],
+    rustlibs: [
+        "libbinder_rs",
+        "libbinder_random_parcel_bindgen",
+    ],
+    lints: "none",
+    clippy_lints: "none",
+}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs
new file mode 100644
index 0000000..ee3b6f8
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+use binder::binder_impl::Parcel;
+use binder::unstable_api::{AParcel, AsNative};
+use binder_random_parcel_bindgen::createRandomParcel;
+use std::os::raw::c_void;
+
+/// This API creates a random parcel to be used by fuzzers
+pub fn create_random_parcel(fuzzer_data: &[u8]) -> Parcel {
+    let mut parcel = Parcel::new();
+    let aparcel_ptr: *mut AParcel = parcel.as_native_mut();
+    let ptr = aparcel_ptr as *mut c_void;
+    unsafe {
+        // Safety: `Parcel::as_native_mut` and `slice::as_ptr` always
+        // return valid pointers.
+        createRandomParcel(ptr, fuzzer_data.as_ptr(), fuzzer_data.len());
+    }
+    parcel
+}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp
new file mode 100644
index 0000000..167a64e
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp
@@ -0,0 +1,22 @@
+/*
+ * 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 <cstdint>
+#include <cstddef>
+
+extern "C" {
+    // This API is used by rust to fill random parcel.
+    void createRandomParcel(void* aParcel, const uint8_t* data, size_t len);
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp b/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp
new file mode 100644
index 0000000..b48dc27
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp
@@ -0,0 +1,24 @@
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+    name: "libbinder_create_parcel",
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    srcs: [
+        "RandomParcelWrapper.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libbinder_ndk",
+    ],
+    static_libs: [
+        "libbinder_random_parcel",
+    ],
+}
diff --git a/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp b/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp
new file mode 100644
index 0000000..2fb7820
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 <android-base/logging.h>
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <fuzzbinder/random_parcel.h>
+
+extern "C" {
+
+void createRandomParcel(void* aParcel, const uint8_t* data, size_t len) {
+    CHECK_NE(aParcel, nullptr);
+    AParcel* parcel = static_cast<AParcel*>(aParcel);
+    FuzzedDataProvider provider(data, len);
+    android::RandomParcelOptions options;
+
+    android::Parcel* platformParcel = AParcel_viewPlatformParcel(parcel);
+    fillRandomParcel(platformParcel, std::move(provider), &options);
+}
+
+} // extern "C"
\ No newline at end of file
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 0021bd6..97e45c6 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -360,11 +360,12 @@
                     }
                 }
                 for (const auto& staleRelease : staleReleases) {
-                    BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback");
-                    BBQ_TRACE("FakeReleaseCallback");
                     releaseBufferCallbackLocked(staleRelease,
-                        stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE,
-                        stat.currentMaxAcquiredBufferCount);
+                                                stat.previousReleaseFence
+                                                        ? stat.previousReleaseFence
+                                                        : Fence::NO_FENCE,
+                                                stat.currentMaxAcquiredBufferCount,
+                                                true /* fakeRelease */);
                 }
             } else {
                 BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
@@ -408,11 +409,13 @@
     BBQ_TRACE();
 
     std::unique_lock _lock{mMutex};
-    releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount);
+    releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount,
+                                false /* fakeRelease */);
 }
 
-void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id,
-        const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount) {
+void BLASTBufferQueue::releaseBufferCallbackLocked(
+        const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+        std::optional<uint32_t> currentMaxAcquiredBufferCount, bool fakeRelease) {
     ATRACE_CALL();
     BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
 
@@ -435,6 +438,11 @@
     auto rb = ReleasedBuffer{id, releaseFence};
     if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) {
         mPendingRelease.emplace_back(rb);
+        if (fakeRelease) {
+            BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback %" PRIu64,
+                     id.framenumber);
+            BBQ_TRACE("FakeReleaseCallback");
+        }
     }
 
     // Release all buffers that are beyond the ones that we need to hold
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 957652e..47dcc42 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -93,7 +93,8 @@
     void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
                                std::optional<uint32_t> currentMaxAcquiredBufferCount);
     void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
-                               std::optional<uint32_t> currentMaxAcquiredBufferCount);
+                                     std::optional<uint32_t> currentMaxAcquiredBufferCount,
+                                     bool fakeRelease);
     void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
                              bool acquireSingleBuffer = true);
     void stopContinuousSyncTransaction();
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c1eb8e2..cf5a7e7 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -438,14 +438,6 @@
     return true;
 }
 
-void PointerCoords::copyFrom(const PointerCoords& other) {
-    bits = other.bits;
-    uint32_t count = BitSet64::count(bits);
-    for (uint32_t i = 0; i < count; i++) {
-        values[i] = other.values[i];
-    }
-}
-
 void PointerCoords::transform(const ui::Transform& transform) {
     const vec2 xy = transform.transform(getXYValue());
     setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
new file mode 100644
index 0000000..49ab34d
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -0,0 +1,46 @@
+/*
+ * 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/Errors.h>
+
+namespace android::recoverymap {
+
+enum {
+    // status_t map for errors in the media framework
+    // OK or NO_ERROR or 0 represents no error.
+
+    // See system/core/include/utils/Errors.h
+    // System standard errors from -1 through (possibly) -133
+    //
+    // Errors with special meanings and side effects.
+    // INVALID_OPERATION:  Operation attempted in an illegal state (will try to signal to app).
+    // DEAD_OBJECT:        Signal from CodecBase to MediaCodec that MediaServer has died.
+    // NAME_NOT_FOUND:     Signal from CodecBase to MediaCodec that the component was not found.
+
+    // JPEGR errors
+    JPEGR_IO_ERROR_BASE                 = -10000,
+    ERROR_JPEGR_INVALID_INPUT_TYPE      = JPEGR_IO_ERROR_BASE,
+    ERROR_JPEGR_INVALID_OUTPUT_TYPE     = JPEGR_IO_ERROR_BASE - 1,
+    ERROR_JPEGR_INVALID_NULL_PTR        = JPEGR_IO_ERROR_BASE - 2,
+
+    JPEGR_RUNTIME_ERROR_BASE            = -20000,
+    ERROR_JPEGR_ENCODE_ERROR            = JPEGR_RUNTIME_ERROR_BASE - 1,
+    ERROR_JPEGR_DECODE_ERROR            = JPEGR_RUNTIME_ERROR_BASE - 2,
+    ERROR_JPEGR_CALCULATION_ERROR       = JPEGR_RUNTIME_ERROR_BASE - 3,
+    ERROR_JPEGR_METADATA_ERROR          = JPEGR_RUNTIME_ERROR_BASE - 4,
+};
+
+}  // namespace android::recoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index 15eca1e..31f1872 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
- #include <utils/Errors.h>
+#include "jpegrerrorcode.h"
 
 namespace android::recoverymap {
 
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index 5d25722..67c23e9 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -24,7 +24,7 @@
   if (uncompressed_p010_image == nullptr
    || uncompressed_yuv_420_image == nullptr
    || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -40,7 +40,7 @@
    || uncompressed_yuv_420_image == nullptr
    || compressed_jpeg_image == nullptr
    || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -53,7 +53,7 @@
   if (uncompressed_p010_image == nullptr
    || compressed_jpeg_image == nullptr
    || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -62,7 +62,7 @@
 
 status_t RecoveryMap::decodeJPEGR(void* compressed_jpegr_image, jr_uncompressed_ptr dest) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -72,7 +72,7 @@
 status_t RecoveryMap::decodeRecoveryMap(jr_compressed_ptr compressed_recovery_map,
                                         jr_uncompressed_ptr dest) {
   if (compressed_recovery_map == nullptr || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -82,7 +82,7 @@
 status_t RecoveryMap::encodeRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
                                         jr_compressed_ptr dest) {
   if (uncompressed_recovery_map == nullptr || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -95,7 +95,7 @@
   if (uncompressed_yuv_420_image == nullptr
    || uncompressed_p010_image == nullptr
    || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -108,7 +108,7 @@
   if (uncompressed_yuv_420_image == nullptr
    || uncompressed_recovery_map == nullptr
    || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -117,7 +117,7 @@
 
 status_t RecoveryMap::extractRecoveryMap(void* compressed_jpegr_image, jr_compressed_ptr dest) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
@@ -130,7 +130,7 @@
   if (compressed_jpeg_image == nullptr
    || compressed_recovery_map == nullptr
    || dest == nullptr) {
-    return BAD_VALUE;
+    return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
   // TBD
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index ec90645..80e911c 100644
--- a/libs/vibrator/ExternalVibration.cpp
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -22,15 +22,6 @@
 #include <log/log.h>
 #include <utils/Errors.h>
 
-
-// To guarantee if HapticScale enum has the same value as IExternalVibratorService
-static_assert(static_cast<int>(android::os::HapticScale::MUTE) == static_cast<int>(android::os::IExternalVibratorService::SCALE_MUTE));
-static_assert(static_cast<int>(android::os::HapticScale::VERY_LOW) == static_cast<int>(android::os::IExternalVibratorService::SCALE_VERY_LOW));
-static_assert(static_cast<int>(android::os::HapticScale::LOW) == static_cast<int>(android::os::IExternalVibratorService::SCALE_LOW));
-static_assert(static_cast<int>(android::os::HapticScale::NONE) == static_cast<int>(android::os::IExternalVibratorService::SCALE_NONE));
-static_assert(static_cast<int>(android::os::HapticScale::HIGH) == static_cast<int>(android::os::IExternalVibratorService::SCALE_HIGH));
-static_assert(static_cast<int>(android::os::HapticScale::VERY_HIGH) == static_cast<int>(android::os::IExternalVibratorService::SCALE_VERY_HIGH));
-
 void writeAudioAttributes(const audio_attributes_t& attrs, android::Parcel* out) {
     out->writeInt32(attrs.usage);
     out->writeInt32(attrs.content_type);
@@ -74,5 +65,25 @@
     return mToken == rhs.mToken;
 }
 
+os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(int externalVibrationScale) {
+    switch (externalVibrationScale) {
+        case IExternalVibratorService::SCALE_MUTE:
+            return os::HapticScale::MUTE;
+        case IExternalVibratorService::SCALE_VERY_LOW:
+            return os::HapticScale::VERY_LOW;
+        case IExternalVibratorService::SCALE_LOW:
+            return os::HapticScale::LOW;
+        case IExternalVibratorService::SCALE_NONE:
+            return os::HapticScale::NONE;
+        case IExternalVibratorService::SCALE_HIGH:
+            return os::HapticScale::HIGH;
+        case IExternalVibratorService::SCALE_VERY_HIGH:
+            return os::HapticScale::VERY_HIGH;
+        default:
+          ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", externalVibrationScale);
+          return os::HapticScale::NONE;
+      }
+}
+
 } // namespace os
 } // namespace android
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
index 760dbce..00cd3cd 100644
--- a/libs/vibrator/include/vibrator/ExternalVibration.h
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -23,6 +23,7 @@
 #include <binder/Parcelable.h>
 #include <system/audio.h>
 #include <utils/RefBase.h>
+#include <vibrator/ExternalVibrationUtils.h>
 
 namespace android {
 namespace os {
@@ -44,6 +45,10 @@
     audio_attributes_t getAudioAttributes() const { return mAttrs; }
     sp<IExternalVibrationController> getController() { return mController; }
 
+    /* Converts the scale from non-public ExternalVibrationService into the HapticScale
+     * used by the utils.
+     */
+    static os::HapticScale externalVibrationScaleToHapticScale(int externalVibrationScale);
 
 private:
     int32_t mUid;
@@ -53,7 +58,7 @@
     sp<IBinder> mToken = new BBinder();
 };
 
-} // namespace android
 } // namespace os
+} // namespace android
 
 #endif // ANDROID_EXTERNAL_VIBRATION_H
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index c588bfd..ca219d3 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -19,8 +19,6 @@
 
 namespace android::os {
 
-// Copied from frameworks/base/core/java/android/os/IExternalVibratorService.aidl
-// The values are checked in ExternalVibration.cpp
 enum class HapticScale {
     MUTE = -100,
     VERY_LOW = -2,
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
index 6db89d4..628ce6f 100644
--- a/services/inputflinger/InputCommonConverter.cpp
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -304,8 +304,8 @@
         common::PointerCoords coords;
         // OK to copy bits because we have static_assert for pointerCoords axes
         coords.bits = args.pointerCoords[i].bits;
-        coords.values = std::vector<float>(args.pointerCoords[i].values,
-                                           args.pointerCoords[i].values +
+        coords.values = std::vector<float>(args.pointerCoords[i].values.cbegin(),
+                                           args.pointerCoords[i].values.cbegin() +
                                                    BitSet64::count(args.pointerCoords[i].bits));
         outPointerCoords.push_back(coords);
     }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index e191d93..9a6ebaa 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -541,6 +541,17 @@
                          entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)));
 }
 
+std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
+    if (eventEntry.type == EventEntry::Type::KEY) {
+        const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+        return keyEntry.downTime;
+    } else if (eventEntry.type == EventEntry::Type::MOTION) {
+        const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+        return motionEntry.downTime;
+    }
+    return std::nullopt;
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -1568,9 +1579,10 @@
     }
 
     // Identify targets.
-    std::vector<InputTarget> inputTargets;
-    InputEventInjectionResult injectionResult =
-            findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
+    InputEventInjectionResult injectionResult;
+    sp<WindowInfoHandle> focusedWindow =
+            findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime,
+                                          /*byref*/ injectionResult);
     if (injectionResult == InputEventInjectionResult::PENDING) {
         return false;
     }
@@ -1579,6 +1591,12 @@
     if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
         return true;
     }
+    LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
+
+    std::vector<InputTarget> inputTargets;
+    addWindowTargetLocked(focusedWindow,
+                          InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
+                          BitSet32(0), getDownTime(*entry), inputTargets);
 
     // Add monitor channels from event's or focused display.
     addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
@@ -1673,13 +1691,26 @@
             pilferPointersLocked(mDragState->dragWindow->getToken());
         }
 
-        injectionResult =
-                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
-                                               &conflictingPointerActions);
+        std::vector<TouchedWindow> touchedWindows =
+                findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
+                                               /*byref*/ injectionResult);
+        for (const TouchedWindow& touchedWindow : touchedWindows) {
+            LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED,
+                                "Shouldn't be adding window if the injection didn't succeed.");
+            addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+                                  touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
+                                  inputTargets);
+        }
     } else {
         // Non touch event.  (eg. trackball)
-        injectionResult =
-                findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
+        sp<WindowInfoHandle> focusedWindow =
+                findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, injectionResult);
+        if (injectionResult == InputEventInjectionResult::SUCCEEDED) {
+            LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
+            addWindowTargetLocked(focusedWindow,
+                                  InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
+                                  BitSet32(0), getDownTime(*entry), inputTargets);
+        }
     }
     if (injectionResult == InputEventInjectionResult::PENDING) {
         return false;
@@ -1881,21 +1912,11 @@
     return false;
 }
 
-static std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
-    if (eventEntry.type == EventEntry::Type::KEY) {
-        const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
-        return keyEntry.downTime;
-    } else if (eventEntry.type == EventEntry::Type::MOTION) {
-        const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
-        return motionEntry.downTime;
-    }
-    return std::nullopt;
-}
-
-InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
-        nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
-        nsecs_t* nextWakeupTime) {
+sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(
+        nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
+        InputEventInjectionResult& outInjectionResult) {
     std::string reason;
+    outInjectionResult = InputEventInjectionResult::FAILED; // Default result
 
     int32_t displayId = getTargetDisplayId(entry);
     sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
@@ -1908,12 +1929,12 @@
         ALOGI("Dropping %s event because there is no focused window or focused application in "
               "display %" PRId32 ".",
               ftl::enum_string(entry.type).c_str(), displayId);
-        return InputEventInjectionResult::FAILED;
+        return nullptr;
     }
 
     // Drop key events if requested by input feature
     if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
-        return InputEventInjectionResult::FAILED;
+        return nullptr;
     }
 
     // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
@@ -1933,15 +1954,17 @@
                   "window when it finishes starting up. Will wait for %" PRId64 "ms",
                   mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
             *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
-            return InputEventInjectionResult::PENDING;
+            outInjectionResult = InputEventInjectionResult::PENDING;
+            return nullptr;
         } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
             // Already raised ANR. Drop the event
             ALOGE("Dropping %s event because there is no focused window",
                   ftl::enum_string(entry.type).c_str());
-            return InputEventInjectionResult::FAILED;
+            return nullptr;
         } else {
             // Still waiting for the focused window
-            return InputEventInjectionResult::PENDING;
+            outInjectionResult = InputEventInjectionResult::PENDING;
+            return nullptr;
         }
     }
 
@@ -1951,13 +1974,15 @@
     // Verify targeted injection.
     if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {
         ALOGW("Dropping injected event: %s", (*err).c_str());
-        return InputEventInjectionResult::TARGET_MISMATCH;
+        outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
+        return nullptr;
     }
 
     if (focusedWindowHandle->getInfo()->inputConfig.test(
                 WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
         ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
-        return InputEventInjectionResult::PENDING;
+        outInjectionResult = InputEventInjectionResult::PENDING;
+        return nullptr;
     }
 
     // If the event is a key event, then we must wait for all previous events to
@@ -1974,17 +1999,13 @@
     if (entry.type == EventEntry::Type::KEY) {
         if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
             *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
-            return InputEventInjectionResult::PENDING;
+            outInjectionResult = InputEventInjectionResult::PENDING;
+            return nullptr;
         }
     }
 
-    // Success!  Output targets.
-    addWindowTargetLocked(focusedWindowHandle,
-                          InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
-                          BitSet32(0), getDownTime(entry), inputTargets);
-
-    // Done.
-    return InputEventInjectionResult::SUCCEEDED;
+    outInjectionResult = InputEventInjectionResult::SUCCEEDED;
+    return focusedWindowHandle;
 }
 
 /**
@@ -2013,11 +2034,42 @@
     return responsiveMonitors;
 }
 
-InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
-        nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
-        nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
+/**
+ * In general, touch should be always split between windows. Some exceptions:
+ * 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's
+ * from the same device, *and* the window that's receiving the current pointer does not support
+ * split touch.
+ * 2. Don't split mouse events
+ */
+bool InputDispatcher::shouldSplitTouch(const TouchState& touchState,
+                                       const MotionEntry& entry) const {
+    if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
+        // We should never split mouse events
+        return false;
+    }
+    for (const TouchedWindow& touchedWindow : touchState.windows) {
+        if (touchedWindow.windowHandle->getInfo()->isSpy()) {
+            // Spy windows should not affect whether or not touch is split.
+            continue;
+        }
+        if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
+            continue;
+        }
+        // Eventually, touchedWindow will contain the deviceId of each pointer that's currently
+        // being sent there. For now, use deviceId from touch state.
+        if (entry.deviceId == touchState.deviceId && !touchedWindow.pointerIds.isEmpty()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
+        nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
+        InputEventInjectionResult& outInjectionResult) {
     ATRACE_CALL();
 
+    std::vector<TouchedWindow> touchedWindows;
     // For security reasons, we defer updating the touch state until we are sure that
     // event injection will be allowed.
     const int32_t displayId = entry.displayId;
@@ -2025,7 +2077,7 @@
     const int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
     // Update the touch state as needed based on the properties of the touch event.
-    InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
+    outInjectionResult = InputEventInjectionResult::PENDING;
     sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle);
     sp<WindowInfoHandle> newTouchedWindowHandle;
 
@@ -2039,8 +2091,8 @@
         tempTouchState = *oldState;
     }
 
-    bool isSplit = tempTouchState.split;
-    bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
+    bool isSplit = shouldSplitTouch(tempTouchState, entry);
+    const bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
             (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
              tempTouchState.displayId != displayId);
 
@@ -2050,7 +2102,6 @@
     const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
                              maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
     const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
-    bool wrongDevice = false;
     if (newGesture) {
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         if (switchedDevice && tempTouchState.down && !down && !isHoverAction) {
@@ -2058,10 +2109,8 @@
                   "in display %" PRId32,
                   displayId);
             // TODO: test multiple simultaneous input streams.
-            injectionResult = InputEventInjectionResult::FAILED;
-            switchedDevice = false;
-            wrongDevice = true;
-            goto Failed;
+            outInjectionResult = InputEventInjectionResult::FAILED;
+            return touchedWindows; // wrong device
         }
         tempTouchState.reset();
         tempTouchState.down = down;
@@ -2074,10 +2123,8 @@
               "in display %" PRId32,
               displayId);
         // TODO: test multiple simultaneous input streams.
-        injectionResult = InputEventInjectionResult::FAILED;
-        switchedDevice = false;
-        wrongDevice = true;
-        goto Failed;
+        outInjectionResult = InputEventInjectionResult::FAILED;
+        return touchedWindows; // wrong device
     }
 
     if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
@@ -2100,7 +2147,7 @@
         // Verify targeted injection.
         if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
             ALOGW("Dropping injected touch event: %s", (*err).c_str());
-            injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+            outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
             newTouchedWindowHandle = nullptr;
             goto Failed;
         }
@@ -2119,7 +2166,7 @@
             // No window is touched, so set split to true. This will allow the next pointer down to
             // be delivered to a new window which supports split touch. Pointers from a mouse device
             // should never be split.
-            tempTouchState.split = isSplit = !isFromMouse;
+            isSplit = !isFromMouse;
         }
 
         // Update hover state.
@@ -2141,7 +2188,7 @@
         if (newTouchedWindows.empty()) {
             ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
                   x, y, displayId);
-            injectionResult = InputEventInjectionResult::FAILED;
+            outInjectionResult = InputEventInjectionResult::FAILED;
             goto Failed;
         }
 
@@ -2193,7 +2240,7 @@
                       "dropped the pointer down event in display %" PRId32,
                       displayId);
             }
-            injectionResult = InputEventInjectionResult::FAILED;
+            outInjectionResult = InputEventInjectionResult::FAILED;
             goto Failed;
         }
 
@@ -2212,7 +2259,7 @@
             // Verify targeted injection.
             if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
                 ALOGW("Dropping injected event: %s", (*err).c_str());
-                injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+                outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
                 newTouchedWindowHandle = nullptr;
                 goto Failed;
             }
@@ -2303,7 +2350,7 @@
                      })) {
         ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
               displayId, entry.getDescription().c_str());
-        injectionResult = InputEventInjectionResult::FAILED;
+        outInjectionResult = InputEventInjectionResult::FAILED;
         goto Failed;
     }
 
@@ -2323,7 +2370,7 @@
             ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
                   "%d:%s",
                   *entry.injectionState->targetUid, errs.c_str());
-            injectionResult = InputEventInjectionResult::TARGET_MISMATCH;
+            outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
             goto Failed;
         }
     }
@@ -2380,13 +2427,8 @@
     }
 
     // Success!  Output targets.
-    injectionResult = InputEventInjectionResult::SUCCEEDED;
-
-    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
-        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
-                              touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
-                              inputTargets);
-    }
+    touchedWindows = tempTouchState.windows;
+    outInjectionResult = InputEventInjectionResult::SUCCEEDED;
 
     // Drop the outside or hover touch windows since we will not care about them
     // in the next iteration.
@@ -2394,84 +2436,82 @@
 
 Failed:
     // Update final pieces of touch state if the injector had permission.
-    if (!wrongDevice) {
-        if (switchedDevice) {
+    if (switchedDevice) {
+        if (DEBUG_FOCUS) {
+            ALOGD("Conflicting pointer actions: Switched to a different device.");
+        }
+        *outConflictingPointerActions = true;
+    }
+
+    if (isHoverAction) {
+        // Started hovering, therefore no longer down.
+        if (oldState && oldState->down) {
             if (DEBUG_FOCUS) {
-                ALOGD("Conflicting pointer actions: Switched to a different device.");
+                ALOGD("Conflicting pointer actions: Hover received while pointer was "
+                      "down.");
             }
             *outConflictingPointerActions = true;
         }
-
-        if (isHoverAction) {
-            // Started hovering, therefore no longer down.
-            if (oldState && oldState->down) {
-                if (DEBUG_FOCUS) {
-                    ALOGD("Conflicting pointer actions: Hover received while pointer was "
-                          "down.");
-                }
-                *outConflictingPointerActions = true;
-            }
-            tempTouchState.reset();
-            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
-                maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                tempTouchState.deviceId = entry.deviceId;
-                tempTouchState.source = entry.source;
-                tempTouchState.displayId = displayId;
-            }
-        } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
-                   maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
-            // All pointers up or canceled.
-            tempTouchState.reset();
-        } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
-            // First pointer went down.
-            if (oldState && oldState->down) {
-                if (DEBUG_FOCUS) {
-                    ALOGD("Conflicting pointer actions: Down received while already down.");
-                }
-                *outConflictingPointerActions = true;
-            }
-        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
-            // One pointer went up.
-            int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-            uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-
-            for (size_t i = 0; i < tempTouchState.windows.size();) {
-                TouchedWindow& touchedWindow = tempTouchState.windows[i];
-                touchedWindow.pointerIds.clearBit(pointerId);
-                if (touchedWindow.pointerIds.isEmpty()) {
-                    tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
-                    continue;
-                }
-                i += 1;
-            }
-        } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
-            // If no split, we suppose all touched windows should receive pointer down.
-            const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-            for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
-                TouchedWindow& touchedWindow = tempTouchState.windows[i];
-                // Ignore drag window for it should just track one pointer.
-                if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
-                    continue;
-                }
-                touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
-            }
+        tempTouchState.reset();
+        if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+            maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+            tempTouchState.deviceId = entry.deviceId;
+            tempTouchState.source = entry.source;
+            tempTouchState.displayId = displayId;
         }
-
-        // Save changes unless the action was scroll in which case the temporary touch
-        // state was only valid for this one action.
-        if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
-            if (tempTouchState.displayId >= 0) {
-                mTouchStatesByDisplay[displayId] = tempTouchState;
-            } else {
-                mTouchStatesByDisplay.erase(displayId);
+    } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+               maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+        // All pointers up or canceled.
+        tempTouchState.reset();
+    } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+        // First pointer went down.
+        if (oldState && oldState->down) {
+            if (DEBUG_FOCUS) {
+                ALOGD("Conflicting pointer actions: Down received while already down.");
             }
+            *outConflictingPointerActions = true;
         }
+    } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+        // One pointer went up.
+        int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+        uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
 
-        // Update hover state.
-        mLastHoverWindowHandle = newHoverWindowHandle;
+        for (size_t i = 0; i < tempTouchState.windows.size();) {
+            TouchedWindow& touchedWindow = tempTouchState.windows[i];
+            touchedWindow.pointerIds.clearBit(pointerId);
+            if (touchedWindow.pointerIds.isEmpty()) {
+                tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
+                continue;
+            }
+            i += 1;
+        }
+    } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+        // If no split, we suppose all touched windows should receive pointer down.
+        const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+        for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
+            TouchedWindow& touchedWindow = tempTouchState.windows[i];
+            // Ignore drag window for it should just track one pointer.
+            if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+                continue;
+            }
+            touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
+        }
     }
 
-    return injectionResult;
+    // Save changes unless the action was scroll in which case the temporary touch
+    // state was only valid for this one action.
+    if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
+        if (tempTouchState.displayId >= 0) {
+            mTouchStatesByDisplay[displayId] = tempTouchState;
+        } else {
+            mTouchStatesByDisplay.erase(displayId);
+        }
+    }
+
+    // Update hover state.
+    mLastHoverWindowHandle = newHoverWindowHandle;
+
+    return touchedWindows;
 }
 
 void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
@@ -3205,6 +3245,55 @@
     postCommandLocked(std::move(command));
 }
 
+status_t InputDispatcher::publishMotionEvent(Connection& connection,
+                                             DispatchEntry& dispatchEntry) const {
+    const EventEntry& eventEntry = *(dispatchEntry.eventEntry);
+    const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+
+    PointerCoords scaledCoords[MAX_POINTERS];
+    const PointerCoords* usingCoords = motionEntry.pointerCoords;
+
+    // Set the X and Y offset and X and Y scale depending on the input source.
+    if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
+        !(dispatchEntry.targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
+        float globalScaleFactor = dispatchEntry.globalScaleFactor;
+        if (globalScaleFactor != 1.0f) {
+            for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+                scaledCoords[i] = motionEntry.pointerCoords[i];
+                // Don't apply window scale here since we don't want scale to affect raw
+                // coordinates. The scale will be sent back to the client and applied
+                // later when requesting relative coordinates.
+                scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+                                      1 /* windowYScale */);
+            }
+            usingCoords = scaledCoords;
+        }
+    } else if (dispatchEntry.targetFlags & InputTarget::FLAG_ZERO_COORDS) {
+        // We don't want the dispatch target to know the coordinates
+        for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+            scaledCoords[i].clear();
+        }
+        usingCoords = scaledCoords;
+    }
+
+    std::array<uint8_t, 32> hmac = getSignature(motionEntry, dispatchEntry);
+
+    // Publish the motion event.
+    return connection.inputPublisher
+            .publishMotionEvent(dispatchEntry.seq, dispatchEntry.resolvedEventId,
+                                motionEntry.deviceId, motionEntry.source, motionEntry.displayId,
+                                std::move(hmac), dispatchEntry.resolvedAction,
+                                motionEntry.actionButton, dispatchEntry.resolvedFlags,
+                                motionEntry.edgeFlags, motionEntry.metaState,
+                                motionEntry.buttonState, motionEntry.classification,
+                                dispatchEntry.transform, motionEntry.xPrecision,
+                                motionEntry.yPrecision, motionEntry.xCursorPosition,
+                                motionEntry.yCursorPosition, dispatchEntry.rawTransform,
+                                motionEntry.downTime, motionEntry.eventTime,
+                                motionEntry.pointerCount, motionEntry.pointerProperties,
+                                usingCoords);
+}
+
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                                const sp<Connection>& connection) {
     if (ATRACE_ENABLED()) {
@@ -3244,58 +3333,7 @@
             }
 
             case EventEntry::Type::MOTION: {
-                const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
-
-                PointerCoords scaledCoords[MAX_POINTERS];
-                const PointerCoords* usingCoords = motionEntry.pointerCoords;
-
-                // Set the X and Y offset and X and Y scale depending on the input source.
-                if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
-                    !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
-                    float globalScaleFactor = dispatchEntry->globalScaleFactor;
-                    if (globalScaleFactor != 1.0f) {
-                        for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
-                            scaledCoords[i] = motionEntry.pointerCoords[i];
-                            // Don't apply window scale here since we don't want scale to affect raw
-                            // coordinates. The scale will be sent back to the client and applied
-                            // later when requesting relative coordinates.
-                            scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
-                                                  1 /* windowYScale */);
-                        }
-                        usingCoords = scaledCoords;
-                    }
-                } else {
-                    // We don't want the dispatch target to know.
-                    if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
-                        for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
-                            scaledCoords[i].clear();
-                        }
-                        usingCoords = scaledCoords;
-                    }
-                }
-
-                std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry);
-
-                // Publish the motion event.
-                status = connection->inputPublisher
-                                 .publishMotionEvent(dispatchEntry->seq,
-                                                     dispatchEntry->resolvedEventId,
-                                                     motionEntry.deviceId, motionEntry.source,
-                                                     motionEntry.displayId, std::move(hmac),
-                                                     dispatchEntry->resolvedAction,
-                                                     motionEntry.actionButton,
-                                                     dispatchEntry->resolvedFlags,
-                                                     motionEntry.edgeFlags, motionEntry.metaState,
-                                                     motionEntry.buttonState,
-                                                     motionEntry.classification,
-                                                     dispatchEntry->transform,
-                                                     motionEntry.xPrecision, motionEntry.yPrecision,
-                                                     motionEntry.xCursorPosition,
-                                                     motionEntry.yCursorPosition,
-                                                     dispatchEntry->rawTransform,
-                                                     motionEntry.downTime, motionEntry.eventTime,
-                                                     motionEntry.pointerCount,
-                                                     motionEntry.pointerProperties, usingCoords);
+                status = publishMotionEvent(*connection, *dispatchEntry);
                 break;
             }
 
@@ -5288,9 +5326,9 @@
         dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
         for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) {
             const TouchState& state = pair.second;
-            dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
-                                 state.displayId, toString(state.down), toString(state.split),
-                                 state.deviceId, state.source);
+            dump += StringPrintf(INDENT2 "%d: down=%s, deviceId=%d, source=0x%08x\n",
+                                 state.displayId, toString(state.down), state.deviceId,
+                                 state.source);
             if (!state.windows.empty()) {
                 dump += INDENT3 "Windows:\n";
                 for (size_t i = 0; i < state.windows.size(); i++) {
@@ -5663,10 +5701,7 @@
                                "input channel stole pointer stream");
     options.deviceId = state.deviceId;
     options.displayId = state.displayId;
-    if (state.split) {
-        // If split pointers then selectively cancel pointers otherwise cancel all pointers
-        options.pointerIds = window.pointerIds;
-    }
+    options.pointerIds = window.pointerIds;
     std::string canceledWindows;
     for (const TouchedWindow& w : state.windows) {
         const std::shared_ptr<InputChannel> channel =
@@ -5685,11 +5720,7 @@
     // This only blocks relevant pointers to be sent to other windows
     window.isPilferingPointers = true;
 
-    if (state.split) {
-        state.cancelPointersForWindowsExcept(window.pointerIds, token);
-    } else {
-        state.filterWindowsExcept(token);
-    }
+    state.cancelPointersForWindowsExcept(window.pointerIds, token);
     return OK;
 }
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 8356f3d..3b44a8e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -541,13 +541,14 @@
     // shade is pulled down while we are counting down the timeout).
     void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
 
+    bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) const;
     int32_t getTargetDisplayId(const EventEntry& entry);
-    android::os::InputEventInjectionResult findFocusedWindowTargetsLocked(
-            nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
-            nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
-            nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
-            nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
+    sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
+            nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
+            android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
+    std::vector<TouchedWindow> findTouchedWindowTargetsLocked(
+            nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
+            android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
     std::vector<Monitor> selectResponsiveMonitorsLocked(
             const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
 
@@ -601,6 +602,7 @@
     void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>,
                                     const InputTarget& inputTarget, int32_t dispatchMode)
             REQUIRES(mLock);
+    status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const;
     void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
     void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index cf0c38a..eb31046 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -31,10 +31,6 @@
 
 void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags,
                                    BitSet32 pointerIds, std::optional<nsecs_t> eventTime) {
-    if (targetFlags & InputTarget::FLAG_SPLIT) {
-        split = true;
-    }
-
     for (size_t i = 0; i < windows.size(); i++) {
         TouchedWindow& touchedWindow = windows[i];
         if (touchedWindow.windowHandle == windowHandle) {
@@ -105,11 +101,6 @@
     std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
 }
 
-void TouchState::filterWindowsExcept(const sp<IBinder>& token) {
-    std::erase_if(windows,
-                  [&token](const TouchedWindow& w) { return w.windowHandle->getToken() != token; });
-}
-
 sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
     for (size_t i = 0; i < windows.size(); i++) {
         const TouchedWindow& window = windows[i];
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index cf5f1e5..d324611 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -29,7 +29,6 @@
 
 struct TouchState {
     bool down = false;
-    bool split = false;
 
     // id of the device that is currently down, others are rejected
     int32_t deviceId = -1;
@@ -50,7 +49,6 @@
                            std::optional<nsecs_t> eventTime = std::nullopt);
     void removeWindowByToken(const sp<IBinder>& token);
     void filterNonAsIsTouchWindows();
-    void filterWindowsExcept(const sp<IBinder>& token);
 
     // Cancel pointers for current set of windows except the window with particular binder token.
     void cancelPointersForWindowsExcept(const BitSet32 pointerIds, const sp<IBinder>& token);
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index cacb63c..3b0f2ac 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -142,6 +142,9 @@
     virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
     /* Get light player ID */
     virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
+
+    /* Get the Bluetooth address of an input device, if known. */
+    virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
 };
 
 // --- InputReaderConfiguration ---
@@ -393,6 +396,8 @@
     /* Gets the affine calibration associated with the specified device. */
     virtual TouchAffineTransformation getTouchAffineTransformation(
             const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+    /* Notifies the input reader policy that a stylus gesture has started. */
+    virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index c5e1f0c..a53fcd7 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -54,6 +54,7 @@
         "mapper/VibratorInputMapper.cpp",
         "mapper/accumulator/CursorButtonAccumulator.cpp",
         "mapper/accumulator/CursorScrollAccumulator.cpp",
+        "mapper/accumulator/HidUsageAccumulator.cpp",
         "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
         "mapper/accumulator/TouchButtonAccumulator.cpp",
     ],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index b97c466..0aaef53 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -43,6 +43,7 @@
 #include <ftl/enum.h>
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
+#include <input/PrintTools.h>
 #include <input/VirtualKeyMap.h>
 #include <openssl/sha.h>
 #include <statslog.h>
@@ -134,10 +135,6 @@
                                                                   {"green", LightColor::GREEN},
                                                                   {"blue", LightColor::BLUE}};
 
-static inline const char* toString(bool value) {
-    return value ? "true" : "false";
-}
-
 static std::string sha1(const std::string& in) {
     SHA_CTX ctx;
     SHA1_Init(&ctx);
@@ -152,6 +149,14 @@
     return out;
 }
 
+/* The set of all Android key codes that correspond to buttons (bit-switches) on a stylus. */
+static constexpr std::array<int32_t, 4> STYLUS_BUTTON_KEYCODES = {
+        AKEYCODE_STYLUS_BUTTON_PRIMARY,
+        AKEYCODE_STYLUS_BUTTON_SECONDARY,
+        AKEYCODE_STYLUS_BUTTON_TERTIARY,
+        AKEYCODE_STYLUS_BUTTON_TAIL,
+};
+
 /**
  * Return true if name matches "v4l-touch*"
  */
@@ -2128,6 +2133,17 @@
         identifier.uniqueId = buffer;
     }
 
+    // Attempt to get the bluetooth address of an input device from the uniqueId.
+    if (identifier.bus == BUS_BLUETOOTH &&
+        std::regex_match(identifier.uniqueId,
+                         std::regex("^[A-Fa-f0-9]{2}(?::[A-Fa-f0-9]{2}){5}$"))) {
+        identifier.bluetoothAddress = identifier.uniqueId;
+        // The Bluetooth stack requires alphabetic characters to be uppercase in a valid address.
+        for (auto& c : *identifier.bluetoothAddress) {
+            c = ::toupper(c);
+        }
+    }
+
     // Fill in the descriptor.
     assignDescriptorLocked(identifier);
 
@@ -2163,13 +2179,15 @@
     device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
     device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
 
-    // See if this is a keyboard.  Ignore everything in the button range except for
-    // joystick and gamepad buttons which are handled like keyboards for the most part.
+    // See if this is a device with keys. This could be full keyboard, or other devices like
+    // gamepads, joysticks, and styluses with buttons that should generate key presses.
     bool haveKeyboardKeys =
             device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
     bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
             device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
-    if (haveKeyboardKeys || haveGamepadButtons) {
+    bool haveStylusButtons = device->keyBitmask.test(BTN_STYLUS) ||
+            device->keyBitmask.test(BTN_STYLUS2) || device->keyBitmask.test(BTN_STYLUS3);
+    if (haveKeyboardKeys || haveGamepadButtons || haveStylusButtons) {
         device->classes |= InputDeviceClass::KEYBOARD;
     }
 
@@ -2179,11 +2197,13 @@
         device->classes |= InputDeviceClass::CURSOR;
     }
 
-    // See if this is a rotary encoder type device.
+    // See if the device is specially configured to be of a certain type.
     std::string deviceType;
     if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
         if (deviceType == "rotaryEncoder") {
             device->classes |= InputDeviceClass::ROTARY_ENCODER;
+        } else if (deviceType == "externalStylus") {
+            device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
         }
     }
 
@@ -2200,14 +2220,10 @@
     } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
                device->absBitmask.test(ABS_Y)) {
         device->classes |= InputDeviceClass::TOUCH;
-        // Is this a BT stylus?
+        // Is this a stylus that reports contact/pressure independently of touch coordinates?
     } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
                !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
         device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
-        // Keyboard will try to claim some of the buttons but we really want to reserve those so we
-        // can fuse it with the touch screen data, so just take them back. Note this means an
-        // external stylus cannot also be a keyboard device.
-        device->classes &= ~InputDeviceClass::KEYBOARD;
     }
 
     // See if this device is a joystick.
@@ -2292,6 +2308,16 @@
                 break;
             }
         }
+
+        // See if this device has any stylus buttons that we would want to fuse with touch data.
+        if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT)) {
+            for (int32_t keycode : STYLUS_BUTTON_KEYCODES) {
+                if (device->hasKeycodeLocked(keycode)) {
+                    device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
+                    break;
+                }
+            }
+        }
     }
 
     // If the device isn't recognized as something we handle, don't monitor it.
@@ -2625,9 +2651,10 @@
             dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
             dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str());
             dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
-                                         "product=0x%04x, version=0x%04x\n",
+                                         "product=0x%04x, version=0x%04x, bluetoothAddress=%s\n",
                                  device->identifier.bus, device->identifier.vendor,
-                                 device->identifier.product, device->identifier.version);
+                                 device->identifier.product, device->identifier.version,
+                                 toString(device->identifier.bluetoothAddress).c_str());
             dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n",
                                  device->keyMap.keyLayoutFile.c_str());
             dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 5291776..e6ab872 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -313,7 +313,10 @@
             }
         }
 
-        if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
+        if (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE) {
+            // Do not execute this code on the first configure, because 'setEnabled' would call
+            // InputMapper::reset, and you can't reset a mapper before it has been configured.
+            // The mappers are configured for the first time at the bottom of this function.
             auto it = config->disabledDevices.find(mId);
             bool enabled = it == config->disabledDevices.end();
             out += setEnabled(enabled, when);
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 428e999..f8b1b3f 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -58,6 +58,18 @@
             identifier1.location == identifier2.location);
 }
 
+static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
+    const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
+    if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
+        actionMasked != AMOTION_EVENT_ACTION_DOWN &&
+        actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN) {
+        return false;
+    }
+    const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action);
+    return motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+            motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
+}
+
 // --- InputReader ---
 
 InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -101,8 +113,10 @@
 void InputReader::loopOnce() {
     int32_t oldGeneration;
     int32_t timeoutMillis;
+    // Copy some state so that we can access it outside the lock later.
     bool inputDevicesChanged = false;
     std::vector<InputDeviceInfo> inputDevices;
+    std::list<NotifyArgs> notifyArgs;
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -127,7 +141,7 @@
         mReaderIsAliveCondition.notify_all();
 
         if (!events.empty()) {
-            notifyAll(processEventsLocked(events.data(), events.size()));
+            notifyArgs += processEventsLocked(events.data(), events.size());
         }
 
         if (mNextTimeout != LLONG_MAX) {
@@ -137,7 +151,7 @@
                     ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
                 }
                 mNextTimeout = LLONG_MAX;
-                notifyAll(timeoutExpiredLocked(now));
+                notifyArgs += timeoutExpiredLocked(now);
             }
         }
 
@@ -152,6 +166,16 @@
         mPolicy->notifyInputDevicesChanged(inputDevices);
     }
 
+    // Notify the policy of the start of every new stylus gesture outside the lock.
+    for (const auto& args : notifyArgs) {
+        const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
+        if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {
+            mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime);
+        }
+    }
+
+    notifyAll(std::move(notifyArgs));
+
     // Flush queued events out to the listener.
     // This must happen outside of the lock because the listener could potentially call
     // back into the InputReader's methods, such as getScanCodeState, or become blocked
@@ -851,6 +875,16 @@
     return std::nullopt;
 }
 
+std::optional<std::string> InputReader::getBluetoothAddress(int32_t deviceId) const {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        return device->getBluetoothAddress();
+    }
+    return std::nullopt;
+}
+
 bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
     std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 6933ec7..8e5f15f 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -67,22 +67,15 @@
 
 /* Describes an absolute axis. */
 struct RawAbsoluteAxisInfo {
-    bool valid; // true if the information is valid, false otherwise
+    bool valid{false}; // true if the information is valid, false otherwise
 
-    int32_t minValue;   // minimum value
-    int32_t maxValue;   // maximum value
-    int32_t flat;       // center flat position, eg. flat == 8 means center is between -8 and 8
-    int32_t fuzz;       // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
-    int32_t resolution; // resolution in units per mm or radians per mm
+    int32_t minValue{};   // minimum value
+    int32_t maxValue{};   // maximum value
+    int32_t flat{};       // center flat position, eg. flat == 8 means center is between -8 and 8
+    int32_t fuzz{};       // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
+    int32_t resolution{}; // resolution in units per mm or radians per mm
 
-    inline void clear() {
-        valid = false;
-        minValue = 0;
-        maxValue = 0;
-        flat = 0;
-        fuzz = 0;
-        resolution = 0;
-    }
+    inline void clear() { *this = RawAbsoluteAxisInfo(); }
 };
 
 /*
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index afb1bed..439123b 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -51,6 +51,9 @@
     inline int32_t getGeneration() const { return mGeneration; }
     inline const std::string getName() const { return mIdentifier.name; }
     inline const std::string getDescriptor() { return mIdentifier.descriptor; }
+    inline std::optional<std::string> getBluetoothAddress() const {
+        return mIdentifier.bluetoothAddress;
+    }
     inline ftl::Flags<InputDeviceClass> getClasses() const { return mClasses; }
     inline uint32_t getSources() const { return mSources; }
     inline bool hasEventHubDevices() const { return !mDevices.empty(); }
@@ -375,8 +378,11 @@
         mEventHub->getAbsoluteAxisInfo(mId, code, &info);
         return info.valid;
     }
-    inline bool isKeyPressed(int32_t code) const {
-        return mEventHub->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
+    inline bool isKeyPressed(int32_t scanCode) const {
+        return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN;
+    }
+    inline bool isKeyCodePressed(int32_t keyCode) const {
+        return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN;
     }
     inline int32_t getAbsoluteAxisValue(int32_t code) const {
         int32_t value;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index de268cf..4f2503a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -113,6 +113,8 @@
 
     std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) override;
 
+    std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override;
+
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 0404c9a..56fc5fa 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -24,7 +24,7 @@
 namespace android {
 
 ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
+      : InputMapper(deviceContext), mTouchButtonAccumulator(deviceContext) {}
 
 uint32_t ExternalStylusInputMapper::getSources() const {
     return AINPUT_SOURCE_STYLUS;
@@ -48,13 +48,13 @@
                                                            const InputReaderConfiguration* config,
                                                            uint32_t changes) {
     getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
-    mTouchButtonAccumulator.configure(getDeviceContext());
+    mTouchButtonAccumulator.configure();
     return {};
 }
 
 std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) {
     mSingleTouchMotionAccumulator.reset(getDeviceContext());
-    mTouchButtonAccumulator.reset(getDeviceContext());
+    mTouchButtonAccumulator.reset();
     return InputMapper::reset(when);
 }
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8704d1b..da9413e 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -24,67 +24,70 @@
 
 // --- Static Definitions ---
 
-static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
-                                           const int32_t map[][4], size_t mapSize) {
-    if (orientation != DISPLAY_ORIENTATION_0) {
-        for (size_t i = 0; i < mapSize; i++) {
-            if (value == map[i][0]) {
-                return map[i][orientation];
-            }
-        }
-    }
-    return value;
-}
-
-static const int32_t keyCodeRotationMap[][4] = {
-        // key codes enumerated counter-clockwise with the original (unrotated) key first
-        // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
-        {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
-        {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
-        {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
-        {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP},
-        {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
-         AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT},
-        {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
-         AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN},
-        {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
-         AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT},
-        {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
-         AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
-};
-
-static const size_t keyCodeRotationMapSize =
-        sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
-
-static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2],
-                             size_t mapSize) {
-    if (orientation == DISPLAY_ORIENTATION_180) {
-        for (size_t i = 0; i < mapSize; i++) {
-            if (value == map[i][0]) {
-                return map[i][1];
-            }
-        }
-    }
-    return value;
-}
-
-// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X
-static int32_t stemKeyRotationMap[][2] = {
-        // key codes enumerated with the original (unrotated) key first
-        // no rotation,           180 degree rotation
-        {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY},
-        {AKEYCODE_STEM_1, AKEYCODE_STEM_1},
-        {AKEYCODE_STEM_2, AKEYCODE_STEM_2},
-        {AKEYCODE_STEM_3, AKEYCODE_STEM_3},
-};
-
-static const size_t stemKeyRotationMapSize =
-        sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
-
 static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
-    keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize);
-    return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap,
-                                       keyCodeRotationMapSize);
+    static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = {
+            // key codes enumerated counter-clockwise with the original (unrotated) key first
+            // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
+            {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
+            {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
+            {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
+            {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP},
+            {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
+             AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT},
+            {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
+             AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN},
+            {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
+             AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT},
+            {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
+             AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
+    };
+
+    LOG_ALWAYS_FATAL_IF(orientation < 0 || orientation > 3, "Invalid orientation: %d", orientation);
+    if (orientation != DISPLAY_ORIENTATION_0) {
+        for (const auto& rotation : KEYCODE_ROTATION_MAP) {
+            if (rotation[DISPLAY_ORIENTATION_0] == keyCode) {
+                return rotation[orientation];
+            }
+        }
+    }
+    return keyCode;
+}
+
+static bool isSupportedScanCode(int32_t scanCode) {
+    // KeyboardInputMapper handles keys from keyboards, gamepads, and styluses.
+    return scanCode < BTN_MOUSE || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI) ||
+            scanCode == BTN_STYLUS || scanCode == BTN_STYLUS2 || scanCode == BTN_STYLUS3 ||
+            scanCode >= BTN_WHEEL;
+}
+
+static bool isMediaKey(int32_t keyCode) {
+    switch (keyCode) {
+        case AKEYCODE_MEDIA_PLAY:
+        case AKEYCODE_MEDIA_PAUSE:
+        case AKEYCODE_MEDIA_PLAY_PAUSE:
+        case AKEYCODE_MUTE:
+        case AKEYCODE_HEADSETHOOK:
+        case AKEYCODE_MEDIA_STOP:
+        case AKEYCODE_MEDIA_NEXT:
+        case AKEYCODE_MEDIA_PREVIOUS:
+        case AKEYCODE_MEDIA_REWIND:
+        case AKEYCODE_MEDIA_RECORD:
+        case AKEYCODE_MEDIA_FAST_FORWARD:
+        case AKEYCODE_MEDIA_SKIP_FORWARD:
+        case AKEYCODE_MEDIA_SKIP_BACKWARD:
+        case AKEYCODE_MEDIA_STEP_FORWARD:
+        case AKEYCODE_MEDIA_STEP_BACKWARD:
+        case AKEYCODE_MEDIA_AUDIO_TRACK:
+        case AKEYCODE_VOLUME_UP:
+        case AKEYCODE_VOLUME_DOWN:
+        case AKEYCODE_VOLUME_MUTE:
+        case AKEYCODE_TV_AUDIO_DESCRIPTION:
+        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
+        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
+            return true;
+        default:
+            return false;
+    }
 }
 
 // --- KeyboardInputMapper ---
@@ -93,8 +96,6 @@
                                          int32_t keyboardType)
       : InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {}
 
-KeyboardInputMapper::~KeyboardInputMapper() {}
-
 uint32_t KeyboardInputMapper::getSources() const {
     return mSource;
 }
@@ -130,7 +131,7 @@
 }
 
 std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
-        nsecs_t when, const InputReaderConfiguration* config) {
+        const InputReaderConfiguration* config) {
     if (getDeviceContext().getAssociatedViewport()) {
         return getDeviceContext().getAssociatedViewport();
     }
@@ -154,35 +155,16 @@
     }
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        mViewport = findViewport(when, config);
+        mViewport = findViewport(config);
     }
     return out;
 }
 
-static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
-    int32_t mapped = 0;
-    if (config.tryGetProperty(property, mapped) && mapped > 0) {
-        for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
-            if (stemKeyRotationMap[i][0] == keyCode) {
-                stemKeyRotationMap[i][1] = mapped;
-                return;
-            }
-        }
-    }
-}
-
 void KeyboardInputMapper::configureParameters() {
     mParameters.orientationAware = false;
     const PropertyMap& config = getDeviceContext().getConfiguration();
     config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);
 
-    if (mParameters.orientationAware) {
-        mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
-        mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
-        mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
-        mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3");
-    }
-
     mParameters.handlesKeyRepeat = false;
     config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);
 
@@ -190,7 +172,7 @@
     config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
 }
 
-void KeyboardInputMapper::dumpParameters(std::string& dump) {
+void KeyboardInputMapper::dumpParameters(std::string& dump) const {
     dump += INDENT3 "Parameters:\n";
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
     dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat));
@@ -198,7 +180,7 @@
 
 std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) {
     std::list<NotifyArgs> out = cancelAllDownKeys(when);
-    mCurrentHidUsage = 0;
+    mHidUsageAccumulator.reset();
 
     resetLedState();
 
@@ -208,68 +190,21 @@
 
 std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
     std::list<NotifyArgs> out;
+    mHidUsageAccumulator.process(*rawEvent);
     switch (rawEvent->type) {
         case EV_KEY: {
             int32_t scanCode = rawEvent->code;
-            int32_t usageCode = mCurrentHidUsage;
-            mCurrentHidUsage = 0;
 
-            if (isKeyboardOrGamepadKey(scanCode)) {
+            if (isSupportedScanCode(scanCode)) {
                 out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
-                                  scanCode, usageCode);
+                                  scanCode, mHidUsageAccumulator.consumeCurrentHidUsage());
             }
             break;
         }
-        case EV_MSC: {
-            if (rawEvent->code == MSC_SCAN) {
-                mCurrentHidUsage = rawEvent->value;
-            }
-            break;
-        }
-        case EV_SYN: {
-            if (rawEvent->code == SYN_REPORT) {
-                mCurrentHidUsage = 0;
-            }
-        }
     }
     return out;
 }
 
-bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
-    return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL ||
-            (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
-            (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
-}
-
-bool KeyboardInputMapper::isMediaKey(int32_t keyCode) {
-    switch (keyCode) {
-        case AKEYCODE_MEDIA_PLAY:
-        case AKEYCODE_MEDIA_PAUSE:
-        case AKEYCODE_MEDIA_PLAY_PAUSE:
-        case AKEYCODE_MUTE:
-        case AKEYCODE_HEADSETHOOK:
-        case AKEYCODE_MEDIA_STOP:
-        case AKEYCODE_MEDIA_NEXT:
-        case AKEYCODE_MEDIA_PREVIOUS:
-        case AKEYCODE_MEDIA_REWIND:
-        case AKEYCODE_MEDIA_RECORD:
-        case AKEYCODE_MEDIA_FAST_FORWARD:
-        case AKEYCODE_MEDIA_SKIP_FORWARD:
-        case AKEYCODE_MEDIA_SKIP_BACKWARD:
-        case AKEYCODE_MEDIA_STEP_FORWARD:
-        case AKEYCODE_MEDIA_STEP_BACKWARD:
-        case AKEYCODE_MEDIA_AUDIO_TRACK:
-        case AKEYCODE_VOLUME_UP:
-        case AKEYCODE_VOLUME_DOWN:
-        case AKEYCODE_VOLUME_MUTE:
-        case AKEYCODE_TV_AUDIO_DESCRIPTION:
-        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
-        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
-            return true;
-    }
-    return false;
-}
-
 std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down,
                                                       int32_t scanCode, int32_t usageCode) {
     std::list<NotifyArgs> out;
@@ -285,6 +220,7 @@
     }
 
     nsecs_t downTime = when;
+    std::optional<size_t> keyDownIndex = findKeyDownIndex(scanCode);
     if (down) {
         // Rotate key codes according to orientation if needed.
         if (mParameters.orientationAware) {
@@ -292,11 +228,10 @@
         }
 
         // Add key down.
-        ssize_t keyDownIndex = findKeyDown(scanCode);
-        if (keyDownIndex >= 0) {
+        if (keyDownIndex) {
             // key repeat, be sure to use same keycode as before in case of rotation
-            keyCode = mKeyDowns[keyDownIndex].keyCode;
-            downTime = mKeyDowns[keyDownIndex].downTime;
+            keyCode = mKeyDowns[*keyDownIndex].keyCode;
+            downTime = mKeyDowns[*keyDownIndex].downTime;
         } else {
             // key down
             if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
@@ -315,12 +250,11 @@
         }
     } else {
         // Remove key down.
-        ssize_t keyDownIndex = findKeyDown(scanCode);
-        if (keyDownIndex >= 0) {
+        if (keyDownIndex) {
             // key up, be sure to use same keycode as before in case of rotation
-            keyCode = mKeyDowns[keyDownIndex].keyCode;
-            downTime = mKeyDowns[keyDownIndex].downTime;
-            mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
+            keyCode = mKeyDowns[*keyDownIndex].keyCode;
+            downTime = mKeyDowns[*keyDownIndex].downTime;
+            mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex);
         } else {
             // key was not actually down
             ALOGI("Dropping key up from device %s because the key was not down.  "
@@ -353,22 +287,22 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    out.push_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                                getDisplayId(), policyFlags,
-                                down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-                                AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
-                                downTime));
+    out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                   mSource, getDisplayId(), policyFlags,
+                                   down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+                                   AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
+                                   downTime));
     return out;
 }
 
-ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
+std::optional<size_t> KeyboardInputMapper::findKeyDownIndex(int32_t scanCode) {
     size_t n = mKeyDowns.size();
     for (size_t i = 0; i < n; i++) {
         if (mKeyDowns[i].scanCode == scanCode) {
             return i;
         }
     }
-    return -1;
+    return {};
 }
 
 int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -481,12 +415,12 @@
     std::list<NotifyArgs> out;
     size_t n = mKeyDowns.size();
     for (size_t i = 0; i < n; i++) {
-        out.push_back(NotifyKeyArgs(getContext()->getNextId(), when,
-                                    systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
-                                    getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP,
-                                    AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
-                                    mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
-                                    mKeyDowns[i].downTime));
+        out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
+                                       systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
+                                       getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP,
+                                       AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+                                       mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
+                                       mKeyDowns[i].downTime));
     }
     mKeyDowns.clear();
     mMetaState = AMETA_NONE;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 8d72ee9..11d5ad2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "HidUsageAccumulator.h"
 #include "InputMapper.h"
 
 namespace android {
@@ -23,7 +24,7 @@
 class KeyboardInputMapper : public InputMapper {
 public:
     KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
-    virtual ~KeyboardInputMapper();
+    ~KeyboardInputMapper() override = default;
 
     uint32_t getSources() const override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
@@ -47,58 +48,54 @@
 
 private:
     // The current viewport.
-    std::optional<DisplayViewport> mViewport;
+    std::optional<DisplayViewport> mViewport{};
 
     struct KeyDown {
-        nsecs_t downTime;
-        int32_t keyCode;
-        int32_t scanCode;
+        nsecs_t downTime{};
+        int32_t keyCode{};
+        int32_t scanCode{};
     };
 
-    uint32_t mSource;
-    int32_t mKeyboardType;
+    uint32_t mSource{};
+    int32_t mKeyboardType{};
 
-    std::vector<KeyDown> mKeyDowns; // keys that are down
-    int32_t mMetaState;
+    std::vector<KeyDown> mKeyDowns{}; // keys that are down
+    int32_t mMetaState{};
 
-    int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
+    HidUsageAccumulator mHidUsageAccumulator;
 
     struct LedState {
-        bool avail; // led is available
-        bool on;    // we think the led is currently on
+        bool avail{}; // led is available
+        bool on{};    // we think the led is currently on
     };
-    LedState mCapsLockLedState;
-    LedState mNumLockLedState;
-    LedState mScrollLockLedState;
+    LedState mCapsLockLedState{};
+    LedState mNumLockLedState{};
+    LedState mScrollLockLedState{};
 
     // Immutable configuration parameters.
     struct Parameters {
-        bool orientationAware;
-        bool handlesKeyRepeat;
-        bool doNotWakeByDefault;
-    } mParameters;
+        bool orientationAware{};
+        bool handlesKeyRepeat{};
+        bool doNotWakeByDefault{};
+    } mParameters{};
 
     void configureParameters();
-    void dumpParameters(std::string& dump);
+    void dumpParameters(std::string& dump) const;
 
     int32_t getOrientation();
     int32_t getDisplayId();
 
-    bool isKeyboardOrGamepadKey(int32_t scanCode);
-    bool isMediaKey(int32_t keyCode);
-
     [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
                                                    int32_t scanCode, int32_t usageCode);
 
     bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
 
-    ssize_t findKeyDown(int32_t scanCode);
+    std::optional<size_t> findKeyDownIndex(int32_t scanCode);
 
     void resetLedState();
     void initializeLedState(LedState& ledState, int32_t led);
     void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
-    std::optional<DisplayViewport> findViewport(nsecs_t when,
-                                                const InputReaderConfiguration* config);
+    std::optional<DisplayViewport> findViewport(const InputReaderConfiguration* config);
     [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
 };
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 615889e..ba3d980 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -73,53 +73,8 @@
     return value >= 8 ? value - 16 : value;
 }
 
-// --- RawPointerAxes ---
-
-RawPointerAxes::RawPointerAxes() {
-    clear();
-}
-
-void RawPointerAxes::clear() {
-    x.clear();
-    y.clear();
-    pressure.clear();
-    touchMajor.clear();
-    touchMinor.clear();
-    toolMajor.clear();
-    toolMinor.clear();
-    orientation.clear();
-    distance.clear();
-    tiltX.clear();
-    tiltY.clear();
-    trackingId.clear();
-    slot.clear();
-}
-
 // --- RawPointerData ---
 
-RawPointerData::RawPointerData() {
-    clear();
-}
-
-void RawPointerData::clear() {
-    pointerCount = 0;
-    clearIdBits();
-}
-
-void RawPointerData::copyFrom(const RawPointerData& other) {
-    pointerCount = other.pointerCount;
-    hoveringIdBits = other.hoveringIdBits;
-    touchingIdBits = other.touchingIdBits;
-    canceledIdBits = other.canceledIdBits;
-
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        pointers[i] = other.pointers[i];
-
-        int id = pointers[i].id;
-        idToIndex[id] = other.idToIndex[id];
-    }
-}
-
 void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
     float x = 0, y = 0;
     uint32_t count = touchingIdBits.count();
@@ -137,39 +92,11 @@
     *outY = y;
 }
 
-// --- CookedPointerData ---
-
-CookedPointerData::CookedPointerData() {
-    clear();
-}
-
-void CookedPointerData::clear() {
-    pointerCount = 0;
-    hoveringIdBits.clear();
-    touchingIdBits.clear();
-    canceledIdBits.clear();
-    validIdBits.clear();
-}
-
-void CookedPointerData::copyFrom(const CookedPointerData& other) {
-    pointerCount = other.pointerCount;
-    hoveringIdBits = other.hoveringIdBits;
-    touchingIdBits = other.touchingIdBits;
-    validIdBits = other.validIdBits;
-
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].copyFrom(other.pointerProperties[i]);
-        pointerCoords[i].copyFrom(other.pointerCoords[i]);
-
-        int id = pointerProperties[i].id;
-        idToIndex[id] = other.idToIndex[id];
-    }
-}
-
 // --- TouchInputMapper ---
 
 TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
       : InputMapper(deviceContext),
+        mTouchButtonAccumulator(deviceContext),
         mSource(0),
         mDeviceMode(DeviceMode::DISABLED),
         mDisplayWidth(-1),
@@ -360,7 +287,7 @@
 
         // Configure common accumulators.
         mCursorScrollAccumulator.configure(getDeviceContext());
-        mTouchButtonAccumulator.configure(getDeviceContext());
+        mTouchButtonAccumulator.configure();
 
         // Configure absolute axis information.
         configureRawPointerAxes();
@@ -1449,7 +1376,7 @@
 
     mCursorButtonAccumulator.reset(getDeviceContext());
     mCursorScrollAccumulator.reset(getDeviceContext());
-    mTouchButtonAccumulator.reset(getDeviceContext());
+    mTouchButtonAccumulator.reset();
 
     mPointerVelocityControl.reset();
     mWheelXVelocityControl.reset();
@@ -1582,7 +1509,7 @@
 
         // All ready to go.
         clearStylusDataPendingFlags();
-        mCurrentRawState.copyFrom(next);
+        mCurrentRawState = next;
         if (mCurrentRawState.when < mLastRawState.when) {
             mCurrentRawState.when = mLastRawState.when;
             mCurrentRawState.readTime = mLastRawState.readTime;
@@ -1597,7 +1524,7 @@
         if (timeout) {
             nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
             clearStylusDataPendingFlags();
-            mCurrentRawState.copyFrom(mLastRawState);
+            mCurrentRawState = mLastRawState;
             ALOGD_IF(DEBUG_STYLUS_FUSION,
                      "Timeout expired, synthesizing event with new stylus data");
             const nsecs_t readTime = when; // consider this synthetic event to be zero latency
@@ -1722,8 +1649,8 @@
     mCurrentRawState.rawHScroll = 0;
 
     // Copy current touch to last touch in preparation for the next cycle.
-    mLastRawState.copyFrom(mCurrentRawState);
-    mLastCookedState.copyFrom(mCurrentCookedState);
+    mLastRawState = mCurrentRawState;
+    mLastCookedState = mCurrentCookedState;
     return out;
 }
 
@@ -1743,8 +1670,8 @@
     mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
 
     mPointerController->setButtonState(mCurrentRawState.buttonState);
-    mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
-                                 mCurrentCookedState.cookedPointerData.idToIndex,
+    mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(),
+                                 mCurrentCookedState.cookedPointerData.idToIndex.cbegin(),
                                  mCurrentCookedState.cookedPointerData.touchingIdBits,
                                  mViewport.displayId);
 }
@@ -1992,6 +1919,36 @@
     return out;
 }
 
+// Updates pointer coords and properties for pointers with specified ids that have moved.
+// Returns true if any of them changed.
+static bool updateMovedPointers(const PropertiesArray& inProperties, CoordsArray& inCoords,
+                                const IdToIndexArray& inIdToIndex, PropertiesArray& outProperties,
+                                CoordsArray& outCoords, IdToIndexArray& outIdToIndex,
+                                BitSet32 idBits) {
+    bool changed = false;
+    while (!idBits.isEmpty()) {
+        uint32_t id = idBits.clearFirstMarkedBit();
+        uint32_t inIndex = inIdToIndex[id];
+        uint32_t outIndex = outIdToIndex[id];
+
+        const PointerProperties& curInProperties = inProperties[inIndex];
+        const PointerCoords& curInCoords = inCoords[inIndex];
+        PointerProperties& curOutProperties = outProperties[outIndex];
+        PointerCoords& curOutCoords = outCoords[outIndex];
+
+        if (curInProperties != curOutProperties) {
+            curOutProperties.copyFrom(curInProperties);
+            changed = true;
+        }
+
+        if (curInCoords != curOutCoords) {
+            curOutCoords.copyFrom(curInCoords);
+            changed = true;
+        }
+    }
+    return changed;
+}
+
 std::list<NotifyArgs> TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime,
                                                         uint32_t policyFlags) {
     std::list<NotifyArgs> out;
@@ -2159,9 +2116,9 @@
         out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
                                      AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                                      metaState, buttonState, 0,
-                                     mCurrentCookedState.cookedPointerData.pointerProperties,
-                                     mCurrentCookedState.cookedPointerData.pointerCoords,
-                                     mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+                                     mLastCookedState.cookedPointerData.pointerProperties,
+                                     mLastCookedState.cookedPointerData.pointerCoords,
+                                     mLastCookedState.cookedPointerData.idToIndex, idBits, -1,
                                      mOrientedXPrecision, mOrientedYPrecision, mDownTime,
                                      MotionClassification::NONE));
     }
@@ -2538,8 +2495,8 @@
         }
 
         if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
-            mPointerController->setSpots(mPointerGesture.currentGestureCoords,
-                                         mPointerGesture.currentGestureIdToIndex,
+            mPointerController->setSpots(mPointerGesture.currentGestureCoords.cbegin(),
+                                         mPointerGesture.currentGestureIdToIndex.cbegin(),
                                          mPointerGesture.currentGestureIdBits,
                                          mPointerController->getDisplayId());
         }
@@ -3742,8 +3699,8 @@
 NotifyMotionArgs TouchInputMapper::dispatchMotion(
         nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
         int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
-        int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords,
-        const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
+        int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
+        const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
         float yPrecision, nsecs_t downTime, MotionClassification classification) {
     PointerCoords pointerCoords[MAX_POINTERS];
     PointerProperties pointerProperties[MAX_POINTERS];
@@ -3797,36 +3754,6 @@
                             downTime, std::move(frames));
 }
 
-bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
-                                           const PointerCoords* inCoords,
-                                           const uint32_t* inIdToIndex,
-                                           PointerProperties* outProperties,
-                                           PointerCoords* outCoords, const uint32_t* outIdToIndex,
-                                           BitSet32 idBits) const {
-    bool changed = false;
-    while (!idBits.isEmpty()) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-        uint32_t inIndex = inIdToIndex[id];
-        uint32_t outIndex = outIdToIndex[id];
-
-        const PointerProperties& curInProperties = inProperties[inIndex];
-        const PointerCoords& curInCoords = inCoords[inIndex];
-        PointerProperties& curOutProperties = outProperties[outIndex];
-        PointerCoords& curOutCoords = outCoords[outIndex];
-
-        if (curInProperties != curOutProperties) {
-            curOutProperties.copyFrom(curInProperties);
-            changed = true;
-        }
-
-        if (curInCoords != curOutCoords) {
-            curOutCoords.copyFrom(curInCoords);
-            changed = true;
-        }
-    }
-    return changed;
-}
-
 std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
     std::list<NotifyArgs> out;
     out += abortPointerUsage(when, readTime, 0 /*policyFlags*/);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 7680090..2bb9ece 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -29,53 +29,56 @@
 
 /* Raw axis information from the driver. */
 struct RawPointerAxes {
-    RawAbsoluteAxisInfo x;
-    RawAbsoluteAxisInfo y;
-    RawAbsoluteAxisInfo pressure;
-    RawAbsoluteAxisInfo touchMajor;
-    RawAbsoluteAxisInfo touchMinor;
-    RawAbsoluteAxisInfo toolMajor;
-    RawAbsoluteAxisInfo toolMinor;
-    RawAbsoluteAxisInfo orientation;
-    RawAbsoluteAxisInfo distance;
-    RawAbsoluteAxisInfo tiltX;
-    RawAbsoluteAxisInfo tiltY;
-    RawAbsoluteAxisInfo trackingId;
-    RawAbsoluteAxisInfo slot;
+    RawAbsoluteAxisInfo x{};
+    RawAbsoluteAxisInfo y{};
+    RawAbsoluteAxisInfo pressure{};
+    RawAbsoluteAxisInfo touchMajor{};
+    RawAbsoluteAxisInfo touchMinor{};
+    RawAbsoluteAxisInfo toolMajor{};
+    RawAbsoluteAxisInfo toolMinor{};
+    RawAbsoluteAxisInfo orientation{};
+    RawAbsoluteAxisInfo distance{};
+    RawAbsoluteAxisInfo tiltX{};
+    RawAbsoluteAxisInfo tiltY{};
+    RawAbsoluteAxisInfo trackingId{};
+    RawAbsoluteAxisInfo slot{};
 
-    RawPointerAxes();
     inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
     inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
-    void clear();
+    inline void clear() { *this = RawPointerAxes(); }
 };
 
+using PropertiesArray = std::array<PointerProperties, MAX_POINTERS>;
+using CoordsArray = std::array<PointerCoords, MAX_POINTERS>;
+using IdToIndexArray = std::array<uint32_t, MAX_POINTER_ID + 1>;
+
 /* Raw data for a collection of pointers including a pointer id mapping table. */
 struct RawPointerData {
     struct Pointer {
-        uint32_t id;
-        int32_t x;
-        int32_t y;
-        int32_t pressure;
-        int32_t touchMajor;
-        int32_t touchMinor;
-        int32_t toolMajor;
-        int32_t toolMinor;
-        int32_t orientation;
-        int32_t distance;
-        int32_t tiltX;
-        int32_t tiltY;
-        int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant
-        bool isHovering;
+        uint32_t id{0xFFFFFFFF};
+        int32_t x{};
+        int32_t y{};
+        int32_t pressure{};
+        int32_t touchMajor{};
+        int32_t touchMinor{};
+        int32_t toolMajor{};
+        int32_t toolMinor{};
+        int32_t orientation{};
+        int32_t distance{};
+        int32_t tiltX{};
+        int32_t tiltY{};
+        // A fully decoded AMOTION_EVENT_TOOL_TYPE constant.
+        int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN};
+        bool isHovering{false};
     };
 
-    uint32_t pointerCount;
-    Pointer pointers[MAX_POINTERS];
-    BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits;
-    uint32_t idToIndex[MAX_POINTER_ID + 1];
+    uint32_t pointerCount{};
+    std::array<Pointer, MAX_POINTERS> pointers{};
+    BitSet32 hoveringIdBits{}, touchingIdBits{}, canceledIdBits{};
+    IdToIndexArray idToIndex{};
 
-    RawPointerData();
-    void clear();
-    void copyFrom(const RawPointerData& other);
+    inline void clear() { *this = RawPointerData(); }
+
     void getCentroidOfTouchingPointers(float* outX, float* outY) const;
 
     inline void markIdBit(uint32_t id, bool isHovering) {
@@ -99,15 +102,13 @@
 
 /* Cooked data for a collection of pointers including a pointer id mapping table. */
 struct CookedPointerData {
-    uint32_t pointerCount;
-    PointerProperties pointerProperties[MAX_POINTERS];
-    PointerCoords pointerCoords[MAX_POINTERS];
-    BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits, validIdBits;
-    uint32_t idToIndex[MAX_POINTER_ID + 1];
+    uint32_t pointerCount{};
+    PropertiesArray pointerProperties{};
+    CoordsArray pointerCoords{};
+    BitSet32 hoveringIdBits{}, touchingIdBits{}, canceledIdBits{}, validIdBits{};
+    IdToIndexArray idToIndex{};
 
-    CookedPointerData();
-    void clear();
-    void copyFrom(const CookedPointerData& other);
+    inline void clear() { *this = CookedPointerData(); }
 
     inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
         return pointerCoords[idToIndex[id]];
@@ -314,65 +315,33 @@
     RawPointerAxes mRawPointerAxes;
 
     struct RawState {
-        nsecs_t when;
-        nsecs_t readTime;
+        nsecs_t when{};
+        nsecs_t readTime{};
 
         // Raw pointer sample data.
-        RawPointerData rawPointerData;
+        RawPointerData rawPointerData{};
 
-        int32_t buttonState;
+        int32_t buttonState{};
 
         // Scroll state.
-        int32_t rawVScroll;
-        int32_t rawHScroll;
+        int32_t rawVScroll{};
+        int32_t rawHScroll{};
 
-        explicit inline RawState() { clear(); }
-
-        void copyFrom(const RawState& other) {
-            when = other.when;
-            readTime = other.readTime;
-            rawPointerData.copyFrom(other.rawPointerData);
-            buttonState = other.buttonState;
-            rawVScroll = other.rawVScroll;
-            rawHScroll = other.rawHScroll;
-        }
-
-        void clear() {
-            when = 0;
-            readTime = 0;
-            rawPointerData.clear();
-            buttonState = 0;
-            rawVScroll = 0;
-            rawHScroll = 0;
-        }
+        inline void clear() { *this = RawState(); }
     };
 
     struct CookedState {
         // Cooked pointer sample data.
-        CookedPointerData cookedPointerData;
+        CookedPointerData cookedPointerData{};
 
         // Id bits used to differentiate fingers, stylus and mouse tools.
-        BitSet32 fingerIdBits;
-        BitSet32 stylusIdBits;
-        BitSet32 mouseIdBits;
+        BitSet32 fingerIdBits{};
+        BitSet32 stylusIdBits{};
+        BitSet32 mouseIdBits{};
 
-        int32_t buttonState;
+        int32_t buttonState{};
 
-        void copyFrom(const CookedState& other) {
-            cookedPointerData.copyFrom(other.cookedPointerData);
-            fingerIdBits = other.fingerIdBits;
-            stylusIdBits = other.stylusIdBits;
-            mouseIdBits = other.mouseIdBits;
-            buttonState = other.buttonState;
-        }
-
-        void clear() {
-            cookedPointerData.clear();
-            fingerIdBits.clear();
-            stylusIdBits.clear();
-            mouseIdBits.clear();
-            buttonState = 0;
-        }
+        inline void clear() { *this = CookedState(); }
     };
 
     std::vector<RawState> mRawStatesPending;
@@ -528,9 +497,9 @@
     float mPointerGestureMaxSwipeWidth;
 
     struct PointerDistanceHeapElement {
-        uint32_t currentPointerIndex : 8;
-        uint32_t lastPointerIndex : 8;
-        uint64_t distance : 48; // squared distance
+        uint32_t currentPointerIndex : 8 {};
+        uint32_t lastPointerIndex : 8 {};
+        uint64_t distance : 48 {}; // squared distance
     };
 
     enum class PointerUsage {
@@ -627,15 +596,15 @@
         // Pointer coords and ids for the current and previous pointer gesture.
         Mode currentGestureMode;
         BitSet32 currentGestureIdBits;
-        uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
-        PointerProperties currentGestureProperties[MAX_POINTERS];
-        PointerCoords currentGestureCoords[MAX_POINTERS];
+        IdToIndexArray currentGestureIdToIndex{};
+        PropertiesArray currentGestureProperties{};
+        CoordsArray currentGestureCoords{};
 
         Mode lastGestureMode;
         BitSet32 lastGestureIdBits;
-        uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
-        PointerProperties lastGestureProperties[MAX_POINTERS];
-        PointerCoords lastGestureCoords[MAX_POINTERS];
+        IdToIndexArray lastGestureIdToIndex{};
+        PropertiesArray lastGestureProperties{};
+        CoordsArray lastGestureCoords{};
 
         // Time the pointer gesture last went down.
         nsecs_t downTime;
@@ -812,17 +781,10 @@
     [[nodiscard]] NotifyMotionArgs dispatchMotion(
             nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
             int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
-            int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords,
-            const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
+            int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
+            const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
             float yPrecision, nsecs_t downTime, MotionClassification classification);
 
-    // Updates pointer coords and properties for pointers with specified ids that have moved.
-    // Returns true if any of them changed.
-    bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords,
-                             const uint32_t* inIdToIndex, PointerProperties* outProperties,
-                             PointerCoords* outCoords, const uint32_t* outIdToIndex,
-                             BitSet32 idBits) const;
-
     // Returns if this touch device is a touch screen with an associated display.
     bool isTouchScreen();
     // Updates touch spots if they are enabled. Should only be used when this device is a
@@ -834,7 +796,6 @@
 
     static void assignPointerIds(const RawState& last, RawState& current);
 
-    const char* modeToString(DeviceMode deviceMode);
     void rotateAndScale(float& x, float& y) const;
 };
 
diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp
new file mode 100644
index 0000000..2da1d81
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "HidUsageAccumulator.h"
+
+namespace android {
+
+void HidUsageAccumulator::process(const RawEvent& rawEvent) {
+    if (rawEvent.type == EV_MSC && rawEvent.code == MSC_SCAN) {
+        mCurrentHidUsage = rawEvent.value;
+        return;
+    }
+
+    if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+        reset();
+        return;
+    }
+}
+
+int32_t HidUsageAccumulator::consumeCurrentHidUsage() {
+    const int32_t currentHidUsage = mCurrentHidUsage;
+    reset();
+    return currentHidUsage;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h
new file mode 100644
index 0000000..740a710
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h
@@ -0,0 +1,40 @@
+/*
+ * 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 "EventHub.h"
+
+#include <cstdint>
+
+namespace android {
+
+/* Keeps track of the state of currently reported HID usage code. */
+class HidUsageAccumulator {
+public:
+    explicit HidUsageAccumulator() = default;
+    inline void reset() { *this = HidUsageAccumulator(); }
+
+    void process(const RawEvent& rawEvent);
+
+    /* This must be called when processing the `EV_KEY` event. Returns 0 if invalid. */
+    int32_t consumeCurrentHidUsage();
+
+private:
+    int32_t mCurrentHidUsage{};
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index 86153d3..5d5bee7 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -21,55 +21,39 @@
 
 namespace android {
 
-TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) {
-    clearButtons();
+void TouchButtonAccumulator::configure() {
+    mHaveBtnTouch = mDeviceContext.hasScanCode(BTN_TOUCH);
+    mHaveStylus = mDeviceContext.hasScanCode(BTN_TOOL_PEN) ||
+            mDeviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
+            mDeviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
+            mDeviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
+            mDeviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
 }
 
-void TouchButtonAccumulator::configure(InputDeviceContext& deviceContext) {
-    mHaveBtnTouch = deviceContext.hasScanCode(BTN_TOUCH);
-    mHaveStylus = deviceContext.hasScanCode(BTN_TOOL_PEN) ||
-            deviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
-            deviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
-            deviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
-            deviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
-}
-
-void TouchButtonAccumulator::reset(InputDeviceContext& deviceContext) {
-    mBtnTouch = deviceContext.isKeyPressed(BTN_TOUCH);
-    mBtnStylus = deviceContext.isKeyPressed(BTN_STYLUS);
+void TouchButtonAccumulator::reset() {
+    mBtnTouch = mDeviceContext.isKeyPressed(BTN_TOUCH);
+    mBtnStylus = mDeviceContext.isKeyPressed(BTN_STYLUS) ||
+            mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_PRIMARY);
     // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
-    mBtnStylus2 = deviceContext.isKeyPressed(BTN_STYLUS2) || deviceContext.isKeyPressed(BTN_0);
-    mBtnToolFinger = deviceContext.isKeyPressed(BTN_TOOL_FINGER);
-    mBtnToolPen = deviceContext.isKeyPressed(BTN_TOOL_PEN);
-    mBtnToolRubber = deviceContext.isKeyPressed(BTN_TOOL_RUBBER);
-    mBtnToolBrush = deviceContext.isKeyPressed(BTN_TOOL_BRUSH);
-    mBtnToolPencil = deviceContext.isKeyPressed(BTN_TOOL_PENCIL);
-    mBtnToolAirbrush = deviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
-    mBtnToolMouse = deviceContext.isKeyPressed(BTN_TOOL_MOUSE);
-    mBtnToolLens = deviceContext.isKeyPressed(BTN_TOOL_LENS);
-    mBtnToolDoubleTap = deviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
-    mBtnToolTripleTap = deviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
-    mBtnToolQuadTap = deviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
-}
-
-void TouchButtonAccumulator::clearButtons() {
-    mBtnTouch = 0;
-    mBtnStylus = 0;
-    mBtnStylus2 = 0;
-    mBtnToolFinger = 0;
-    mBtnToolPen = 0;
-    mBtnToolRubber = 0;
-    mBtnToolBrush = 0;
-    mBtnToolPencil = 0;
-    mBtnToolAirbrush = 0;
-    mBtnToolMouse = 0;
-    mBtnToolLens = 0;
-    mBtnToolDoubleTap = 0;
-    mBtnToolTripleTap = 0;
-    mBtnToolQuadTap = 0;
+    mBtnStylus2 = mDeviceContext.isKeyPressed(BTN_STYLUS2) || mDeviceContext.isKeyPressed(BTN_0) ||
+            mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_SECONDARY);
+    mBtnToolFinger = mDeviceContext.isKeyPressed(BTN_TOOL_FINGER);
+    mBtnToolPen = mDeviceContext.isKeyPressed(BTN_TOOL_PEN);
+    mBtnToolRubber = mDeviceContext.isKeyPressed(BTN_TOOL_RUBBER);
+    mBtnToolBrush = mDeviceContext.isKeyPressed(BTN_TOOL_BRUSH);
+    mBtnToolPencil = mDeviceContext.isKeyPressed(BTN_TOOL_PENCIL);
+    mBtnToolAirbrush = mDeviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
+    mBtnToolMouse = mDeviceContext.isKeyPressed(BTN_TOOL_MOUSE);
+    mBtnToolLens = mDeviceContext.isKeyPressed(BTN_TOOL_LENS);
+    mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
+    mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
+    mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+    mHidUsageAccumulator.reset();
 }
 
 void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
+    mHidUsageAccumulator.process(*rawEvent);
+
     if (rawEvent->type == EV_KEY) {
         switch (rawEvent->code) {
             case BTN_TOUCH:
@@ -116,7 +100,29 @@
             case BTN_TOOL_QUADTAP:
                 mBtnToolQuadTap = rawEvent->value;
                 break;
+            default:
+                processMappedKey(rawEvent->code, rawEvent->value);
         }
+        return;
+    }
+}
+
+void TouchButtonAccumulator::processMappedKey(int32_t scanCode, bool down) {
+    int32_t outKeyCode, outMetaState;
+    uint32_t outFlags;
+    if (mDeviceContext.mapKey(scanCode, mHidUsageAccumulator.consumeCurrentHidUsage(),
+                              0 /*metaState*/, &outKeyCode, &outMetaState, &outFlags) != OK) {
+        return;
+    }
+    switch (outKeyCode) {
+        case AKEYCODE_STYLUS_BUTTON_PRIMARY:
+            mBtnStylus = down;
+            break;
+        case AKEYCODE_STYLUS_BUTTON_SECONDARY:
+            mBtnStylus2 = down;
+            break;
+        default:
+            break;
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 7b889be..65b0a62 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -16,7 +16,8 @@
 
 #pragma once
 
-#include <stdint.h>
+#include <cstdint>
+#include "HidUsageAccumulator.h"
 
 namespace android {
 
@@ -26,9 +27,11 @@
 /* Keeps track of the state of touch, stylus and tool buttons. */
 class TouchButtonAccumulator {
 public:
-    TouchButtonAccumulator();
-    void configure(InputDeviceContext& deviceContext);
-    void reset(InputDeviceContext& deviceContext);
+    explicit TouchButtonAccumulator(InputDeviceContext& deviceContext)
+          : mDeviceContext(deviceContext){};
+
+    void configure();
+    void reset();
 
     void process(const RawEvent* rawEvent);
 
@@ -39,25 +42,29 @@
     bool hasStylus() const;
 
 private:
-    bool mHaveBtnTouch;
-    bool mHaveStylus;
+    bool mHaveBtnTouch{};
+    bool mHaveStylus{};
 
-    bool mBtnTouch;
-    bool mBtnStylus;
-    bool mBtnStylus2;
-    bool mBtnToolFinger;
-    bool mBtnToolPen;
-    bool mBtnToolRubber;
-    bool mBtnToolBrush;
-    bool mBtnToolPencil;
-    bool mBtnToolAirbrush;
-    bool mBtnToolMouse;
-    bool mBtnToolLens;
-    bool mBtnToolDoubleTap;
-    bool mBtnToolTripleTap;
-    bool mBtnToolQuadTap;
+    bool mBtnTouch{};
+    bool mBtnStylus{};
+    bool mBtnStylus2{};
+    bool mBtnToolFinger{};
+    bool mBtnToolPen{};
+    bool mBtnToolRubber{};
+    bool mBtnToolBrush{};
+    bool mBtnToolPencil{};
+    bool mBtnToolAirbrush{};
+    bool mBtnToolMouse{};
+    bool mBtnToolLens{};
+    bool mBtnToolDoubleTap{};
+    bool mBtnToolTripleTap{};
+    bool mBtnToolQuadTap{};
 
-    void clearButtons();
+    HidUsageAccumulator mHidUsageAccumulator{};
+
+    InputDeviceContext& mDeviceContext;
+
+    void processMappedKey(int32_t scanCode, bool down);
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bc70584..0c2742f 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -39,6 +39,7 @@
 #include <gtest/gtest.h>
 #include <gui/constants.h>
 
+#include <thread>
 #include "android/hardware/input/InputDeviceCountryCode.h"
 #include "input/DisplayViewport.h"
 #include "input/Input.h"
@@ -141,6 +142,16 @@
     ASSERT_EQ(nullptr, motionRange);
 }
 
+[[maybe_unused]] static void dumpReader(InputReader& reader) {
+    std::string dump;
+    reader.dump(dump);
+    std::istringstream iss(dump);
+    for (std::string line; std::getline(iss, line);) {
+        ALOGE("%s", line.c_str());
+        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+    }
+}
+
 // --- FakePointerController ---
 
 class FakePointerController : public PointerControllerInterface {
@@ -244,6 +255,7 @@
     bool mInputDevicesChanged GUARDED_BY(mLock){false};
     std::vector<DisplayViewport> mViewports;
     TouchAffineTransformation transform;
+    std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
 
 protected:
     virtual ~FakeInputReaderPolicy() {}
@@ -268,6 +280,18 @@
         });
     }
 
+    void assertStylusGestureNotified(int32_t deviceId) {
+        std::scoped_lock lock(mLock);
+        ASSERT_TRUE(mStylusGestureNotified);
+        ASSERT_EQ(deviceId, *mStylusGestureNotified);
+        mStylusGestureNotified.reset();
+    }
+
+    void assertStylusGestureNotNotified() {
+        std::scoped_lock lock(mLock);
+        ASSERT_FALSE(mStylusGestureNotified);
+    }
+
     virtual void clearViewports() {
         mViewports.clear();
         mConfig.setDisplayViewports(mViewports);
@@ -428,6 +452,11 @@
         ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
         mInputDevicesChanged = false;
     }
+
+    void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override {
+        std::scoped_lock<std::mutex> lock(mLock);
+        mStylusGestureNotified = deviceId;
+    }
 };
 
 // --- FakeEventHub ---
@@ -740,7 +769,7 @@
     status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
                                  RawAbsoluteAxisInfo* outAxisInfo) const override {
         Device* device = getDevice(deviceId);
-        if (device && device->enabled) {
+        if (device) {
             ssize_t index = device->absoluteAxes.indexOfKey(axis);
             if (index >= 0) {
                 *outAxisInfo = device->absoluteAxes.valueAt(index);
@@ -2329,6 +2358,15 @@
         mTestListener.reset();
         mFakePolicy.clear();
     }
+
+    std::optional<InputDeviceInfo> findDeviceByName(const std::string& name) {
+        const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
+        const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(),
+                                      [&name](const InputDeviceInfo& info) {
+                                          return info.getIdentifier().name == name;
+                                      });
+        return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
+    }
 };
 
 TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
@@ -2364,18 +2402,11 @@
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
     ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
 
-    // Find the test device by its name.
-    const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
-    const auto& it =
-            std::find_if(inputDevices.begin(), inputDevices.end(),
-                         [&keyboard](const InputDeviceInfo& info) {
-                             return info.getIdentifier().name == keyboard->getName();
-                         });
-
-    ASSERT_NE(it, inputDevices.end());
-    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType());
-    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources());
-    ASSERT_EQ(0U, it->getMotionRanges().size());
+    const auto device = findDeviceByName(keyboard->getName());
+    ASSERT_TRUE(device.has_value());
+    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources());
+    ASSERT_EQ(0U, device->getMotionRanges().size());
 
     keyboard.reset();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -2410,6 +2441,41 @@
     ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
 }
 
+TEST_F(InputReaderIntegrationTest, ExternalStylusesButtons) {
+    std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+    const auto device = findDeviceByName(stylus->getName());
+    ASSERT_TRUE(device.has_value());
+
+    // An external stylus with buttons should also be recognized as a keyboard.
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_STYLUS, device->getSources())
+            << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str();
+    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());
+
+    const auto DOWN =
+            AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD));
+    const auto UP = AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD));
+
+    stylus->pressAndReleaseKey(BTN_STYLUS);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+            AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+            AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+
+    stylus->pressAndReleaseKey(BTN_STYLUS2);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+            AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY))));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+            AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY))));
+
+    stylus->pressAndReleaseKey(BTN_STYLUS3);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+            AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+            AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
+}
+
 /**
  * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
  * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
@@ -2450,6 +2516,9 @@
         mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+        const auto info = findDeviceByName(mDevice->getName());
+        ASSERT_TRUE(info);
+        mDeviceInfo = *info;
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
@@ -2473,6 +2542,7 @@
     }
 
     std::unique_ptr<UinputTouchScreen> mDevice;
+    InputDeviceInfo mDeviceInfo;
 };
 
 TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
@@ -2689,6 +2759,72 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
 
+TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // Send down with the pen tool selected. The policy should be notified of the stylus presence.
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendToolType(MT_TOOL_PEN);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
+
+    // Release the stylus touch.
+    mDevice->sendUp();
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());
+
+    // Touch down with the finger, without the pen tool selected. The policy is not notified.
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendToolType(MT_TOOL_FINGER);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());
+
+    mDevice->sendUp();
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+    // Send a move event with the stylus tool without BTN_TOUCH to generate a hover enter.
+    // The policy should be notified of the stylus presence.
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendToolType(MT_TOOL_PEN);
+    mDevice->sendMove(centerPoint);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
+}
+
+TEST_F(TouchIntegrationTest, StylusButtonsGenerateKeyEvents) {
+    mDevice->sendKey(BTN_STYLUS, 1);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+            AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+                  WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+
+    mDevice->sendKey(BTN_STYLUS, 0);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+            AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+                  WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+}
+
 // --- InputDeviceTest ---
 class InputDeviceTest : public testing::Test {
 protected:
@@ -2699,6 +2835,7 @@
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
     static const int32_t EVENTHUB_ID;
+    static const std::string DEVICE_BLUETOOTH_ADDRESS;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -2715,6 +2852,7 @@
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         identifier.location = DEVICE_LOCATION;
+        identifier.bluetoothAddress = DEVICE_BLUETOOTH_ADDRESS;
         mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION,
                                                 identifier);
         mReader->pushNextDevice(mDevice);
@@ -2736,6 +2874,7 @@
 const ftl::Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES =
         InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK;
 const int32_t InputDeviceTest::EVENTHUB_ID = 1;
+const std::string InputDeviceTest::DEVICE_BLUETOOTH_ADDRESS = "11:AA:22:BB:33:CC";
 
 TEST_F(InputDeviceTest, ImmutableProperties) {
     ASSERT_EQ(DEVICE_ID, mDevice->getId());
@@ -3002,6 +3141,12 @@
     device.dump(dumpStr, eventHubDevStr);
 }
 
+TEST_F(InputDeviceTest, GetBluetoothAddress) {
+    const auto& address = mReader->getBluetoothAddress(DEVICE_ID);
+    ASSERT_TRUE(address);
+    ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
+}
+
 // --- InputMapperTest ---
 
 class InputMapperTest : public testing::Test {
@@ -7020,6 +7165,44 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
 }
 
+TEST_F(SingleTouchInputMapperTest, ButtonIsReleasedOnTouchUp) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+    // Press a stylus button.
+    processKey(mapper, BTN_STYLUS, 1);
+    processSync(mapper);
+
+    // Start a touch gesture and ensure the BUTTON_PRESS event is generated.
+    processDown(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                  WithCoords(toDisplayX(100), toDisplayY(200)),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                  WithCoords(toDisplayX(100), toDisplayY(200)),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+    // Release the touch gesture. Ensure that the BUTTON_RELEASE event is generated even though
+    // the button has not actually been released, since there will be no pointers through which the
+    // button state can be reported. The event is generated at the location of the pointer before
+    // it went up.
+    processUp(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+                  WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                  WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
+}
+
 // --- TouchDisplayProjectionTest ---
 
 class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -7204,6 +7387,7 @@
     void processSlot(MultiTouchInputMapper& mapper, int32_t slot);
     void processToolType(MultiTouchInputMapper& mapper, int32_t toolType);
     void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value);
+    void processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, int32_t value);
     void processMTSync(MultiTouchInputMapper& mapper);
     void processSync(MultiTouchInputMapper& mapper);
 };
@@ -7308,6 +7492,12 @@
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
 }
 
+void MultiTouchInputMapperTest::processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode,
+                                                int32_t value) {
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, usageCode);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, value);
+}
+
 void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0);
 }
@@ -8423,6 +8613,63 @@
     ASSERT_EQ(0, motionArgs.buttonState);
 }
 
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_STYLUS_BUTTON_PRIMARY, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, 0xabcd, AKEYCODE_STYLUS_BUTTON_SECONDARY, 0);
+
+    // Touch down.
+    processId(mapper, 1);
+    processPosition(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0))));
+
+    // Press and release button mapped to the primary stylus button.
+    processKey(mapper, BTN_A, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+    processKey(mapper, BTN_A, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+    // Press and release the HID usage mapped to the secondary stylus button.
+    processHidUsage(mapper, 0xabcd, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+                  WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY))));
+
+    processHidUsage(mapper, 0xabcd, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0))));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+    // Release touch.
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
+}
+
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 29093ef..5e47b80 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -59,6 +59,12 @@
             assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
 }
 
+void TestInputListener::assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher) {
+    NotifyKeyArgs outEventArgs;
+    ASSERT_NO_FATAL_FAILURE(assertNotifyKeyWasCalled(&outEventArgs));
+    ASSERT_THAT(outEventArgs, matcher);
+}
+
 void TestInputListener::assertNotifyKeyWasNotCalled() {
     ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
 }
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 4ad1c42..87752e1 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -44,6 +44,8 @@
 
     void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);
 
+    void assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher);
+
     void assertNotifyKeyWasNotCalled();
 
     void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index ff7455b..5107af7 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -19,10 +19,11 @@
 #include <android/input.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <input/Input.h>
 
 namespace android {
 
-MATCHER_P(WithMotionAction, action, "InputEvent with specified action") {
+MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
     bool matches = action == arg.action;
     if (!matches) {
         *result_listener << "expected action " << MotionEvent::actionToString(action)
@@ -38,6 +39,12 @@
     return matches;
 }
 
+MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") {
+    *result_listener << "expected action " << KeyEvent::actionToString(action) << ", but got "
+                     << KeyEvent::actionToString(arg.action);
+    return arg.action == action;
+}
+
 MATCHER_P(WithSource, source, "InputEvent with specified source") {
     *result_listener << "expected source " << source << ", but got " << arg.source;
     return arg.source == source;
@@ -48,6 +55,11 @@
     return arg.displayId == displayId;
 }
 
+MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
+    *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
+    return arg.keyCode == keyCode;
+}
+
 MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
     const auto argX = arg.pointerCoords[0].getX();
     const auto argY = arg.pointerCoords[0].getY();
@@ -62,9 +74,21 @@
     return argPressure;
 }
 
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+    const auto argToolType = arg.pointerProperties[0].toolType;
+    *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got "
+                     << motionToolTypeToString(argToolType);
+    return argToolType == toolType;
+}
+
 MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
     *result_listener << "expected flags " << flags << ", but got " << arg.flags;
     return arg.flags == flags;
 }
 
+MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
+    *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
+    return arg.buttonState == buttons;
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index a23c873..c4830dc 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -76,8 +76,8 @@
 
 // --- UinputKeyboard ---
 
-UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys)
-      : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {}
+UinputKeyboard::UinputKeyboard(const char* name, std::initializer_list<int> keys)
+      : UinputDevice(name), mKeys(keys.begin(), keys.end()) {}
 
 void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) {
     // enable key press/release event
@@ -121,14 +121,19 @@
 
 // --- UinputHomeKey ---
 
-UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
+UinputHomeKey::UinputHomeKey() : UinputKeyboard("Test Uinput Home Key", {KEY_HOME}) {}
 
 void UinputHomeKey::pressAndReleaseHomeKey() {
     pressAndReleaseKey(KEY_HOME);
 }
 
 // --- UinputSteamController
-UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+UinputSteamController::UinputSteamController()
+      : UinputKeyboard("Test Uinput Steam Controller", {BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+
+// --- UinputExternalStylus
+UinputExternalStylus::UinputExternalStylus()
+      : UinputKeyboard("Test Uinput External Stylus", {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {}
 
 // --- UinputTouchScreen ---
 UinputTouchScreen::UinputTouchScreen(const Rect* size)
@@ -147,6 +152,9 @@
     ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
     ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
     ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+    ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
+    ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
+    ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS3);
 
     device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
     device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
@@ -158,6 +166,8 @@
     device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
     device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
     device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
+    device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER;
+    device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX;
 }
 
 void UinputTouchScreen::sendSlot(int32_t slot) {
@@ -196,6 +206,10 @@
     injectEvent(EV_SYN, SYN_REPORT, 0);
 }
 
+void UinputTouchScreen::sendKey(int32_t scanCode, int32_t value) {
+    injectEvent(EV_KEY, scanCode, value);
+}
+
 // Get the center x, y base on the range definition.
 const Point UinputTouchScreen::getCenterPoint() {
     return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2);
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index e0ff8c3..53dcfd0 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -84,7 +84,7 @@
     friend std::unique_ptr<D> createUinputDevice(Ts... args);
 
 protected:
-    UinputKeyboard(std::initializer_list<int> keys = {});
+    UinputKeyboard(const char* name, std::initializer_list<int> keys = {});
 
 private:
     void configureDevice(int fd, uinput_user_dev* device) override;
@@ -117,6 +117,16 @@
     UinputSteamController();
 };
 
+// A stylus that reports button presses.
+class UinputExternalStylus : public UinputKeyboard {
+public:
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+    UinputExternalStylus();
+};
+
 // --- UinputTouchScreen ---
 // A touch screen device with specific size.
 class UinputTouchScreen : public UinputDevice {
@@ -142,6 +152,7 @@
     void sendUp();
     void sendToolType(int32_t toolType);
     void sendSync();
+    void sendKey(int32_t scanCode, int32_t value);
 
     const Point getCenterPoint();
 
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index a9f5a3a..2eed997 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -157,6 +157,10 @@
         return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
     }
 
+    std::optional<std::string> getBluetoothAddress(int32_t deviceId) const {
+        return reader->getBluetoothAddress(deviceId);
+    }
+
 private:
     std::unique_ptr<InputReaderInterface> reader;
 };
@@ -273,6 +277,7 @@
                                          std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()),
                                          std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()));
                 },
+                [&]() -> void { reader->getBluetoothAddress(fdp->ConsumeIntegral<int32_t>()); },
         })();
     }
 
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index bd81761..64316ba 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -315,6 +315,7 @@
         return mTransform;
     }
     void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
+    void notifyStylusGestureStarted(int32_t, nsecs_t) {}
 };
 
 class FuzzInputListener : public virtual InputListenerInterface {
@@ -363,6 +364,7 @@
 
     void updateLedMetaState(int32_t metaState) override{};
     int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
+    void notifyStylusGestureStarted(int32_t, nsecs_t) {}
 };
 
 } // namespace android
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 3de51a4..9e13849 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -69,6 +69,10 @@
             case VendorAtomValue::repeatedIntValue: {
                 const std::optional<std::vector<int>>& repeatedIntValue =
                         atomValue.get<VendorAtomValue::repeatedIntValue>();
+                if (!repeatedIntValue) {
+                    AStatsEvent_writeInt32Array(event, {}, 0);
+                    break;
+                }
                 AStatsEvent_writeInt32Array(event, repeatedIntValue->data(),
                                             repeatedIntValue->size());
                 break;
@@ -76,6 +80,10 @@
             case VendorAtomValue::repeatedLongValue: {
                 const std::optional<std::vector<int64_t>>& repeatedLongValue =
                         atomValue.get<VendorAtomValue::repeatedLongValue>();
+                if (!repeatedLongValue) {
+                    AStatsEvent_writeInt64Array(event, {}, 0);
+                    break;
+                }
                 AStatsEvent_writeInt64Array(event, repeatedLongValue->data(),
                                             repeatedLongValue->size());
                 break;
@@ -83,6 +91,10 @@
             case VendorAtomValue::repeatedFloatValue: {
                 const std::optional<std::vector<float>>& repeatedFloatValue =
                         atomValue.get<VendorAtomValue::repeatedFloatValue>();
+                if (!repeatedFloatValue) {
+                    AStatsEvent_writeFloatArray(event, {}, 0);
+                    break;
+                }
                 AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(),
                                             repeatedFloatValue->size());
                 break;
@@ -90,12 +102,18 @@
             case VendorAtomValue::repeatedStringValue: {
                 const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue =
                         atomValue.get<VendorAtomValue::repeatedStringValue>();
+                if (!repeatedStringValue) {
+                    AStatsEvent_writeStringArray(event, {}, 0);
+                    break;
+                }
                 const std::vector<std::optional<std::string>>& repeatedStringVector =
                         *repeatedStringValue;
                 const char* cStringArray[repeatedStringVector.size()];
 
                 for (int i = 0; i < repeatedStringVector.size(); ++i) {
-                    cStringArray[i] = repeatedStringVector[i]->c_str();
+                    cStringArray[i] = repeatedStringVector[i].has_value()
+                            ? repeatedStringVector[i]->c_str()
+                            : "";
                 }
 
                 AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size());
@@ -104,6 +122,10 @@
             case VendorAtomValue::repeatedBoolValue: {
                 const std::optional<std::vector<bool>>& repeatedBoolValue =
                         atomValue.get<VendorAtomValue::repeatedBoolValue>();
+                if (!repeatedBoolValue) {
+                    AStatsEvent_writeBoolArray(event, {}, 0);
+                    break;
+                }
                 const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue;
                 bool boolArray[repeatedBoolValue->size()];
 
@@ -117,7 +139,10 @@
             case VendorAtomValue::byteArrayValue: {
                 const std::optional<std::vector<uint8_t>>& byteArrayValue =
                         atomValue.get<VendorAtomValue::byteArrayValue>();
-
+                if (!byteArrayValue) {
+                    AStatsEvent_writeByteArray(event, {}, 0);
+                    break;
+                }
                 AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size());
                 break;
             }
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index e76b191..892c07d 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -181,7 +181,7 @@
         "Scheduler/LayerHistory.cpp",
         "Scheduler/LayerInfo.cpp",
         "Scheduler/MessageQueue.cpp",
-        "Scheduler/RefreshRateConfigs.cpp",
+        "Scheduler/RefreshRateSelector.cpp",
         "Scheduler/Scheduler.cpp",
         "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncPredictor.cpp",
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 7202bef..bdbc79b 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -25,6 +25,7 @@
 
 #include "Client.h"
 #include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 
@@ -47,36 +48,6 @@
     return NO_ERROR;
 }
 
-void Client::attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer)
-{
-    Mutex::Autolock _l(mLock);
-    mLayers.add(handle, layer);
-}
-
-void Client::detachLayer(const Layer* layer)
-{
-    Mutex::Autolock _l(mLock);
-    // we do a linear search here, because this doesn't happen often
-    const size_t count = mLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        if (mLayers.valueAt(i) == layer) {
-            mLayers.removeItemsAt(i, 1);
-            break;
-        }
-    }
-}
-sp<Layer> Client::getLayerUser(const sp<IBinder>& handle) const
-{
-    Mutex::Autolock _l(mLock);
-    sp<Layer> lbc;
-    wp<Layer> layer(mLayers.valueFor(handle));
-    if (layer != 0) {
-        lbc = layer.promote();
-        ALOGE_IF(lbc==0, "getLayerUser(name=%p) is dead", handle.get());
-    }
-    return lbc;
-}
-
 binder::Status Client::createSurface(const std::string& name, int32_t flags,
                                      const sp<IBinder>& parent, const gui::LayerMetadata& metadata,
                                      gui::CreateSurfaceResult* outResult) {
@@ -91,7 +62,7 @@
 
 binder::Status Client::clearLayerFrameStats(const sp<IBinder>& handle) {
     status_t status;
-    sp<Layer> layer = getLayerUser(handle);
+    sp<Layer> layer = LayerHandle::getLayer(handle);
     if (layer == nullptr) {
         status = NAME_NOT_FOUND;
     } else {
@@ -103,7 +74,7 @@
 
 binder::Status Client::getLayerFrameStats(const sp<IBinder>& handle, gui::FrameStats* outStats) {
     status_t status;
-    sp<Layer> layer = getLayerUser(handle);
+    sp<Layer> layer = LayerHandle::getLayer(handle);
     if (layer == nullptr) {
         status = NAME_NOT_FOUND;
     } else {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 02079a3..af410ea 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -38,12 +38,6 @@
 
     status_t initCheck() const;
 
-    // protected by SurfaceFlinger::mStateLock
-    void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer);
-    void detachLayer(const Layer* layer);
-
-    sp<Layer> getLayerUser(const sp<IBinder>& handle) const;
-
 private:
     // ISurfaceComposerClient interface
 
@@ -64,9 +58,6 @@
     // constant
     sp<SurfaceFlinger> mFlinger;
 
-    // protected by mLock
-    DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers;
-
     // thread-safe
     mutable Mutex mLock;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 6832ae1..8661063 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -56,7 +56,7 @@
     virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
 
     virtual renderengine::RenderEngine& getRenderEngine() const = 0;
-    virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+    virtual void setRenderEngine(renderengine::RenderEngine*) = 0;
 
     virtual TimeStats& getTimeStats() const = 0;
     virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index dd4dbe9..2af6c80 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -34,7 +34,7 @@
     void setHwComposer(std::unique_ptr<HWComposer>) override;
 
     renderengine::RenderEngine& getRenderEngine() const override;
-    void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
+    void setRenderEngine(renderengine::RenderEngine*) override;
 
     TimeStats& getTimeStats() const override;
     void setTimeStats(const std::shared_ptr<TimeStats>&) override;
@@ -58,7 +58,7 @@
 
 private:
     std::unique_ptr<HWComposer> mHwComposer;
-    std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    renderengine::RenderEngine* mRenderEngine;
     std::shared_ptr<TimeStats> mTimeStats;
     bool mNeedsAnotherUpdate = false;
     nsecs_t mRefreshStartTime = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index a48cc6f..fc71649 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -40,7 +40,7 @@
     MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
 
     MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
-    MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+    MOCK_METHOD1(setRenderEngine, void(renderengine::RenderEngine*));
 
     MOCK_CONST_METHOD0(getTimeStats, TimeStats&());
     MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index a4e1fff..e42f11a 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -65,11 +65,11 @@
 }
 
 renderengine::RenderEngine& CompositionEngine::getRenderEngine() const {
-    return *mRenderEngine.get();
+    return *mRenderEngine;
 }
 
-void CompositionEngine::setRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
-    mRenderEngine = std::move(renderEngine);
+void CompositionEngine::setRenderEngine(renderengine::RenderEngine* renderEngine) {
+    mRenderEngine = renderEngine;
 }
 
 TimeStats& CompositionEngine::getTimeStats() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 0b69d44..1c5cbed 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -248,12 +248,17 @@
         return false;
     }
 
-    const TimePoint startTime = TimePoint::now();
-
     // Get any composition changes requested by the HWC device, and apply them.
     std::optional<android::HWComposer::DeviceRequestedChanges> changes;
     auto& hwc = getCompositionEngine().getHwComposer();
     const bool requiresClientComposition = anyLayersRequireClientComposition();
+
+    if (isPowerHintSessionEnabled()) {
+        mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+    }
+
+    const TimePoint hwcValidateStartTime = TimePoint::now();
+
     if (status_t result =
                 hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
                                                 getState().earliestPresentTime,
@@ -266,8 +271,10 @@
     }
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcValidateTiming(mId, startTime, TimePoint::now());
-        mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+        mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now());
+        if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+            mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+        }
     }
 
     return true;
@@ -432,13 +439,6 @@
     }
 
     impl::Output::finishFrame(refreshArgs, std::move(result));
-
-    if (isPowerHintSessionEnabled()) {
-        auto& hwc = getCompositionEngine().getHwComposer();
-        if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
-            mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
-        }
-    }
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index b570979..8d99f89 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -62,11 +62,10 @@
 }
 
 TEST_F(CompositionEngineTest, canSetRenderEngine) {
-    renderengine::mock::RenderEngine* renderEngine =
-            new StrictMock<renderengine::mock::RenderEngine>();
-    mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(renderEngine));
+    auto renderEngine = std::make_unique<StrictMock<renderengine::mock::RenderEngine>>();
+    mEngine.setRenderEngine(renderEngine.get());
 
-    EXPECT_EQ(renderEngine, &mEngine.getRenderEngine());
+    EXPECT_EQ(renderEngine.get(), &mEngine.getRenderEngine());
 }
 
 TEST_F(CompositionEngineTest, canSetTimeStats) {
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index c63d57f..18ddfbc 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -69,7 +69,7 @@
         mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
         mPhysicalOrientation(args.physicalOrientation),
         mIsPrimary(args.isPrimary),
-        mRefreshRateConfigs(std::move(args.refreshRateConfigs)) {
+        mRefreshRateSelector(std::move(args.refreshRateSelector)) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
     mCompositionDisplay->createRenderSurface(
             compositionengine::RenderSurfaceCreationArgsBuilder()
@@ -200,7 +200,7 @@
 
     ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue());
 
-    mRefreshRateConfigs->setActiveModeId(modeId);
+    mRefreshRateSelector->setActiveModeId(modeId);
 
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->changeRefreshRate(fps);
@@ -234,7 +234,7 @@
         return vsyncPeriod;
     }
 
-    return refreshRateConfigs().getActiveModePtr()->getVsyncPeriod();
+    return refreshRateSelector().getActiveModePtr()->getVsyncPeriod();
 }
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
@@ -335,8 +335,8 @@
     utils::Dumper::Indent indent(dumper);
     dumper.dump("powerMode"sv, mPowerMode);
 
-    if (mRefreshRateConfigs) {
-        mRefreshRateConfigs->dump(dumper);
+    if (mRefreshRateSelector) {
+        mRefreshRateSelector->dump(dumper);
     }
 }
 
@@ -430,7 +430,7 @@
         return;
     }
 
-    const auto fpsRange = mRefreshRateConfigs->getSupportedRefreshRateRange();
+    const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
@@ -439,9 +439,9 @@
 
 bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
                                          bool timerExpired) {
-    if (mRefreshRateConfigs && mRefreshRateOverlay) {
+    if (mRefreshRateSelector && mRefreshRateOverlay) {
         const auto newRefreshRate =
-                mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired);
+                mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
         if (newRefreshRate) {
             mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
             return true;
@@ -475,7 +475,7 @@
     }
 
     // Check if we are already at the desired mode
-    if (refreshRateConfigs().getActiveModePtr()->getId() == info.mode->getId()) {
+    if (refreshRateSelector().getActiveModePtr()->getId() == info.mode->getId()) {
         return false;
     }
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 7abb94b..6c848bb 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 "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "ThreadContext.h"
 #include "TracedOrdinal.h"
 #include "Utils/Dumper.h"
@@ -219,7 +219,7 @@
     }
 
     const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext) {
-        return mRefreshRateConfigs->getActiveMode();
+        return mRefreshRateSelector->getActiveMode();
     }
 
     // Precondition: DisplaySnapshot must contain a mode with DisplayModeId.
@@ -230,14 +230,11 @@
                                 hal::VsyncPeriodChangeTimeline* outTimeline)
             REQUIRES(kMainThreadContext);
 
-    // Returns the refresh rate configs for this display.
-    scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
+    scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
 
-    // Returns a shared pointer to the refresh rate configs for this display.
-    // Clients can store this refresh rate configs and use it even if the DisplayDevice
-    // is destroyed.
-    std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const {
-        return mRefreshRateConfigs;
+    // Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice.
+    std::shared_ptr<scheduler::RefreshRateSelector> holdRefreshRateSelector() const {
+        return mRefreshRateSelector;
     }
 
     // Enables an overlay to be displayed with the current refresh rate
@@ -287,7 +284,7 @@
 
     std::vector<ui::Hdr> mOverrideHdrTypes;
 
-    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
     std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
 
     mutable std::mutex mActiveModeLock;
@@ -337,7 +334,7 @@
     HWComposer& hwComposer;
     const wp<IBinder> displayToken;
     const std::shared_ptr<compositionengine::Display> compositionDisplay;
-    std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs;
+    std::shared_ptr<scheduler::RefreshRateSelector> refreshRateSelector;
 
     int32_t sequenceId{0};
     bool isSecure{false};
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9777092..3478eaa 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -196,7 +196,7 @@
     }
 
     mFrameTracker.setDisplayRefreshPeriod(
-            args.flinger->mScheduler->getVsyncPeriodFromRefreshRateConfigs());
+            args.flinger->mScheduler->getVsyncPeriodFromRefreshRateSelector());
 
     mOwnerUid = args.ownerUid;
     mOwnerPid = args.ownerPid;
@@ -240,11 +240,6 @@
     mFlinger->mTimeStats->onDestroy(layerId);
     mFlinger->mFrameTracer->onDestroy(layerId);
 
-    sp<Client> c(mClientRef.promote());
-    if (c != 0) {
-        c->detachLayer(this);
-    }
-
     mFrameTracker.logAndResetStats(mName);
     mFlinger->onLayerDestroyed(this);
 
@@ -1129,7 +1124,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
-    // RefreshRateConfigs::getBestRefreshRate for more details.
+    // RefreshRateSelector::rankRefreshRates for details.
     const auto layerVotedWithDefaultCompatibility =
             frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default;
     const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote;
@@ -3598,7 +3593,7 @@
     }
 
     if (display) {
-        const Fps refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps();
+        const Fps refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps();
         const std::optional<Fps> renderRate =
                 mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
 
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 8dd3b0f..6e64e0a 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -40,6 +40,7 @@
 
 #include "DisplayDevice.h"
 #include "DisplayRenderArea.h"
+#include "FrontEnd/LayerCreationArgs.h"
 #include "Layer.h"
 #include "Scheduler/VsyncController.h"
 #include "SurfaceFlinger.h"
@@ -129,12 +130,12 @@
     }
 }
 
-void RegionSamplingThread::addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
+void RegionSamplingThread::addListener(const Rect& samplingArea, uint32_t stopLayerId,
                                        const sp<IRegionSamplingListener>& listener) {
     sp<IBinder> asBinder = IInterface::asBinder(listener);
     asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
     std::lock_guard lock(mSamplingMutex);
-    mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener});
+    mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayerId, listener});
 }
 
 void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) {
@@ -291,8 +292,8 @@
             if (stopLayerFound) return;
 
             // Likewise if we just found a stop layer, set the flag and abort
-            for (const auto& [area, stopLayer, listener] : descriptors) {
-                if (layer == stopLayer.promote().get()) {
+            for (const auto& [area, stopLayerId, listener] : descriptors) {
+                if (stopLayerId != UNASSIGNED_LAYER_ID && layer->getSequence() == stopLayerId) {
                     stopLayerFound = true;
                     return;
                 }
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 686b4b1..b62b15c 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -26,6 +26,7 @@
 
 #include <chrono>
 #include <condition_variable>
+#include <cstdint>
 #include <mutex>
 #include <thread>
 #include <unordered_map>
@@ -73,7 +74,7 @@
 
     // Add a listener to receive luma notifications. The luma reported via listener will
     // report the median luma for the layers under the stopLayerHandle, in the samplingArea region.
-    void addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
+    void addListener(const Rect& samplingArea, uint32_t stopLayerId,
                      const sp<IRegionSamplingListener>& listener);
     // Remove the listener to stop receiving median luma notifications.
     void removeListener(const sp<IRegionSamplingListener>& listener);
@@ -87,7 +88,7 @@
 private:
     struct Descriptor {
         Rect area = Rect::EMPTY_RECT;
-        wp<Layer> stopLayer;
+        uint32_t stopLayerId;
         sp<IRegionSamplingListener> listener;
     };
 
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ae111c3..b884dc8 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -164,7 +164,7 @@
             getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled));
 }
 
-auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> Summary {
+auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
     Summary summary;
 
     std::lock_guard lock(mLock);
@@ -178,7 +178,7 @@
         ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
               layerFocused ? "" : "not");
 
-        const auto vote = info->getRefreshRateVote(configs, now);
+        const auto vote = info->getRefreshRateVote(selector, now);
         // Skip NoVote layer as those don't have any requirements
         if (vote.type == LayerVoteType::NoVote) {
             continue;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 12bec8d..5022906 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -27,7 +27,7 @@
 #include <utility>
 #include <vector>
 
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
 
 namespace android {
 
@@ -39,7 +39,7 @@
 
 class LayerHistory {
 public:
-    using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+    using LayerVoteType = RefreshRateSelector::LayerVoteType;
 
     LayerHistory();
     ~LayerHistory();
@@ -67,10 +67,10 @@
     // does not set a preference for refresh rate.
     void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled);
 
-    using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
+    using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
-    Summary summarize(const RefreshRateConfigs&, nsecs_t now);
+    Summary summarize(const RefreshRateSelector&, nsecs_t now);
 
     void clear();
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 943615c..7247e4b 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -187,8 +187,8 @@
     return static_cast<nsecs_t>(averageFrameTime);
 }
 
-std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(
-        const RefreshRateConfigs& refreshRateConfigs, nsecs_t now) {
+std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector,
+                                                             nsecs_t now) {
     static constexpr float MARGIN = 1.0f; // 1Hz
     if (!hasEnoughDataForHeuristic()) {
         ALOGV("Not enough data");
@@ -199,7 +199,7 @@
         const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
         const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
         if (refreshRateConsistent) {
-            const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
+            const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate);
             using fps_approx_ops::operator!=;
 
             // To avoid oscillation, use the last calculated refresh rate if it is close enough.
@@ -222,7 +222,7 @@
                                                : std::nullopt;
 }
 
-LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& refreshRateConfigs,
+LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
                                                    nsecs_t now) {
     if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
         ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
@@ -250,7 +250,7 @@
         clearHistory(now);
     }
 
-    auto refreshRate = calculateRefreshRateIfPossible(refreshRateConfigs, now);
+    auto refreshRate = calculateRefreshRateIfPossible(selector, now);
     if (refreshRate.has_value()) {
         ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
         return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 28cb24a..a5ffbbe 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -28,7 +28,7 @@
 #include <scheduler/Seamlessness.h>
 
 #include "LayerHistory.h"
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
 
 namespace android {
 
@@ -162,7 +162,7 @@
 
     uid_t getOwnerUid() const { return mOwnerUid; }
 
-    LayerVote getRefreshRateVote(const RefreshRateConfigs&, nsecs_t now);
+    LayerVote getRefreshRateVote(const RefreshRateSelector&, nsecs_t now);
 
     // Return the last updated time. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
@@ -261,7 +261,7 @@
     bool isFrequent(nsecs_t now) const;
     bool isAnimating(nsecs_t now) const;
     bool hasEnoughDataForHeuristic() const;
-    std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateConfigs&, nsecs_t now);
+    std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateSelector&, nsecs_t now);
     std::optional<nsecs_t> calculateAverageFrameTime() const;
     bool isFrameTimeValid(const FrameTimeData&) const;
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
similarity index 90%
rename from services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
rename to services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 39850c7..40af6ee 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -33,10 +33,10 @@
 #include <utils/Trace.h>
 
 #include "../SurfaceFlingerProperties.h"
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
 
 #undef LOG_TAG
-#define LOG_TAG "RefreshRateConfigs"
+#define LOG_TAG "RefreshRateSelector"
 
 namespace android::scheduler {
 namespace {
@@ -50,9 +50,9 @@
     } fixedRateBelowThresholdLayersScore;
 };
 
-constexpr RefreshRateConfigs::GlobalSignals kNoSignals;
+constexpr RefreshRateSelector::GlobalSignals kNoSignals;
 
-std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
+std::string formatLayerInfo(const RefreshRateSelector::LayerRequirement& layer, float weight) {
     return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(),
                               ftl::enum_string(layer.vote).c_str(), weight,
                               ftl::enum_string(layer.seamlessness).c_str(),
@@ -111,7 +111,7 @@
         for (const auto it2 : sortedModes) {
             const auto& mode2 = it2->second;
 
-            if (RefreshRateConfigs::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
+            if (RefreshRateSelector::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
                 return true;
             }
         }
@@ -119,23 +119,23 @@
     return false;
 }
 
-std::string toString(const RefreshRateConfigs::PolicyVariant& policy) {
+std::string toString(const RefreshRateSelector::PolicyVariant& policy) {
     using namespace std::string_literals;
 
     return ftl::match(
             policy,
-            [](const RefreshRateConfigs::DisplayManagerPolicy& policy) {
+            [](const RefreshRateSelector::DisplayManagerPolicy& policy) {
                 return "DisplayManagerPolicy"s + policy.toString();
             },
-            [](const RefreshRateConfigs::OverridePolicy& policy) {
+            [](const RefreshRateSelector::OverridePolicy& policy) {
                 return "OverridePolicy"s + policy.toString();
             },
-            [](RefreshRateConfigs::NoOverridePolicy) { return "NoOverridePolicy"s; });
+            [](RefreshRateSelector::NoOverridePolicy) { return "NoOverridePolicy"s; });
 }
 
 } // namespace
 
-struct RefreshRateConfigs::RefreshRateScoreComparator {
+struct RefreshRateSelector::RefreshRateScoreComparator {
     bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
         const auto& [modeIt, overallScore, _] = lhs;
 
@@ -162,15 +162,15 @@
     const RefreshRateOrder refreshRateOrder;
 };
 
-std::string RefreshRateConfigs::Policy::toString() const {
+std::string RefreshRateSelector::Policy::toString() const {
     return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
                               ", primaryRange=%s, appRequestRange=%s}",
                               defaultMode.value(), allowGroupSwitching ? "true" : "false",
                               to_string(primaryRange).c_str(), to_string(appRequestRange).c_str());
 }
 
-std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
-                                                                 nsecs_t displayPeriod) const {
+std::pair<nsecs_t, nsecs_t> RefreshRateSelector::getDisplayFrames(nsecs_t layerPeriod,
+                                                                  nsecs_t displayPeriod) const {
     auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
     if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
         std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
@@ -181,8 +181,8 @@
     return {quotient, remainder};
 }
 
-float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
-                                                                    Fps refreshRate) const {
+float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
+                                                                     Fps refreshRate) const {
     constexpr float kScoreForFractionalPairs = .8f;
 
     const auto displayPeriod = refreshRate.getPeriodNsecs();
@@ -243,15 +243,15 @@
     return 0;
 }
 
-float RefreshRateConfigs::calculateRefreshRateScoreForFps(Fps refreshRate) const {
+float RefreshRateSelector::calculateRefreshRateScoreForFps(Fps refreshRate) const {
     const float ratio =
             refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue();
     // Use ratio^2 to get a lower score the more we get further from peak
     return ratio * ratio;
 }
 
-float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
-                                                    bool isSeamlessSwitch) const {
+float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
+                                                     bool isSeamlessSwitch) const {
     // Slightly prefer seamless switches.
     constexpr float kSeamedSwitchPenalty = 0.95f;
     const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
@@ -287,8 +287,8 @@
             kNonExactMatchingPenalty;
 }
 
-auto RefreshRateConfigs::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
-                                               GlobalSignals signals) const -> RankedRefreshRates {
+auto RefreshRateSelector::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
+                                                GlobalSignals signals) const -> RankedRefreshRates {
     std::lock_guard lock(mLock);
 
     if (mGetRankedRefreshRatesCache &&
@@ -301,8 +301,8 @@
     return result;
 }
 
-auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
-                                                     GlobalSignals signals) const
+auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
+                                                      GlobalSignals signals) const
         -> RankedRefreshRates {
     using namespace fps_approx_ops;
     ATRACE_CALL();
@@ -608,40 +608,44 @@
     return {ranking, kNoSignals};
 }
 
-std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
-groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) {
-    std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid;
+using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>;
+using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>;
+
+PerUidLayerRequirements groupLayersByUid(
+        const std::vector<RefreshRateSelector::LayerRequirement>& layers) {
+    PerUidLayerRequirements layersByUid;
     for (const auto& layer : layers) {
-        auto iter = layersByUid.emplace(layer.ownerUid,
-                                        std::vector<const RefreshRateConfigs::LayerRequirement*>());
-        auto& layersWithSameUid = iter.first->second;
+        const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first;
+        auto& layersWithSameUid = it->second;
         layersWithSameUid.push_back(&layer);
     }
 
     // Remove uids that can't have a frame rate override
-    for (auto iter = layersByUid.begin(); iter != layersByUid.end();) {
-        const auto& layersWithSameUid = iter->second;
+    for (auto it = layersByUid.begin(); it != layersByUid.end();) {
+        const auto& layersWithSameUid = it->second;
         bool skipUid = false;
         for (const auto& layer : layersWithSameUid) {
-            if (layer->vote == RefreshRateConfigs::LayerVoteType::Max ||
-                layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) {
+            using LayerVoteType = RefreshRateSelector::LayerVoteType;
+
+            if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) {
                 skipUid = true;
                 break;
             }
         }
         if (skipUid) {
-            iter = layersByUid.erase(iter);
+            it = layersByUid.erase(it);
         } else {
-            ++iter;
+            ++it;
         }
     }
 
     return layersByUid;
 }
 
-RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
-        const std::vector<LayerRequirement>& layers, Fps displayRefreshRate,
-        GlobalSignals globalSignals) const {
+auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
+                                                Fps displayRefreshRate,
+                                                GlobalSignals globalSignals) const
+        -> UidToFrameRateOverride {
     ATRACE_CALL();
 
     ALOGV("%s: %zu layers", __func__, layers.size());
@@ -661,8 +665,7 @@
         return isStrictlyLess(mode1->getFps(), mode2->getFps());
     });
 
-    std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
-            groupLayersByUid(layers);
+    const auto layersByUid = groupLayersByUid(layers);
     UidToFrameRateOverride frameRateOverrides;
     for (const auto& [uid, layersWithSameUid] : layersByUid) {
         // Layers with ExplicitExactOrMultiple expect touch boost
@@ -723,7 +726,7 @@
     return frameRateOverrides;
 }
 
-std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
+std::optional<Fps> RefreshRateSelector::onKernelTimerChanged(
         std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
     std::lock_guard lock(mLock);
 
@@ -740,7 +743,7 @@
     return mode->getFps();
 }
 
-const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
     const auto& activeMode = *getActiveModeItLocked()->second;
 
     for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
@@ -757,7 +760,7 @@
     return mPrimaryRefreshRates.front()->second;
 }
 
-const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
+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()) {
@@ -771,7 +774,7 @@
     return mPrimaryRefreshRates.back()->second;
 }
 
-auto RefreshRateConfigs::rankRefreshRates(
+auto RefreshRateSelector::rankRefreshRates(
         std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
         std::optional<DisplayModeId> preferredDisplayModeOpt) const -> RefreshRateRanking {
     std::deque<ScoredRefreshRate> ranking;
@@ -817,23 +820,23 @@
     return rankRefreshRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
 }
 
-DisplayModePtr RefreshRateConfigs::getActiveModePtr() const {
+DisplayModePtr RefreshRateSelector::getActiveModePtr() const {
     std::lock_guard lock(mLock);
     return getActiveModeItLocked()->second;
 }
 
-const DisplayMode& RefreshRateConfigs::getActiveMode() const {
+const DisplayMode& RefreshRateSelector::getActiveMode() const {
     // Reads from kMainThreadContext do not require mLock.
     ftl::FakeGuard guard(mLock);
     return *mActiveModeIt->second;
 }
 
-DisplayModeIterator RefreshRateConfigs::getActiveModeItLocked() const {
+DisplayModeIterator RefreshRateSelector::getActiveModeItLocked() const {
     // Reads under mLock do not require kMainThreadContext.
     return FTL_FAKE_GUARD(kMainThreadContext, mActiveModeIt);
 }
 
-void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) {
+void RefreshRateSelector::setActiveModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
 
     // Invalidate the cached invocation to getRankedRefreshRates. This forces
@@ -844,14 +847,14 @@
     LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
 }
 
-RefreshRateConfigs::RefreshRateConfigs(DisplayModes modes, DisplayModeId activeModeId,
-                                       Config config)
+RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId,
+                                         Config config)
       : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
     initializeIdleTimer();
     FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId));
 }
 
-void RefreshRateConfigs::initializeIdleTimer() {
+void RefreshRateSelector::initializeIdleTimer() {
     if (mConfig.idleTimerTimeout > 0ms) {
         mIdleTimer.emplace(
                 "IdleTimer", mConfig.idleTimerTimeout,
@@ -870,7 +873,7 @@
     }
 }
 
-void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
+void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
     std::lock_guard lock(mLock);
 
     // Invalidate the cached invocation to getRankedRefreshRates. This forces
@@ -896,7 +899,7 @@
     constructAvailableRefreshRates();
 }
 
-bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
+bool RefreshRateSelector::isPolicyValidLocked(const Policy& policy) const {
     // defaultMode must be a valid mode, and within the given refresh rate range.
     if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
         if (!policy.primaryRange.includes(mode->get()->getFps())) {
@@ -913,7 +916,7 @@
             policy.appRequestRange.max >= policy.primaryRange.max;
 }
 
-auto RefreshRateConfigs::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
+auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
     Policy oldPolicy;
     {
         std::lock_guard lock(mLock);
@@ -969,21 +972,21 @@
     return SetPolicyResult::Changed;
 }
 
-const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const {
+auto RefreshRateSelector::getCurrentPolicyLocked() const -> const Policy* {
     return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy;
 }
 
-RefreshRateConfigs::Policy RefreshRateConfigs::getCurrentPolicy() const {
+auto RefreshRateSelector::getCurrentPolicy() const -> Policy {
     std::lock_guard lock(mLock);
     return *getCurrentPolicyLocked();
 }
 
-RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const {
+auto RefreshRateSelector::getDisplayManagerPolicy() const -> Policy {
     std::lock_guard lock(mLock);
     return mDisplayManagerPolicy;
 }
 
-bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const {
+bool RefreshRateSelector::isModeAllowed(DisplayModeId modeId) const {
     std::lock_guard lock(mLock);
     return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
                        [modeId](DisplayModeIterator modeIt) {
@@ -991,7 +994,7 @@
                        });
 }
 
-void RefreshRateConfigs::constructAvailableRefreshRates() {
+void RefreshRateSelector::constructAvailableRefreshRates() {
     // Filter modes based on current policy and sort on refresh rate.
     const Policy* policy = getCurrentPolicyLocked();
     ALOGV("%s: %s ", __func__, policy->toString().c_str());
@@ -1027,7 +1030,7 @@
     mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRange, "app request");
 }
 
-Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
+Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const {
     using namespace fps_approx_ops;
 
     if (frameRate <= mKnownFrameRates.front()) {
@@ -1046,7 +1049,7 @@
     return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
 }
 
-RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
+auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
     std::lock_guard lock(mLock);
 
     const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps();
@@ -1074,7 +1077,7 @@
     return KernelIdleTimerAction::TurnOn;
 }
 
-int RefreshRateConfigs::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) {
+int RefreshRateSelector::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) {
     // This calculation needs to be in sync with the java code
     // in DisplayManagerService.getDisplayInfoForFrameRateOverride
 
@@ -1090,7 +1093,7 @@
     return static_cast<int>(numPeriodsRounded);
 }
 
-bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
+bool RefreshRateSelector::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
     if (isStrictlyLess(bigger, smaller)) {
         return isFractionalPairOrMultiple(bigger, smaller);
     }
@@ -1101,7 +1104,7 @@
             isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
 }
 
-void RefreshRateConfigs::dump(utils::Dumper& dumper) const {
+void RefreshRateSelector::dump(utils::Dumper& dumper) const {
     using namespace std::string_view_literals;
 
     std::lock_guard lock(mLock);
@@ -1142,7 +1145,7 @@
     dumper.dump("idleTimer"sv, idleTimer);
 }
 
-std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() {
+std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() {
     return mConfig.idleTimerTimeout;
 }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
similarity index 95%
rename from services/surfaceflinger/Scheduler/RefreshRateConfigs.h
rename to services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 99f81aa..bff16d3 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -49,12 +49,10 @@
 
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
 
-/**
- * This class is used to encapsulate configuration for refresh rates. It holds information
- * about available refresh rates on the device, and the mapping between the numbers and human
- * readable names.
- */
-class RefreshRateConfigs {
+// Selects the refresh rate of a display by ranking its `DisplayModes` in accordance with
+// the DisplayManager (or override) `Policy`, the `LayerRequirement` of each active layer,
+// and `GlobalSignals`.
+class RefreshRateSelector {
 public:
     // Margin used when matching refresh rates to the content desired ones.
     static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
@@ -277,14 +275,14 @@
         std::optional<KernelIdleTimerController> kernelIdleTimerController;
     };
 
-    RefreshRateConfigs(DisplayModes, DisplayModeId activeModeId,
-                       Config config = {.enableFrameRateOverride = false,
-                                        .frameRateMultipleThreshold = 0,
-                                        .idleTimerTimeout = 0ms,
-                                        .kernelIdleTimerController = {}});
+    RefreshRateSelector(DisplayModes, DisplayModeId activeModeId,
+                        Config config = {.enableFrameRateOverride = false,
+                                         .frameRateMultipleThreshold = 0,
+                                         .idleTimerTimeout = 0ms,
+                                         .kernelIdleTimerController = {}});
 
-    RefreshRateConfigs(const RefreshRateConfigs&) = delete;
-    RefreshRateConfigs& operator=(const RefreshRateConfigs&) = delete;
+    RefreshRateSelector(const RefreshRateSelector&) = delete;
+    RefreshRateSelector& operator=(const RefreshRateSelector&) = delete;
 
     // Returns whether switching modes (refresh rate or resolution) is possible.
     // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
@@ -296,8 +294,8 @@
 
     // Class to enumerate options around toggling the kernel timer on and off.
     enum class KernelIdleTimerAction {
-        TurnOff,  // Turn off the idle timer.
-        TurnOn    // Turn on the idle timer.
+        TurnOff, // Turn off the idle timer.
+        TurnOn   // Turn on the idle timer.
     };
 
     // Checks whether kernel idle timer should be active depending the policy decisions around
@@ -373,7 +371,7 @@
     std::chrono::milliseconds getIdleTimerTimeout();
 
 private:
-    friend struct TestableRefreshRateConfigs;
+    friend struct TestableRefreshRateSelector;
 
     void constructAvailableRefreshRates() REQUIRES(mLock);
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index be3ebb7..499cee6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -68,8 +68,8 @@
     mDisplayPowerTimer.reset();
     mTouchTimer.reset();
 
-    // Stop idle timer and clear callbacks, as the RefreshRateConfigs may outlive the Scheduler.
-    setRefreshRateConfigs(nullptr);
+    // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
+    setRefreshRateSelector(nullptr);
 }
 
 void Scheduler::startTimers() {
@@ -94,17 +94,17 @@
     }
 }
 
-void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
-    // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
+void Scheduler::setRefreshRateSelector(std::shared_ptr<RefreshRateSelector> selectorPtr) {
+    // The current RefreshRateSelector instance may outlive this call, so unbind its idle timer.
     {
-        // mRefreshRateConfigsLock is not locked here to avoid the deadlock
+        // mRefreshRateSelectorLock is not locked here to avoid the deadlock
         // as the callback can attempt to acquire the lock before stopIdleTimer can finish
         // the execution. It's safe to FakeGuard as main thread is the only thread that
-        // writes to the mRefreshRateConfigs.
-        ftl::FakeGuard guard(mRefreshRateConfigsLock);
-        if (mRefreshRateConfigs) {
-            mRefreshRateConfigs->stopIdleTimer();
-            mRefreshRateConfigs->clearIdleTimerCallbacks();
+        // writes to the mRefreshRateSelector.
+        ftl::FakeGuard guard(mRefreshRateSelectorLock);
+        if (mRefreshRateSelector) {
+            mRefreshRateSelector->stopIdleTimer();
+            mRefreshRateSelector->clearIdleTimerCallbacks();
         }
     }
     {
@@ -113,17 +113,17 @@
         mPolicy = {};
     }
 
-    std::scoped_lock lock(mRefreshRateConfigsLock);
-    mRefreshRateConfigs = std::move(configs);
-    if (!mRefreshRateConfigs) return;
+    std::scoped_lock lock(mRefreshRateSelectorLock);
+    mRefreshRateSelector = std::move(selectorPtr);
+    if (!mRefreshRateSelector) return;
 
-    mRefreshRateConfigs->setIdleTimerCallbacks(
+    mRefreshRateSelector->setIdleTimerCallbacks(
             {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
                           .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
              .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
                         .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
 
-    mRefreshRateConfigs->startIdleTimer();
+    mRefreshRateSelector->startIdleTimer();
 }
 
 void Scheduler::registerDisplay(sp<const DisplayDevice> display) {
@@ -174,9 +174,8 @@
 }
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
     const bool supportsFrameRateOverrideByContent =
-            refreshRateConfigs->supportsFrameRateOverrideByContent();
+            holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
     return mFrameRateOverrideMappings
             .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
 }
@@ -191,7 +190,7 @@
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
-    std::scoped_lock lock(mRefreshRateConfigsLock);
+    std::scoped_lock lock(mRefreshRateSelectorLock);
 
     return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
         return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
@@ -200,7 +199,7 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = holdRefreshRateConfigs()->getActiveModePtr()->getFps();
+        const Fps refreshRate = holdRefreshRateSelector()->getActiveModePtr()->getFps();
         const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
 
         const auto frameRate = getFrameRateOverride(uid);
@@ -208,7 +207,7 @@
             return currentPeriod;
         }
 
-        const auto divisor = RefreshRateConfigs::getFrameRateDivisor(refreshRate, *frameRate);
+        const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
         if (divisor <= 1) {
             return currentPeriod;
         }
@@ -293,9 +292,8 @@
 }
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
     const bool supportsFrameRateOverrideByContent =
-            refreshRateConfigs->supportsFrameRateOverrideByContent();
+            holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
 
     std::vector<FrameRateOverride> overrides =
             mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -336,8 +334,8 @@
     // If the mode is not the current mode, this means that a
     // mode change is in progress. In that case we shouldn't dispatch an event
     // as it will be dispatched when the current mode changes.
-    if (std::scoped_lock lock(mRefreshRateConfigsLock);
-        mRefreshRateConfigs->getActiveModePtr() != mPolicy.mode) {
+    if (std::scoped_lock lock(mRefreshRateSelectorLock);
+        mRefreshRateSelector->getActiveModePtr() != mPolicy.mode) {
         return;
     }
 
@@ -431,8 +429,8 @@
 
     if (now - last > kIgnoreDelay) {
         const auto refreshRate = [&] {
-            std::scoped_lock lock(mRefreshRateConfigsLock);
-            return mRefreshRateConfigs->getActiveModePtr()->getFps();
+            std::scoped_lock lock(mRefreshRateSelectorLock);
+            return mRefreshRateSelector->getActiveModePtr()->getFps();
         }();
         resyncToHardwareVsync(false, refreshRate);
     }
@@ -493,8 +491,8 @@
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
     {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        if (!mRefreshRateConfigs->canSwitch()) return;
+        std::scoped_lock lock(mRefreshRateSelectorLock);
+        if (!mRefreshRateSelector->canSwitch()) return;
     }
 
     mLayerHistory.record(layer, presentTime, systemTime(), updateType);
@@ -510,26 +508,26 @@
 }
 
 void Scheduler::chooseRefreshRateForContent() {
-    const auto configs = holdRefreshRateConfigs();
-    if (!configs->canSwitch()) return;
+    const auto selectorPtr = holdRefreshRateSelector();
+    if (!selectorPtr->canSwitch()) return;
 
     ATRACE_CALL();
 
-    LayerHistory::Summary summary = mLayerHistory.summarize(*configs, systemTime());
+    LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime());
     applyPolicy(&Policy::contentRequirements, std::move(summary));
 }
 
 void Scheduler::resetIdleTimer() {
-    std::scoped_lock lock(mRefreshRateConfigsLock);
-    mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false);
+    std::scoped_lock lock(mRefreshRateSelectorLock);
+    mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ false);
 }
 
 void Scheduler::onTouchHint() {
     if (mTouchTimer) {
         mTouchTimer->reset();
 
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ true);
+        std::scoped_lock lock(mRefreshRateSelectorLock);
+        mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ true);
     }
 }
 
@@ -555,8 +553,8 @@
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
     const Fps refreshRate = [&] {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getActiveModePtr()->getFps();
+        std::scoped_lock lock(mRefreshRateSelectorLock);
+        return mRefreshRateSelector->getActiveModePtr()->getFps();
     }();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
@@ -623,15 +621,14 @@
 }
 
 bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
-
     // we always update mFrameRateOverridesByContent here
     // supportsFrameRateOverridesByContent will be checked
     // when getting FrameRateOverrides from mFrameRateOverrideMappings
     if (!consideredSignals.idle) {
         const auto frameRateOverrides =
-                refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
-                                                          displayRefreshRate, consideredSignals);
+                holdRefreshRateSelector()->getFrameRateOverrides(mPolicy.contentRequirements,
+                                                                 displayRefreshRate,
+                                                                 consideredSignals);
         return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
     }
     return false;
@@ -645,7 +642,6 @@
     bool refreshRateChanged = false;
     bool frameRateOverridesChanged;
 
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
     {
         std::lock_guard<std::mutex> lock(mPolicyLock);
 
@@ -698,7 +694,7 @@
 auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
     ATRACE_CALL();
 
-    using RankedRefreshRates = RefreshRateConfigs::RankedRefreshRates;
+    using RankedRefreshRates = RefreshRateSelector::RankedRefreshRates;
     display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
 
     // Tallies the score of a refresh rate across `displayCount` displays.
@@ -717,7 +713,7 @@
 
     for (const auto& [id, display] : mDisplays) {
         auto rankedRefreshRates =
-                display->holdRefreshRateConfigs()
+                display->holdRefreshRateSelector()
                         ->getRankedRefreshRates(mPolicy.contentRequirements, globalSignals);
 
         for (const auto& [modePtr, score] : rankedRefreshRates.ranking) {
@@ -793,9 +789,9 @@
     std::lock_guard<std::mutex> lock(mPolicyLock);
     // Make sure the stored mode is up to date.
     if (mPolicy.mode) {
-        const auto configs = holdRefreshRateConfigs();
         const auto ranking =
-                configs->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
+                holdRefreshRateSelector()
+                        ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
                         .ranking;
 
         mPolicy.mode = ranking.front().modePtr;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 33f6126..39c41b9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -45,7 +45,7 @@
 #include "LayerHistory.h"
 #include "MessageQueue.h"
 #include "OneShotTimer.h"
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
 #include "VsyncSchedule.h"
 
 namespace android::scheduler {
@@ -87,7 +87,7 @@
 
 namespace scheduler {
 
-using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+using GlobalSignals = RefreshRateSelector::GlobalSignals;
 
 struct ISchedulerCallback {
     virtual void setVsyncEnabled(bool) = 0;
@@ -107,8 +107,8 @@
     virtual ~Scheduler();
 
     void startTimers();
-    void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs>)
-            EXCLUDES(mRefreshRateConfigsLock);
+    void setRefreshRateSelector(std::shared_ptr<RefreshRateSelector>)
+            EXCLUDES(mRefreshRateSelectorLock);
 
     void registerDisplay(sp<const DisplayDevice>);
     void unregisterDisplay(PhysicalDisplayId);
@@ -163,7 +163,7 @@
     // Otherwise, if hardware vsync is not already enabled then this method will
     // no-op.
     void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate);
-    void resync() EXCLUDES(mRefreshRateConfigsLock);
+    void resync() EXCLUDES(mRefreshRateSelectorLock);
     void forceNextResync() { mLastResyncTime = 0; }
 
     // Passes a vsync sample to VsyncController. periodFlushed will be true if
@@ -175,13 +175,13 @@
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
     void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType)
-            EXCLUDES(mRefreshRateConfigsLock);
+            EXCLUDES(mRefreshRateSelectorLock);
     void setModeChangePending(bool pending);
     void setDefaultFrameRateCompatibility(Layer*);
     void deregisterLayer(Layer*);
 
     // Detects content using layer history, and selects a matching refresh rate.
-    void chooseRefreshRateForContent() EXCLUDES(mRefreshRateConfigsLock);
+    void chooseRefreshRateForContent() EXCLUDES(mRefreshRateSelectorLock);
 
     void resetIdleTimer();
 
@@ -226,11 +226,11 @@
     void setGameModeRefreshRateForUid(FrameRateOverride);
 
     // Retrieves the overridden refresh rate for a given uid.
-    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateConfigsLock);
+    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateSelectorLock);
 
-    nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getActiveModePtr()->getFps().getPeriodNsecs();
+    nsecs_t getVsyncPeriodFromRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) {
+        std::scoped_lock lock(mRefreshRateSelectorLock);
+        return mRefreshRateSelector->getActiveModePtr()->getFps().getPeriodNsecs();
     }
 
     // Returns the framerate of the layer with the given sequence ID
@@ -254,7 +254,7 @@
             EventThread*, EventRegistrationFlags eventRegistration = {});
 
     // Update feature state machine to given state when corresponding timer resets or expires.
-    void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateConfigsLock);
+    void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateSelectorLock);
     void idleTimerCallback(TimerState);
     void touchTimerCallback(TimerState);
     void displayPowerTimerCallback(TimerState);
@@ -293,16 +293,16 @@
 
     bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
 
-    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
+    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateSelectorLock);
 
     android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
-            EXCLUDES(mRefreshRateConfigsLock);
+            EXCLUDES(mRefreshRateSelectorLock);
     android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
-    std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const
-            EXCLUDES(mRefreshRateConfigsLock) {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs;
+    std::shared_ptr<RefreshRateSelector> holdRefreshRateSelector() const
+            EXCLUDES(mRefreshRateSelectorLock) {
+        std::scoped_lock lock(mRefreshRateSelectorLock);
+        return mRefreshRateSelector;
     }
 
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
@@ -359,8 +359,8 @@
         std::optional<ModeChangedParams> cachedModeChangedParams;
     } mPolicy GUARDED_BY(mPolicyLock);
 
-    mutable std::mutex mRefreshRateConfigsLock;
-    std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock);
+    mutable std::mutex mRefreshRateSelectorLock;
+    std::shared_ptr<RefreshRateSelector> mRefreshRateSelector GUARDED_BY(mRefreshRateSelectorLock);
 
     std::mutex mVsyncTimelineLock;
     std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 898e865..0ad4236 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -34,7 +34,7 @@
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
 #include "VSyncPredictor.h"
 
 namespace android::scheduler {
@@ -274,7 +274,7 @@
 
     std::lock_guard lock(mMutex);
     const auto divisor =
-            RefreshRateConfigs::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+            RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
     if (divisor <= 1 || timePoint == 0) {
         return true;
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cfebec7..89d905a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -185,7 +185,7 @@
 using ui::DisplayPrimaries;
 using ui::RenderIntent;
 
-using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController;
+using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
 
 namespace hal = android::hardware::graphics::composer::hal;
 
@@ -602,7 +602,7 @@
 }
 
 renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const {
-    return mCompositionEngine->getRenderEngine();
+    return *mRenderEngine;
 }
 
 compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const {
@@ -763,7 +763,8 @@
     if (auto type = chooseRenderEngineTypeViaSysProp()) {
         builder.setRenderEngineType(type.value());
     }
-    mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build()));
+    mRenderEngine = renderengine::RenderEngine::create(builder.build());
+    mCompositionEngine->setRenderEngine(mRenderEngine.get());
     mMaxRenderTargetSize =
             std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
 
@@ -1031,7 +1032,7 @@
 
     const PhysicalDisplayId displayId = snapshot.displayId();
 
-    info->activeDisplayModeId = display->refreshRateConfigs().getActiveModePtr()->getId().value();
+    info->activeDisplayModeId = display->refreshRateSelector().getActiveModePtr()->getId().value();
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
     info->hdrCapabilities = display->getHdrCapabilities();
 
@@ -1128,11 +1129,11 @@
 
         // Keep the old switching type.
         const bool allowGroupSwitching =
-                display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching;
+                display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching;
 
-        const scheduler::RefreshRateConfigs::DisplayManagerPolicy policy{modeId,
-                                                                         allowGroupSwitching,
-                                                                         {fps, fps}};
+        const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId,
+                                                                          allowGroupSwitching,
+                                                                          {fps, fps}};
 
         return setDesiredDisplayModeSpecsInternal(display, policy);
     });
@@ -1250,7 +1251,7 @@
         // Desired active mode was set, it is different than the mode currently in use, however
         // allowed modes might have changed by the time we process the refresh.
         // Make sure the desired mode is still allowed
-        const auto displayModeAllowed = display->refreshRateConfigs().isModeAllowed(desiredModeId);
+        const auto displayModeAllowed = display->refreshRateSelector().isModeAllowed(desiredModeId);
         if (!displayModeAllowed) {
             clearDesiredActiveModeState(display);
             continue;
@@ -1272,7 +1273,7 @@
             continue;
         }
 
-        display->refreshRateConfigs().onModeChangeInitiated();
+        display->refreshRateSelector().onModeChangeInitiated();
         mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
 
         if (outTimeline.refreshRequired) {
@@ -1580,9 +1581,10 @@
 
     // LayerHandle::getLayer promotes the layer object in a binder thread but we will not destroy
     // the layer here since the caller has a strong ref to the layer's handle.
-    // TODO (b/238781169): replace layer with layer id
-    const wp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle);
-    mRegionSamplingThread->addListener(samplingArea, stopLayer, listener);
+    const sp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle);
+    mRegionSamplingThread->addListener(samplingArea,
+                                       stopLayer ? stopLayer->getSequence() : UNASSIGNED_LAYER_ID,
+                                       listener);
     return NO_ERROR;
 }
 
@@ -2775,21 +2777,21 @@
         const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
                 getKernelIdleTimerProperties(compositionDisplay->getId());
 
-        scheduler::RefreshRateConfigs::Config config =
+        scheduler::RefreshRateSelector::Config config =
                 {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
                  .frameRateMultipleThreshold =
                          base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
                  .idleTimerTimeout = idleTimerTimeoutMs,
                  .kernelIdleTimerController = kernelIdleTimerController};
 
-        creationArgs.refreshRateConfigs =
+        creationArgs.refreshRateSelector =
                 mPhysicalDisplays.get(physical->id)
                         .transform(&PhysicalDisplay::snapshotRef)
                         .transform([&](const display::DisplaySnapshot& snapshot) {
                             return std::make_shared<
-                                    scheduler::RefreshRateConfigs>(snapshot.displayModes(),
-                                                                   creationArgs.activeModeId,
-                                                                   config);
+                                    scheduler::RefreshRateSelector>(snapshot.displayModes(),
+                                                                    creationArgs.activeModeId,
+                                                                    config);
                         })
                         .value_or(nullptr);
 
@@ -2937,7 +2939,7 @@
     if (mScheduler && !display->isVirtual()) {
         // Display modes are reloaded on hotplug reconnect.
         if (display->isPrimary()) {
-            mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
+            mScheduler->setRefreshRateSelector(display->holdRefreshRateSelector());
         }
         mScheduler->registerDisplay(display);
         dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
@@ -3353,7 +3355,7 @@
 
         if (!display) continue;
 
-        if (display->refreshRateConfigs().isModeAllowed(modePtr->getId())) {
+        if (display->refreshRateSelector().isModeAllowed(modePtr->getId())) {
             setDesiredActiveMode(std::move(request));
         } else {
             ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
@@ -3374,7 +3376,7 @@
 void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
     LOG_ALWAYS_FATAL_IF(mScheduler);
 
-    const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr();
+    const auto activeModePtr = display->refreshRateSelector().getActiveModePtr();
     const Fps activeRefreshRate = activeModePtr->getFps();
     mRefreshRateStats =
             std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate,
@@ -3401,13 +3403,13 @@
                                                         static_cast<ISchedulerCallback&>(*this),
                                                         features);
     {
-        auto configs = display->holdRefreshRateConfigs();
-        if (configs->kernelIdleTimerController().has_value()) {
+        auto selectorPtr = display->holdRefreshRateSelector();
+        if (selectorPtr->kernelIdleTimerController()) {
             features |= Feature::kKernelIdleTimer;
         }
 
         mScheduler->createVsyncSchedule(features);
-        mScheduler->setRefreshRateConfigs(std::move(configs));
+        mScheduler->setRefreshRateSelector(std::move(selectorPtr));
         mScheduler->registerDisplay(display);
     }
     setVsyncEnabled(false);
@@ -3640,11 +3642,6 @@
         mCreatedLayers.emplace_back(layer, parent, args.addToRoot);
     }
 
-    // attach this layer to the client
-    if (args.client != nullptr) {
-        args.client->attachLayer(handle, layer);
-    }
-
     setTransactionFlags(eTransactionNeeded);
     return NO_ERROR;
 }
@@ -4650,7 +4647,7 @@
 
     display->setPowerMode(mode);
 
-    const auto refreshRate = display->refreshRateConfigs().getActiveMode().getFps();
+    const auto refreshRate = display->refreshRateSelector().getActiveMode().getFps();
     if (*currentMode == hal::PowerMode::OFF) {
         // Turn on the display
         if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
@@ -5189,7 +5186,7 @@
 
     if (const auto display = getDefaultDisplayDeviceLocked()) {
         std::string fps, xDpi, yDpi;
-        if (const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr()) {
+        if (const auto activeModePtr = display->refreshRateSelector().getActiveModePtr()) {
             fps = to_string(activeModePtr->getFps());
 
             const auto dpi = activeModePtr->getDpi();
@@ -5735,8 +5732,8 @@
                                 // defaultMode. The defaultMode doesn't matter for the override
                                 // policy though, since we set allowGroupSwitching to true, so it's
                                 // not a problem.
-                                scheduler::RefreshRateConfigs::OverridePolicy overridePolicy;
-                                overridePolicy.defaultMode = display->refreshRateConfigs()
+                                scheduler::RefreshRateSelector::OverridePolicy overridePolicy;
+                                overridePolicy.defaultMode = display->refreshRateSelector()
                                                                      .getDisplayManagerPolicy()
                                                                      .defaultMode;
                                 overridePolicy.allowGroupSwitching = true;
@@ -5749,7 +5746,8 @@
                                 const auto display =
                                         FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
                                 return setDesiredDisplayModeSpecsInternal(
-                                        display, scheduler::RefreshRateConfigs::NoOverridePolicy{});
+                                        display,
+                                        scheduler::RefreshRateSelector::NoOverridePolicy{});
                             })
                             .get();
                 }
@@ -5860,7 +5858,7 @@
     if (!updateOverlay) return;
 
     // Update the overlay on the main thread to avoid race conditions with
-    // mRefreshRateConfigs->getActiveMode()
+    // RefreshRateSelector::getActiveMode.
     static_cast<void>(mScheduler->schedule([=] {
         const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
         if (!display) {
@@ -5924,7 +5922,7 @@
 }
 
 void SurfaceFlinger::toggleKernelIdleTimer() {
-    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+    using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
 
     const auto display = getDefaultDisplayDeviceLocked();
     if (!display) {
@@ -5935,12 +5933,12 @@
     // If the support for kernel idle timer is disabled for the active display,
     // don't do anything.
     const std::optional<KernelIdleTimerController> kernelIdleTimerController =
-            display->refreshRateConfigs().kernelIdleTimerController();
+            display->refreshRateSelector().kernelIdleTimerController();
     if (!kernelIdleTimerController.has_value()) {
         return;
     }
 
-    const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction();
+    const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction();
 
     switch (action) {
         case KernelIdleTimerAction::TurnOff:
@@ -5956,7 +5954,7 @@
             if (!mKernelIdleTimerEnabled) {
                 ATRACE_INT("KernelIdleTimer", 1);
                 const std::chrono::milliseconds timeout =
-                        display->refreshRateConfigs().getIdleTimerTimeout();
+                        display->refreshRateSelector().getIdleTimerTimeout();
                 updateKernelIdleTimer(timeout, kernelIdleTimerController.value(),
                                       display->getPhysicalId());
                 mKernelIdleTimerEnabled = true;
@@ -6598,7 +6596,7 @@
 
 status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
         const sp<DisplayDevice>& display,
-        const scheduler::RefreshRateConfigs::PolicyVariant& policy) {
+        const scheduler::RefreshRateSelector::PolicyVariant& policy) {
     const auto displayId = display->getPhysicalId();
 
     Mutex::Autolock lock(mStateLock);
@@ -6608,10 +6606,10 @@
         return NO_ERROR;
     }
 
-    auto& configs = display->refreshRateConfigs();
-    using SetPolicyResult = scheduler::RefreshRateConfigs::SetPolicyResult;
+    auto& selector = display->refreshRateSelector();
+    using SetPolicyResult = scheduler::RefreshRateSelector::SetPolicyResult;
 
-    switch (configs.setPolicy(policy)) {
+    switch (selector.setPolicy(policy)) {
         case SetPolicyResult::Invalid:
             return BAD_VALUE;
         case SetPolicyResult::Unchanged:
@@ -6620,12 +6618,12 @@
             break;
     }
 
-    const scheduler::RefreshRateConfigs::Policy currentPolicy = configs.getCurrentPolicy();
+    const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
-    if (const auto activeModePtr = configs.getActiveModePtr(); displayId == mActiveDisplayId) {
+    if (const auto activeModePtr = selector.getActiveModePtr(); displayId == mActiveDisplayId) {
         mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
         toggleKernelIdleTimer();
     } else {
@@ -6644,7 +6642,7 @@
     ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
           to_string(preferredMode->getFps()).c_str());
 
-    if (!configs.isModeAllowed(preferredModeId)) {
+    if (!selector.isModeAllowed(preferredModeId)) {
         ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
         return INVALID_OPERATION;
     }
@@ -6673,7 +6671,7 @@
             ALOGW("Attempt to set desired display modes for virtual display");
             return INVALID_OPERATION;
         } else {
-            using Policy = scheduler::RefreshRateConfigs::DisplayManagerPolicy;
+            using Policy = scheduler::RefreshRateSelector::DisplayManagerPolicy;
             const Policy policy{DisplayModeId(defaultMode),
                                 allowGroupSwitching,
                                 {Fps::fromValue(primaryRefreshRateMin),
@@ -6712,8 +6710,8 @@
         return INVALID_OPERATION;
     }
 
-    scheduler::RefreshRateConfigs::Policy policy =
-            display->refreshRateConfigs().getDisplayManagerPolicy();
+    scheduler::RefreshRateSelector::Policy policy =
+            display->refreshRateSelector().getDisplayManagerPolicy();
     *outDefaultMode = policy.defaultMode.value();
     *outAllowGroupSwitching = policy.allowGroupSwitching;
     *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue();
@@ -6830,7 +6828,7 @@
 
     if (!getHwComposer().isHeadless()) {
         if (const auto display = getDefaultDisplayDevice()) {
-            maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max;
+            maxRefreshRate = display->refreshRateSelector().getSupportedRefreshRateRange().max;
         }
     }
 
@@ -6845,7 +6843,7 @@
         refreshRate = *frameRateOverride;
     } else if (!getHwComposer().isHeadless()) {
         if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) {
-            refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps();
+            refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps();
         }
     }
 
@@ -6934,7 +6932,7 @@
     activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
     updateInternalDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
-    mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs());
+    mScheduler->setRefreshRateSelector(activeDisplay->holdRefreshRateSelector());
     onActiveDisplaySizeChanged(activeDisplay);
     mActiveDisplayTransformHint = activeDisplay->getTransformHint();
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 85c194b..d290762 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -69,7 +69,7 @@
 #include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/TransactionHandler.h"
 #include "LayerVector.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/RefreshRateStats.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncModulator.h"
@@ -464,7 +464,7 @@
               typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
     void modulateVsync(Handler handler, Args... args) {
         if (const auto config = (*mVsyncModulator.*handler)(args...)) {
-            const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateConfigs();
+            const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateSelector();
             setVsyncConfig(*config, vsyncPeriod);
         }
     }
@@ -633,7 +633,7 @@
     // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
     void toggleKernelIdleTimer() REQUIRES(mStateLock);
 
-    using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController;
+    using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
 
     // Get the controller and timeout that will help decide how the kernel idle timer will be
     // configured and what value to use as the timeout.
@@ -672,8 +672,8 @@
     std::optional<ftl::NonNull<DisplayModePtr>> getPreferredDisplayMode(
             PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock);
 
-    status_t setDesiredDisplayModeSpecsInternal(const sp<DisplayDevice>&,
-                                                const scheduler::RefreshRateConfigs::PolicyVariant&)
+    status_t setDesiredDisplayModeSpecsInternal(
+            const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&)
             EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
 
     void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
@@ -1248,6 +1248,7 @@
     ui::Dataspace mColorSpaceAgnosticDataspace;
     float mDimmingRatio = -1.f;
 
+    std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
     // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
     // any mutex.
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 41edd22..f310c4a 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -52,7 +52,6 @@
 namespace scheduler {
 class VsyncConfiguration;
 class VsyncController;
-class RefreshRateConfigs;
 } // namespace scheduler
 
 namespace frametimeline {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index 14384a7..ce4d18f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -245,6 +245,7 @@
     setDisplayStateLocked();
 
     setTransactionState();
+    mTestableFlinger.flushTransactionQueues();
 
     onTransact(data, size);
 }
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index e555867..94de6e5 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -39,7 +39,7 @@
 #include "NativeWindowSurface.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/MessageQueue.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/VSyncTracker.h"
 #include "Scheduler/VsyncConfiguration.h"
 #include "Scheduler/VsyncController.h"
@@ -218,18 +218,19 @@
 
 class TestableScheduler : public Scheduler, private ICompositor {
 public:
-    TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs> &refreshRateConfigs,
-                      ISchedulerCallback &callback)
+    TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
+                      ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
-                              std::make_unique<android::mock::VSyncTracker>(), refreshRateConfigs,
+                              std::make_unique<android::mock::VSyncTracker>(), selectorPtr,
                               callback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::unique_ptr<VSyncTracker> tracker,
-                      std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback &callback)
+                      std::shared_ptr<RefreshRateSelector> selectorPtr,
+                      ISchedulerCallback& callback)
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-        setRefreshRateConfigs(std::move(configs));
+        setRefreshRateSelector(std::move(selectorPtr));
     }
 
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -241,7 +242,7 @@
 
     auto &mutableLayerHistory() { return mLayerHistory; }
 
-    auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
+    auto refreshRateSelector() { return holdRefreshRateSelector(); }
 
     void replaceTouchTimer(int64_t millis) {
         if (mTouchTimer) {
@@ -310,8 +311,8 @@
     }
 
     std::unique_ptr<scheduler::Scheduler> createScheduler(
-            const std::shared_ptr<scheduler::RefreshRateConfigs> &,
-            scheduler::ISchedulerCallback &) {
+            const std::shared_ptr<scheduler::RefreshRateSelector>&,
+            scheduler::ISchedulerCallback&) {
         return nullptr;
     }
 
@@ -633,7 +634,8 @@
     }
 
     void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
-        mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
+        mFlinger->mRenderEngine = std::move(renderEngine);
+        mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
     }
 
     void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
@@ -660,9 +662,9 @@
             modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
         }
 
-        mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
+        mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
         const auto fps =
-                FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateConfigs->getActiveMode().getFps());
+                FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateSelector->getActiveMode().getFps());
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -671,7 +673,7 @@
                                                               hal::PowerMode::OFF);
 
         mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
-                                                      std::move(vsyncTracker), mRefreshRateConfigs,
+                                                      std::move(vsyncTracker), mRefreshRateSelector,
                                                       *(callback ?: this));
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
@@ -748,6 +750,11 @@
                                              listenerCallbacks, transactionId);
     }
 
+    auto flushTransactionQueues() {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return mFlinger->flushTransactionQueues(VsyncId{0});
+    }
+
     auto onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
         return mFlinger->onTransact(code, data, reply, flags);
     }
@@ -775,8 +782,8 @@
         mutableDrawingState().displays.clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
-        mFlinger->mCompositionEngine->setRenderEngine(
-                std::unique_ptr<renderengine::RenderEngine>());
+        mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
+        mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
     }
 
 private:
@@ -789,7 +796,7 @@
     sp<SurfaceFlinger> mFlinger =
             sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
     scheduler::TestableScheduler *mScheduler = nullptr;
-    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 2614288..f2d008d 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -23,6 +23,7 @@
 
 #include "Scheduler/DispSyncSource.h"
 #include "Scheduler/OneShotTimer.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/VSyncDispatchTimerQueue.h"
 #include "Scheduler/VSyncPredictor.h"
 #include "Scheduler/VSyncReactor.h"
@@ -38,7 +39,7 @@
                                      (72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(),
                                      (120_Hz).getPeriodNsecs()};
 
-constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateConfigs::LayerVoteType>();
+constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>();
 
 constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
                                      PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
@@ -59,7 +60,7 @@
 
 private:
     void fuzzRefreshRateSelection();
-    void fuzzRefreshRateConfigs();
+    void fuzzRefreshRateSelector();
     void fuzzPresentLatencyTracker();
     void fuzzVSyncModulator();
     void fuzzVSyncPredictor();
@@ -238,8 +239,8 @@
         time1 += mFdp.PickValueInArray(kVsyncPeriods);
         time2 += mFdp.PickValueInArray(kVsyncPeriods);
     }
-    historyV1.summarize(*scheduler->refreshRateConfigs(), time1);
-    historyV1.summarize(*scheduler->refreshRateConfigs(), time2);
+    historyV1.summarize(*scheduler->refreshRateSelector(), time1);
+    historyV1.summarize(*scheduler->refreshRateSelector(), time2);
 
     scheduler->createConnection(std::make_unique<android::mock::EventThread>());
 
@@ -327,9 +328,9 @@
     layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>());
 }
 
-void SchedulerFuzzer::fuzzRefreshRateConfigs() {
-    using RefreshRateConfigs = scheduler::RefreshRateConfigs;
-    using LayerRequirement = RefreshRateConfigs::LayerRequirement;
+void SchedulerFuzzer::fuzzRefreshRateSelector() {
+    using RefreshRateSelector = scheduler::RefreshRateSelector;
+    using LayerRequirement = RefreshRateSelector::LayerRequirement;
     using RefreshRateStats = scheduler::RefreshRateStats;
 
     const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1);
@@ -345,48 +346,48 @@
                                                          Fps::fromValue(static_cast<float>(fps))));
     }
 
-    RefreshRateConfigs refreshRateConfigs(displayModes, modeId);
+    RefreshRateSelector refreshRateSelector(displayModes, modeId);
 
-    const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+    const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
     std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
 
-    refreshRateConfigs.getRankedRefreshRates(layers, globalSignals);
+    refreshRateSelector.getRankedRefreshRates(layers, globalSignals);
 
     layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
     layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
     layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>());
     layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values);
     auto frameRateOverrides =
-            refreshRateConfigs.getFrameRateOverrides(layers,
-                                                     Fps::fromValue(
-                                                             mFdp.ConsumeFloatingPoint<float>()),
-                                                     globalSignals);
+            refreshRateSelector.getFrameRateOverrides(layers,
+                                                      Fps::fromValue(
+                                                              mFdp.ConsumeFloatingPoint<float>()),
+                                                      globalSignals);
 
     {
         ftl::FakeGuard guard(kMainThreadContext);
 
-        refreshRateConfigs.setPolicy(
-                RefreshRateConfigs::
+        refreshRateSelector.setPolicy(
+                RefreshRateSelector::
                         DisplayManagerPolicy{modeId,
                                              {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
                                               Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
-        refreshRateConfigs.setPolicy(
-                RefreshRateConfigs::OverridePolicy{modeId,
-                                                   {Fps::fromValue(
-                                                            mFdp.ConsumeFloatingPoint<float>()),
-                                                    Fps::fromValue(
-                                                            mFdp.ConsumeFloatingPoint<float>())}});
-        refreshRateConfigs.setPolicy(RefreshRateConfigs::NoOverridePolicy{});
+        refreshRateSelector.setPolicy(
+                RefreshRateSelector::OverridePolicy{modeId,
+                                                    {Fps::fromValue(
+                                                             mFdp.ConsumeFloatingPoint<float>()),
+                                                     Fps::fromValue(
+                                                             mFdp.ConsumeFloatingPoint<float>())}});
+        refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{});
 
-        refreshRateConfigs.setActiveModeId(modeId);
+        refreshRateSelector.setActiveModeId(modeId);
     }
 
-    RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue(
-                                                           mFdp.ConsumeFloatingPoint<float>()),
-                                                   Fps::fromValue(
-                                                           mFdp.ConsumeFloatingPoint<float>()));
-    RefreshRateConfigs::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
-                                            Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
+    RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue(
+                                                            mFdp.ConsumeFloatingPoint<float>()),
+                                                    Fps::fromValue(
+                                                            mFdp.ConsumeFloatingPoint<float>()));
+    RefreshRateSelector::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+                                             Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
 
     android::mock::TimeStats timeStats;
     RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
@@ -407,7 +408,7 @@
 
 void SchedulerFuzzer::process() {
     fuzzRefreshRateSelection();
-    fuzzRefreshRateConfigs();
+    fuzzRefreshRateSelector();
     fuzzPresentLatencyTracker();
     fuzzVSyncModulator();
     fuzzVSyncPredictor();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 1a49ead..88e32e1 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -27,7 +27,6 @@
 #include "Clock.h"
 #include "Layer.h"
 #include "Scheduler/EventThread.h"
-#include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncTracker.h"
 #include "Scheduler/VsyncModulator.h"
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index d2b5813..5daa398 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -111,7 +111,7 @@
         "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
         "SchedulerTest.cpp",
         "SetFrameRateTest.cpp",
-        "RefreshRateConfigsTest.cpp",
+        "RefreshRateSelectorTest.cpp",
         "RefreshRateSelectionTest.cpp",
         "RefreshRateStatsTest.cpp",
         "RegionSamplingTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 972198c..979924a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -69,11 +69,11 @@
         // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
         // however, for testing only, a stable order is required, therefore we sort the list here.
         // Any tests requiring ordered results must create layers with names.
-        auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now);
+        auto summary = history().summarize(*mScheduler->refreshRateSelector(), now);
         std::sort(summary.begin(), summary.end(),
-                  [](const RefreshRateConfigs::LayerRequirement& a,
-                     const RefreshRateConfigs::LayerRequirement& b) -> bool {
-                      return a.name < b.name;
+                  [](const RefreshRateSelector::LayerRequirement& lhs,
+                     const RefreshRateSelector::LayerRequirement& rhs) -> bool {
+                      return lhs.name < rhs.name;
                   });
         return summary;
     }
@@ -125,16 +125,16 @@
         ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
     }
 
-    std::shared_ptr<RefreshRateConfigs> mConfigs =
-            std::make_shared<RefreshRateConfigs>(makeModes(createDisplayMode(DisplayModeId(0),
-                                                                             LO_FPS),
-                                                           createDisplayMode(DisplayModeId(1),
-                                                                             HI_FPS)),
-                                                 DisplayModeId(0));
+    std::shared_ptr<RefreshRateSelector> mSelector =
+            std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
+                                                                              LO_FPS),
+                                                            createDisplayMode(DisplayModeId(1),
+                                                                              HI_FPS)),
+                                                  DisplayModeId(0));
 
     mock::SchedulerCallback mSchedulerCallback;
 
-    TestableScheduler* mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
+    TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
 
     TestableSurfaceFlinger mFlinger;
 };
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
similarity index 61%
rename from services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
rename to services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 924c5be..e7ae53c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -28,7 +28,7 @@
 
 #include "DisplayHardware/HWC2.h"
 #include "FpsOps.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
 
 using namespace std::chrono_literals;
@@ -37,26 +37,26 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
-using LayerRequirement = RefreshRateConfigs::LayerRequirement;
-using LayerVoteType = RefreshRateConfigs::LayerVoteType;
-using SetPolicyResult = RefreshRateConfigs::SetPolicyResult;
+using LayerRequirement = RefreshRateSelector::LayerRequirement;
+using LayerVoteType = RefreshRateSelector::LayerVoteType;
+using SetPolicyResult = RefreshRateSelector::SetPolicyResult;
 
 using mock::createDisplayMode;
 
-struct TestableRefreshRateConfigs : RefreshRateConfigs {
-    using RefreshRateConfigs::RefreshRateOrder;
-    using RefreshRateConfigs::RefreshRateRanking;
+struct TestableRefreshRateSelector : RefreshRateSelector {
+    using RefreshRateSelector::RefreshRateOrder;
+    using RefreshRateSelector::RefreshRateRanking;
 
-    using RefreshRateConfigs::RefreshRateConfigs;
+    using RefreshRateSelector::RefreshRateSelector;
 
     void setActiveModeId(DisplayModeId modeId) {
         ftl::FakeGuard guard(kMainThreadContext);
-        return RefreshRateConfigs::setActiveModeId(modeId);
+        return RefreshRateSelector::setActiveModeId(modeId);
     }
 
     const DisplayMode& getActiveMode() const {
         ftl::FakeGuard guard(kMainThreadContext);
-        return RefreshRateConfigs::getActiveMode();
+        return RefreshRateSelector::getActiveMode();
     }
 
     DisplayModePtr getMinSupportedRefreshRate() const {
@@ -82,17 +82,17 @@
     RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
                                         RefreshRateOrder refreshRateOrder) const {
         std::lock_guard lock(mLock);
-        return RefreshRateConfigs::rankRefreshRates(anchorGroupOpt, refreshRateOrder);
+        return RefreshRateSelector::rankRefreshRates(anchorGroupOpt, refreshRateOrder);
     }
 
     const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
 
-    using RefreshRateConfigs::GetRankedRefreshRatesCache;
+    using RefreshRateSelector::GetRankedRefreshRatesCache;
     auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; }
 
     auto getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
                                GlobalSignals signals) const {
-        const auto result = RefreshRateConfigs::getRankedRefreshRates(layers, signals);
+        const auto result = RefreshRateSelector::getRankedRefreshRates(layers, signals);
 
         EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
                                    ScoredRefreshRate::DescendingScore{}));
@@ -113,7 +113,7 @@
 
     SetPolicyResult setPolicy(const PolicyVariant& policy) {
         ftl::FakeGuard guard(kMainThreadContext);
-        return RefreshRateConfigs::setPolicy(policy);
+        return RefreshRateSelector::setPolicy(policy);
     }
 
     SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) {
@@ -121,12 +121,12 @@
     }
 };
 
-class RefreshRateConfigsTest : public testing::Test {
+class RefreshRateSelectorTest : public testing::Test {
 protected:
-    using RefreshRateOrder = TestableRefreshRateConfigs::RefreshRateOrder;
+    using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder;
 
-    RefreshRateConfigsTest();
-    ~RefreshRateConfigsTest();
+    RefreshRateSelectorTest();
+    ~RefreshRateSelectorTest();
 
     static constexpr DisplayModeId kModeId60{0};
     static constexpr DisplayModeId kModeId90{1};
@@ -186,13 +186,13 @@
                       kMode60Frac);
 };
 
-RefreshRateConfigsTest::RefreshRateConfigsTest() {
+RefreshRateSelectorTest::RefreshRateSelectorTest() {
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-RefreshRateConfigsTest::~RefreshRateConfigsTest() {
+RefreshRateSelectorTest::~RefreshRateSelectorTest() {
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
@@ -200,358 +200,359 @@
 
 namespace {
 
-TEST_F(RefreshRateConfigsTest, oneMode_canSwitch) {
-    RefreshRateConfigs configs(kModes_60, kModeId60);
-    EXPECT_FALSE(configs.canSwitch());
+TEST_F(RefreshRateSelectorTest, oneMode_canSwitch) {
+    RefreshRateSelector selector(kModes_60, kModeId60);
+    EXPECT_FALSE(selector.canSwitch());
 }
 
-TEST_F(RefreshRateConfigsTest, invalidPolicy) {
-    TestableRefreshRateConfigs configs(kModes_60, kModeId60);
+TEST_F(RefreshRateSelectorTest, invalidPolicy) {
+    TestableRefreshRateSelector selector(kModes_60, kModeId60);
 
     EXPECT_EQ(SetPolicyResult::Invalid,
-              configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
+              selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
     EXPECT_EQ(SetPolicyResult::Invalid,
-              configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
 }
 
-TEST_F(RefreshRateConfigsTest, unchangedPolicy) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, unchangedPolicy) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
 
     EXPECT_EQ(SetPolicyResult::Unchanged,
-              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
 
     // Override to the same policy.
     EXPECT_EQ(SetPolicyResult::Unchanged,
-              configs.setPolicy(RefreshRateConfigs::OverridePolicy{kModeId90, {60_Hz, 90_Hz}}));
+              selector.setPolicy(RefreshRateSelector::OverridePolicy{kModeId90, {60_Hz, 90_Hz}}));
 
     // Clear override to restore DisplayManagerPolicy.
     EXPECT_EQ(SetPolicyResult::Unchanged,
-              configs.setPolicy(RefreshRateConfigs::NoOverridePolicy{}));
+              selector.setPolicy(RefreshRateSelector::NoOverridePolicy{}));
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
 }
 
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
-    const auto minRate = configs.getMinSupportedRefreshRate();
-    const auto performanceRate = configs.getMaxSupportedRefreshRate();
+    const auto minRate = selector.getMinSupportedRefreshRate();
+    const auto performanceRate = selector.getMaxSupportedRefreshRate();
 
     EXPECT_EQ(kMode60, minRate);
     EXPECT_EQ(kMode90, performanceRate);
 
-    const auto minRateByPolicy = configs.getMinRefreshRateByPolicy();
-    const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy();
+    const auto minRateByPolicy = selector.getMinRefreshRateByPolicy();
+    const auto performanceRateByPolicy = selector.getMaxRefreshRateByPolicy();
 
     EXPECT_EQ(minRateByPolicy, minRate);
     EXPECT_EQ(performanceRateByPolicy, performanceRate);
 }
 
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentGroups) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
-    const auto minRate = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate = configs.getMaxSupportedRefreshRate();
-    const auto minRate60 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
+    const auto minRate = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate = selector.getMaxSupportedRefreshRate();
+    const auto minRate60 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
 
     EXPECT_EQ(kMode60, minRate);
     EXPECT_EQ(kMode60, minRate60);
     EXPECT_EQ(kMode60, performanceRate60);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
-    configs.setActiveModeId(kModeId90);
+              selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+    selector.setActiveModeId(kModeId90);
 
-    const auto minRate90 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
+    const auto minRate90 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
 
     EXPECT_EQ(kMode90_G1, performanceRate);
     EXPECT_EQ(kMode90_G1, minRate90);
     EXPECT_EQ(kMode90_G1, performanceRate90);
 }
 
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
-    TestableRefreshRateConfigs configs(kModes_60_90_4K, kModeId60);
+TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
+    TestableRefreshRateSelector selector(kModes_60_90_4K, kModeId60);
 
-    const auto minRate = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate = configs.getMaxSupportedRefreshRate();
-    const auto minRate60 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
+    const auto minRate = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate = selector.getMaxSupportedRefreshRate();
+    const auto minRate60 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
 
     EXPECT_EQ(kMode60, minRate);
     EXPECT_EQ(kMode60, minRate60);
     EXPECT_EQ(kMode60, performanceRate60);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
-    configs.setActiveModeId(kModeId90);
+              selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+    selector.setActiveModeId(kModeId90);
 
-    const auto minRate90 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
+    const auto minRate90 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
 
     EXPECT_EQ(kMode90_4K, performanceRate);
     EXPECT_EQ(kMode90_4K, minRate90);
     EXPECT_EQ(kMode90_4K, performanceRate90);
 }
 
-TEST_F(RefreshRateConfigsTest, twoModes_policyChange) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, twoModes_policyChange) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
-    const auto minRate = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate = configs.getMaxRefreshRateByPolicy();
+    const auto minRate = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate = selector.getMaxRefreshRateByPolicy();
 
     EXPECT_EQ(kMode60, minRate);
     EXPECT_EQ(kMode90, performanceRate);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
 
-    const auto minRate60 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
+    const auto minRate60 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
 
     EXPECT_EQ(kMode60, minRate60);
     EXPECT_EQ(kMode60, performanceRate60);
 }
 
-TEST_F(RefreshRateConfigsTest, twoModes_getActiveMode) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, twoModes_getActiveMode) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
     {
-        const auto& mode = configs.getActiveMode();
+        const auto& mode = selector.getActiveMode();
         EXPECT_EQ(mode.getId(), kModeId60);
     }
 
-    configs.setActiveModeId(kModeId90);
+    selector.setActiveModeId(kModeId90);
     {
-        const auto& mode = configs.getActiveMode();
+        const auto& mode = selector.getActiveMode();
         EXPECT_EQ(mode.getId(), kModeId90);
     }
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
     {
-        const auto& mode = configs.getActiveMode();
+        const auto& mode = selector.getActiveMode();
         EXPECT_EQ(mode.getId(), kModeId90);
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_noLayers) {
     {
-        TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId72);
+        TestableRefreshRateSelector selector(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, configs.getBestRefreshRate());
+        EXPECT_EQ(kMode90, selector.getBestRefreshRate());
 
         EXPECT_EQ(SetPolicyResult::Changed,
-                  configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
-        EXPECT_EQ(kMode60, configs.getBestRefreshRate());
+                  selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+        EXPECT_EQ(kMode60, selector.getBestRefreshRate());
     }
     {
         // We select max even when this will cause a non-seamless switch.
-        TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+        TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
         constexpr bool kAllowGroupSwitching = true;
         EXPECT_EQ(SetPolicyResult::Changed,
-                  configs.setDisplayManagerPolicy(
+                  selector.setDisplayManagerPolicy(
                           {kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}));
-        EXPECT_EQ(kMode90_G1, configs.getBestRefreshRate());
+        EXPECT_EQ(kMode90_G1, selector.getBestRefreshRate());
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId72);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId72);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].vote = LayerVoteType::ExplicitExact;
     layers[0].desiredRefreshRate = 120_Hz;
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+              selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_90) {
+    TestableRefreshRateSelector selector(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, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.name = "";
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90});
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_multipleThreshold_60_90) {
+    TestableRefreshRateSelector selector(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, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
-    TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_72_90) {
+    TestableRefreshRateSelector selector(kModes_60_72_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90_120) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -561,23 +562,23 @@
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -589,7 +590,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -597,7 +598,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -605,7 +606,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -613,7 +614,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -621,7 +622,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -629,7 +630,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
@@ -637,7 +638,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -645,7 +646,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -653,12 +654,12 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
-                                       {.frameRateMultipleThreshold = 120});
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
+    TestableRefreshRateSelector selector(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];
@@ -671,7 +672,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -679,7 +680,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -687,7 +688,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -695,7 +696,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -703,7 +704,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -711,7 +712,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
@@ -719,7 +720,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -727,7 +728,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -735,14 +736,14 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.name = "24Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -750,7 +751,7 @@
     lr2.desiredRefreshRate = 120_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "120Hz ExplicitDefault";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -758,7 +759,7 @@
     lr2.desiredRefreshRate = 120_Hz;
     lr2.vote = LayerVoteType::ExplicitExact;
     lr2.name = "120Hz ExplicitExact";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 10_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -766,7 +767,7 @@
     lr2.desiredRefreshRate = 120_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "120Hz ExplicitExact";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 30_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -777,86 +778,86 @@
     lr3.vote = LayerVoteType::Heuristic;
     lr3.desiredRefreshRate = 120_Hz;
     lr3.name = "120Hz Heuristic";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
-    TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60) {
+    TestableRefreshRateSelector selector(kModes_30_60, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90) {
+    TestableRefreshRateSelector selector(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, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.name = "24Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
-    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_PriorityTest) {
+    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -864,43 +865,43 @@
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 15_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 30_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -908,15 +909,15 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = configs.getBestRefreshRate(layers);
+        const auto mode = selector.getBestRefreshRate(layers);
         EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
                                  << to_string(mode->getFps());
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
-    TestableRefreshRateConfigs configs(kModes_60_120, kModeId60,
-                                       {.frameRateMultipleThreshold = 120});
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
+    TestableRefreshRateSelector selector(kModes_60_120, kModeId60,
+                                         {.frameRateMultipleThreshold = 120});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -924,14 +925,14 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = configs.getBestRefreshRate(layers);
+        const auto mode = selector.getBestRefreshRate(layers);
         EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
                                  << to_string(mode->getFps());
     }
 }
 
-TEST_F(RefreshRateConfigsTest, twoModes_getBestRefreshRate_Explicit) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, twoModes_getBestRefreshRate_Explicit) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -941,23 +942,23 @@
     lr1.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_75HzContent) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -965,14 +966,14 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = configs.getBestRefreshRate(layers, {});
+        const auto mode = selector.getBestRefreshRate(layers, {});
         EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
                                  << to_string(mode->getFps());
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -984,7 +985,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
@@ -992,14 +993,14 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30_Hz;
@@ -1007,18 +1008,18 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30_Hz;
     lr1.name = "30Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
     auto& lr1 = layers[0];
@@ -1029,28 +1030,28 @@
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     // The other layer starts to provide buffers
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1059,16 +1060,16 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicy) {
+TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
     // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
     // different group.
-    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
+    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
 
-    const auto refreshRates = configs.rankRefreshRates(configs.getActiveMode().getGroup(),
-                                                       RefreshRateOrder::Descending);
+    const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
+                                                        RefreshRateOrder::Descending);
 
     const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
@@ -1080,13 +1081,13 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicy) {
+TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
     // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
     // different group.
-    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
+    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
 
-    const auto refreshRates = configs.rankRefreshRates(configs.getActiveMode().getGroup(),
-                                                       RefreshRateOrder::Ascending);
+    const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
+                                                        RefreshRateOrder::Ascending);
 
     const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
@@ -1098,16 +1099,16 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
+TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
     // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
     // different group.
-    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
+    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
 
     const auto refreshRates =
-            configs.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
+            selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
 
     const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
@@ -1119,16 +1120,16 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
+TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
     // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
     // different group.
-    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
+    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
 
-    const auto refreshRates =
-            configs.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Descending);
+    const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt,
+                                                        RefreshRateOrder::Descending);
 
     const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
@@ -1140,10 +1141,10 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, powerOnImminentConsidered) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
-    auto [refreshRates, signals] = configs.getRankedRefreshRates({}, {});
+    auto [refreshRates, signals] = selector.getRankedRefreshRates({}, {});
     EXPECT_FALSE(signals.powerOnImminent);
 
     std::array expectedRefreshRates = {kMode90, kMode60};
@@ -1156,7 +1157,7 @@
     }
 
     std::tie(refreshRates, signals) =
-            configs.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true});
+            selector.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true});
     EXPECT_TRUE(signals.powerOnImminent);
 
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
@@ -1174,7 +1175,7 @@
     lr1.name = "60Hz ExplicitExactOrMultiple";
 
     std::tie(refreshRates, signals) =
-            configs.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true});
+            selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true});
     EXPECT_TRUE(signals.powerOnImminent);
 
     ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
@@ -1186,7 +1187,7 @@
     }
 
     std::tie(refreshRates, signals) =
-            configs.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
+            selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
     EXPECT_FALSE(signals.powerOnImminent);
 
     expectedRefreshRates = {kMode60, kMode90};
@@ -1199,13 +1200,13 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest, touchConsidered) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, touchConsidered) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
-    auto [_, signals] = configs.getRankedRefreshRates({}, {});
+    auto [_, signals] = selector.getRankedRefreshRates({}, {});
     EXPECT_FALSE(signals.touch);
 
-    std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair({}, {.touch = true});
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
@@ -1218,7 +1219,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true});
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -1227,7 +1228,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true});
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
     EXPECT_FALSE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1236,7 +1237,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true});
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
     EXPECT_TRUE(signals.touch);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -1245,12 +1246,12 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getRankedRefreshRatesAsPair(layers, {.touch = true});
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
     EXPECT_FALSE(signals.touch);
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
-    TestableRefreshRateConfigs configs(kModes_60_90_72_120, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitDefault) {
+    TestableRefreshRateSelector selector(kModes_60_90_72_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1282,57 +1283,57 @@
         ss << "ExplicitDefault " << desired;
         lr.name = ss.str();
 
-        EXPECT_EQ(expected, configs.getBestRefreshRate(layers)->getFps());
+        EXPECT_EQ(expected, selector.getBestRefreshRate(layers)->getFps());
     }
 }
 
-TEST_F(RefreshRateConfigsTest,
+TEST_F(RefreshRateSelectorTest,
        getBestRefreshRate_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
     {
-        TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
+        TestableRefreshRateSelector selector(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, configs.getBestRefreshRate(layers)->getId());
+        EXPECT_EQ(kModeId24, selector.getBestRefreshRate(layers)->getId());
     }
 
     // Test that 24 will choose 23.976 if 24 is not supported
     {
-        TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
+        TestableRefreshRateSelector selector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
+                                                       kMode60, kMode60Frac),
+                                             kModeId60);
 
         lr.desiredRefreshRate = 24_Hz;
         lr.name = "ExplicitExactOrMultiple 24 Hz";
-        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
+        EXPECT_EQ(kModeId24Frac, selector.getBestRefreshRate(layers)->getId());
     }
 
     // Test that 29.97 will prefer 59.94 over 60 and 30
     {
-        TestableRefreshRateConfigs configs(makeModes(kMode24, kMode24Frac, kMode25, kMode30,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
+        TestableRefreshRateSelector selector(makeModes(kMode24, kMode24Frac, kMode25, kMode30,
+                                                       kMode60, kMode60Frac),
+                                             kModeId60);
 
         lr.desiredRefreshRate = 29.97_Hz;
         lr.name = "ExplicitExactOrMultiple 29.97 Hz";
-        EXPECT_EQ(kModeId60Frac, configs.getBestRefreshRate(layers)->getId());
+        EXPECT_EQ(kModeId60Frac, selector.getBestRefreshRate(layers)->getId());
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_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
     {
-        TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60);
+        TestableRefreshRateSelector selector(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;
@@ -1341,17 +1342,17 @@
             ss << "ExplicitExact " << desired;
             lr.name = ss.str();
 
-            EXPECT_EQ(lr.desiredRefreshRate, configs.getBestRefreshRate(layers)->getFps());
+            EXPECT_EQ(lr.desiredRefreshRate, selector.getBestRefreshRate(layers)->getFps());
         }
     }
 }
 
-TEST_F(RefreshRateConfigsTest,
+TEST_F(RefreshRateSelectorTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1362,18 +1363,18 @@
     lr.focused = true;
 
     const auto [mode, signals] =
-            configs.getRankedRefreshRates(layers, {.touch = true, .idle = true});
+            selector.getRankedRefreshRates(layers, {.touch = true, .idle = true});
 
     EXPECT_EQ(mode.begin()->modePtr, kMode60);
     EXPECT_FALSE(signals.touch);
 }
 
-TEST_F(RefreshRateConfigsTest,
+TEST_F(RefreshRateSelectorTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1382,11 +1383,11 @@
     lr.desiredRefreshRate = 90_Hz;
     lr.name = "90Hz ExplicitDefault";
     lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.idle = true}));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.idle = true}));
 }
 
-TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f},
                                             {.weight = 1.f},
@@ -1420,7 +1421,7 @@
     lr5.focused = true;
 
     std::array expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
-    auto actualRanking = configs.getRankedRefreshRates(layers, {}).ranking;
+    auto actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
 
     ASSERT_EQ(expectedRanking.size(), actualRanking.size());
 
@@ -1446,7 +1447,7 @@
     lr5.name = "120Hz";
 
     expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
-    actualRanking = configs.getRankedRefreshRates(layers, {}).ranking;
+    actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
 
     ASSERT_EQ(expectedRanking.size(), actualRanking.size());
 
@@ -1470,7 +1471,7 @@
     lr5.name = "72Hz";
 
     expectedRanking = {kMode30, kMode60, kMode90, kMode120, kMode72};
-    actualRanking = configs.getRankedRefreshRates(layers, {}).ranking;
+    actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
 
     ASSERT_EQ(expectedRanking.size(), actualRanking.size());
 
@@ -1497,7 +1498,7 @@
     lr5.name = "120Hz-2";
 
     expectedRanking = {kMode90, kMode60, kMode120, kMode72, kMode30};
-    actualRanking = configs.getRankedRefreshRates(layers, {}).ranking;
+    actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
 
     ASSERT_EQ(expectedRanking.size(), actualRanking.size());
 
@@ -1508,14 +1509,14 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest,
+TEST_F(RefreshRateSelectorTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
-    const auto [ranking, signals] = configs.getRankedRefreshRates({}, {});
+    const auto [ranking, signals] = selector.getRankedRefreshRates({}, {});
     EXPECT_EQ(ranking.front().modePtr, kMode90);
     EXPECT_FALSE(signals.touch);
 
@@ -1526,50 +1527,50 @@
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitExactOrMultiple";
     lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::ExplicitDefault;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitDefault";
     lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Heuristic;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
     lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Max";
     lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Min;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Min";
     lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
     // The default policy doesn't allow group switching. Verify that no
     // group switches are performed.
@@ -1581,16 +1582,16 @@
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1599,16 +1600,16 @@
     layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
     // Verify that we won't change the group if seamless switch is required.
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1618,18 +1619,18 @@
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    configs.setActiveModeId(kModeId90);
+    selector.setActiveModeId(kModeId90);
 
     // Verify that we won't do a seamless switch if we request the same mode as the default
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1639,21 +1640,21 @@
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    configs.setActiveModeId(kModeId90);
+    selector.setActiveModeId(kModeId90);
 
-    // Verify that if the current config is in another group and there are no layers with
-    // seamlessness=SeamedAndSeamless we'll go back to the default group.
+    // Verify that if the active mode is in another group and there are no layers with
+    // Seamlessness::SeamedAndSeamless, we should switch back to the default group.
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1663,21 +1664,21 @@
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    configs.setActiveModeId(kModeId90);
+    selector.setActiveModeId(kModeId90);
 
-    // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
-    // seamlessness=OnlySeamless can't change the mode group.
+    // If there's a layer with Seamlessness::SeamedAndSeamless, another layer with
+    // Seamlessness::OnlySeamless can't change the mode group.
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].vote = LayerVoteType::ExplicitDefault;
     layers[0].desiredRefreshRate = 60_Hz;
@@ -1692,21 +1693,21 @@
     layers[1].name = "90Hz ExplicitDefault";
     layers[1].focused = false;
 
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    configs.setActiveModeId(kModeId90);
+    selector.setActiveModeId(kModeId90);
 
-    // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
-    // seamlessness=Default can't change the mode group back to the group of the default
+    // If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with
+    // Seamlessness::Default can't change the mode group back to the group of the default
     // mode.
     // For example, this may happen when a video playback requests and gets a seamed switch,
     // but another layer (with default seamlessness) starts animating. The animating layer
@@ -1725,21 +1726,21 @@
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
+TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    configs.setActiveModeId(kModeId90);
+    selector.setActiveModeId(kModeId90);
 
-    // Layer with seamlessness=Default can change the mode group if there's a not
-    // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
+    // Layer with Seamlessness::Default can change the mode group if there's an
+    // unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens
     // when in split screen mode the user switches between the two visible applications.
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].seamlessness = Seamlessness::Default;
@@ -1755,17 +1756,17 @@
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
-    TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
+TEST_F(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
+    TestableRefreshRateSelector selector(kModes_30_60, kModeId60);
 
     // Allow group switching.
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1775,20 +1776,20 @@
     layer.name = "60Hz ExplicitExactOrMultiple";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
 
-    configs.setActiveModeId(kModeId120);
-    EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers)->getId());
+    selector.setActiveModeId(kModeId120);
+    EXPECT_EQ(kModeId120, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
-    TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60);
+TEST_F(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+    TestableRefreshRateSelector selector(kModes_25_30_50_60, kModeId60);
 
     // Allow group switching.
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
                                              .vote = LayerVoteType::ExplicitDefault,
@@ -1803,33 +1804,33 @@
                                              .weight = 1.f,
                                              .focused = true}};
 
-    EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId50, selector.getBestRefreshRate(layers)->getId());
 
     auto& seamedLayer = layers[0];
     seamedLayer.desiredRefreshRate = 30_Hz;
     seamedLayer.name = "30Hz ExplicitDefault";
-    configs.setActiveModeId(kModeId30);
+    selector.setActiveModeId(kModeId30);
 
-    EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId25, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90);
+TEST_F(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
+    TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId90);
 
     // Allow group switching.
-    RefreshRateConfigs::DisplayManagerPolicy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {
             {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
 
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
+    EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
-    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
+    TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
@@ -1839,19 +1840,19 @@
         bool focused = true;
     };
 
-    // Return the config ID from calling getBestRefreshRate() for a single layer with the
-    // given voteType and fps.
-    auto getFrameRate = [&](LayerVoteType voteType, Fps fps, Args args = {}) -> DisplayModeId {
+    // Returns the mode selected by getBestRefreshRate 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 configs.getBestRefreshRate(layers, {.touch = args.touch})->getId();
+        return selector.getBestRefreshRate(layers, {.touch = args.touch})->getId();
     };
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}));
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate()->getId());
+    EXPECT_EQ(kModeId60, selector.getBestRefreshRate()->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));
@@ -1859,7 +1860,7 @@
     EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
 
-    // Unfocused layers are not allowed to override primary config.
+    // Unfocused layers are not allowed to override primary range.
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
     EXPECT_EQ(kModeId60,
               getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false}));
@@ -1874,7 +1875,7 @@
               getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}));
 
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
@@ -1884,8 +1885,8 @@
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
 }
 
-TEST_F(RefreshRateConfigsTest, idle) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, idle) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
@@ -1895,7 +1896,7 @@
         layers[0].desiredRefreshRate = 90_Hz;
 
         const auto [ranking, signals] =
-                configs.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true});
+                selector.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true});
 
         // Refresh rate will be chosen by either touch state or idle state.
         EXPECT_EQ(!touchActive, signals.idle);
@@ -1903,7 +1904,7 @@
     };
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
     // Idle should be lower priority than touch boost.
     {
@@ -1918,10 +1919,10 @@
     }
 
     // With no layers, idle should still be lower priority than touch boost.
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true})->getId());
+    EXPECT_EQ(kModeId90, selector.getBestRefreshRate({}, {.touch = true, .idle = true})->getId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
-    configs.setActiveModeId(kModeId90);
+    selector.setActiveModeId(kModeId90);
 
     {
         constexpr bool kTouchActive = false;
@@ -1934,15 +1935,15 @@
                   getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
     }
 
-    // Idle should be applied rather than the current config when there are no layers.
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true})->getId());
+    // Idle should be applied rather than the active mode when there are no layers.
+    EXPECT_EQ(kModeId60, selector.getBestRefreshRate({}, {.idle = true})->getId());
 }
 
-TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, findClosestKnownFrameRate) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
-        const auto knownFrameRate = configs.findClosestKnownFrameRate(Fps::fromValue(fps));
+        const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps));
         const Fps expectedFrameRate = [fps] {
             if (fps < 26.91f) return 24_Hz;
             if (fps < 37.51f) return 30_Hz;
@@ -1956,8 +1957,8 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_KnownFrameRate) {
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
     struct Expectation {
         Fps fps;
@@ -1970,7 +1971,7 @@
     };
 
     // Make sure the test tests all the known frame rate
-    const auto& knownFrameRates = configs.knownFrameRates();
+    const auto& knownFrameRates = selector.knownFrameRates();
     const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(),
                                   knownFrameRatesExpectations.begin(),
                                   [](Fps fps, const Expectation& expected) {
@@ -1984,12 +1985,12 @@
 
     for (const auto& [fps, mode] : knownFrameRatesExpectations) {
         layer.desiredRefreshRate = fps;
-        EXPECT_EQ(mode, configs.getBestRefreshRate(layers));
+        EXPECT_EQ(mode, selector.getBestRefreshRate(layers));
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
@@ -2003,26 +2004,26 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers, {.touch = true}));
+    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, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
-                                       {.enableFrameRateOverride = true});
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60,
+                                         {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
@@ -2036,58 +2037,59 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
     explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ReadsCache) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
 
-    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+    using GlobalSignals = RefreshRateSelector::GlobalSignals;
     const auto args = std::make_pair(std::vector<LayerRequirement>{},
                                      GlobalSignals{.touch = true, .idle = true});
 
-    const RefreshRateConfigs::RankedRefreshRates result = {{RefreshRateConfigs::ScoredRefreshRate{
-                                                                   kMode90}},
-                                                           {.touch = true}};
+    const RefreshRateSelector::RankedRefreshRates result = {{RefreshRateSelector::ScoredRefreshRate{
+                                                                    kMode90}},
+                                                            {.touch = true}};
 
-    configs.mutableGetRankedRefreshRatesCache() = {args, result};
+    selector.mutableGetRankedRefreshRatesCache() = {args, result};
 
-    EXPECT_EQ(result, configs.getRankedRefreshRates(args.first, args.second));
+    EXPECT_EQ(result, selector.getRankedRefreshRates(args.first, args.second));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_WritesCache) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
 
-    EXPECT_FALSE(configs.mutableGetRankedRefreshRatesCache());
+    EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
-    RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true};
+    RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
 
-    const auto result = configs.getRankedRefreshRates(layers, globalSignals);
+    const auto result = selector.getRankedRefreshRates(layers, globalSignals);
 
-    const auto& cache = configs.mutableGetRankedRefreshRatesCache();
+    const auto& cache = selector.mutableGetRankedRefreshRatesCache();
     ASSERT_TRUE(cache);
 
     EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
     EXPECT_EQ(cache->result, result);
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) {
-    TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, {.enableFrameRateOverride = true});
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactTouchBoost) {
+    TestableRefreshRateSelector selector(kModes_60_120, kModeId60,
+                                         {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
     auto& explicitExactLayer = layers[0];
@@ -2101,18 +2103,18 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
 
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers, {.touch = true}));
 }
 
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
-    TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60,
-                                       {.enableFrameRateOverride = true});
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
+    TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60,
+                                         {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
     auto& explicitDefaultLayer = layers[0];
@@ -2126,11 +2128,11 @@
     explicitDefaultLayer.name = "ExplicitDefault";
     explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
 
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
 }
 
 // b/190578904
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) {
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withCloseRefreshRates) {
     constexpr int kMinRefreshRate = 10;
     constexpr int kMaxRefreshRate = 240;
 
@@ -2142,14 +2144,14 @@
                                                    Fps::fromValue(static_cast<float>(fps))));
     }
 
-    const TestableRefreshRateConfigs configs(std::move(displayModes),
-                                             DisplayModeId(kMinRefreshRate));
+    const TestableRefreshRateSelector selector(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(), configs.getBestRefreshRate(layers)->getFps().getIntValue())
+        EXPECT_EQ(fps.getIntValue(), selector.getBestRefreshRate(layers)->getFps().getIntValue())
                 << "Failed for " << ftl::enum_string(vote);
     };
 
@@ -2163,15 +2165,15 @@
 }
 
 // b/190578904
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
+TEST_F(RefreshRateSelectorTest, getBestRefreshRate_conflictingVotes) {
     constexpr DisplayModeId kActiveModeId{0};
     DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz),
                                           createDisplayMode(DisplayModeId(1), 53_Hz),
                                           createDisplayMode(DisplayModeId(2), 55_Hz),
                                           createDisplayMode(DisplayModeId(3), 60_Hz));
 
-    const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
-    const TestableRefreshRateConfigs configs(std::move(displayModes), kActiveModeId);
+    const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
+    const TestableRefreshRateSelector selector(std::move(displayModes), kActiveModeId);
 
     const std::vector<LayerRequirement> layers = {
             {
@@ -2188,133 +2190,125 @@
             },
     };
 
-    EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals)->getFps());
+    EXPECT_EQ(53_Hz, selector.getBestRefreshRate(layers, globalSignals)->getFps());
 }
 
-TEST_F(RefreshRateConfigsTest, modeComparison) {
+TEST_F(RefreshRateSelectorTest, modeComparison) {
     EXPECT_LT(kMode60->getFps(), kMode90->getFps());
     EXPECT_GE(kMode60->getFps(), kMode60->getFps());
     EXPECT_GE(kMode90->getFps(), kMode90->getFps());
 }
 
-TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
-    using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
+TEST_F(RefreshRateSelectorTest, testKernelIdleTimerAction) {
+    using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
 
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
+    TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
 
-    // setPolicy(60, 90), current 90Hz => TurnOn.
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
 
-    // setPolicy(60, 90), current 60Hz => TurnOn.
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
 
-    // setPolicy(60, 60), current 60Hz => TurnOff
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
 
-    // setPolicy(90, 90), current 90Hz => TurnOff.
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
+              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
 }
 
-TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
-    using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
+TEST_F(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
+    using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
 
-    TestableRefreshRateConfigs configs(kModes_60_120, kModeId120);
+    TestableRefreshRateSelector selector(kModes_60_120, kModeId120);
 
-    // setPolicy(0, 60), current 60Hz => TurnOn.
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
+              selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
 
-    // setPolicy(60, 60), current 60Hz => TurnOff.
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
 
-    // setPolicy(60, 120), current 60Hz => TurnOn.
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}));
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
 
-    // setPolicy(120, 120), current 120Hz => TurnOff.
     EXPECT_EQ(SetPolicyResult::Changed,
-              configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}));
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
+              selector.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
 }
 
-TEST_F(RefreshRateConfigsTest, getFrameRateDivisor) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
+TEST_F(RefreshRateSelectorTest, getFrameRateDivisor) {
+    TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId30);
 
     const auto frameRate = 30_Hz;
-    Fps displayRefreshRate = configs.getActiveMode().getFps();
-    EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
+    Fps displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setActiveModeId(kModeId60);
-    displayRefreshRate = configs.getActiveMode().getFps();
-    EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
+    selector.setActiveModeId(kModeId60);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setActiveModeId(kModeId72);
-    displayRefreshRate = configs.getActiveMode().getFps();
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
+    selector.setActiveModeId(kModeId72);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setActiveModeId(kModeId90);
-    displayRefreshRate = configs.getActiveMode().getFps();
-    EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
+    selector.setActiveModeId(kModeId90);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setActiveModeId(kModeId120);
-    displayRefreshRate = configs.getActiveMode().getFps();
-    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
+    selector.setActiveModeId(kModeId120);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setActiveModeId(kModeId90);
-    displayRefreshRate = configs.getActiveMode().getFps();
-    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
+    selector.setActiveModeId(kModeId90);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
 
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 23.976_Hz));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(30_Hz, 29.97_Hz));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(60_Hz, 59.94_Hz));
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 25_Hz));
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 23.976_Hz));
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(30_Hz, 29.97_Hz));
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz));
 }
 
-TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
+TEST_F(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
 
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 30_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 29.97_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 30_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 29.97_Hz));
 
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 60_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 59.94_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 60_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 59.94_Hz));
 
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 60_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 29.97_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 60_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 29.97_Hz));
 
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 30_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 59.94_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 30_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 59.94_Hz));
 
     const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz};
     for (auto refreshRate : refreshRates) {
-        EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(refreshRate, refreshRate));
+        EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(refreshRate, refreshRate));
     }
 
-    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 25_Hz));
-    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(23.978_Hz, 25_Hz));
-    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
+    EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 25_Hz));
+    EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(23.978_Hz, 25_Hz));
+    EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
 }
 
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120);
+TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
+    RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120);
 
-    EXPECT_TRUE(configs.getFrameRateOverrides({}, 120_Hz, {}).empty());
+    EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty());
 }
 
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
-                               {.enableFrameRateOverride = true});
+TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
+    RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
+                                 {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     layers[0].name = "Test layer";
@@ -2322,37 +2316,37 @@
     layers[0].desiredRefreshRate = 60_Hz;
     layers[0].vote = LayerVoteType::ExplicitDefault;
 
-    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    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;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    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 = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Min;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Max;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_TRUE(frameRateOverrides.empty());
 
     layers[0].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_TRUE(frameRateOverrides.empty());
 }
 
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
-                               {.enableFrameRateOverride = true});
+TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
+    RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
+                                 {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
                                             {.ownerUid = 5678, .weight = 1.f}};
@@ -2364,7 +2358,7 @@
     layers[1].name = "Test layer 5678";
     layers[1].desiredRefreshRate = 30_Hz;
     layers[1].vote = LayerVoteType::ExplicitDefault;
-    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
 
     EXPECT_EQ(2u, frameRateOverrides.size());
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
@@ -2373,53 +2367,53 @@
     EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
 
     layers[1].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    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[1].ownerUid = 1234;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_TRUE(frameRateOverrides.empty());
 }
 
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
-                               {.enableFrameRateOverride = true});
+TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
+    RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
+                                 {.enableFrameRateOverride = true});
 
     std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
     layers[0].name = "Test layer";
     layers[0].desiredRefreshRate = 60_Hz;
     layers[0].vote = LayerVoteType::ExplicitDefault;
 
-    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    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));
 
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
     EXPECT_EQ(1u, frameRateOverrides.size());
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
     EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExact;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_EQ(1u, frameRateOverrides.size());
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
     EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
     EXPECT_EQ(1u, frameRateOverrides.size());
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
     EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
     layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
     EXPECT_EQ(1u, frameRateOverrides.size());
     ASSERT_EQ(1u, frameRateOverrides.count(1234));
     EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
 
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
     EXPECT_TRUE(frameRateOverrides.empty());
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 147433b..066083f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -22,7 +22,7 @@
 
 #include "FakeDisplayInjector.h"
 #include "Scheduler/EventThread.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
@@ -78,12 +78,12 @@
             createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz);
     static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60);
 
-    std::shared_ptr<RefreshRateConfigs> mConfigs =
-            std::make_shared<RefreshRateConfigs>(makeModes(kDisplay1Mode60),
-                                                 kDisplay1Mode60->getId());
+    std::shared_ptr<RefreshRateSelector> mSelector =
+            std::make_shared<RefreshRateSelector>(makeModes(kDisplay1Mode60),
+                                                  kDisplay1Mode60->getId());
 
     mock::SchedulerCallback mSchedulerCallback;
-    TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
+    TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
 
     ConnectionHandle mConnectionHandle;
     MockEventThread* mEventThread;
@@ -196,8 +196,8 @@
     sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     ASSERT_EQ(1u, mScheduler->layerHistorySize());
 
-    mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(kDisplay1Modes, kDisplay1Mode60->getId()));
+    mScheduler->setRefreshRateSelector(
+            std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -247,7 +247,7 @@
             {.displayId = kDisplayId1});
 
     mScheduler->registerDisplay(display);
-    mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
+    mScheduler->setRefreshRateSelector(display->holdRefreshRateSelector());
 
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
@@ -277,8 +277,8 @@
 
     mScheduler->registerDisplay(display);
 
-    std::vector<RefreshRateConfigs::LayerRequirement> layers =
-            std::vector<RefreshRateConfigs::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+    std::vector<RefreshRateSelector::LayerRequirement> layers =
+            std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
     mScheduler->setContentRequirements(layers);
     GlobalSignals globalSignals = {.idle = true};
     mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -343,8 +343,8 @@
                                                   globalSignals)(kDisplayId2, kDisplay2Mode60,
                                                                  globalSignals);
 
-        std::vector<RefreshRateConfigs::LayerRequirement> layers = {{.weight = 1.f},
-                                                                    {.weight = 1.f}};
+        std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
+                                                                     {.weight = 1.f}};
         mScheduler->setContentRequirements(layers);
         mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
 
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index dfcfd91..6adcd52 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -383,8 +383,8 @@
     history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
     history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
 
-    const auto configs = mFlinger.mutableScheduler().refreshRateConfigs();
-    const auto summary = history.summarize(*configs, 0);
+    const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
+    const auto summary = history.summarize(*selectorPtr, 0);
 
     ASSERT_EQ(2u, summary.size());
     EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 6b7e353..4c25463 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -42,15 +42,15 @@
         PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
 
         DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
-        auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
+        auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
 
-        setupScheduler(configs);
+        setupScheduler(selectorPtr);
 
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
         mFlinger.configureAndCommit();
 
         mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
-                           .setDisplayModes(std::move(modes), kModeId60, std::move(configs))
+                           .setDisplayModes(std::move(modes), kModeId60, std::move(selectorPtr))
                            .inject();
 
         // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
@@ -60,7 +60,7 @@
     }
 
 protected:
-    void setupScheduler(std::shared_ptr<scheduler::RefreshRateConfigs>);
+    void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>);
 
     sp<DisplayDevice> mDisplay;
     mock::EventThread* mAppEventThread;
@@ -80,7 +80,7 @@
 };
 
 void DisplayModeSwitchingTest::setupScheduler(
-        std::shared_ptr<scheduler::RefreshRateConfigs> configs) {
+        std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr) {
     auto eventThread = std::make_unique<mock::EventThread>();
     mAppEventThread = eventThread.get();
     auto sfEventThread = std::make_unique<mock::EventThread>();
@@ -108,7 +108,7 @@
     mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
                             std::move(eventThread), std::move(sfEventThread),
                             TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
-                            std::move(configs));
+                            std::move(selectorPtr));
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 26b2b67..2814d38 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -32,17 +32,19 @@
 
 class TestableScheduler : public Scheduler, private ICompositor {
 public:
-    TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
+    TestableScheduler(std::shared_ptr<RefreshRateSelector> selectorPtr,
+                      ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<mock::VsyncController>(),
-                              std::make_unique<mock::VSyncTracker>(), std::move(configs),
+                              std::make_unique<mock::VSyncTracker>(), std::move(selectorPtr),
                               callback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::unique_ptr<VSyncTracker> tracker,
-                      std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
+                      std::shared_ptr<RefreshRateSelector> selectorPtr,
+                      ISchedulerCallback& callback)
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-        setRefreshRateConfigs(std::move(configs));
+        setRefreshRateSelector(std::move(selectorPtr));
 
         ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
             // Execute task to prevent broken promise exception on destruction.
@@ -74,7 +76,7 @@
         return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
     }
 
-    auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
+    auto refreshRateSelector() { return holdRefreshRateSelector(); }
 
     size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
         return mLayerHistory.mActiveLayerInfos.size();
@@ -102,7 +104,7 @@
         mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
     }
 
-    void setContentRequirements(std::vector<RefreshRateConfigs::LayerRequirement> layers) {
+    void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) {
         std::lock_guard<std::mutex> lock(mPolicyLock);
         mPolicy.contentRequirements = std::move(layers);
     }
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 7f471bc..ff79ce0 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -38,7 +38,7 @@
 #include "Layer.h"
 #include "NativeWindowSurface.h"
 #include "Scheduler/MessageQueue.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerDefaultFactory.h"
@@ -168,7 +168,8 @@
     // functions.
 
     void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
-        mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
+        mFlinger->mRenderEngine = std::move(renderEngine);
+        mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
     }
 
     void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
@@ -192,10 +193,10 @@
     static constexpr struct TwoDisplayModes {
     } kTwoDisplayModes;
 
-    using RefreshRateConfigsPtr = std::shared_ptr<scheduler::RefreshRateConfigs>;
+    using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>;
 
     using DisplayModesVariant =
-            std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateConfigsPtr>;
+            std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateSelectorPtr>;
 
     void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
                         std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
@@ -204,9 +205,9 @@
                         SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
                         DisplayModesVariant modesVariant = kOneDisplayMode,
                         bool useNiceMock = false) {
-        RefreshRateConfigsPtr configs;
-        if (std::holds_alternative<RefreshRateConfigsPtr>(modesVariant)) {
-            configs = std::move(std::get<RefreshRateConfigsPtr>(modesVariant));
+        RefreshRateSelectorPtr selectorPtr;
+        if (std::holds_alternative<RefreshRateSelectorPtr>(modesVariant)) {
+            selectorPtr = std::move(std::get<RefreshRateSelectorPtr>(modesVariant));
         } else {
             constexpr DisplayModeId kModeId60{0};
             DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
@@ -216,10 +217,10 @@
                 modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
             }
 
-            configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
+            selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
         }
 
-        const auto fps = FTL_FAKE_GUARD(kMainThreadContext, configs->getActiveMode().getFps());
+        const auto fps = FTL_FAKE_GUARD(kMainThreadContext, selectorPtr->getActiveMode().getFps());
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -237,12 +238,12 @@
             mScheduler =
                     new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController),
                                                                         std::move(vsyncTracker),
-                                                                        std::move(configs),
+                                                                        std::move(selectorPtr),
                                                                         callback);
         } else {
             mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
                                                           std::move(vsyncTracker),
-                                                          std::move(configs), callback);
+                                                          std::move(selectorPtr), callback);
         }
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
@@ -543,8 +544,8 @@
         mutableDrawingState().displays.clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
-        mFlinger->mCompositionEngine->setRenderEngine(
-                std::unique_ptr<renderengine::RenderEngine>());
+        mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
+        mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
     }
 
     /* ------------------------------------------------------------------------
@@ -756,16 +757,17 @@
             return mFlinger.mutableDisplays().get(mDisplayToken)->get();
         }
 
-        // If `configs` is nullptr, the injector creates RefreshRateConfigs from the `modes`.
-        // Otherwise, it uses `configs`, which the caller must create using the same `modes`.
+        // If `selectorPtr` is nullptr, the injector creates RefreshRateSelector from the `modes`.
+        // Otherwise, it uses `selectorPtr`, which the caller must create using the same `modes`.
         //
-        // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateConfigs, remove
-        // the `configs` parameter in favor of an alternative setRefreshRateConfigs API.
-        auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId,
-                              std::shared_ptr<scheduler::RefreshRateConfigs> configs = nullptr) {
+        // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateSelector, remove
+        // the `selectorPtr` parameter in favor of an alternative setRefreshRateSelector API.
+        auto& setDisplayModes(
+                DisplayModes modes, DisplayModeId activeModeId,
+                std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr = nullptr) {
             mDisplayModes = std::move(modes);
             mCreationArgs.activeModeId = activeModeId;
-            mCreationArgs.refreshRateConfigs = std::move(configs);
+            mCreationArgs.refreshRateSelector = std::move(selectorPtr);
             return *this;
         }
 
@@ -812,7 +814,7 @@
             auto& modes = mDisplayModes;
             auto& activeModeId = mCreationArgs.activeModeId;
 
-            if (displayId && !mCreationArgs.refreshRateConfigs) {
+            if (displayId && !mCreationArgs.refreshRateSelector) {
                 if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
                     if (modes.empty()) {
                         constexpr DisplayModeId kModeId{0};
@@ -832,8 +834,8 @@
                         activeModeId = kModeId;
                     }
 
-                    mCreationArgs.refreshRateConfigs =
-                            std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId);
+                    mCreationArgs.refreshRateSelector =
+                            std::make_shared<scheduler::RefreshRateSelector>(modes, activeModeId);
                 }
             }