Merge "SurfaceControl: Ensure unused fields are zeroed" into tm-dev
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 4b64203..260ee8d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -67,5 +67,66 @@
         }
       ]
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "SurfaceFlinger_test",
+      "options": [
+        {
+          "include-filter": "*CredentialsTest.*"
+        },
+        {
+          "include-filter": "*SurfaceFlingerStress.*"
+        },
+        {
+          "include-filter": "*SurfaceInterceptorTest.*"
+        },
+        {
+          "include-filter": "*LayerTransactionTest.*"
+        },
+        {
+          "include-filter": "*LayerTypeTransactionTest.*"
+        },
+        {
+          "include-filter": "*LayerUpdateTest.*"
+        },
+        {
+          "include-filter": "*GeometryLatchingTest.*"
+        },
+        {
+          "include-filter": "*CropLatchingTest.*"
+        },
+        {
+          "include-filter": "*ChildLayerTest.*"
+        },
+        {
+          "include-filter": "*ScreenCaptureTest.*"
+        },
+        {
+          "include-filter": "*ScreenCaptureChildOnlyTest.*"
+        },
+        {
+          "include-filter": "*DereferenceSurfaceControlTest.*"
+        },
+        {
+          "include-filter": "*BoundlessLayerTest.*"
+        },
+        {
+          "include-filter": "*MultiDisplayLayerBoundsTest.*"
+        },
+        {
+          "include-filter": "*InvalidHandleTest.*"
+        },
+        {
+          "include-filter": "*VirtualDisplayTest.*"
+        },
+        {
+          "include-filter": "*RelativeZTest.*"
+        },
+        {
+          "include-filter": "*RefreshRateOverlayTest.*"
+        }
+      ]
+    }
   ]
 }
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 7bad351..741b264 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -55,6 +55,7 @@
 #include <cutils/fs.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
+#include <linux/quota.h>
 #include <log/log.h>               // TODO: Move everything to base/logging.
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
@@ -457,15 +458,37 @@
     free(after);
     return res;
 }
+static bool internal_storage_has_project_id() {
+    // The following path is populated in setFirstBoot, so if this file is present
+    // then project ids can be used.
 
-static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid) {
+    auto using_project_ids =
+            StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str());
+    return access(using_project_ids.c_str(), F_OK) == 0;
+}
+
+static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid,
+                           long project_id) {
     if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
         PLOG(ERROR) << "Failed to prepare " << path;
         return -1;
     }
+    if (internal_storage_has_project_id()) {
+        return set_quota_project_id(path, project_id, true);
+    }
     return 0;
 }
 
+static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+                                 uid_t uid, gid_t gid, long project_id) {
+    auto path = StringPrintf("%s/%s", parent.c_str(), name);
+    int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid);
+    if (ret == 0 && internal_storage_has_project_id()) {
+        return set_quota_project_id(path, project_id, true);
+    }
+    return ret;
+}
+
 static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
     if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
         return true;
@@ -625,9 +648,11 @@
     }
 
     // Prepare only the parent app directory
-    if (prepare_app_dir(path, targetMode, uid, gid) ||
-        prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
-        prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+    long project_id_app = get_project_id(uid, PROJECT_ID_APP_START);
+    long project_id_cache_app = get_project_id(uid, PROJECT_ID_APP_CACHE_START);
+    if (prepare_app_dir(path, targetMode, uid, gid, project_id_app) ||
+        prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid, project_id_cache_app) ||
+        prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid, project_id_cache_app)) {
         return error("Failed to prepare " + path);
     }
 
@@ -771,7 +796,7 @@
         LOG(DEBUG) << "Creating app-level sdk data directory: " << packagePath;
 #endif
 
-        if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM)) {
+        if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM, 0)) {
             return error("Failed to prepare " + packagePath);
         }
     }
@@ -2088,26 +2113,70 @@
 }
 #endif
 
+// On devices without sdcardfs, if internal and external are on
+// the same volume, a uid such as u0_a123 is used for both
+// internal and external storage; therefore, subtract that
+// amount from internal to make sure we don't count it double.
+// This needs to happen for data, cache and OBB
+static void deductDoubleSpaceIfNeeded(stats* stats, int64_t doubleSpaceToBeDeleted, uid_t uid,
+                                      const std::string& uuid) {
+    if (!supports_sdcardfs()) {
+        stats->dataSize -= doubleSpaceToBeDeleted;
+        long obbProjectId = get_project_id(uid, PROJECT_ID_EXT_OBB_START);
+        int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId);
+        stats->dataSize -= appObbSize;
+    }
+}
+
 static void collectQuotaStats(const std::string& uuid, int32_t userId,
         int32_t appId, struct stats* stats, struct stats* extStats) {
-    int64_t space;
+    int64_t space, doubleSpaceToBeDeleted = 0;
     uid_t uid = multiuser_get_uid(userId, appId);
-    if (stats != nullptr) {
-        if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) {
-            stats->dataSize += space;
+    static const bool supportsProjectId = internal_storage_has_project_id();
+
+    if (extStats != nullptr) {
+        space = get_occupied_app_space_external(uuid, userId, appId);
+
+        if (space != -1) {
+            extStats->dataSize += space;
+            doubleSpaceToBeDeleted += space;
         }
 
-        int sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
-        if (sdkSandboxUid != -1) {
-            if ((space = GetOccupiedSpaceForUid(uuid, sdkSandboxUid)) != -1) {
+        space = get_occupied_app_cache_space_external(uuid, userId, appId);
+        if (space != -1) {
+            extStats->dataSize += space; // cache counts for "data"
+            extStats->cacheSize += space;
+            doubleSpaceToBeDeleted += space;
+        }
+    }
+
+    if (stats != nullptr) {
+        if (!supportsProjectId) {
+            if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) {
                 stats->dataSize += space;
             }
-        }
-
-        int cacheGid = multiuser_get_cache_gid(userId, appId);
-        if (cacheGid != -1) {
-            if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) {
+            deductDoubleSpaceIfNeeded(stats, doubleSpaceToBeDeleted, uid, uuid);
+            int sdkSandboxUid = multiuser_get_sdk_sandbox_uid(userId, appId);
+            if (sdkSandboxUid != -1) {
+                if ((space = GetOccupiedSpaceForUid(uuid, sdkSandboxUid)) != -1) {
+                    stats->dataSize += space;
+                }
+            }
+            int cacheGid = multiuser_get_cache_gid(userId, appId);
+            if (cacheGid != -1) {
+                if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) {
+                    stats->cacheSize += space;
+                }
+            }
+        } else {
+            long projectId = get_project_id(uid, PROJECT_ID_APP_START);
+            if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) {
+                stats->dataSize += space;
+            }
+            projectId = get_project_id(uid, PROJECT_ID_APP_CACHE_START);
+            if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) {
                 stats->cacheSize += space;
+                stats->dataSize += space;
             }
         }
 
@@ -2118,47 +2187,6 @@
             }
         }
     }
-
-    if (extStats != nullptr) {
-        static const bool supportsSdCardFs = supports_sdcardfs();
-        space = get_occupied_app_space_external(uuid, userId, appId);
-
-        if (space != -1) {
-            extStats->dataSize += space;
-            if (!supportsSdCardFs && stats != nullptr) {
-                // On devices without sdcardfs, if internal and external are on
-                // the same volume, a uid such as u0_a123 is used for
-                // application dirs on both internal and external storage;
-                // therefore, substract that amount from internal to make sure
-                // we don't count it double.
-                stats->dataSize -= space;
-            }
-        }
-
-        space = get_occupied_app_cache_space_external(uuid, userId, appId);
-        if (space != -1) {
-            extStats->dataSize += space; // cache counts for "data"
-            extStats->cacheSize += space;
-            if (!supportsSdCardFs && stats != nullptr) {
-                // On devices without sdcardfs, if internal and external are on
-                // the same volume, a uid such as u0_a123 is used for both
-                // internal and external storage; therefore, substract that
-                // amount from internal to make sure we don't count it double.
-                stats->dataSize -= space;
-            }
-        }
-
-        if (!supportsSdCardFs && stats != nullptr) {
-            // On devices without sdcardfs, the UID of OBBs on external storage
-            // matches the regular app UID (eg u0_a123); therefore, to avoid
-            // OBBs being include in stats->dataSize, compute the OBB size for
-            // this app, and substract it from the size reported on internal
-            // storage
-            long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START;
-            int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId);
-            stats->dataSize -= appObbSize;
-        }
-    }
 }
 
 static void collectManualStats(const std::string& path, struct stats* stats) {
@@ -2293,6 +2321,12 @@
     fts_close(fts);
 }
 static bool ownsExternalStorage(int32_t appId) {
+    // if project id calculation is supported then, there is no need to
+    // calculate in a different way and project_id based calculation can work
+    if (internal_storage_has_project_id()) {
+        return false;
+    }
+
     //  Fetch external storage owner appid  and check if it is the same as the
     //  current appId whose size is calculated
     struct stat s;
@@ -3356,6 +3390,38 @@
         dexPath, packageName, uid, volumeUuid, storageFlag, _aidl_return);
     return result ? ok() : error();
 }
+/**
+ * Returns true if ioctl feature (F2FS_IOC_FS{GET,SET}XATTR) is supported as
+ * these were introduced in Linux 4.14, so kernel versions before that will fail
+ * while setting project id attributes. Only when these features are enabled,
+ * storage calculation using project_id is enabled
+ */
+bool check_if_ioctl_feature_is_supported() {
+    bool result = false;
+    auto temp_path = StringPrintf("%smisc/installd/ioctl_check", android_data_dir.c_str());
+    if (access(temp_path.c_str(), F_OK) != 0) {
+        int fd = open(temp_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644);
+        result = set_quota_project_id(temp_path, 0, true) == 0;
+        close(fd);
+        // delete the temp file
+        remove(temp_path.c_str());
+    }
+    return result;
+}
+
+binder::Status InstalldNativeService::setFirstBoot() {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+    std::string uuid;
+    if (GetOccupiedSpaceForProjectId(uuid, 0) != -1 && check_if_ioctl_feature_is_supported()) {
+        auto first_boot_path =
+                StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str());
+        if (access(first_boot_path.c_str(), F_OK) != 0) {
+            close(open(first_boot_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644));
+        }
+    }
+    return ok();
+}
 
 binder::Status InstalldNativeService::invalidateMounts() {
     ENFORCE_UID(AID_SYSTEM);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index e6be5d8..87a9206 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -168,6 +168,7 @@
         int32_t storageFlag, std::vector<uint8_t>* _aidl_return);
 
     binder::Status invalidateMounts();
+    binder::Status setFirstBoot();
     binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid,
             bool* _aidl_return);
     binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 2b5a35e..79c02e8 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -20,7 +20,7 @@
 interface IInstalld {
     void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
     void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
-
+    void setFirstBoot();
     android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args);
     android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args);
 
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 123e3d4..9801a9b 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -37,6 +37,7 @@
 #include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/properties.h>
+#include <linux/fs.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_projectid_config.h>
@@ -424,6 +425,45 @@
     return users;
 }
 
+long get_project_id(uid_t uid, long start_project_id_range) {
+    return uid - AID_APP_START + start_project_id_range;
+}
+
+int set_quota_project_id(const std::string& path, long project_id, bool set_inherit) {
+    struct fsxattr fsx;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << path << " to set project id.";
+        return -1;
+    }
+
+    if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx) == -1) {
+        PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id.";
+        return -1;
+    }
+
+    fsx.fsx_projid = project_id;
+    if (ioctl(fd, FS_IOC_FSSETXATTR, &fsx) == -1) {
+        PLOG(ERROR) << "Failed to set project id on " << path;
+        return -1;
+    }
+    if (set_inherit) {
+        unsigned int flags;
+        if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
+            PLOG(ERROR) << "Failed to get flags for " << path << " to set project id inheritance.";
+            return -1;
+        }
+
+        flags |= FS_PROJINHERIT_FL;
+
+        if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) {
+            PLOG(ERROR) << "Failed to set flags for " << path << " to set project id inheritance.";
+            return -1;
+        }
+    }
+    return 0;
+}
+
 int calculate_tree_size(const std::string& path, int64_t* size,
         int32_t include_gid, int32_t exclude_gid, bool exclude_apps) {
     FTS *fts;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index cb30993..ecea1d2 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -171,6 +171,8 @@
         uid_t uid, gid_t gid);
 
 bool supports_sdcardfs();
+long get_project_id(uid_t uid, long start_project_id_range);
+int set_quota_project_id(const std::string& path, long project_id, bool set_inherit);
 int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId);
 int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId);
 
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 555be1ed7..3cfe529 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -295,28 +295,27 @@
 Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
     auto ctx = mAccess->getCallingContext();
 
-    // apps cannot add services
     if (multiuser_get_app_id(ctx.uid) >= AID_APP) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services");
     }
 
     if (!mAccess->canAdd(ctx, name)) {
-        return Status::fromExceptionCode(Status::EX_SECURITY);
+        return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial");
     }
 
     if (binder == nullptr) {
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Null binder");
     }
 
     if (!isValidServiceName(name)) {
         LOG(ERROR) << "Invalid service name: " << name;
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name");
     }
 
 #ifndef VENDORSERVICEMANAGER
     if (!meetsDeclarationRequirements(binder, name)) {
         // already logged
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error");
     }
 #endif  // !VENDORSERVICEMANAGER
 
@@ -324,7 +323,7 @@
     if (binder->remoteBinder() != nullptr &&
         binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
         LOG(ERROR) << "Could not linkToDeath when adding " << name;
-        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure");
     }
 
     // Overwrite the old service if it exists
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 39d380d..dfdce20 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -197,4 +197,9 @@
     return gotVsync;
 }
 
+status_t DisplayEventDispatcher::getLatestVsyncEventData(
+        ParcelableVsyncEventData* outVsyncEventData) const {
+    return mReceiver.getLatestVsyncEventData(outVsyncEventData);
+}
+
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 71968fa..a342539 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -34,6 +34,7 @@
     void injectEvent(const DisplayEventReceiver::Event& event);
     int getFd() const;
     virtual int handleEvent(int receiveFd, int events, void* data);
+    status_t getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) const;
 
 protected:
     virtual ~DisplayEventDispatcher() = default;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index fcfe21b..e69fc78 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -76,16 +76,30 @@
 
 class InputSurface {
 public:
-    InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
+    InputSurface(const sp<SurfaceControl> &sc, int width, int height, bool noInputChannel = false) {
         mSurfaceControl = sc;
 
         mInputFlinger = getInputFlinger();
-        mClientChannel = std::make_shared<InputChannel>();
-        mInputFlinger->createInputChannel("testchannels", mClientChannel.get());
+        if (noInputChannel) {
+            mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true);
+        } else {
+            mClientChannel = std::make_shared<InputChannel>();
+            mInputFlinger->createInputChannel("testchannels", mClientChannel.get());
+            mInputInfo.token = mClientChannel->getConnectionToken();
+            mInputConsumer = new InputConsumer(mClientChannel);
+        }
 
-        populateInputInfo(width, height);
+        mInputInfo.name = "Test info";
+        mInputInfo.dispatchingTimeout = 5s;
+        mInputInfo.globalScaleFactor = 1.0;
+        mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
 
-        mInputConsumer = new InputConsumer(mClientChannel);
+        InputApplicationInfo aInfo;
+        aInfo.token = new BBinder();
+        aInfo.name = "Test app info";
+        aInfo.dispatchingTimeoutMillis =
+                std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
+        mInputInfo.applicationInfo = aInfo;
     }
 
     static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc,
@@ -114,6 +128,16 @@
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
+    static std::unique_ptr<InputSurface> makeContainerInputSurfaceNoInputChannel(
+            const sp<SurfaceComposerClient> &scc, int width, int height) {
+        sp<SurfaceControl> surfaceControl =
+                scc->createSurface(String8("Test Container Surface"), 100 /* height */,
+                                   100 /* width */, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceContainer);
+        return std::make_unique<InputSurface>(surfaceControl, width, height,
+                                              true /* noInputChannel */);
+    }
+
     static std::unique_ptr<InputSurface> makeCursorInputSurface(
             const sp<SurfaceComposerClient> &scc, int width, int height) {
         sp<SurfaceControl> surfaceControl =
@@ -219,7 +243,9 @@
     }
 
     virtual ~InputSurface() {
-        mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
+        if (mClientChannel) {
+            mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
+        }
     }
 
     virtual void doTransaction(
@@ -263,21 +289,6 @@
         poll(&fd, 1, timeoutMs);
     }
 
-    void populateInputInfo(int width, int height) {
-        mInputInfo.token = mClientChannel->getConnectionToken();
-        mInputInfo.name = "Test info";
-        mInputInfo.dispatchingTimeout = 5s;
-        mInputInfo.globalScaleFactor = 1.0;
-        mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
-
-        InputApplicationInfo aInfo;
-        aInfo.token = new BBinder();
-        aInfo.name = "Test app info";
-        aInfo.dispatchingTimeoutMillis =
-                std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
-
-        mInputInfo.applicationInfo = aInfo;
-    }
 public:
     sp<SurfaceControl> mSurfaceControl;
     std::shared_ptr<InputChannel> mClientChannel;
@@ -1085,6 +1096,23 @@
     EXPECT_EQ(containerSurface->consumeEvent(100), nullptr);
 }
 
+TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) {
+    std::unique_ptr<InputSurface> parent = makeSurface(100, 100);
+
+    parent->showAt(100, 100);
+    injectTap(101, 101);
+    parent->expectTap(1, 1);
+
+    std::unique_ptr<InputSurface> childContainerSurface =
+            InputSurface::makeContainerInputSurfaceNoInputChannel(mComposerClient, 100, 100);
+    childContainerSurface->showAt(0, 0);
+    childContainerSurface->doTransaction(
+            [&](auto &t, auto &sc) { t.reparent(sc, parent->mSurfaceControl); });
+    injectTap(101, 101);
+
+    EXPECT_EQ(parent->consumeEvent(100), nullptr);
+}
+
 class MultiDisplayTests : public InputSurfacesTest {
 public:
     MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 5e5618b..1665550 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -872,18 +872,21 @@
             // save a snapshot of the activeSurface to use as input to the blur shaders
             blurInput = activeSurface->makeImageSnapshot();
 
-            // TODO we could skip this step if we know the blur will cover the entire image
-            //  blit the offscreen framebuffer into the destination AHB
-            SkPaint paint;
-            paint.setBlendMode(SkBlendMode::kSrc);
-            if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
-                uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
-                dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
-                                          String8::format("SurfaceID|%" PRId64, id).c_str(),
-                                          nullptr);
-                dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
-            } else {
-                activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
+            // blit the offscreen framebuffer into the destination AHB, but only
+            // if there are blur regions. backgroundBlurRadius blurs the entire
+            // image below, so it can skip this step.
+            if (layer.blurRegions.size()) {
+                SkPaint paint;
+                paint.setBlendMode(SkBlendMode::kSrc);
+                if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
+                    uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
+                    dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
+                                              String8::format("SurfaceID|%" PRId64, id).c_str(),
+                                              nullptr);
+                    dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
+                } else {
+                    activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
+                }
             }
 
             // assign dstCanvas to canvas and ensure that the canvas state is up to date
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index ec1bd47..38ae2fd 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -2619,8 +2619,7 @@
             [](vec3 color) { return EOTF_HLG(color); },
             [](vec3 color, float currentLuminaceNits) {
                 static constexpr float kMaxHLGLuminance = 1000.f;
-                static const float kHLGGamma = 1.2 + 0.42 * std::log10(currentLuminaceNits / 1000);
-                return color * kMaxHLGLuminance * std::pow(color.y, kHLGGamma - 1);
+                return color * kMaxHLGLuminance;
             });
 }
 
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index 0d77519..5935589 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -185,9 +185,8 @@
             break;
         case HAL_DATASPACE_TRANSFER_HLG:
             shader.append(R"(
-                    uniform float in_hlgGamma;
                     float3 ScaleLuminance(float3 xyz) {
-                        return xyz * 1000.0 * pow(xyz.y, in_hlgGamma - 1);
+                        return xyz * 1000.0;
                     }
                 )");
             break;
@@ -228,10 +227,8 @@
             break;
         case HAL_DATASPACE_TRANSFER_HLG:
             shader.append(R"(
-                    uniform float in_hlgGamma;
                     float3 NormalizeLuminance(float3 xyz) {
-                        return xyz / 1000.0 *
-                                pow(xyz.y / 1000.0, (1 - in_hlgGamma) / (in_hlgGamma));
+                        return xyz / 1000.0;
                     }
                 )");
             break;
@@ -451,11 +448,6 @@
     return result;
 }
 
-// Refer to BT2100-2
-float computeHlgGamma(float currentDisplayBrightnessNits) {
-    return 1.2 + 0.42 * std::log10(currentDisplayBrightnessNits / 1000);
-}
-
 } // namespace
 
 std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
@@ -493,12 +485,6 @@
                                     colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
     }
 
-    if ((linearEffect.inputDataspace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_HLG) {
-        uniforms.push_back(
-                {.name = "in_hlgGamma",
-                 .value = buildUniformValue<float>(computeHlgGamma(currentDisplayLuminanceNits))});
-    }
-
     tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
                                // If the input luminance is unknown, use display luminance (aka,
                                // no-op any luminance changes)
@@ -506,6 +492,9 @@
                                // uncalibrated displays
                                .contentMaxLuminance =
                                        maxLuminance > 0 ? maxLuminance : maxDisplayLuminance,
+                               .currentDisplayLuminance = currentDisplayLuminanceNits > 0
+                                       ? currentDisplayLuminanceNits
+                                       : maxDisplayLuminance,
                                .buffer = buffer};
 
     for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) {
diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h
index e380323..c51016d 100644
--- a/libs/tonemap/include/tonemap/tonemap.h
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -49,6 +49,9 @@
     // The maximum luminance of the content in nits
     float contentMaxLuminance = 0.0;
 
+    // The current brightness of the display in nits
+    float currentDisplayLuminance = 0.0;
+
     // Reference to an AHardwareBuffer.
     // Devices that support gralloc 4.0 and higher may attach metadata onto a
     // particular frame's buffer, including metadata used by HDR-standards like
diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp
index c4f46bd..19e1eea 100644
--- a/libs/tonemap/tonemap.cpp
+++ b/libs/tonemap/tonemap.cpp
@@ -48,6 +48,22 @@
     return result;
 }
 
+// Refer to BT2100-2
+float computeHlgGamma(float currentDisplayBrightnessNits) {
+    // BT 2100-2's recommendation for taking into account the nominal max
+    // brightness of the display does not work when the current brightness is
+    // very low. For instance, the gamma becomes negative when the current
+    // brightness is between 1 and 2 nits, which would be a bad experience in a
+    // dark environment. Furthermore, BT2100-2 recommends applying
+    // channel^(gamma - 1) as its OOTF, which means that when the current
+    // brightness is lower than 335 nits then channel * channel^(gamma - 1) >
+    // channel, which makes dark scenes very bright. As a workaround for those
+    // problems, lower-bound the brightness to 500 nits.
+    constexpr float minBrightnessNits = 500.f;
+    currentDisplayBrightnessNits = std::max(minBrightnessNits, currentDisplayBrightnessNits);
+    return 1.2 + 0.42 * std::log10(currentDisplayBrightnessNits / 1000);
+}
+
 class ToneMapperO : public ToneMapper {
 public:
     std::string generateTonemapGainShaderSkSL(
@@ -79,11 +95,27 @@
                         // HLG output.
                         program.append(R"(
                                     float libtonemap_ToneMapTargetNits(vec3 xyz) {
-                                        return clamp(xyz.y, 0.0, 1000.0);
+                                        float nits = clamp(xyz.y, 0.0, 1000.0);
+                                        return nits * pow(nits / 1000.0, -0.2 / 1.2);
                                     }
                                 )");
                         break;
                     default:
+                        // HLG follows BT2100, but this tonemapping version
+                        // does not take into account current display brightness
+                        if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+                            program.append(R"(
+                                        float libtonemap_applyBaseOOTFGain(float nits) {
+                                            return pow(nits, 0.2);
+                                        }
+                                    )");
+                        } else {
+                            program.append(R"(
+                                        float libtonemap_applyBaseOOTFGain(float nits) {
+                                            return 1.0;
+                                        }
+                                    )");
+                        }
                         // Here we're mapping from HDR to SDR content, so interpolate using a
                         // Hermitian polynomial onto the smaller luminance range.
                         program.append(R"(
@@ -91,6 +123,8 @@
                                         float maxInLumi = in_libtonemap_inputMaxLuminance;
                                         float maxOutLumi = in_libtonemap_displayMaxLuminance;
 
+                                        xyz = xyz * libtonemap_applyBaseOOTFGain(xyz.y);
+
                                         float nits = xyz.y;
 
                                         // if the max input luminance is less than what we can
@@ -153,6 +187,21 @@
                 switch (destinationDataspaceInt & kTransferMask) {
                     case kTransferST2084:
                     case kTransferHLG:
+                        // HLG follows BT2100, but this tonemapping version
+                        // does not take into account current display brightness
+                        if ((destinationDataspaceInt & kTransferMask) == kTransferHLG) {
+                            program.append(R"(
+                                        float libtonemap_applyBaseOOTFGain(float nits) {
+                                            return pow(nits / 1000.0, -0.2 / 1.2);
+                                        }
+                                    )");
+                        } else {
+                            program.append(R"(
+                                        float libtonemap_applyBaseOOTFGain(float nits) {
+                                            return 1.0;
+                                        }
+                                    )");
+                        }
                         // Map from SDR onto an HDR output buffer
                         // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
                         // [0, maxOutLumi] which is hard-coded to be 3000 nits.
@@ -178,7 +227,7 @@
                                         if (nits <= x0) {
                                             // scale [0.0, x0] to [0.0, y0] linearly
                                             float slope = y0 / x0;
-                                            return nits * slope;
+                                            nits = nits * slope;
                                         } else if (nits <= x1) {
                                             // scale [x0, x1] to [y0, y1] using a curve
                                             float t = (nits - x0) / (x1 - x0);
@@ -196,7 +245,7 @@
                                                     2.0 * (1.0 - t) * t * c3 + t * t * y3;
                                         }
 
-                                        return nits;
+                                        return nits * libtonemap_applyBaseOOTFGain(nits);
                                     }
                                 )");
                         break;
@@ -264,12 +313,17 @@
                             // so we'll clamp the luminance range in case we're mapping from PQ
                             // input to HLG output.
                             targetNits = std::clamp(xyz.y, 0.0f, 1000.0f);
+                            targetNits *= std::pow(targetNits / 1000.f, -0.2 / 1.2);
                             break;
                         default:
                             // Here we're mapping from HDR to SDR content, so interpolate using a
                             // Hermitian polynomial onto the smaller luminance range.
 
                             targetNits = xyz.y;
+
+                            if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+                                targetNits *= std::pow(targetNits, 0.2);
+                            }
                             // if the max input luminance is less than what we can output then
                             // no tone mapping is needed as all color values will be in range.
                             if (metadata.contentMaxLuminance > metadata.displayMaxLuminance) {
@@ -362,6 +416,10 @@
                                 targetNits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 +
                                         t * t * y3;
                             }
+
+                            if ((destinationDataspaceInt & kTransferMask) == kTransferHLG) {
+                                targetNits *= std::pow(targetNits / 1000.0, -0.2 / 1.2);
+                            }
                         } break;
                         default:
                             // For completeness, this is tone-mapping from SDR to SDR, where this is
@@ -411,6 +469,7 @@
         program.append(R"(
                 uniform float in_libtonemap_displayMaxLuminance;
                 uniform float in_libtonemap_inputMaxLuminance;
+                uniform float in_libtonemap_hlgGamma;
             )");
         switch (sourceDataspaceInt & kTransferMask) {
             case kTransferST2084:
@@ -428,7 +487,10 @@
                         // HLG output.
                         program.append(R"(
                                     float libtonemap_ToneMapTargetNits(float maxRGB) {
-                                        return clamp(maxRGB, 0.0, 1000.0);
+                                        float nits = clamp(maxRGB, 0.0, 1000.0);
+                                        float gamma = (1 - in_libtonemap_hlgGamma)
+                                                / in_libtonemap_hlgGamma;
+                                        return nits * pow(nits / 1000.0, gamma);
                                     }
                                 )");
                         break;
@@ -497,8 +559,15 @@
                 break;
             case kTransferHLG:
                 switch (destinationDataspaceInt & kTransferMask) {
-                    // HLG -> HDR does not tone-map at all
+                    // HLG uses the OOTF from BT 2100.
                     case kTransferST2084:
+                        program.append(R"(
+                                    float libtonemap_ToneMapTargetNits(float maxRGB) {
+                                        return maxRGB
+                                                * pow(maxRGB / 1000.0, in_libtonemap_hlgGamma - 1);
+                                    }
+                                )");
+                        break;
                     case kTransferHLG:
                         program.append(R"(
                                     float libtonemap_ToneMapTargetNits(float maxRGB) {
@@ -507,13 +576,14 @@
                                 )");
                         break;
                     default:
-                        // libshaders follows BT2100 OOTF, but with a nominal peak display luminance
-                        // of 1000 nits. Renormalize to max display luminance if we're tone-mapping
-                        // down to SDR, as libshaders normalizes all SDR output from [0,
-                        // maxDisplayLumins] -> [0, 1]
+                        // Follow BT 2100 and renormalize to max display luminance if we're
+                        // tone-mapping down to SDR, as libshaders normalizes all SDR output from
+                        // [0, maxDisplayLumins] -> [0, 1]
                         program.append(R"(
                                     float libtonemap_ToneMapTargetNits(float maxRGB) {
-                                        return maxRGB * in_libtonemap_displayMaxLuminance / 1000.0;
+                                        return maxRGB
+                                                * pow(maxRGB / 1000.0, in_libtonemap_hlgGamma - 1)
+                                                * in_libtonemap_displayMaxLuminance / 1000.0;
                                     }
                                 )");
                         break;
@@ -545,11 +615,14 @@
         // Hardcode the max content luminance to a "reasonable" level
         static const constexpr float kContentMaxLuminance = 4000.f;
         std::vector<ShaderUniform> uniforms;
-        uniforms.reserve(2);
+        uniforms.reserve(3);
         uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
                             .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
         uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
                             .value = buildUniformValue<float>(kContentMaxLuminance)});
+        uniforms.push_back({.name = "in_libtonemap_hlgGamma",
+                            .value = buildUniformValue<float>(
+                                    computeHlgGamma(metadata.currentDisplayLuminance))});
         return uniforms;
     }
 
@@ -580,6 +653,8 @@
         const double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
         const double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2);
 
+        const double hlgGamma = computeHlgGamma(metadata.currentDisplayLuminance);
+
         for (const auto [linearRGB, _] : colors) {
             double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b});
 
@@ -603,6 +678,7 @@
                             // so we'll clamp the luminance range in case we're mapping from PQ
                             // input to HLG output.
                             targetNits = std::clamp(maxRGB, 0.0, 1000.0);
+                            targetNits *= pow(targetNits / 1000.0, (1 - hlgGamma) / (hlgGamma));
                             break;
                         default:
                             targetNits = maxRGB;
@@ -630,11 +706,14 @@
                 case kTransferHLG:
                     switch (destinationDataspaceInt & kTransferMask) {
                         case kTransferST2084:
+                            targetNits = maxRGB * pow(maxRGB / 1000.0, hlgGamma - 1);
+                            break;
                         case kTransferHLG:
                             targetNits = maxRGB;
                             break;
                         default:
-                            targetNits = maxRGB * metadata.displayMaxLuminance / 1000.0;
+                            targetNits = maxRGB * pow(maxRGB / 1000.0, hlgGamma - 1) *
+                                    metadata.displayMaxLuminance / 1000.0;
                             break;
                     }
                     break;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6bfac6c..c8a3ccf 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -556,6 +556,17 @@
              entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER);
 }
 
+// Determines if the given window can be targeted as InputTarget::FLAG_FOREGROUND.
+// Foreground events are only sent to "foreground targetable" windows, but not all gestures sent to
+// such window are necessarily targeted with the flag. For example, an event with ACTION_OUTSIDE can
+// be sent to such a window, but it is not a foreground event and doesn't use
+// InputTarget::FLAG_FOREGROUND.
+bool canReceiveForegroundTouches(const WindowInfo& info) {
+    // A non-touchable window can still receive touch events (e.g. in the case of
+    // STYLUS_INTERCEPTOR), so prevent such windows from receiving foreground events for touches.
+    return !info.inputConfig.test(gui::WindowInfo::InputConfig::NOT_TOUCHABLE) && !info.isSpy();
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -2203,8 +2214,8 @@
             // Set target flags.
             int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS;
 
-            if (!info.isSpy()) {
-                // There should only be one new foreground (non-spy) window at this location.
+            if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
+                // There should only be one touched window that can be "foreground" for the pointer.
                 targetFlags |= InputTarget::FLAG_FOREGROUND;
             }
 
@@ -2277,8 +2288,10 @@
                     isSplit = !isFromMouse;
                 }
 
-                int32_t targetFlags =
-                        InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
+                int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
+                if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) {
+                    targetFlags |= InputTarget::FLAG_FOREGROUND;
+                }
                 if (isSplit) {
                     targetFlags |= InputTarget::FLAG_SPLIT;
                 }
@@ -2327,13 +2340,15 @@
         }
     }
 
-    // Ensure that we have at least one foreground or spy window. It's possible that we dropped some
-    // of the touched windows we previously found if they became paused or unresponsive or were
-    // removed.
+    // Ensure that we have at least one foreground window or at least one window that cannot be a
+    // foreground target. If we only have windows that are not receiving foreground touches (e.g. we
+    // only have windows getting ACTION_OUTSIDE), then drop the event, because there is no window
+    // that is actually receiving the entire gesture.
     if (std::none_of(tempTouchState.windows.begin(), tempTouchState.windows.end(),
                      [](const TouchedWindow& touchedWindow) {
-                         return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 ||
-                                 touchedWindow.windowHandle->getInfo()->isSpy();
+                         return !canReceiveForegroundTouches(
+                                        *touchedWindow.windowHandle->getInfo()) ||
+                                 (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0;
                      })) {
         ALOGI("Dropping event because there is no touched window on display %d to receive it.",
               displayId);
@@ -5072,9 +5087,11 @@
         state->removeWindowByToken(fromToken);
 
         // Add new window.
-        int32_t newTargetFlags = oldTargetFlags &
-                (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
-                 InputTarget::FLAG_DISPATCH_AS_IS);
+        int32_t newTargetFlags =
+                oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
+        if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) {
+            newTargetFlags |= InputTarget::FLAG_FOREGROUND;
+        }
         state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
 
         // Store the dragging window.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 838e6aa..bf58705 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -6915,4 +6915,38 @@
     window->assertNoEvents();
 }
 
+/**
+ * Set up a scenario to test the behavior used by the stylus handwriting detection feature.
+ * The scenario is as follows:
+ *   - The stylus interceptor overlay is configured as a spy window.
+ *   - The stylus interceptor spy receives the start of a new stylus gesture.
+ *   - It pilfers pointers and then configures itself to no longer be a spy.
+ *   - The stylus interceptor continues to receive the rest of the gesture.
+ */
+TEST_F(InputDispatcherStylusInterceptorTest, StylusHandwritingScenario) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    overlay->setSpy(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+
+    sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
+    overlay->consumeMotionDown();
+    window->consumeMotionDown();
+
+    // The interceptor pilfers the pointers.
+    EXPECT_EQ(OK, mDispatcher->pilferPointers(overlay->getToken()));
+    window->consumeMotionCancel();
+
+    // The interceptor configures itself so that it is no longer a spy.
+    overlay->setSpy(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+
+    // It continues to receive the rest of the stylus gesture.
+    sendStylusEvent(AMOTION_EVENT_ACTION_MOVE);
+    overlay->consumeMotionMove();
+    sendStylusEvent(AMOTION_EVENT_ACTION_UP);
+    overlay->consumeMotionUp();
+
+    window->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 0b23a5a..635b088 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -404,7 +404,7 @@
     }
 
     if (display) {
-        const Fps refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
+        const Fps refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
         const std::optional<Fps> renderRate =
                 mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 6a3fcb7..16cb41b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -56,9 +56,6 @@
     // similar requests if needed.
     virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
 
-    // Returns the boot display mode preferred by HWC.
-    virtual int32_t getPreferredBootHwcConfigId() const = 0;
-
 protected:
     ~Display() = default;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 58d2530..e12d1b4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -62,7 +62,6 @@
     bool isSecure() const override;
     bool isVirtual() const override;
     void disconnect() override;
-    int32_t getPreferredBootHwcConfigId() const override;
     void createDisplayColorProfile(
             const compositionengine::DisplayColorProfileCreationArgs&) override;
     void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
@@ -88,7 +87,6 @@
     DisplayId mId;
     bool mIsDisconnected = false;
     Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
-    int32_t mPreferredBootHwcConfigId = -1;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 6a75283..2165e1d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -58,16 +58,6 @@
     editState().isSecure = args.isSecure;
     editState().displaySpace.setBounds(args.pixels);
     setName(args.name);
-    bool isBootModeSupported = getCompositionEngine().getHwComposer().getBootDisplayModeSupport();
-    const auto physicalId = PhysicalDisplayId::tryCast(mId);
-    if (!physicalId || !isBootModeSupported) {
-        return;
-    }
-    std::optional<hal::HWConfigId> preferredBootModeId =
-            getCompositionEngine().getHwComposer().getPreferredBootDisplayMode(*physicalId);
-    if (preferredBootModeId.has_value()) {
-        mPreferredBootHwcConfigId = static_cast<int32_t>(preferredBootModeId.value());
-    }
 }
 
 bool Display::isValid() const {
@@ -90,10 +80,6 @@
     return mId;
 }
 
-int32_t Display::getPreferredBootHwcConfigId() const {
-    return mPreferredBootHwcConfigId;
-}
-
 void Display::disconnect() {
     if (mIsDisconnected) {
         return;
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 1f1dd1a..af013b0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -112,8 +112,8 @@
     MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
     MOCK_METHOD0(getBootDisplayModeSupport, bool());
     MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
-    MOCK_METHOD2(getSupportedContentTypes,
-                 status_t(PhysicalDisplayId, std::vector<hal::ContentType>*));
+    MOCK_METHOD(status_t, getSupportedContentTypes,
+                (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
     MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType));
     MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
                        const std::unordered_map<std::string, bool>&());
@@ -134,7 +134,9 @@
                           std::optional<aidl::android::hardware::graphics::common::
                                                 DisplayDecorationSupport>* support));
     MOCK_METHOD2(setIdleTimerEnabled, status_t(PhysicalDisplayId, std::chrono::milliseconds));
-    MOCK_METHOD1(hasDisplayIdleTimerCapability, bool(PhysicalDisplayId displayId));
+    MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
+                (const, override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 66a9ef7..dd3858b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1310,7 +1310,8 @@
     static const Region kLowerHalfBoundsNoRotation;
     static const Region kFullBounds90Rotation;
     static const Region kTransparentRegionHint;
-    static const Region kTransparentRegionHint90Rotation;
+    static const Region kTransparentRegionHintTwo;
+    static const Region kTransparentRegionHintTwo90Rotation;
 
     StrictMock<OutputPartialMock> mOutput;
     LayerFESet mGeomSnapshots;
@@ -1329,8 +1330,10 @@
 const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
         Region(Rect(0, 0, 200, 100));
 const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHint =
+        Region(Rect(0, 0, 100, 100));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo =
         Region(Rect(25, 20, 50, 75));
-const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHint90Rotation =
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation =
         Region(Rect(125, 25, 180, 50));
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
@@ -1787,6 +1790,7 @@
     mLayer.layerFEState.contentDirty = true;
     mLayer.layerFEState.compositionType =
             aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+    mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintTwo;
 
     mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
     mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
@@ -1797,7 +1801,7 @@
     ensureOutputLayerIfVisible();
 
     EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint,
-                RegionEq(kTransparentRegionHint90Rotation));
+                RegionEq(kTransparentRegionHintTwo90Rotation));
 }
 
 /*
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 45b98bb..f5a4b3d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -196,7 +196,7 @@
     ATRACE_INT(mActiveModeFPSTrace.c_str(), mode->getFps().getIntValue());
     mActiveMode = mode;
     if (mRefreshRateConfigs) {
-        mRefreshRateConfigs->setCurrentModeId(mActiveMode->getId());
+        mRefreshRateConfigs->setActiveModeId(mActiveMode->getId());
     }
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->changeRefreshRate(mActiveMode->getFps());
@@ -227,21 +227,18 @@
 }
 
 DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const {
-    const auto it = std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
-                                 [&](DisplayModePtr mode) { return mode->getId() == modeId; });
-    if (it != mSupportedModes.end()) {
-        return *it;
-    }
-    return nullptr;
+    const DisplayModePtr nullMode;
+    return mSupportedModes.get(modeId).value_or(std::cref(nullMode));
 }
 
-DisplayModePtr DisplayDevice::getModefromHwcId(uint32_t hwcId) const {
-    const auto it = std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
-                                 [&](DisplayModePtr mode) { return mode->getHwcId() == hwcId; });
+std::optional<DisplayModeId> DisplayDevice::translateModeId(hal::HWConfigId hwcId) const {
+    const auto it =
+            std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
+                         [hwcId](const auto& pair) { return pair.second->getHwcId() == hwcId; });
     if (it != mSupportedModes.end()) {
-        return *it;
+        return it->second->getId();
     }
-    return nullptr;
+    return {};
 }
 
 nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -364,12 +361,12 @@
                   activeMode ? to_string(*activeMode).c_str() : "none");
 
     result.append("   supportedModes=\n");
-
-    for (const auto& mode : mSupportedModes) {
-        result.append("     ");
+    for (const auto& [id, mode] : mSupportedModes) {
+        result.append("      ");
         result.append(to_string(*mode));
-        result.append("\n");
+        result.push_back('\n');
     }
+
     StringAppendF(&result, "   deviceProductInfo=");
     if (mDeviceProductInfo) {
         mDeviceProductInfo->dump(result);
@@ -468,16 +465,6 @@
                            capabilities.getDesiredMinLuminance());
 }
 
-ui::DisplayModeId DisplayDevice::getPreferredBootModeId() const {
-    const auto preferredBootHwcModeId = mCompositionDisplay->getPreferredBootHwcConfigId();
-    const auto mode = getModefromHwcId(preferredBootHwcModeId);
-    if (mode == nullptr) {
-        ALOGE("%s: invalid display mode (%d)", __FUNCTION__, preferredBootHwcModeId);
-        return BAD_VALUE;
-    }
-    return mode->getId().value();
-}
-
 void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
     if (!enable) {
         mRefreshRateOverlay.reset();
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 690f240..2c4a300 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -157,9 +157,6 @@
     // respectively if hardware composer doesn't return meaningful values.
     HdrCapabilities getHdrCapabilities() const;
 
-    // Returns the boot display mode preferred by the implementation.
-    ui::DisplayModeId getPreferredBootModeId() const;
-
     // Return true if intent is supported by the display.
     bool hasRenderIntent(ui::RenderIntent intent) const;
 
@@ -232,8 +229,7 @@
     // set-top boxes after a hotplug reconnect.
     DisplayModePtr getMode(DisplayModeId) const;
 
-    // Returns nullptr if the given mode ID is not supported.
-    DisplayModePtr getModefromHwcId(uint32_t) const;
+    std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
 
     // Returns the refresh rate configs for this display.
     scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 92592f7..8b376f1 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -249,6 +249,7 @@
         case OptionalFeature::DisplayBrightnessCommand:
         case OptionalFeature::BootDisplayConfig:
         case OptionalFeature::KernelIdleTimer:
+        case OptionalFeature::PhysicalDisplayOrientation:
             return true;
     }
 }
@@ -1126,5 +1127,17 @@
     return Error::NONE;
 }
 
+Error AidlComposer::getPhysicalDisplayOrientation(Display displayId,
+                                                  AidlTransform* outDisplayOrientation) {
+    const auto status =
+            mAidlComposerClient->getDisplayPhysicalOrientation(translate<int64_t>(displayId),
+                                                               outDisplayOrientation);
+    if (!status.isOk()) {
+        ALOGE("getPhysicalDisplayOrientation failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
 } // namespace Hwc2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 6c0f636..28ff167 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -223,6 +223,9 @@
                                       std::optional<DisplayDecorationSupport>* support) override;
     Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) override;
 
+    Error getPhysicalDisplayOrientation(Display displayId,
+                                        AidlTransform* outDisplayOrientation) override;
+
 private:
     // Many public functions above simply write a command into the command
     // queue to batch the calls.  validateDisplay and presentDisplay will call
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 6abe7d1..7d9946d 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -38,6 +38,7 @@
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
 
+#include <aidl/android/hardware/graphics/common/Transform.h>
 #include <optional>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -80,6 +81,7 @@
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
+using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
 
 class Composer {
 public:
@@ -94,6 +96,7 @@
         DisplayBrightnessCommand,
         BootDisplayConfig,
         KernelIdleTimer,
+        PhysicalDisplayOrientation,
     };
 
     virtual bool isSupported(OptionalFeature) const = 0;
@@ -277,6 +280,8 @@
             std::optional<::aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) = 0;
     virtual Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) = 0;
+    virtual Error getPhysicalDisplayOrientation(Display displayId,
+                                                AidlTransform* outDisplayOrientation) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 0ab9605..61a9a08 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -18,10 +18,10 @@
 
 #include <cstddef>
 #include <memory>
-#include <vector>
 
 #include <android-base/stringprintf.h>
 #include <android/configuration.h>
+#include <ftl/small_map.h>
 #include <ui/DisplayId.h>
 #include <ui/DisplayMode.h>
 #include <ui/Size.h>
@@ -38,8 +38,17 @@
 
 class DisplayMode;
 using DisplayModePtr = std::shared_ptr<const DisplayMode>;
-using DisplayModes = std::vector<DisplayModePtr>;
-using DisplayModeId = StrongTyping<ui::DisplayModeId, struct DisplayModeIdTag, Compare, Hash>;
+
+// Prevent confusion with fps_approx_ops on the underlying Fps.
+bool operator<(const DisplayModePtr&, const DisplayModePtr&) = delete;
+bool operator>(const DisplayModePtr&, const DisplayModePtr&) = delete;
+bool operator<=(const DisplayModePtr&, const DisplayModePtr&) = delete;
+bool operator>=(const DisplayModePtr&, const DisplayModePtr&) = delete;
+
+using DisplayModeId = StrongTyping<ui::DisplayModeId, struct DisplayModeIdTag, Compare>;
+
+using DisplayModes = ftl::SmallMap<DisplayModeId, DisplayModePtr, 3>;
+using DisplayModeIterator = DisplayModes::const_iterator;
 
 class DisplayMode {
 public:
@@ -61,35 +70,30 @@
             return *this;
         }
 
-        Builder& setWidth(int32_t width) {
-            mDisplayMode->mWidth = width;
+        Builder& setResolution(ui::Size resolution) {
+            mDisplayMode->mResolution = resolution;
             return *this;
         }
 
-        Builder& setHeight(int32_t height) {
-            mDisplayMode->mHeight = height;
-            return *this;
-        }
-
-        Builder& setVsyncPeriod(int32_t vsyncPeriod) {
+        Builder& setVsyncPeriod(nsecs_t vsyncPeriod) {
             mDisplayMode->mFps = Fps::fromPeriodNsecs(vsyncPeriod);
             return *this;
         }
 
         Builder& setDpiX(int32_t dpiX) {
             if (dpiX == -1) {
-                mDisplayMode->mDpiX = getDefaultDensity();
+                mDisplayMode->mDpi.x = getDefaultDensity();
             } else {
-                mDisplayMode->mDpiX = dpiX / 1000.0f;
+                mDisplayMode->mDpi.x = dpiX / 1000.f;
             }
             return *this;
         }
 
         Builder& setDpiY(int32_t dpiY) {
             if (dpiY == -1) {
-                mDisplayMode->mDpiY = getDefaultDensity();
+                mDisplayMode->mDpi.y = getDefaultDensity();
             } else {
-                mDisplayMode->mDpiY = dpiY / 1000.0f;
+                mDisplayMode->mDpi.y = dpiY / 1000.f;
             }
             return *this;
         }
@@ -107,59 +111,76 @@
             // information to begin with. This is also used for virtual displays and
             // older HWC implementations, so be careful about orientation.
 
-            auto longDimension = std::max(mDisplayMode->mWidth, mDisplayMode->mHeight);
-            if (longDimension >= 1080) {
+            if (std::max(mDisplayMode->getWidth(), mDisplayMode->getHeight()) >= 1080) {
                 return ACONFIGURATION_DENSITY_XHIGH;
             } else {
                 return ACONFIGURATION_DENSITY_TV;
             }
         }
+
         std::shared_ptr<DisplayMode> mDisplayMode;
     };
 
     DisplayModeId getId() const { return mId; }
+
     hal::HWConfigId getHwcId() const { return mHwcId; }
     PhysicalDisplayId getPhysicalDisplayId() const { return mPhysicalDisplayId; }
 
-    int32_t getWidth() const { return mWidth; }
-    int32_t getHeight() const { return mHeight; }
-    ui::Size getSize() const { return {mWidth, mHeight}; }
+    ui::Size getResolution() const { return mResolution; }
+    int32_t getWidth() const { return mResolution.getWidth(); }
+    int32_t getHeight() const { return mResolution.getHeight(); }
+
     Fps getFps() const { return mFps; }
     nsecs_t getVsyncPeriod() const { return mFps.getPeriodNsecs(); }
-    float getDpiX() const { return mDpiX; }
-    float getDpiY() const { return mDpiY; }
+
+    struct Dpi {
+        float x = -1;
+        float y = -1;
+
+        bool operator==(Dpi other) const { return x == other.x && y == other.y; }
+    };
+
+    Dpi getDpi() const { return mDpi; }
 
     // Switches between modes in the same group are seamless, i.e.
     // without visual interruptions such as a black screen.
     int32_t getGroup() const { return mGroup; }
 
-    bool equalsExceptDisplayModeId(const DisplayModePtr& other) const {
-        return mHwcId == other->mHwcId && mWidth == other->mWidth && mHeight == other->mHeight &&
-                getVsyncPeriod() == other->getVsyncPeriod() && mDpiX == other->mDpiX &&
-                mDpiY == other->mDpiY && mGroup == other->mGroup;
-    }
-
 private:
     explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
 
-    hal::HWConfigId mHwcId;
+    const hal::HWConfigId mHwcId;
     DisplayModeId mId;
+
     PhysicalDisplayId mPhysicalDisplayId;
 
-    int32_t mWidth = -1;
-    int32_t mHeight = -1;
+    ui::Size mResolution;
     Fps mFps;
-    float mDpiX = -1;
-    float mDpiY = -1;
+    Dpi mDpi;
     int32_t mGroup = -1;
 };
 
+inline bool equalsExceptDisplayModeId(const DisplayMode& lhs, const DisplayMode& rhs) {
+    return lhs.getHwcId() == rhs.getHwcId() && lhs.getResolution() == rhs.getResolution() &&
+            lhs.getVsyncPeriod() == rhs.getVsyncPeriod() && lhs.getDpi() == rhs.getDpi() &&
+            lhs.getGroup() == rhs.getGroup();
+}
+
 inline std::string to_string(const DisplayMode& mode) {
-    return base::StringPrintf("{id=%d, hwcId=%d, width=%d, height=%d, refreshRate=%s, "
-                              "dpiX=%.2f, dpiY=%.2f, group=%d}",
+    return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, refreshRate=%s, "
+                              "dpi=%.2fx%.2f, group=%d}",
                               mode.getId().value(), mode.getHwcId(), mode.getWidth(),
-                              mode.getHeight(), to_string(mode.getFps()).c_str(), mode.getDpiX(),
-                              mode.getDpiY(), mode.getGroup());
+                              mode.getHeight(), to_string(mode.getFps()).c_str(), mode.getDpi().x,
+                              mode.getDpi().y, mode.getGroup());
+}
+
+template <typename... DisplayModePtrs>
+inline DisplayModes makeModes(const DisplayModePtrs&... modePtrs) {
+    DisplayModes modes;
+    // Note: The omission of std::move(modePtrs) is intentional, because order of evaluation for
+    // arguments is unspecified.
+    (modes.try_emplace(modePtrs->getId(), modePtrs), ...);
+    return modes;
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 6501276..c0432bf 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -154,6 +154,11 @@
             isCapabilitySupported;
 }
 
+Error Display::getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const {
+    auto error = mComposer.getPhysicalDisplayOrientation(mId, outTransform);
+    return static_cast<Error>(error);
+}
+
 Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
     std::vector<Hwc2::Layer> layerIds;
     std::vector<Composition> types;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index c03cede..d78562d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -169,6 +169,8 @@
                     support) = 0;
     [[clang::warn_unused_result]] virtual hal::Error setIdleTimerEnabled(
             std::chrono::milliseconds timeout) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getPhysicalDisplayOrientation(
+            Hwc2::AidlTransform* outTransform) const = 0;
 };
 
 namespace impl {
@@ -256,6 +258,7 @@
     bool isVsyncPeriodSwitchSupported() const override;
     bool hasDisplayIdleTimerCapability() const override;
     void onLayerDestroyed(hal::HWLayerId layerId) override;
+    hal::Error getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const override;
 
 private:
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 02b3772..e2e4a99 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -814,10 +814,11 @@
 }
 
 status_t HWComposer::getSupportedContentTypes(
-        PhysicalDisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
+        PhysicalDisplayId displayId,
+        std::vector<hal::ContentType>* outSupportedContentTypes) const {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
-    const auto error =
-            mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
+    const auto error = mDisplayData.at(displayId).hwcDisplay->getSupportedContentTypes(
+            outSupportedContentTypes);
 
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
 
@@ -986,9 +987,19 @@
     return NO_ERROR;
 }
 
-bool HWComposer::hasDisplayIdleTimerCapability(PhysicalDisplayId displayId) {
+bool HWComposer::hasDisplayIdleTimerCapability(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
-    return mDisplayData[displayId].hwcDisplay->hasDisplayIdleTimerCapability();
+    return mDisplayData.at(displayId).hwcDisplay->hasDisplayIdleTimerCapability();
+}
+
+Hwc2::AidlTransform HWComposer::getPhysicalDisplayOrientation(PhysicalDisplayId displayId) const {
+    ATRACE_CALL();
+    RETURN_IF_INVALID_DISPLAY(displayId, Hwc2::AidlTransform::NONE);
+    Hwc2::AidlTransform outTransform;
+    const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
+    const auto error = hwcDisplay->getPhysicalDisplayOrientation(&outTransform);
+    RETURN_IF_HWC_ERROR(error, displayId, Hwc2::AidlTransform::NONE);
+    return outTransform;
 }
 
 void HWComposer::loadLayerMetadataSupport() {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index f9637f0..91fe1b7 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -234,8 +234,16 @@
                                                   hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
     virtual status_t setAutoLowLatencyMode(PhysicalDisplayId, bool on) = 0;
     virtual status_t getSupportedContentTypes(
-            PhysicalDisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
+            PhysicalDisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) const = 0;
+
+    bool supportsContentType(PhysicalDisplayId displayId, hal::ContentType type) const {
+        std::vector<hal::ContentType> types;
+        return getSupportedContentTypes(displayId, &types) == NO_ERROR &&
+                std::find(types.begin(), types.end(), type) != types.end();
+    }
+
     virtual status_t setContentType(PhysicalDisplayId, hal::ContentType) = 0;
+
     virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
             const = 0;
 
@@ -268,7 +276,8 @@
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) = 0;
     virtual status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) = 0;
-    virtual bool hasDisplayIdleTimerCapability(PhysicalDisplayId) = 0;
+    virtual bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const = 0;
+    virtual Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const = 0;
 };
 
 namespace impl {
@@ -390,7 +399,8 @@
                                           const hal::VsyncPeriodChangeConstraints&,
                                           hal::VsyncPeriodChangeTimeline* outTimeline) override;
     status_t setAutoLowLatencyMode(PhysicalDisplayId, bool) override;
-    status_t getSupportedContentTypes(PhysicalDisplayId, std::vector<hal::ContentType>*) override;
+    status_t getSupportedContentTypes(PhysicalDisplayId,
+                                      std::vector<hal::ContentType>*) const override;
     status_t setContentType(PhysicalDisplayId, hal::ContentType) override;
 
     const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
@@ -405,7 +415,8 @@
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) override;
     status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) override;
-    bool hasDisplayIdleTimerCapability(PhysicalDisplayId) override;
+    bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const override;
+    Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 33adceb..e8dc084 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -237,6 +237,7 @@
         case OptionalFeature::DisplayBrightnessCommand:
         case OptionalFeature::BootDisplayConfig:
         case OptionalFeature::KernelIdleTimer:
+        case OptionalFeature::PhysicalDisplayOrientation:
             return false;
     }
 }
@@ -1331,6 +1332,11 @@
                      "OptionalFeature::KernelIdleTimer is not supported on HIDL");
 }
 
+Error HidlComposer::getPhysicalDisplayOrientation(Display, AidlTransform*) {
+    LOG_ALWAYS_FATAL("getPhysicalDisplayOrientation should have never been called on this as "
+                     "OptionalFeature::PhysicalDisplayOrientation is not supported on HIDL");
+}
+
 void HidlComposer::registerCallback(ComposerCallback& callback) {
     const bool vsyncSwitchingSupported =
             isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index a1ea4f2..5869ae5 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -334,6 +334,9 @@
                     support) override;
     Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) override;
 
+    Error getPhysicalDisplayOrientation(Display displayId,
+                                        AidlTransform* outDisplayOrientation) override;
+
 private:
     class CommandWriter : public CommandWriterBase {
     public:
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index aeaf1e1..ac7bcde 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2392,7 +2392,8 @@
 }
 
 bool Layer::hasInputInfo() const {
-    return mDrawingState.inputInfo.token != nullptr;
+    return mDrawingState.inputInfo.token != nullptr ||
+            mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
 }
 
 bool Layer::canReceiveInput() const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 65c8613..3226f22 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -38,10 +38,33 @@
 namespace android::scheduler {
 namespace {
 
+struct RefreshRateScore {
+    DisplayModeIterator modeIt;
+    float score;
+};
+
+template <typename Iterator>
+const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) {
+    const auto it =
+            std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) {
+                const auto& [modeIt, score] = current;
+
+                std::string name = to_string(modeIt->second->getFps());
+                ALOGV("%s scores %.2f", name.c_str(), score);
+
+                ATRACE_INT(name.c_str(), static_cast<int>(std::round(score * 100)));
+
+                constexpr float kEpsilon = 0.0001f;
+                return score > max.score * (1 + kEpsilon);
+            });
+
+    return it->modeIt->second;
+}
+
 constexpr RefreshRateConfigs::GlobalSignals kNoSignals;
 
 std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
-    return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(),
+    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(),
                               to_string(layer.desiredRefreshRate).c_str());
@@ -52,8 +75,8 @@
     knownFrameRates.reserve(knownFrameRates.size() + modes.size());
 
     // Add all supported refresh rates.
-    for (const auto& mode : modes) {
-        knownFrameRates.push_back(Fps::fromPeriodNsecs(mode->getVsyncPeriod()));
+    for (const auto& [id, mode] : modes) {
+        knownFrameRates.push_back(mode->getFps());
     }
 
     // Sort and remove duplicates.
@@ -64,17 +87,51 @@
     return knownFrameRates;
 }
 
-} // namespace
+// The Filter is a `bool(const DisplayMode&)` predicate.
+template <typename Filter>
+std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) {
+    std::vector<DisplayModeIterator> sortedModes;
+    sortedModes.reserve(modes.size());
 
-using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
-using RefreshRate = RefreshRateConfigs::RefreshRate;
+    for (auto it = modes.begin(); it != modes.end(); ++it) {
+        const auto& [id, mode] = *it;
 
-std::string RefreshRate::toString() const {
-    return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
-                              getModeId().value(), mode->getHwcId(), getFps().getValue(),
-                              mode->getWidth(), mode->getHeight(), getModeGroup());
+        if (filter(*mode)) {
+            ALOGV("%s: including mode %d", __func__, id.value());
+            sortedModes.push_back(it);
+        }
+    }
+
+    std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
+        const auto& mode1 = it1->second;
+        const auto& mode2 = it2->second;
+
+        if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) {
+            return mode1->getGroup() > mode2->getGroup();
+        }
+
+        return mode1->getVsyncPeriod() > mode2->getVsyncPeriod();
+    });
+
+    return sortedModes;
 }
 
+bool canModesSupportFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
+    for (const auto it1 : sortedModes) {
+        const auto& mode1 = it1->second;
+        for (const auto it2 : sortedModes) {
+            const auto& mode2 = it2->second;
+
+            if (RefreshRateConfigs::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+} // namespace
+
 std::string RefreshRateConfigs::Policy::toString() const {
     return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d"
                               ", primary range: %s, app request range: %s",
@@ -94,15 +151,14 @@
     return {quotient, remainder};
 }
 
-bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
-                                       const RefreshRate& refreshRate) const {
+bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, Fps refreshRate) const {
     using namespace fps_approx_ops;
 
     switch (layer.vote) {
         case LayerVoteType::ExplicitExactOrMultiple:
         case LayerVoteType::Heuristic:
             if (mConfig.frameRateMultipleThreshold != 0 &&
-                refreshRate.getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
+                refreshRate >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
                 layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) {
                 // Don't vote high refresh rates past the threshold for layers with a low desired
                 // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
@@ -120,11 +176,11 @@
     return true;
 }
 
-float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(
-        const LayerRequirement& layer, const RefreshRate& refreshRate) const {
+float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
+                                                                    Fps refreshRate) const {
     constexpr float kScoreForFractionalPairs = .8f;
 
-    const auto displayPeriod = refreshRate.getVsyncPeriod();
+    const auto displayPeriod = refreshRate.getPeriodNsecs();
     const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
     if (layer.vote == LayerVoteType::ExplicitDefault) {
         // Find the actual rate the layer will render, assuming
@@ -147,7 +203,7 @@
 
     if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
         layer.vote == LayerVoteType::Heuristic) {
-        if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) {
+        if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
             return kScoreForFractionalPairs;
         }
 
@@ -182,8 +238,7 @@
     return 0;
 }
 
-float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
-                                                    const RefreshRate& refreshRate,
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
                                                     bool isSeamlessSwitch) const {
     if (!isVoteAllowed(layer, refreshRate)) {
         return 0;
@@ -195,14 +250,14 @@
 
     // If the layer wants Max, give higher score to the higher refresh rate
     if (layer.vote == LayerVoteType::Max) {
-        const auto ratio = refreshRate.getFps().getValue() /
-                mAppRequestRefreshRates.back()->getFps().getValue();
+        const auto& maxRefreshRate = mAppRequestRefreshRates.back()->second;
+        const auto ratio = refreshRate.getValue() / maxRefreshRate->getFps().getValue();
         // use ratio^2 to get a lower score the more we get further from peak
         return ratio * ratio;
     }
 
     if (layer.vote == LayerVoteType::ExplicitExact) {
-        const int divisor = getFrameRateDivisor(refreshRate.getFps(), layer.desiredRefreshRate);
+        const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
         if (mSupportsFrameRateOverrideByContent) {
             // Since we support frame rate override, allow refresh rates which are
             // multiples of the layer's request, as those apps would be throttled
@@ -215,7 +270,7 @@
 
     // If the layer frame rate is a divisor of the refresh rate it should score
     // the highest score.
-    if (getFrameRateDivisor(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+    if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
         return 1.0f * seamlessness;
     }
 
@@ -227,14 +282,9 @@
             kNonExactMatchingPenalty;
 }
 
-struct RefreshRateScore {
-    const RefreshRate* refreshRate;
-    float score;
-};
-
 auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
                                             GlobalSignals signals) const
-        -> std::pair<RefreshRate, GlobalSignals> {
+        -> std::pair<DisplayModePtr, GlobalSignals> {
     std::lock_guard lock(mLock);
 
     if (mGetBestRefreshRateCache &&
@@ -249,7 +299,7 @@
 
 auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
                                                   GlobalSignals signals) const
-        -> std::pair<RefreshRate, GlobalSignals> {
+        -> std::pair<DisplayModePtr, GlobalSignals> {
     ATRACE_CALL();
     ALOGV("%s: %zu layers", __func__, layers.size());
 
@@ -298,21 +348,22 @@
             explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
 
     const Policy* policy = getCurrentPolicyLocked();
-    const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
+    const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
     // If the default mode group is different from the group of current mode,
     // this means a layer requesting a seamed mode switch just disappeared and
     // we should switch back to the default group.
     // However if a seamed layer is still present we anchor around the group
     // of the current mode, in order to prevent unnecessary seamed mode switches
     // (e.g. when pausing a video playback).
-    const auto anchorGroup = seamedFocusedLayers > 0 ? mCurrentRefreshRate->getModeGroup()
-                                                     : defaultMode->getModeGroup();
+    const auto anchorGroup =
+            seamedFocusedLayers > 0 ? mActiveModeIt->second->getGroup() : defaultMode->getGroup();
 
     // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
     // selected a refresh rate to see if we should apply touch boost.
     if (signals.touch && !hasExplicitVoteLayers) {
-        ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
-        return {getMaxRefreshRateByPolicyLocked(anchorGroup), GlobalSignals{.touch = true}};
+        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
+        ALOGV("TouchBoost - choose %s", to_string(max->getFps()).c_str());
+        return {max, GlobalSignals{.touch = true}};
     }
 
     // If the primary range consists of a single refresh rate then we can only
@@ -322,28 +373,30 @@
             isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
 
     if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
-        ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
-        return {getMinRefreshRateByPolicyLocked(), GlobalSignals{.idle = true}};
+        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
+        ALOGV("Idle - choose %s", to_string(min->getFps()).c_str());
+        return {min, GlobalSignals{.idle = true}};
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
-        const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
-        ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str());
-        return {refreshRate, kNoSignals};
+        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
+        ALOGV("no layers with votes - choose %s", to_string(max->getFps()).c_str());
+        return {max, kNoSignals};
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
-        ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
-        return {getMinRefreshRateByPolicyLocked(), kNoSignals};
+        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
+        ALOGV("all layers Min - choose %s", to_string(min->getFps()).c_str());
+        return {min, kNoSignals};
     }
 
     // Find the best refresh rate based on score
     std::vector<RefreshRateScore> scores;
     scores.reserve(mAppRequestRefreshRates.size());
 
-    for (const auto refreshRate : mAppRequestRefreshRates) {
-        scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
+    for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) {
+        scores.emplace_back(RefreshRateScore{modeIt, 0.0f});
     }
 
     for (const auto& layer : layers) {
@@ -354,17 +407,16 @@
             continue;
         }
 
-        auto weight = layer.weight;
+        const auto weight = layer.weight;
 
-        for (auto i = 0u; i < scores.size(); i++) {
-            const bool isSeamlessSwitch =
-                    scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup();
+        for (auto& [modeIt, score] : scores) {
+            const auto& [id, mode] = *modeIt;
+            const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup();
 
             if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
                 ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
-                      formatLayerInfo(layer, weight).c_str(),
-                      scores[i].refreshRate->toString().c_str(),
-                      mCurrentRefreshRate->toString().c_str());
+                      formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+                      to_string(*mActiveModeIt->second).c_str());
                 continue;
             }
 
@@ -372,9 +424,8 @@
                 !layer.focused) {
                 ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
                       " Current mode = %s",
-                      formatLayerInfo(layer, weight).c_str(),
-                      scores[i].refreshRate->toString().c_str(),
-                      mCurrentRefreshRate->toString().c_str());
+                      formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+                      to_string(*mActiveModeIt->second).c_str());
                 continue;
             }
 
@@ -383,17 +434,14 @@
             // mode group otherwise. In second case, if the current mode group is different
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
-            const bool isInPolicyForDefault = scores[i].refreshRate->getModeGroup() == anchorGroup;
+            const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
                 ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].refreshRate->toString().c_str(),
-                      mCurrentRefreshRate->toString().c_str());
+                      to_string(*mode).c_str(), to_string(*mActiveModeIt->second).c_str());
                 continue;
             }
 
-            const bool inPrimaryRange =
-                    policy->primaryRange.includes(scores[i].refreshRate->getFps());
-
+            const bool inPrimaryRange = policy->primaryRange.includes(mode->getFps());
             if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
                 !(layer.focused &&
                   (layer.vote == LayerVoteType::ExplicitDefault ||
@@ -404,30 +452,31 @@
             }
 
             const auto layerScore =
-                    calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
+                    calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
             ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
-                  scores[i].refreshRate->getName().c_str(), layerScore);
-            scores[i].score += weight * layerScore;
+                  to_string(mode->getFps()).c_str(), layerScore);
+
+            score += weight * layerScore;
         }
     }
 
     // Now that we scored all the refresh rates we need to pick the one that got the highest score.
     // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
     // or the lower otherwise.
-    const RefreshRate* bestRefreshRate = maxVoteLayers > 0
-            ? getBestRefreshRate(scores.rbegin(), scores.rend())
-            : getBestRefreshRate(scores.begin(), scores.end());
+    const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0
+            ? getMaxScoreRefreshRate(scores.rbegin(), scores.rend())
+            : getMaxScoreRefreshRate(scores.begin(), scores.end());
 
     if (primaryRangeIsSingleRate) {
         // If we never scored any layers, then choose the rate from the primary
         // range instead of picking a random score from the app range.
         if (std::all_of(scores.begin(), scores.end(),
                         [](RefreshRateScore score) { return score.score == 0; })) {
-            const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
-            ALOGV("layers not scored - choose %s", refreshRate.getName().c_str());
-            return {refreshRate, kNoSignals};
+            const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
+            ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str());
+            return {max, kNoSignals};
         } else {
-            return {*bestRefreshRate, kNoSignals};
+            return {bestRefreshRate, kNoSignals};
         }
     }
 
@@ -435,7 +484,7 @@
     // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
     // vote we should not change it if we get a touch event. Only apply touch boost if it will
     // actually increase the refresh rate over the normal selection.
-    const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+    const DisplayModePtr& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
 
     const bool touchBoostForExplicitExact = [&] {
         if (mSupportsFrameRateOverrideByContent) {
@@ -450,12 +499,12 @@
     using fps_approx_ops::operator<;
 
     if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
-        bestRefreshRate->getFps() < touchRefreshRate.getFps()) {
-        ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
+        bestRefreshRate->getFps() < touchRefreshRate->getFps()) {
+        ALOGV("TouchBoost - choose %s", to_string(touchRefreshRate->getFps()).c_str());
         return {touchRefreshRate, GlobalSignals{.touch = true}};
     }
 
-    return {*bestRefreshRate, kNoSignals};
+    return {bestRefreshRate, kNoSignals};
 }
 
 std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
@@ -489,26 +538,28 @@
     return layersByUid;
 }
 
-std::vector<RefreshRateScore> initializeScoresForAllRefreshRates(
-        const AllRefreshRatesMapType& refreshRates) {
-    std::vector<RefreshRateScore> scores;
-    scores.reserve(refreshRates.size());
-    for (const auto& [ignored, refreshRate] : refreshRates) {
-        scores.emplace_back(RefreshRateScore{refreshRate.get(), 0.0f});
-    }
-    std::sort(scores.begin(), scores.end(),
-              [](const auto& a, const auto& b) { return *a.refreshRate < *b.refreshRate; });
-    return scores;
-}
-
 RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
-        const std::vector<LayerRequirement>& layers, Fps displayFrameRate,
+        const std::vector<LayerRequirement>& layers, Fps displayRefreshRate,
         GlobalSignals globalSignals) const {
     ATRACE_CALL();
 
-    ALOGV("getFrameRateOverrides %zu layers", layers.size());
+    ALOGV("%s: %zu layers", __func__, layers.size());
+
     std::lock_guard lock(mLock);
-    std::vector<RefreshRateScore> scores = initializeScoresForAllRefreshRates(mRefreshRates);
+
+    std::vector<RefreshRateScore> scores;
+    scores.reserve(mDisplayModes.size());
+
+    for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
+        scores.emplace_back(RefreshRateScore{it, 0.0f});
+    }
+
+    std::sort(scores.begin(), scores.end(), [](const auto& lhs, const auto& rhs) {
+        const auto& mode1 = lhs.modeIt->second;
+        const auto& mode2 = rhs.modeIt->second;
+        return isStrictlyLess(mode1->getFps(), mode2->getFps());
+    });
+
     std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
             groupLayersByUid(layers);
     UidToFrameRateOverride frameRateOverrides;
@@ -524,8 +575,8 @@
             continue;
         }
 
-        for (auto& score : scores) {
-            score.score = 0;
+        for (auto& [_, score] : scores) {
+            score = 0;
         }
 
         for (const auto& layer : layersWithSameUid) {
@@ -536,137 +587,114 @@
             LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
                                 layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
                                 layer->vote != LayerVoteType::ExplicitExact);
-            for (RefreshRateScore& score : scores) {
-                const auto layerScore = calculateLayerScoreLocked(*layer, *score.refreshRate,
-                                                                  /*isSeamlessSwitch*/ true);
-                score.score += layer->weight * layerScore;
+            for (auto& [modeIt, score] : scores) {
+                constexpr bool isSeamlessSwitch = true;
+                const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(),
+                                                                  isSeamlessSwitch);
+                score += layer->weight * layerScore;
             }
         }
 
         // We just care about the refresh rates which are a divisor of the
         // display refresh rate
-        auto iter =
-                std::remove_if(scores.begin(), scores.end(), [&](const RefreshRateScore& score) {
-                    return getFrameRateDivisor(displayFrameRate, score.refreshRate->getFps()) == 0;
-                });
-        scores.erase(iter, scores.end());
+        const auto it = std::remove_if(scores.begin(), scores.end(), [&](RefreshRateScore score) {
+            const auto& [id, mode] = *score.modeIt;
+            return getFrameRateDivisor(displayRefreshRate, mode->getFps()) == 0;
+        });
+        scores.erase(it, scores.end());
 
         // If we never scored any layers, we don't have a preferred frame rate
         if (std::all_of(scores.begin(), scores.end(),
-                        [](const RefreshRateScore& score) { return score.score == 0; })) {
+                        [](RefreshRateScore score) { return score.score == 0; })) {
             continue;
         }
 
         // Now that we scored all the refresh rates we need to pick the one that got the highest
         // score.
-        const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end());
+        const DisplayModePtr& bestRefreshRate =
+                getMaxScoreRefreshRate(scores.begin(), scores.end());
+
         frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
     }
 
     return frameRateOverrides;
 }
 
-template <typename Iter>
-const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
-    constexpr auto kEpsilon = 0.0001f;
-    const RefreshRate* bestRefreshRate = begin->refreshRate;
-    float max = begin->score;
-    for (auto i = begin; i != end; ++i) {
-        const auto [refreshRate, score] = *i;
-        ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score);
-
-        ATRACE_INT(refreshRate->getName().c_str(), static_cast<int>(std::round(score * 100)));
-
-        if (score > max * (1 + kEpsilon)) {
-            max = score;
-            bestRefreshRate = refreshRate;
-        }
-    }
-
-    return bestRefreshRate;
-}
-
 std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
-        std::optional<DisplayModeId> desiredActiveConfigId, bool timerExpired) const {
+        std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
     std::lock_guard lock(mLock);
 
-    const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId)
-                                                : *mCurrentRefreshRate;
-    const auto& min = *mMinSupportedRefreshRate;
+    const DisplayModePtr& current = desiredActiveModeId
+            ? mDisplayModes.get(*desiredActiveModeId)->get()
+            : mActiveModeIt->second;
 
-    if (current != min) {
-        const auto& refreshRate = timerExpired ? min : current;
-        return refreshRate.getFps();
+    const DisplayModePtr& min = mMinRefreshRateModeIt->second;
+    if (current == min) {
+        return {};
     }
 
-    return {};
+    const auto& mode = timerExpired ? min : current;
+    return mode->getFps();
 }
 
-const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
-    for (auto refreshRate : mPrimaryRefreshRates) {
-        if (mCurrentRefreshRate->getModeGroup() == refreshRate->getModeGroup()) {
-            return *refreshRate;
+const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+    for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
+        const auto& mode = modeIt->second;
+        if (mActiveModeIt->second->getGroup() == mode->getGroup()) {
+            return mode;
         }
     }
+
     ALOGE("Can't find min refresh rate by policy with the same mode group"
           " as the current mode %s",
-          mCurrentRefreshRate->toString().c_str());
-    // Defaulting to the lowest refresh rate
-    return *mPrimaryRefreshRates.front();
+          to_string(*mActiveModeIt->second).c_str());
+
+    // Default to the lowest refresh rate.
+    return mPrimaryRefreshRates.front()->second;
 }
 
-RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+DisplayModePtr RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
     return getMaxRefreshRateByPolicyLocked();
 }
 
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
-    for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
-        const auto& refreshRate = (**it);
-        if (anchorGroup == refreshRate.getModeGroup()) {
-            return refreshRate;
+const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
+    for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
+        const auto& mode = (*it)->second;
+        if (anchorGroup == mode->getGroup()) {
+            return mode;
         }
     }
+
     ALOGE("Can't find max refresh rate by policy with the same mode group"
           " as the current mode %s",
-          mCurrentRefreshRate->toString().c_str());
-    // Defaulting to the highest refresh rate
-    return *mPrimaryRefreshRates.back();
+          to_string(*mActiveModeIt->second).c_str());
+
+    // Default to the highest refresh rate.
+    return mPrimaryRefreshRates.back()->second;
 }
 
-RefreshRate RefreshRateConfigs::getCurrentRefreshRate() const {
+DisplayModePtr RefreshRateConfigs::getActiveMode() const {
     std::lock_guard lock(mLock);
-    return *mCurrentRefreshRate;
+    return mActiveModeIt->second;
 }
 
-RefreshRate RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
-    std::lock_guard lock(mLock);
-    return getCurrentRefreshRateByPolicyLocked();
-}
-
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() const {
-    if (std::find(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
-                  mCurrentRefreshRate) != mAppRequestRefreshRates.end()) {
-        return *mCurrentRefreshRate;
-    }
-    return *mRefreshRates.at(getCurrentPolicyLocked()->defaultMode);
-}
-
-void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
+void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
 
     // Invalidate the cached invocation to getBestRefreshRate. This forces
     // the refresh rate to be recomputed on the next call to getBestRefreshRate.
     mGetBestRefreshRateCache.reset();
 
-    mCurrentRefreshRate = mRefreshRates.at(modeId).get();
+    mActiveModeIt = mDisplayModes.find(modeId);
+    LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
 }
 
-RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
+RefreshRateConfigs::RefreshRateConfigs(DisplayModes modes, DisplayModeId activeModeId,
                                        Config config)
       : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
     initializeIdleTimer();
-    updateDisplayModes(modes, currentModeId);
+    updateDisplayModes(std::move(modes), activeModeId);
 }
 
 void RefreshRateConfigs::initializeIdleTimer() {
@@ -688,64 +716,43 @@
     }
 }
 
-void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
-                                            DisplayModeId currentModeId) {
+void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
     std::lock_guard lock(mLock);
 
-    // The current mode should be supported
-    LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
-        return mode->getId() == currentModeId;
-    }));
-
     // Invalidate the cached invocation to getBestRefreshRate. This forces
     // the refresh rate to be recomputed on the next call to getBestRefreshRate.
     mGetBestRefreshRateCache.reset();
 
-    mRefreshRates.clear();
-    for (const auto& mode : modes) {
-        const auto modeId = mode->getId();
-        mRefreshRates.emplace(modeId,
-                              std::make_unique<RefreshRate>(mode, RefreshRate::ConstructorTag(0)));
-        if (modeId == currentModeId) {
-            mCurrentRefreshRate = mRefreshRates.at(modeId).get();
-        }
-    }
+    mDisplayModes = std::move(modes);
+    mActiveModeIt = mDisplayModes.find(activeModeId);
+    LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
 
-    std::vector<const RefreshRate*> sortedModes;
-    getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedModes);
+    const auto sortedModes =
+            sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; });
+    mMinRefreshRateModeIt = sortedModes.front();
+    mMaxRefreshRateModeIt = sortedModes.back();
+
     // Reset the policy because the old one may no longer be valid.
     mDisplayManagerPolicy = {};
-    mDisplayManagerPolicy.defaultMode = currentModeId;
-    mMinSupportedRefreshRate = sortedModes.front();
-    mMaxSupportedRefreshRate = sortedModes.back();
+    mDisplayManagerPolicy.defaultMode = activeModeId;
 
-    mSupportsFrameRateOverrideByContent = false;
-    if (mConfig.enableFrameRateOverride) {
-        for (const auto& mode1 : sortedModes) {
-            for (const auto& mode2 : sortedModes) {
-                if (getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
-                    mSupportsFrameRateOverrideByContent = true;
-                    break;
-                }
-            }
-        }
-    }
+    mSupportsFrameRateOverrideByContent =
+            mConfig.enableFrameRateOverride && canModesSupportFrameRateOverride(sortedModes);
 
     constructAvailableRefreshRates();
 }
 
 bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
     // defaultMode must be a valid mode, and within the given refresh rate range.
-    auto iter = mRefreshRates.find(policy.defaultMode);
-    if (iter == mRefreshRates.end()) {
+    if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
+        if (!policy.primaryRange.includes(mode->get()->getFps())) {
+            ALOGE("Default mode is not in the primary range.");
+            return false;
+        }
+    } else {
         ALOGE("Default mode is not found.");
         return false;
     }
-    const RefreshRate& refreshRate = *iter->second;
-    if (!policy.primaryRange.includes(refreshRate.getFps())) {
-        ALOGE("Default mode is not in the primary range.");
-        return false;
-    }
 
     using namespace fps_approx_ops;
     return policy.appRequestRange.min <= policy.primaryRange.min &&
@@ -799,77 +806,46 @@
 
 bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const {
     std::lock_guard lock(mLock);
-    for (const RefreshRate* refreshRate : mAppRequestRefreshRates) {
-        if (refreshRate->getModeId() == modeId) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void RefreshRateConfigs::getSortedRefreshRateListLocked(
-        const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
-        std::vector<const RefreshRate*>* outRefreshRates) {
-    outRefreshRates->clear();
-    outRefreshRates->reserve(mRefreshRates.size());
-    for (const auto& [type, refreshRate] : mRefreshRates) {
-        if (shouldAddRefreshRate(*refreshRate)) {
-            ALOGV("getSortedRefreshRateListLocked: mode %d added to list policy",
-                  refreshRate->getModeId().value());
-            outRefreshRates->push_back(refreshRate.get());
-        }
-    }
-
-    std::sort(outRefreshRates->begin(), outRefreshRates->end(),
-              [](const auto refreshRate1, const auto refreshRate2) {
-                  if (refreshRate1->mode->getVsyncPeriod() !=
-                      refreshRate2->mode->getVsyncPeriod()) {
-                      return refreshRate1->mode->getVsyncPeriod() >
-                              refreshRate2->mode->getVsyncPeriod();
-                  } else {
-                      return refreshRate1->mode->getGroup() > refreshRate2->mode->getGroup();
-                  }
-              });
+    return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
+                       [modeId](DisplayModeIterator modeIt) {
+                           return modeIt->second->getId() == modeId;
+                       });
 }
 
 void RefreshRateConfigs::constructAvailableRefreshRates() {
-    // Filter modes based on current policy and sort based on vsync period
+    // Filter modes based on current policy and sort on refresh rate.
     const Policy* policy = getCurrentPolicyLocked();
-    const auto& defaultMode = mRefreshRates.at(policy->defaultMode)->mode;
-    ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str());
+    ALOGV("%s: %s ", __func__, policy->toString().c_str());
 
-    auto filterRefreshRates =
-            [&](FpsRange range, const char* rangeName,
-                std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock) {
-                getSortedRefreshRateListLocked(
-                        [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
-                            const auto& mode = refreshRate.mode;
+    const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
 
-                            return mode->getHeight() == defaultMode->getHeight() &&
-                                    mode->getWidth() == defaultMode->getWidth() &&
-                                    mode->getDpiX() == defaultMode->getDpiX() &&
-                                    mode->getDpiY() == defaultMode->getDpiY() &&
-                                    (policy->allowGroupSwitching ||
-                                     mode->getGroup() == defaultMode->getGroup()) &&
-                                    range.includes(mode->getFps());
-                        },
-                        outRefreshRates);
+    const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) {
+        const auto filter = [&](const DisplayMode& mode) {
+            return mode.getResolution() == defaultMode->getResolution() &&
+                    mode.getDpi() == defaultMode->getDpi() &&
+                    (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
+                    range.includes(mode.getFps());
+        };
 
-                LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(), "No matching modes for %s range %s",
-                                    rangeName, to_string(range).c_str());
+        const auto modes = sortByRefreshRate(mDisplayModes, filter);
+        LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName,
+                            to_string(range).c_str());
 
-                auto stringifyRefreshRates = [&]() -> std::string {
-                    std::string str;
-                    for (auto refreshRate : *outRefreshRates) {
-                        base::StringAppendF(&str, "%s ", refreshRate->getName().c_str());
-                    }
-                    return str;
-                };
-                ALOGV("%s refresh rates: %s", rangeName, stringifyRefreshRates().c_str());
-            };
+        const auto stringifyModes = [&] {
+            std::string str;
+            for (const auto modeIt : modes) {
+                str += to_string(modeIt->second->getFps());
+                str.push_back(' ');
+            }
+            return str;
+        };
+        ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str());
 
-    filterRefreshRates(policy->primaryRange, "primary", &mPrimaryRefreshRates);
-    filterRefreshRates(policy->appRequestRange, "app request", &mAppRequestRefreshRates);
+        return modes;
+    };
+
+    mPrimaryRefreshRates = filterRefreshRates(policy->primaryRange, "primary");
+    mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRange, "app request");
 }
 
 Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
@@ -893,36 +869,39 @@
 
 RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
     std::lock_guard lock(mLock);
-    const auto& deviceMin = *mMinSupportedRefreshRate;
-    const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
-    const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
-    const auto& currentPolicy = getCurrentPolicyLocked();
+
+    const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps();
+    const DisplayModePtr& minByPolicy = getMinRefreshRateByPolicyLocked();
 
     // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
     // the min allowed refresh rate is higher than the device min, we do not want to enable the
     // timer.
-    if (deviceMin < minByPolicy) {
-        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+    if (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) {
+        return KernelIdleTimerAction::TurnOff;
     }
+
+    const DisplayModePtr& maxByPolicy = getMaxRefreshRateByPolicyLocked();
     if (minByPolicy == maxByPolicy) {
-        // when min primary range in display manager policy is below device min turn on the timer.
-        if (isApproxLess(currentPolicy->primaryRange.min, deviceMin.getFps())) {
-            return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+        // Turn on the timer when the min of the primary range is below the device min.
+        if (const Policy* currentPolicy = getCurrentPolicyLocked();
+            isApproxLess(currentPolicy->primaryRange.min, deviceMinFps)) {
+            return KernelIdleTimerAction::TurnOn;
         }
-        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+        return KernelIdleTimerAction::TurnOff;
     }
+
     // Turn on the timer in all other cases.
-    return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+    return KernelIdleTimerAction::TurnOn;
 }
 
-int RefreshRateConfigs::getFrameRateDivisor(Fps displayFrameRate, Fps layerFrameRate) {
+int RefreshRateConfigs::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) {
     // This calculation needs to be in sync with the java code
     // in DisplayManagerService.getDisplayInfoForFrameRateOverride
 
     // The threshold must be smaller than 0.001 in order to differentiate
     // between the fractional pairs (e.g. 59.94 and 60).
     constexpr float kThreshold = 0.0009f;
-    const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
+    const auto numPeriods = displayRefreshRate.getValue() / layerFrameRate.getValue();
     const auto numPeriodsRounded = std::round(numPeriods);
     if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
         return 0;
@@ -952,29 +931,32 @@
                             currentPolicy.toString().c_str());
     }
 
-    auto mode = mCurrentRefreshRate->mode;
-    base::StringAppendF(&result, "Current mode: %s\n", mCurrentRefreshRate->toString().c_str());
+    base::StringAppendF(&result, "Active mode: %s\n", to_string(*mActiveModeIt->second).c_str());
 
-    result.append("Refresh rates:\n");
-    for (const auto& [id, refreshRate] : mRefreshRates) {
-        mode = refreshRate->mode;
-        base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
+    result.append("Display modes:\n");
+    for (const auto& [id, mode] : mDisplayModes) {
+        result.push_back('\t');
+        result.append(to_string(*mode));
+        result.push_back('\n');
     }
 
     base::StringAppendF(&result, "Supports Frame Rate Override By Content: %s\n",
                         mSupportsFrameRateOverrideByContent ? "yes" : "no");
-    base::StringAppendF(&result, "Idle timer: ");
-    if (mConfig.kernelIdleTimerController.has_value()) {
-        if (mConfig.kernelIdleTimerController == KernelIdleTimerController::Sysprop) {
-            base::StringAppendF(&result, "(kernel(sysprop))");
-        } else {
-            base::StringAppendF(&result, "(kernel(hwc))");
-        }
+
+    result.append("Idle timer: ");
+    if (const auto controller = mConfig.kernelIdleTimerController) {
+        base::StringAppendF(&result, "(kernel via %s) ", ftl::enum_string(*controller).c_str());
     } else {
-        base::StringAppendF(&result, "(platform)");
+        result.append("(platform) ");
     }
-    base::StringAppendF(&result, " %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
-    result.append("\n");
+
+    if (mIdleTimer) {
+        result.append(mIdleTimer->dump());
+    } else {
+        result.append("off");
+    }
+
+    result.append("\n\n");
 }
 
 std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 30d3edd..05a8692 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -56,50 +56,6 @@
     static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
             std::chrono::nanoseconds(800us).count();
 
-    class RefreshRate {
-    private:
-        // Effectively making the constructor private while allowing
-        // std::make_unique to create the object
-        struct ConstructorTag {
-            explicit ConstructorTag(int) {}
-        };
-
-    public:
-        RefreshRate(DisplayModePtr mode, ConstructorTag) : mode(mode) {}
-
-        DisplayModeId getModeId() const { return mode->getId(); }
-        nsecs_t getVsyncPeriod() const { return mode->getVsyncPeriod(); }
-        int32_t getModeGroup() const { return mode->getGroup(); }
-        std::string getName() const { return to_string(getFps()); }
-        Fps getFps() const { return mode->getFps(); }
-        DisplayModePtr getMode() const { return mode; }
-
-        // Checks whether the fps of this RefreshRate struct is within a given min and max refresh
-        // rate passed in. Margin of error is applied to the boundaries for approximation.
-        bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const;
-
-        bool operator==(const RefreshRate& other) const { return mode == other.mode; }
-        bool operator!=(const RefreshRate& other) const { return !operator==(other); }
-
-        bool operator<(const RefreshRate& other) const {
-            return isStrictlyLess(getFps(), other.getFps());
-        }
-
-        std::string toString() const;
-        friend std::ostream& operator<<(std::ostream& os, const RefreshRate& refreshRate) {
-            return os << refreshRate.toString();
-        }
-
-    private:
-        friend RefreshRateConfigs;
-        friend class RefreshRateConfigsTest;
-
-        DisplayModePtr mode;
-    };
-
-    using AllRefreshRatesMapType =
-            std::unordered_map<DisplayModeId, std::unique_ptr<const RefreshRate>>;
-
     struct Policy {
     private:
         static constexpr int kAllowGroupSwitchingDefault = false;
@@ -236,12 +192,12 @@
 
     // Returns the refresh rate that best fits the given layers, and whether the refresh rate was
     // chosen based on touch boost and/or idle timer.
-    std::pair<RefreshRate, GlobalSignals> getBestRefreshRate(const std::vector<LayerRequirement>&,
-                                                             GlobalSignals) const EXCLUDES(mLock);
+    std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRate(
+            const std::vector<LayerRequirement>&, GlobalSignals) const EXCLUDES(mLock);
 
     FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
         std::lock_guard lock(mLock);
-        return {mMinSupportedRefreshRate->getFps(), mMaxSupportedRefreshRate->getFps()};
+        return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
     }
 
     std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
@@ -249,30 +205,15 @@
 
     // Returns the highest refresh rate according to the current policy. May change at runtime. Only
     // uses the primary range, not the app request range.
-    RefreshRate getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+    DisplayModePtr getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
 
-    // Returns the current refresh rate
-    RefreshRate getCurrentRefreshRate() const EXCLUDES(mLock);
-
-    // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
-    // the policy.
-    RefreshRate getCurrentRefreshRateByPolicy() const;
-
-    // Returns the refresh rate that corresponds to a DisplayModeId. This may change at
-    // runtime.
-    // TODO(b/159590486) An invalid mode id may be given here if the dipslay modes have changed.
-    RefreshRate getRefreshRateFromModeId(DisplayModeId modeId) const EXCLUDES(mLock) {
-        std::lock_guard lock(mLock);
-        return *mRefreshRates.at(modeId);
-    };
-
-    // Stores the current modeId the device operates at
-    void setCurrentModeId(DisplayModeId) EXCLUDES(mLock);
+    void setActiveModeId(DisplayModeId) EXCLUDES(mLock);
+    DisplayModePtr getActiveMode() const EXCLUDES(mLock);
 
     // Returns a known frame rate that is the closest to frameRate
     Fps findClosestKnownFrameRate(Fps frameRate) const;
 
-    enum class KernelIdleTimerController { Sysprop, HwcApi };
+    enum class KernelIdleTimerController { Sysprop, HwcApi, ftl_last = HwcApi };
 
     // Configuration flags.
     struct Config {
@@ -291,7 +232,7 @@
         std::optional<KernelIdleTimerController> kernelIdleTimerController;
     };
 
-    RefreshRateConfigs(const DisplayModes&, DisplayModeId,
+    RefreshRateConfigs(DisplayModes, DisplayModeId activeModeId,
                        Config config = {.enableFrameRateOverride = false,
                                         .frameRateMultipleThreshold = 0,
                                         .idleTimerTimeout = 0ms,
@@ -305,7 +246,7 @@
     // differ in resolution.
     bool canSwitch() const EXCLUDES(mLock) {
         std::lock_guard lock(mLock);
-        return mRefreshRates.size() > 1;
+        return mDisplayModes.size() > 1;
     }
 
     // Class to enumerate options around toggling the kernel timer on and off.
@@ -323,7 +264,7 @@
     // Return the display refresh rate divisor to match the layer
     // frame rate, or 0 if the display refresh rate is not a multiple of the
     // layer refresh rate.
-    static int getFrameRateDivisor(Fps displayFrameRate, Fps layerFrameRate);
+    static int getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate);
 
     // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
     // for an integer t.
@@ -391,54 +332,39 @@
 
     void constructAvailableRefreshRates() REQUIRES(mLock);
 
-    void getSortedRefreshRateListLocked(
-            const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
-            std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
-
-    std::pair<RefreshRate, GlobalSignals> getBestRefreshRateLocked(
+    std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRateLocked(
             const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock);
 
-    // Returns the refresh rate with the highest score in the collection specified from begin
-    // to end. If there are more than one with the same highest refresh rate, the first one is
-    // returned.
-    template <typename Iter>
-    const RefreshRate* getBestRefreshRate(Iter begin, Iter end) const;
-
     // Returns number of display frames and remainder when dividing the layer refresh period by
     // display refresh period.
     std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;
 
     // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
     // uses the primary range, not the app request range.
-    const RefreshRate& getMinRefreshRateByPolicyLocked() const REQUIRES(mLock);
+    const DisplayModePtr& getMinRefreshRateByPolicyLocked() const REQUIRES(mLock);
 
     // Returns the highest refresh rate according to the current policy. May change at runtime. Only
     // uses the primary range, not the app request range.
-    const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
-        return getMaxRefreshRateByPolicyLocked(mCurrentRefreshRate->getModeGroup());
+    const DisplayModePtr& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
+    const DisplayModePtr& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
+        return getMaxRefreshRateByPolicyLocked(mActiveModeIt->second->getGroup());
     }
 
-    const RefreshRate& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
-
-    // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
-    // the policy.
-    const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
-
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
     // Returns whether the layer is allowed to vote for the given refresh rate.
-    bool isVoteAllowed(const LayerRequirement&, const RefreshRate&) const;
+    bool isVoteAllowed(const LayerRequirement&, Fps) const;
 
     // calculates a score for a layer. Used to determine the display refresh rate
     // and the frame rate override for certains applications.
-    float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
+    float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
                                     bool isSeamlessSwitch) const REQUIRES(mLock);
 
-    float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&,
-                                                    const RefreshRate&) const REQUIRES(mLock);
+    float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const
+            REQUIRES(mLock);
 
-    void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
+    void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock);
 
     void initializeIdleTimer();
 
@@ -449,32 +375,22 @@
                                                              : mIdleTimerCallbacks->platform;
     }
 
-    // The list of refresh rates, indexed by display modes ID. This may change after this
-    // object is initialized.
-    AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
+    // The display modes of the active display. The DisplayModeIterators below are pointers into
+    // this container, so must be invalidated whenever the DisplayModes change. The Policy below
+    // is also dependent, so must be reset as well.
+    DisplayModes mDisplayModes GUARDED_BY(mLock);
 
-    // The list of refresh rates in the primary range of the current policy, ordered by vsyncPeriod
-    // (the first element is the lowest refresh rate).
-    std::vector<const RefreshRate*> mPrimaryRefreshRates GUARDED_BY(mLock);
+    DisplayModeIterator mActiveModeIt GUARDED_BY(mLock);
+    DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock);
+    DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
 
-    // The list of refresh rates in the app request range of the current policy, ordered by
-    // vsyncPeriod (the first element is the lowest refresh rate).
-    std::vector<const RefreshRate*> mAppRequestRefreshRates GUARDED_BY(mLock);
+    // Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate.
+    std::vector<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock);
+    std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock);
 
-    // The current display mode. This will change at runtime. This is set by SurfaceFlinger on
-    // the main thread, and read by the Scheduler (and other objects) on other threads.
-    const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
-
-    // The policy values will change at runtime. They're set by SurfaceFlinger on the main thread,
-    // and read by the Scheduler (and other objects) on other threads.
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
 
-    // The min and max refresh rates supported by the device.
-    // This may change at runtime.
-    const RefreshRate* mMinSupportedRefreshRate GUARDED_BY(mLock);
-    const RefreshRate* mMaxSupportedRefreshRate GUARDED_BY(mLock);
-
     mutable std::mutex mLock;
 
     // A sorted list of known frame rates that a Heuristic layer will choose
@@ -486,7 +402,7 @@
 
     struct GetBestRefreshRateCache {
         std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
-        std::pair<RefreshRate, GlobalSignals> result;
+        std::pair<DisplayModePtr, GlobalSignals> result;
     };
     mutable std::optional<GetBestRefreshRateCache> mGetBestRefreshRateCache GUARDED_BY(mLock);
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index f1ad755..ed65bc6 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -17,7 +17,9 @@
 #pragma once
 
 #include <chrono>
-#include <numeric>
+#include <cinttypes>
+#include <cstdlib>
+#include <string>
 
 #include <android-base/stringprintf.h>
 #include <ftl/small_map.h>
@@ -25,6 +27,7 @@
 
 #include <scheduler/Fps.h>
 
+#include "DisplayHardware/Hal.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android::scheduler {
@@ -41,16 +44,16 @@
     static constexpr int64_t MS_PER_HOUR = 60 * MS_PER_MIN;
     static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
 
+    using PowerMode = android::hardware::graphics::composer::hal::PowerMode;
+
 public:
     // TODO(b/185535769): Inject clock to avoid sleeping in tests.
-    RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate,
-                     android::hardware::graphics::composer::hal::PowerMode currentPowerMode)
+    RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate, PowerMode currentPowerMode)
           : mTimeStats(timeStats),
             mCurrentRefreshRate(currentRefreshRate),
             mCurrentPowerMode(currentPowerMode) {}
 
-    // Sets power mode.
-    void setPowerMode(android::hardware::graphics::composer::hal::PowerMode mode) {
+    void setPowerMode(PowerMode mode) {
         if (mCurrentPowerMode == mode) {
             return;
         }
@@ -115,7 +118,7 @@
 
         uint32_t fps = 0;
 
-        if (mCurrentPowerMode == android::hardware::graphics::composer::hal::PowerMode::ON) {
+        if (mCurrentPowerMode == PowerMode::ON) {
             // Normal power mode is counted under different config modes.
             const auto total = std::as_const(mFpsTotalTimes)
                                        .get(mCurrentRefreshRate)
@@ -144,7 +147,7 @@
     TimeStats& mTimeStats;
 
     Fps mCurrentRefreshRate;
-    android::hardware::graphics::composer::hal::PowerMode mCurrentPowerMode;
+    PowerMode mCurrentPowerMode;
 
     ftl::SmallMap<Fps, std::chrono::milliseconds, 2, FpsApproxEqual> mFpsTotalTimes;
     std::chrono::milliseconds mScreenOffTime = std::chrono::milliseconds::zero();
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1fa455a..74d7739 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -166,18 +166,15 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const auto refreshRateConfigs = holdRefreshRateConfigs();
-        nsecs_t basePeriod = refreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+        const Fps refreshRate = holdRefreshRateConfigs()->getActiveMode()->getFps();
+        const nsecs_t basePeriod = refreshRate.getPeriodNsecs();
+
         const auto frameRate = getFrameRateOverride(uid);
         if (!frameRate.has_value()) {
             return basePeriod;
         }
 
-        const auto divisor =
-                scheduler::RefreshRateConfigs::getFrameRateDivisor(refreshRateConfigs
-                                                                           ->getCurrentRefreshRate()
-                                                                           .getFps(),
-                                                                   *frameRate);
+        const auto divisor = RefreshRateConfigs::getFrameRateDivisor(refreshRate, *frameRate);
         if (divisor <= 1) {
             return basePeriod;
         }
@@ -307,7 +304,7 @@
     // 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->getCurrentRefreshRate().getMode() != mPolicy.mode) {
+        mRefreshRateConfigs->getActiveMode() != mPolicy.mode) {
         return;
     }
 
@@ -422,7 +419,7 @@
     }
 }
 
-void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
+void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate) {
     {
         std::lock_guard<std::mutex> lock(mHWVsyncLock);
         if (makeAvailable) {
@@ -434,11 +431,7 @@
         }
     }
 
-    if (period <= 0) {
-        return;
-    }
-
-    setVsyncPeriod(period);
+    setVsyncPeriod(refreshRate.getPeriodNsecs());
 }
 
 void Scheduler::resync() {
@@ -448,15 +441,17 @@
     const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        const auto vsyncPeriod = [&] {
+        const auto refreshRate = [&] {
             std::scoped_lock lock(mRefreshRateConfigsLock);
-            return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+            return mRefreshRateConfigs->getActiveMode()->getFps();
         }();
-        resyncToHardwareVsync(false, vsyncPeriod);
+        resyncToHardwareVsync(false, refreshRate);
     }
 }
 
 void Scheduler::setVsyncPeriod(nsecs_t period) {
+    if (period <= 0) return;
+
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     mVsyncSchedule->getController().startPeriodTransition(period);
 
@@ -578,21 +573,20 @@
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const auto refreshRate = [&] {
+    const Fps refreshRate = [&] {
         std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getCurrentRefreshRate();
+        return mRefreshRateConfigs->getActiveMode()->getFps();
     }();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
     using namespace fps_approx_ops;
 
-    if (state == TimerState::Reset && refreshRate.getFps() > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
+    if (state == TimerState::Reset && refreshRate > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // If we're not in performance mode then the kernel timer shouldn't do
         // anything, as the refresh rate during DPU power collapse will be the
         // same.
-        resyncToHardwareVsync(true /* makeAvailable */, refreshRate.getVsyncPeriod());
-    } else if (state == TimerState::Expired &&
-               refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
+        resyncToHardwareVsync(true /* makeAvailable */, refreshRate);
+    } else if (state == TimerState::Expired && refreshRate <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // Disable HW VSYNC if the timer expired, as we don't need it enabled if
         // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
         // need to update the VsyncController model anyway.
@@ -693,11 +687,9 @@
         }
     }
     if (refreshRateChanged) {
-        const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
-
-        mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                             consideredSignals.idle ? DisplayModeEvent::None
-                                                                    : DisplayModeEvent::Changed);
+        mSchedulerCallback.requestDisplayMode(std::move(newMode),
+                                              consideredSignals.idle ? DisplayModeEvent::None
+                                                                     : DisplayModeEvent::Changed);
     }
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -715,16 +707,13 @@
     if (mDisplayPowerTimer &&
         (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
         constexpr GlobalSignals kNoSignals;
-        return {configs->getMaxRefreshRateByPolicy().getMode(), kNoSignals};
+        return {configs->getMaxRefreshRateByPolicy(), kNoSignals};
     }
 
     const GlobalSignals signals{.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
                                 .idle = mPolicy.idleTimer == TimerState::Expired};
 
-    const auto [refreshRate, consideredSignals] =
-            configs->getBestRefreshRate(mPolicy.contentRequirements, signals);
-
-    return {refreshRate.getMode(), consideredSignals};
+    return configs->getBestRefreshRate(mPolicy.contentRequirements, signals);
 }
 
 DisplayModePtr Scheduler::getPreferredDisplayMode() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f6c81c0..a8113d4 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -86,12 +86,11 @@
     // Indicates frame activity, i.e. whether commit and/or composite is taking place.
     enum class FrameHint { kNone, kActive };
 
-    using RefreshRate = RefreshRateConfigs::RefreshRate;
     using DisplayModeEvent = scheduler::DisplayModeEvent;
 
     virtual void scheduleComposite(FrameHint) = 0;
     virtual void setVsyncEnabled(bool) = 0;
-    virtual void changeRefreshRate(const RefreshRate&, DisplayModeEvent) = 0;
+    virtual void requestDisplayMode(DisplayModePtr, DisplayModeEvent) = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
     virtual void triggerOnFrameRateOverridesChanged() = 0;
 
@@ -166,8 +165,7 @@
     // If makeAvailable is true, then hardware vsync will be turned on.
     // Otherwise, if hardware vsync is not already enabled then this method will
     // no-op.
-    // The period is the vsync period from the current display configuration.
-    void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
+    void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate);
     void resync() EXCLUDES(mRefreshRateConfigsLock);
     void forceNextResync() { mLastResyncTime = 0; }
 
@@ -236,7 +234,7 @@
 
     nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
         std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+        return mRefreshRateConfigs->getActiveMode()->getFps().getPeriodNsecs();
     }
 
     // Returns the framerate of the layer with the given sequence ID
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 3186d6d..4923031 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -29,6 +29,7 @@
 
 namespace android::scheduler {
 
+class TimeKeeper;
 class VSyncTracker;
 
 // VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bd831ff..2edc05b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -324,7 +324,6 @@
 uint32_t SurfaceFlinger::maxGraphicsWidth;
 uint32_t SurfaceFlinger::maxGraphicsHeight;
 bool SurfaceFlinger::hasWideColorDisplay;
-ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0;
 bool SurfaceFlinger::useContextPriority;
 Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
@@ -416,22 +415,6 @@
 
     useContextPriority = use_context_priority(true);
 
-    using Values = SurfaceFlingerProperties::primary_display_orientation_values;
-    switch (primary_display_orientation(Values::ORIENTATION_0)) {
-        case Values::ORIENTATION_0:
-            break;
-        case Values::ORIENTATION_90:
-            internalDisplayOrientation = ui::ROTATION_90;
-            break;
-        case Values::ORIENTATION_180:
-            internalDisplayOrientation = ui::ROTATION_180;
-            break;
-        case Values::ORIENTATION_270:
-            internalDisplayOrientation = ui::ROTATION_270;
-            break;
-    }
-    ALOGV("Internal Display Orientation: %s", toCString(internalDisplayOrientation));
-
     mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries();
 
     // debugging stuff...
@@ -578,13 +561,13 @@
 
     const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
     if (index < 0) {
-        ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+        ALOGE("%s: Invalid display token %p", __func__, displayToken.get());
         return;
     }
 
     const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
     if (state.physical) {
-        ALOGE("%s: Invalid operation on physical display", __FUNCTION__);
+        ALOGE("%s: Invalid operation on physical display", __func__);
         return;
     }
     mInterceptor->saveDisplayDeletion(state.sequenceId);
@@ -1014,9 +997,7 @@
 
     info->secure = display->isSecure();
     info->deviceProductInfo = display->getDeviceProductInfo();
-
-    // TODO: Scale this to multiple displays.
-    info->installOrientation = display->isPrimary() ? internalDisplayOrientation : ui::ROTATION_0;
+    info->installOrientation = display->getPhysicalOrientation();
 
     return NO_ERROR;
 }
@@ -1039,24 +1020,21 @@
         return INVALID_OPERATION;
     }
 
-    info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
+    info->activeDisplayModeId = display->getActiveMode()->getId().value();
 
     const auto& supportedModes = display->getSupportedModes();
     info->supportedDisplayModes.clear();
     info->supportedDisplayModes.reserve(supportedModes.size());
-    for (const auto& mode : supportedModes) {
+
+    for (const auto& [id, mode] : supportedModes) {
         ui::DisplayMode outMode;
-        outMode.id = static_cast<int32_t>(mode->getId().value());
+        outMode.id = static_cast<int32_t>(id.value());
 
-        auto width = mode->getWidth();
-        auto height = mode->getHeight();
+        auto [width, height] = mode->getResolution();
+        auto [xDpi, yDpi] = mode->getDpi();
 
-        auto xDpi = mode->getDpiX();
-        auto yDpi = mode->getDpiY();
-
-        if (display->isPrimary() &&
-            (internalDisplayOrientation == ui::ROTATION_90 ||
-             internalDisplayOrientation == ui::ROTATION_270)) {
+        if (const auto physicalOrientation = display->getPhysicalOrientation();
+            physicalOrientation == ui::ROTATION_90 || physicalOrientation == ui::ROTATION_270) {
             std::swap(width, height);
             std::swap(xDpi, yDpi);
         }
@@ -1104,13 +1082,17 @@
     info->autoLowLatencyModeSupported =
             getHwComposer().hasDisplayCapability(*displayId,
                                                  DisplayCapability::AUTO_LOW_LATENCY_MODE);
-    std::vector<hal::ContentType> types;
-    getHwComposer().getSupportedContentTypes(*displayId, &types);
-    info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
-        return type == hal::ContentType::GAME;
-    });
+    info->gameContentTypeSupported =
+            getHwComposer().supportsContentType(*displayId, hal::ContentType::GAME);
 
-    info->preferredBootDisplayMode = display->getPreferredBootModeId();
+    info->preferredBootDisplayMode = static_cast<ui::DisplayModeId>(-1);
+    if (getHwComposer().getBootDisplayModeSupport()) {
+        if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(*displayId)) {
+            if (const auto modeId = display->translateModeId(*hwcId)) {
+                info->preferredBootDisplayMode = modeId->value();
+            }
+        }
+    }
 
     return NO_ERROR;
 }
@@ -1142,7 +1124,7 @@
 
         // Start receiving vsync samples now, so that we can detect a period
         // switch.
-        mScheduler->resyncToHardwareVsync(true, info.mode->getVsyncPeriod());
+        mScheduler->resyncToHardwareVsync(true, info.mode->getFps());
         // As we called to set period, we will call to onRefreshRateChangeCompleted once
         // VsyncController model is locked.
         modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
@@ -1208,7 +1190,7 @@
         return;
     }
 
-    if (display->getActiveMode()->getSize() != upcomingModeInfo.mode->getSize()) {
+    if (display->getActiveMode()->getResolution() != upcomingModeInfo.mode->getResolution()) {
         auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
         // We need to generate new sequenceId in order to recreate the display (and this
         // way the framebuffer).
@@ -1244,7 +1226,7 @@
 void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) {
     const auto refreshRate = display->getDesiredActiveMode()->mode->getFps();
     clearDesiredActiveModeState(display);
-    mScheduler->resyncToHardwareVsync(true, refreshRate.getPeriodNsecs());
+    mScheduler->resyncToHardwareVsync(true, refreshRate);
     updatePhaseConfiguration(refreshRate);
 }
 
@@ -1432,31 +1414,41 @@
     return future.get();
 }
 
-status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) {
+status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken,
+                                            ui::DisplayModeId modeId) {
+    const char* const whence = __func__;
     auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
-        if (const auto displayDevice = getDisplayDeviceLocked(displayToken)) {
-            const auto mode = displayDevice->getMode(DisplayModeId{id});
-            if (mode == nullptr) {
-                ALOGE("%s: invalid display mode (%d)", __FUNCTION__, id);
-                return BAD_VALUE;
-            }
+        const auto display = getDisplayDeviceLocked(displayToken);
+        if (!display) {
+            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+            return NAME_NOT_FOUND;
+        }
 
-            return getHwComposer().setBootDisplayMode(displayDevice->getPhysicalId(),
-                                                      mode->getHwcId());
-        } else {
-            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+        if (display->isVirtual()) {
+            ALOGE("%s: Invalid operation on virtual display", whence);
+            return INVALID_OPERATION;
+        }
+
+        const auto displayId = display->getPhysicalId();
+        const auto mode = display->getMode(DisplayModeId{modeId});
+        if (!mode) {
+            ALOGE("%s: Invalid mode %d for display %s", whence, modeId,
+                  to_string(displayId).c_str());
             return BAD_VALUE;
         }
+
+        return getHwComposer().setBootDisplayMode(displayId, mode->getHwcId());
     });
     return future.get();
 }
 
 status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) {
+    const char* const whence = __func__;
     auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
             return getHwComposer().clearBootDisplayMode(*displayId);
         } else {
-            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
             return BAD_VALUE;
         }
     });
@@ -1504,7 +1496,7 @@
 
     auto display = getDisplayDeviceLocked(displayToken);
     if (!display) {
-        ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+        ALOGE("%s: Invalid display token %p", __func__, displayToken.get());
         return NAME_NOT_FOUND;
     }
 
@@ -2394,6 +2386,42 @@
     return true;
 }
 
+ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId,
+                                                           bool isPrimary) const {
+    const auto id = PhysicalDisplayId::tryCast(displayId);
+    if (!id) {
+        return ui::ROTATION_0;
+    }
+    if (getHwComposer().getComposer()->isSupported(
+                Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
+        switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
+            case Hwc2::AidlTransform::ROT_90:
+                return ui::ROTATION_90;
+            case Hwc2::AidlTransform::ROT_180:
+                return ui::ROTATION_180;
+            case Hwc2::AidlTransform::ROT_270:
+                return ui::ROTATION_270;
+            default:
+                return ui::ROTATION_0;
+        }
+    }
+
+    if (isPrimary) {
+        using Values = SurfaceFlingerProperties::primary_display_orientation_values;
+        switch (primary_display_orientation(Values::ORIENTATION_0)) {
+            case Values::ORIENTATION_90:
+                return ui::ROTATION_90;
+            case Values::ORIENTATION_180:
+                return ui::ROTATION_180;
+            case Values::ORIENTATION_270:
+                return ui::ROTATION_270;
+            default:
+                break;
+        }
+    }
+    return ui::ROTATION_0;
+}
+
 void SurfaceFlinger::postComposition() {
     ATRACE_CALL();
     ALOGV("postComposition");
@@ -2666,11 +2694,11 @@
     mDebugInTransaction = 0;
 }
 
-void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
-                                      DisplayModePtr& outActiveMode) const {
+std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes(
+        PhysicalDisplayId displayId) const {
     std::vector<HWComposer::HWCDisplayMode> hwcModes;
     std::optional<hal::HWDisplayId> activeModeHwcId;
-    bool activeModeIsSupported;
+
     int attempt = 0;
     constexpr int kMaxAttempts = 3;
     do {
@@ -2678,63 +2706,60 @@
         activeModeHwcId = getHwComposer().getActiveMode(displayId);
         LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
 
-        activeModeIsSupported =
-                std::any_of(hwcModes.begin(), hwcModes.end(),
-                            [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
-                                return mode.hwcId == *activeModeHwcId;
-                            });
-    } while (!activeModeIsSupported && ++attempt < kMaxAttempts);
-    LOG_ALWAYS_FATAL_IF(!activeModeIsSupported,
+        const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
+            return mode.hwcId == *activeModeHwcId;
+        };
+
+        if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) {
+            break;
+        }
+    } while (++attempt < kMaxAttempts);
+
+    LOG_ALWAYS_FATAL_IF(attempt == kMaxAttempts,
                         "After %d attempts HWC still returns an active mode which is not"
-                        " supported. Active mode ID = %" PRIu64 " . Supported modes = %s",
+                        " supported. Active mode ID = %" PRIu64 ". Supported modes = %s",
                         kMaxAttempts, *activeModeHwcId, base::Join(hwcModes, ", ").c_str());
 
     DisplayModes oldModes;
-
     if (const auto token = getPhysicalDisplayTokenLocked(displayId)) {
         oldModes = getDisplayDeviceLocked(token)->getSupportedModes();
     }
 
-    int largestUsedModeId = -1; // Use int instead of DisplayModeId for signedness
-    for (const auto& mode : oldModes) {
-        const auto id = static_cast<int>(mode->getId().value());
-        if (id > largestUsedModeId) {
-            largestUsedModeId = id;
-        }
-    }
+    ui::DisplayModeId nextModeId = 1 +
+            std::accumulate(oldModes.begin(), oldModes.end(), static_cast<ui::DisplayModeId>(-1),
+                            [](ui::DisplayModeId max, const auto& pair) {
+                                return std::max(max, pair.first.value());
+                            });
 
     DisplayModes newModes;
-    int32_t nextModeId = largestUsedModeId + 1;
     for (const auto& hwcMode : hwcModes) {
-        newModes.push_back(DisplayMode::Builder(hwcMode.hwcId)
-                                   .setId(DisplayModeId{nextModeId++})
-                                   .setPhysicalDisplayId(displayId)
-                                   .setWidth(hwcMode.width)
-                                   .setHeight(hwcMode.height)
-                                   .setVsyncPeriod(hwcMode.vsyncPeriod)
-                                   .setDpiX(hwcMode.dpiX)
-                                   .setDpiY(hwcMode.dpiY)
-                                   .setGroup(hwcMode.configGroup)
-                                   .build());
+        const DisplayModeId id{nextModeId++};
+        newModes.try_emplace(id,
+                             DisplayMode::Builder(hwcMode.hwcId)
+                                     .setId(id)
+                                     .setPhysicalDisplayId(displayId)
+                                     .setResolution({hwcMode.width, hwcMode.height})
+                                     .setVsyncPeriod(hwcMode.vsyncPeriod)
+                                     .setDpiX(hwcMode.dpiX)
+                                     .setDpiY(hwcMode.dpiY)
+                                     .setGroup(hwcMode.configGroup)
+                                     .build());
     }
 
-    const bool modesAreSame =
+    const bool sameModes =
             std::equal(newModes.begin(), newModes.end(), oldModes.begin(), oldModes.end(),
-                       [](DisplayModePtr left, DisplayModePtr right) {
-                           return left->equalsExceptDisplayModeId(right);
+                       [](const auto& lhs, const auto& rhs) {
+                           return equalsExceptDisplayModeId(*lhs.second, *rhs.second);
                        });
 
-    if (modesAreSame) {
-        // The supported modes have not changed, keep the old IDs.
-        outModes = oldModes;
-    } else {
-        outModes = newModes;
-    }
+    // Keep IDs if modes have not changed.
+    const auto& modes = sameModes ? oldModes : newModes;
+    const DisplayModePtr activeMode =
+            std::find_if(modes.begin(), modes.end(), [activeModeHwcId](const auto& pair) {
+                return pair.second->getHwcId() == activeModeHwcId;
+            })->second;
 
-    outActiveMode = *std::find_if(outModes.begin(), outModes.end(),
-                                  [activeModeHwcId](const DisplayModePtr& mode) {
-                                      return mode->getHwcId() == *activeModeHwcId;
-                                  });
+    return {modes, activeMode};
 }
 
 void SurfaceFlinger::processDisplayHotplugEventsLocked() {
@@ -2750,9 +2775,7 @@
         const auto it = mPhysicalDisplayTokens.find(displayId);
 
         if (event.connection == hal::Connection::CONNECTED) {
-            DisplayModes supportedModes;
-            DisplayModePtr activeMode;
-            loadDisplayModes(displayId, supportedModes, activeMode);
+            auto [supportedModes, activeMode] = loadDisplayModes(displayId);
 
             if (it == mPhysicalDisplayTokens.end()) {
                 ALOGV("Creating display %s", to_string(displayId).c_str());
@@ -2763,7 +2786,7 @@
                                   .hwcDisplayId = event.hwcDisplayId,
                                   .deviceProductInfo = std::move(info->deviceProductInfo),
                                   .supportedModes = std::move(supportedModes),
-                                  .activeMode = activeMode};
+                                  .activeMode = std::move(activeMode)};
                 state.isSecure = true; // All physical displays are currently considered secure.
                 state.displayName = std::move(info->name);
 
@@ -2778,7 +2801,7 @@
                 auto& state = mCurrentState.displays.editValueFor(token);
                 state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId
                 state.physical->supportedModes = std::move(supportedModes);
-                state.physical->activeMode = activeMode;
+                state.physical->activeMode = std::move(activeMode);
                 if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
                     state.physical->deviceProductInfo = std::move(info->deviceProductInfo);
                 }
@@ -2873,7 +2896,8 @@
     }
 
     creationArgs.physicalOrientation =
-            creationArgs.isPrimary ? internalDisplayOrientation : ui::ROTATION_0;
+            getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary);
+    ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
 
     // virtual displays are always considered enabled
     creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
@@ -2911,7 +2935,7 @@
     ui::Size resolution(0, 0);
     ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
     if (state.physical) {
-        resolution = state.physical->activeMode->getSize();
+        resolution = state.physical->activeMode->getResolution();
         pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888);
     } else if (state.surface != nullptr) {
         int status = state.surface->query(NATIVE_WINDOW_WIDTH, &resolution.width);
@@ -2965,7 +2989,7 @@
         LOG_FATAL_IF(!displayId);
         displaySurface =
                 sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,
-                                             state.physical->activeMode->getSize(),
+                                             state.physical->activeMode->getResolution(),
                                              ui::Size(maxGraphicsWidth, maxGraphicsHeight));
         producer = bqProducer;
     }
@@ -3078,7 +3102,7 @@
 }
 void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
     mVsyncConfiguration->reset();
-    const Fps refreshRate = activeDisplay->refreshRateConfigs().getCurrentRefreshRate().getFps();
+    const Fps refreshRate = activeDisplay->refreshRateConfigs().getActiveMode()->getFps();
     updatePhaseConfiguration(refreshRate);
     mRefreshRateStats->setRefreshRate(refreshRate);
 }
@@ -3340,7 +3364,7 @@
     mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
-void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, DisplayModeEvent event) {
+void SurfaceFlinger::requestDisplayMode(DisplayModePtr mode, DisplayModeEvent event) {
     // If this is called from the main thread mStateLock must be locked before
     // Currently the only way to call this function from the main thread is from
     // Scheduler::chooseRefreshRateForContent
@@ -3353,14 +3377,12 @@
     }
     ATRACE_CALL();
 
-    // Don't do any updating if the current fps is the same as the new one.
-    if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) {
-        ALOGV("Skipping mode %d as it is not part of allowed modes",
-              refreshRate.getModeId().value());
+    if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) {
+        ALOGV("Skipping disallowed mode %d", mode->getId().value());
         return;
     }
 
-    setDesiredActiveMode({refreshRate.getMode(), event});
+    setDesiredActiveMode({std::move(mode), event});
 }
 
 void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -4788,8 +4810,7 @@
                           {}, mPid, getuid(), transactionId);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
-    const nsecs_t vsyncPeriod =
-            display->refreshRateConfigs().getCurrentRefreshRate().getVsyncPeriod();
+    const nsecs_t vsyncPeriod = display->refreshRateConfigs().getActiveMode()->getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
     mActiveDisplayTransformHint = display->getTransformHint();
     // Use phase of 0 since phase is not known.
@@ -4805,7 +4826,7 @@
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
     if (display->isVirtual()) {
-        ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
+        ALOGE("%s: Invalid operation on virtual display", __func__);
         return;
     }
 
@@ -4828,7 +4849,7 @@
     if (mInterceptor->isEnabled()) {
         mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
     }
-    const auto vsyncPeriod = display->refreshRateConfigs().getCurrentRefreshRate().getVsyncPeriod();
+    const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
     if (currentMode == hal::PowerMode::OFF) {
         // Turn on the display
         if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
@@ -4846,7 +4867,7 @@
         if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) {
             setHWCVsyncEnabled(displayId, mHWCVsyncPendingState);
             mScheduler->onScreenAcquired(mAppConnectionHandle);
-            mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
+            mScheduler->resyncToHardwareVsync(true, refreshRate);
         }
 
         mVisibleRegionsDirty = true;
@@ -4876,7 +4897,7 @@
         getHwComposer().setPowerMode(displayId, mode);
         if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->onScreenAcquired(mAppConnectionHandle);
-            mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
+            mScheduler->resyncToHardwareVsync(true, refreshRate);
         }
     } else if (mode == hal::PowerMode::DOZE_SUSPEND) {
         // Leave display going to doze
@@ -5362,8 +5383,10 @@
         std::string fps, xDpi, yDpi;
         if (const auto activeMode = display->getActiveMode()) {
             fps = to_string(activeMode->getFps());
-            xDpi = base::StringPrintf("%.2f", activeMode->getDpiX());
-            yDpi = base::StringPrintf("%.2f", activeMode->getDpiY());
+
+            const auto dpi = activeMode->getDpi();
+            xDpi = base::StringPrintf("%.2f", dpi.x);
+            yDpi = base::StringPrintf("%.2f", dpi.y);
         } else {
             fps = "unknown";
             xDpi = "unknown";
@@ -6125,7 +6148,7 @@
     if (!updateOverlay) return;
 
     // Update the overlay on the main thread to avoid race conditions with
-    // mRefreshRateConfigs->getCurrentRefreshRate()
+    // mRefreshRateConfigs->getActiveMode()
     static_cast<void>(mScheduler->schedule([=] {
         const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
         if (!display) {
@@ -7171,7 +7194,7 @@
         refreshRate = *frameRateOverride;
     } else if (!getHwComposer().isHeadless()) {
         if (const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked())) {
-            refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
+            refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
         }
     }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 81afa9b..df59d50 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -256,8 +256,6 @@
     // found on devices with wide color gamut (e.g. Display-P3) display.
     static bool hasWideColorDisplay;
 
-    static ui::Rotation internalDisplayOrientation;
-
     // Indicate if device wants color management on its display.
     static const constexpr bool useColorManagement = true;
 
@@ -680,8 +678,8 @@
 
     // Toggles hardware VSYNC by calling into HWC.
     void setVsyncEnabled(bool) override;
-    // Initiates a refresh rate change to be applied on commit.
-    void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override;
+    // Sets the desired display mode if allowed by policy.
+    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override;
     // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
     void kernelTimerChanged(bool expired) override;
     // Called when the frame rate override list changed to trigger an event.
@@ -976,8 +974,9 @@
     /*
      * Display management
      */
-    void loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
-                          DisplayModePtr& outActiveMode) const REQUIRES(mStateLock);
+    std::pair<DisplayModes, DisplayModePtr> loadDisplayModes(PhysicalDisplayId) const
+            REQUIRES(mStateLock);
+
     sp<DisplayDevice> setupNewDisplayDeviceInternal(
             const wp<IBinder>& displayToken,
             std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -1146,6 +1145,9 @@
 
     bool isHdrLayer(Layer* layer) const;
 
+    ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
+            REQUIRES(mStateLock);
+
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
     pid_t mPid;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index afc1abd..a5a716d 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -29,9 +29,6 @@
         LatchUnsignaledConfig::Disabled,
 };
 
-static constexpr ui::Rotation kRotations[] = {ui::Rotation::Rotation0, ui::Rotation::Rotation90,
-                                              ui::Rotation::Rotation180, ui::Rotation::Rotation270};
-
 static constexpr BnSurfaceComposer::ISurfaceComposerTag kSurfaceComposerTags[]{
         BnSurfaceComposer::BOOT_FINISHED,
         BnSurfaceComposer::CREATE_CONNECTION,
@@ -134,7 +131,6 @@
     mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>();
     mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>();
     mFlinger->hasWideColorDisplay = mFdp.ConsumeBool();
-    mFlinger->internalDisplayOrientation = mFdp.PickValueInArray(kRotations);
     mFlinger->useContextPriority = mFdp.ConsumeBool();
 
     mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index b796dfe..93abc9f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -55,6 +55,7 @@
 #include "renderengine/mock/RenderEngine.h"
 #include "scheduler/TimeKeeper.h"
 #include "tests/unittests/mock/DisplayHardware/MockComposer.h"
+#include "tests/unittests/mock/DisplayHardware/MockDisplayMode.h"
 #include "tests/unittests/mock/DisplayHardware/MockHWC2.h"
 #include "tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h"
 #include "tests/unittests/mock/MockEventThread.h"
@@ -207,7 +208,9 @@
     void setRefreshRateFps(Fps) override {}
     void dump(std::string &) const override {}
 };
+
 namespace scheduler {
+
 class TestableScheduler : public Scheduler, private ICompositor {
 public:
     TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs> &refreshRateConfigs,
@@ -216,9 +219,6 @@
                               std::make_unique<android::mock::VSyncTracker>(), refreshRateConfigs,
                               callback) {}
 
-    void scheduleFrame(){};
-    void postMessage(sp<MessageHandler> &&){};
-
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::unique_ptr<VSyncTracker> tracker,
                       std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback &callback)
@@ -273,8 +273,13 @@
     bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
     void composite(nsecs_t, int64_t) override {}
     void sample() override {}
+
+    // MessageQueue overrides:
+    void scheduleFrame() override {}
+    void postMessage(sp<MessageHandler>&&) override {}
 };
-}; // namespace scheduler
+
+} // namespace scheduler
 
 namespace surfaceflinger::test {
 
@@ -405,8 +410,6 @@
         return mFlinger->onInitializeDisplays();
     }
 
-    void scheduleComposite(FrameHint){};
-
     void setGlobalShadowSettings(FuzzedDataProvider *fdp) {
         const half4 ambientColor{fdp->ConsumeFloatingPoint<float>(),
                                  fdp->ConsumeFloatingPoint<float>(),
@@ -678,31 +681,22 @@
                         std::unique_ptr<EventThread> sfEventThread,
                         scheduler::ISchedulerCallback *callback = nullptr,
                         bool hasMultipleModes = false) {
-        DisplayModes modes{DisplayMode::Builder(0)
-                                   .setId(DisplayModeId(0))
-                                   .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                   .setVsyncPeriod(16'666'667)
-                                   .setGroup(0)
-                                   .build()};
+        constexpr DisplayModeId kModeId60{0};
+        DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
 
         if (hasMultipleModes) {
-            modes.emplace_back(DisplayMode::Builder(1)
-                                       .setId(DisplayModeId(1))
-                                       .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                       .setVsyncPeriod(11'111'111)
-                                       .setGroup(0)
-                                       .build());
+            constexpr DisplayModeId kModeId90{1};
+            modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
         }
 
-        const auto currMode = DisplayModeId(0);
-        mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, currMode);
-        const auto currFps = mRefreshRateConfigs->getCurrentRefreshRate().getFps();
-        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
+        mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
+        const auto fps = mRefreshRateConfigs->getActiveMode()->getFps();
+        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
         mFlinger->mRefreshRateStats =
-                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
-                                                              /*powerMode=*/hal::PowerMode::OFF);
+                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
+                                                              hal::PowerMode::OFF);
 
         mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
                                                       std::move(vsyncTracker), mRefreshRateConfigs,
@@ -765,8 +759,6 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto onMessageReceived(int32_t /*what*/) { return 0; }
-
     auto &getTransactionQueue() { return mFlinger->mTransactionQueue; }
     auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
 
@@ -818,15 +810,16 @@
     }
 
 private:
-    void scheduleRefresh(FrameHint) {}
+    void scheduleComposite(FrameHint) override {}
     void setVsyncEnabled(bool) override {}
-    void changeRefreshRate(const RefreshRate &, DisplayModeEvent) override {}
+    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
     void kernelTimerChanged(bool) override {}
-    void triggerOnFrameRateOverridesChanged() {}
+    void triggerOnFrameRateOverridesChanged() override {}
 
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
     scheduler::TestableScheduler *mScheduler = nullptr;
     std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
 };
+
 } // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index d504155..da60a69 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -15,22 +15,31 @@
  *
  */
 
-#include "surfaceflinger_scheduler_fuzzer.h"
+#include <ftl/enum.h>
 #include <fuzzer/FuzzedDataProvider.h>
 #include <processgroup/sched_policy.h>
+
 #include "Scheduler/DispSyncSource.h"
 #include "Scheduler/OneShotTimer.h"
 #include "Scheduler/VSyncDispatchTimerQueue.h"
 #include "Scheduler/VSyncPredictor.h"
 #include "Scheduler/VSyncReactor.h"
+
 #include "surfaceflinger_fuzzers_utils.h"
+#include "surfaceflinger_scheduler_fuzzer.h"
 
 namespace android::fuzz {
 
 using hardware::graphics::composer::hal::PowerMode;
 
-static constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
-                                            PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
+constexpr nsecs_t kVsyncPeriods[] = {(30_Hz).getPeriodNsecs(), (60_Hz).getPeriodNsecs(),
+                                     (72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(),
+                                     (120_Hz).getPeriodNsecs()};
+
+constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateConfigs::LayerVoteType>();
+
+constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
+                                     PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
 
 constexpr uint16_t kRandomStringLength = 256;
 constexpr std::chrono::duration kSyncPeriod(16ms);
@@ -319,39 +328,42 @@
     using RefreshRateConfigs = scheduler::RefreshRateConfigs;
     using LayerRequirement = RefreshRateConfigs::LayerRequirement;
     using RefreshRateStats = scheduler::RefreshRateStats;
-    uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1);
-    uint16_t maxRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(minRefreshRate + 1, UINT16_MAX);
 
-    DisplayModeId hwcConfigIndexType = DisplayModeId(mFdp.ConsumeIntegralInRange<uint8_t>(0, 10));
+    const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1);
+    const uint16_t maxRefreshRate =
+            mFdp.ConsumeIntegralInRange<uint16_t>(minRefreshRate + 1, UINT16_MAX);
+
+    const DisplayModeId modeId{mFdp.ConsumeIntegralInRange<uint8_t>(0, 10)};
 
     DisplayModes displayModes;
     for (uint16_t fps = minRefreshRate; fps < maxRefreshRate; ++fps) {
-        constexpr int32_t kGroup = 0;
-        const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
-        displayModes.push_back(scheduler::createDisplayMode(hwcConfigIndexType, kGroup,
-                                                            refreshRate.getPeriodNsecs()));
+        displayModes.try_emplace(modeId,
+                                 mock::createDisplayMode(modeId,
+                                                         Fps::fromValue(static_cast<float>(fps))));
     }
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(displayModes, hwcConfigIndexType);
+
+    RefreshRateConfigs refreshRateConfigs(displayModes, modeId);
+
     const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
-    auto layers = std::vector<LayerRequirement>{
-            LayerRequirement{.weight = mFdp.ConsumeFloatingPoint<float>()}};
-    refreshRateConfigs->getBestRefreshRate(layers, globalSignals);
+    std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
+
+    refreshRateConfigs.getBestRefreshRate(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);
+    layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values);
     auto frameRateOverrides =
-            refreshRateConfigs->getFrameRateOverrides(layers,
-                                                      Fps::fromValue(
-                                                              mFdp.ConsumeFloatingPoint<float>()),
-                                                      globalSignals);
+            refreshRateConfigs.getFrameRateOverrides(layers,
+                                                     Fps::fromValue(
+                                                             mFdp.ConsumeFloatingPoint<float>()),
+                                                     globalSignals);
 
-    refreshRateConfigs->setDisplayManagerPolicy(
-            {hwcConfigIndexType,
+    refreshRateConfigs.setDisplayManagerPolicy(
+            {modeId,
              {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
               Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
-    refreshRateConfigs->setCurrentModeId(hwcConfigIndexType);
+    refreshRateConfigs.setActiveModeId(modeId);
 
     RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue(
                                                            mFdp.ConsumeFloatingPoint<float>()),
@@ -361,13 +373,13 @@
                                             Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
 
     android::mock::TimeStats timeStats;
-    std::unique_ptr<RefreshRateStats> refreshRateStats =
-            std::make_unique<RefreshRateStats>(timeStats,
-                                               Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
-                                               PowerMode::OFF);
-    refreshRateStats->setRefreshRate(
-            refreshRateConfigs->getRefreshRateFromModeId(hwcConfigIndexType).getFps());
-    refreshRateStats->setPowerMode(mFdp.PickValueInArray(kPowerModes));
+    RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+                                      PowerMode::OFF);
+
+    const auto fpsOpt = displayModes.get(modeId, [](const auto& mode) { return mode->getFps(); });
+    refreshRateStats.setRefreshRate(*fpsOpt);
+
+    refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes));
 }
 
 void SchedulerFuzzer::process() {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 84b3912..1a49ead 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -22,6 +22,8 @@
 
 #pragma once
 
+#include <scheduler/TimeKeeper.h>
+
 #include "Clock.h"
 #include "Layer.h"
 #include "Scheduler/EventThread.h"
@@ -29,24 +31,9 @@
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncTracker.h"
 #include "Scheduler/VsyncModulator.h"
-#include "scheduler/TimeKeeper.h"
 
 namespace android::fuzz {
 
-constexpr int64_t kVsyncPeriods[] = {static_cast<int64_t>(1e9f / 30),
-                                     static_cast<int64_t>(1e9f / 60),
-                                     static_cast<int64_t>(1e9f / 72),
-                                     static_cast<int64_t>(1e9f / 90),
-                                     static_cast<int64_t>(1e9f / 120)};
-
-android::scheduler::RefreshRateConfigs::LayerVoteType kLayerVoteTypes[] =
-        {android::scheduler::RefreshRateConfigs::LayerVoteType::NoVote,
-         android::scheduler::RefreshRateConfigs::LayerVoteType::Min,
-         android::scheduler::RefreshRateConfigs::LayerVoteType::Max,
-         android::scheduler::RefreshRateConfigs::LayerVoteType::Heuristic,
-         android::scheduler::RefreshRateConfigs::LayerVoteType::ExplicitDefault,
-         android::scheduler::RefreshRateConfigs::LayerVoteType::ExplicitExactOrMultiple};
-
 class FuzzImplClock : public android::scheduler::Clock {
 public:
     nsecs_t now() const { return 1; }
@@ -168,18 +155,6 @@
 
 namespace android::scheduler {
 
-DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
-                                 ui::Size resolution = ui::Size()) {
-    return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
-            .setId(modeId)
-            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-            .setVsyncPeriod(int32_t(vsyncPeriod))
-            .setGroup(group)
-            .setHeight(resolution.height)
-            .setWidth(resolution.width)
-            .build();
-}
-
 class ControllableClock : public TimeKeeper {
 public:
     nsecs_t now() const { return 1; };
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 40a9b1a..93af225 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -44,68 +44,42 @@
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
 
         mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
-                           .setDisplayModes({kDisplayMode60, kDisplayMode90, kDisplayMode120},
-                                            kDisplayModeId60)
+                           .setDisplayModes(makeModes(kMode60, kMode90, kMode120), kModeId60)
                            .inject();
     }
 
 protected:
     sp<DisplayDevice> mDisplay;
 
-    const DisplayModeId kDisplayModeId60 = DisplayModeId(0);
-    const DisplayModePtr kDisplayMode60 =
-            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId60.value()))
-                    .setId(kDisplayModeId60)
-                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
-                    .setVsyncPeriod(int32_t(16'666'667))
-                    .setGroup(0)
-                    .setHeight(1000)
-                    .setWidth(1000)
-                    .build();
+    static constexpr DisplayModeId kModeId60{0};
+    static constexpr DisplayModeId kModeId90{1};
+    static constexpr DisplayModeId kModeId120{2};
 
-    const DisplayModeId kDisplayModeId90 = DisplayModeId(1);
-    const DisplayModePtr kDisplayMode90 =
-            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90.value()))
-                    .setId(kDisplayModeId90)
-                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
-                    .setVsyncPeriod(int32_t(11'111'111))
-                    .setGroup(0)
-                    .setHeight(1000)
-                    .setWidth(1000)
-                    .build();
-
-    const DisplayModeId kDisplayModeId120 = DisplayModeId(2);
-    const DisplayModePtr kDisplayMode120 =
-            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId120.value()))
-                    .setId(kDisplayModeId120)
-                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
-                    .setVsyncPeriod(int32_t(8'333'333))
-                    .setGroup(0)
-                    .setHeight(1000)
-                    .setWidth(1000)
-                    .build();
+    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
+    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
+    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
 };
 
 TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) {
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode60, Event::None}));
+    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode60, Event::None}));
     EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
 }
 
 TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None}));
+    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     // Setting another mode should be cached but return false
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode120, Event::None}));
+    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kDisplayMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 }
 
 TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None}));
+    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
 
     mDisplay->clearDesiredActiveModeState();
@@ -113,9 +87,9 @@
 }
 
 TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None}));
+    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     hal::VsyncPeriodChangeConstraints constraints{
@@ -126,7 +100,7 @@
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     mDisplay->clearDesiredActiveModeState();
@@ -135,9 +109,9 @@
 
 TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged)
 NO_THREAD_SAFETY_ANALYSIS {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None}));
+    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     hal::VsyncPeriodChangeConstraints constraints{
@@ -148,21 +122,21 @@
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode120, Event::None}));
+    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kDisplayMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
-    EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kDisplayMode120, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_EQ(kMode120, mDisplay->getUpcomingActiveMode().mode);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     mDisplay->clearDesiredActiveModeState();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 54b8bcb..565c244 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -45,6 +45,7 @@
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockNativeWindowSurface.h"
@@ -235,9 +236,9 @@
     using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
     using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
 
-    // The display width and height
     static constexpr int WIDTH = width;
     static constexpr int HEIGHT = height;
+    static constexpr ui::Size RESOLUTION{WIDTH, HEIGHT};
 
     static constexpr int GRALLOC_USAGE = grallocUsage;
 
@@ -262,7 +263,7 @@
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
         ceDisplayArgs.setId(DISPLAY_ID::get())
-                .setPixels({WIDTH, HEIGHT})
+                .setPixels(RESOLUTION)
                 .setPowerAdvisor(&test->mPowerAdvisor);
 
         auto compositionDisplay =
@@ -357,8 +358,7 @@
         TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
                                                        static_cast<bool>(DisplayVariant::PRIMARY))
                 .setHwcDisplayId(HWC_DISPLAY_ID)
-                .setWidth(DisplayVariant::WIDTH)
-                .setHeight(DisplayVariant::HEIGHT)
+                .setResolution(DisplayVariant::RESOLUTION)
                 .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
                 .setPowerMode(INIT_POWER_MODE)
                 .inject(&test->mFlinger, test->mComposer);
@@ -381,7 +381,7 @@
 
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setId(DisplayVariant::DISPLAY_ID::get())
-                                     .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
+                                     .setPixels(DisplayVariant::RESOLUTION)
                                      .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
                                      .setPowerAdvisor(&test->mPowerAdvisor)
                                      .setName(std::string("Injected display for ") +
@@ -541,7 +541,7 @@
 
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setId(Base::DISPLAY_ID::get())
-                                     .setPixels({Base::WIDTH, Base::HEIGHT})
+                                     .setPixels(Base::RESOLUTION)
                                      .setIsSecure(static_cast<bool>(Base::SECURE))
                                      .setPowerAdvisor(&test->mPowerAdvisor)
                                      .setName(std::string("Injected display for ") +
@@ -593,7 +593,7 @@
         const auto displayId = Base::DISPLAY_ID::get();
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setId(displayId)
-                                     .setPixels({Base::WIDTH, Base::HEIGHT})
+                                     .setPixels(Base::RESOLUTION)
                                      .setIsSecure(static_cast<bool>(Base::SECURE))
                                      .setPowerAdvisor(&test->mPowerAdvisor)
                                      .setName(std::string("Injected display for ") +
@@ -736,6 +736,12 @@
              HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
              NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
 
+inline DisplayModePtr createDisplayMode(DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
+                                        ui::Size resolution = ui::Size(1920, 1080)) {
+    return mock::createDisplayMode(modeId, refreshRate, group, resolution,
+                                   PrimaryDisplayVariant::DISPLAY_ID::get());
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index e108bea..17511cd 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -31,6 +31,7 @@
 #include "Scheduler/LayerInfo.h"
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
 #include "mock/MockLayer.h"
 #include "mock/MockSchedulerCallback.h"
 
@@ -42,6 +43,8 @@
 
 using MockLayer = android::mock::MockLayer;
 
+using android::mock::createDisplayMode;
+
 class LayerHistoryTest : public testing::Test {
 protected:
     static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
@@ -122,22 +125,12 @@
         ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
     }
 
-    std::shared_ptr<RefreshRateConfigs> mConfigs = std::make_shared<
-            RefreshRateConfigs>(DisplayModes{DisplayMode::Builder(0)
-                                                     .setId(DisplayModeId(0))
-                                                     .setPhysicalDisplayId(
-                                                             PhysicalDisplayId::fromPort(0))
-                                                     .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
-                                                     .setGroup(0)
-                                                     .build(),
-                                             DisplayMode::Builder(1)
-                                                     .setId(DisplayModeId(1))
-                                                     .setPhysicalDisplayId(
-                                                             PhysicalDisplayId::fromPort(0))
-                                                     .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
-                                                     .setGroup(0)
-                                                     .build()},
-                                DisplayModeId(0));
+    std::shared_ptr<RefreshRateConfigs> mConfigs =
+            std::make_shared<RefreshRateConfigs>(makeModes(createDisplayMode(DisplayModeId(0),
+                                                                             LO_FPS),
+                                                           createDisplayMode(DisplayModeId(1),
+                                                                             HI_FPS)),
+                                                 DisplayModeId(0));
 
     mock::SchedulerCallback mSchedulerCallback;
 
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 97f3747..fcde532 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -25,46 +25,33 @@
 #include "DisplayHardware/HWC2.h"
 #include "FpsOps.h"
 #include "Scheduler/RefreshRateConfigs.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
 
 using namespace std::chrono_literals;
 
 namespace android::scheduler {
-namespace {
-
-DisplayModePtr createDisplayMode(DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
-                                 ui::Size resolution = ui::Size()) {
-    return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
-            .setId(modeId)
-            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-            .setVsyncPeriod(static_cast<int32_t>(refreshRate.getPeriodNsecs()))
-            .setGroup(group)
-            .setHeight(resolution.height)
-            .setWidth(resolution.width)
-            .build();
-}
-
-} // namespace
 
 namespace hal = android::hardware::graphics::composer::hal;
 
-using RefreshRate = RefreshRateConfigs::RefreshRate;
 using LayerVoteType = RefreshRateConfigs::LayerVoteType;
 using LayerRequirement = RefreshRateConfigs::LayerRequirement;
 
+using mock::createDisplayMode;
+
 struct TestableRefreshRateConfigs : RefreshRateConfigs {
     using RefreshRateConfigs::RefreshRateConfigs;
 
-    RefreshRate getMinSupportedRefreshRate() const {
+    DisplayModePtr getMinSupportedRefreshRate() const {
         std::lock_guard lock(mLock);
-        return *mMinSupportedRefreshRate;
+        return mMinRefreshRateModeIt->second;
     }
 
-    RefreshRate getMaxSupportedRefreshRate() const {
+    DisplayModePtr getMaxSupportedRefreshRate() const {
         std::lock_guard lock(mLock);
-        return *mMaxSupportedRefreshRate;
+        return mMaxRefreshRateModeIt->second;
     }
 
-    RefreshRate getMinRefreshRateByPolicy() const {
+    DisplayModePtr getMinRefreshRateByPolicy() const {
         std::lock_guard lock(mLock);
         return getMinRefreshRateByPolicyLocked();
     }
@@ -79,8 +66,8 @@
         return RefreshRateConfigs::getBestRefreshRate(layers, signals);
     }
 
-    RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
-                                   GlobalSignals signals = {}) const {
+    DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
+                                      GlobalSignals signals = {}) const {
         return getBestRefreshRateAndSignals(layers, signals).first;
     }
 };
@@ -90,10 +77,6 @@
     RefreshRateConfigsTest();
     ~RefreshRateConfigsTest();
 
-    static RefreshRate asRefreshRate(DisplayModePtr displayMode) {
-        return {displayMode, RefreshRate::ConstructorTag(0)};
-    }
-
     static constexpr DisplayModeId kModeId60{0};
     static constexpr DisplayModeId kModeId90{1};
     static constexpr DisplayModeId kModeId72{2};
@@ -126,30 +109,30 @@
     static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz);
 
     // Test configurations.
-    static inline const DisplayModes kModes_60 = {kMode60};
-    static inline const DisplayModes kModes_60_90 = {kMode60, kMode90};
-    static inline const DisplayModes kModes_60_90_G1 = {kMode60, kMode90_G1};
-    static inline const DisplayModes kModes_60_90_4K = {kMode60, kMode90_4K};
-    static inline const DisplayModes kModes_60_72_90 = {kMode60, kMode90, kMode72};
-    static inline const DisplayModes kModes_60_90_72_120 = {kMode60, kMode90, kMode72, kMode120};
-    static inline const DisplayModes kModes_30_60_72_90_120 = {kMode60, kMode90, kMode72, kMode120,
-                                                               kMode30};
+    static inline const DisplayModes kModes_60 = makeModes(kMode60);
+    static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90);
+    static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1);
+    static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K);
+    static inline const DisplayModes kModes_60_72_90 = makeModes(kMode60, kMode90, kMode72);
+    static inline const DisplayModes kModes_60_90_72_120 =
+            makeModes(kMode60, kMode90, kMode72, kMode120);
+    static inline const DisplayModes kModes_30_60_72_90_120 =
+            makeModes(kMode60, kMode90, kMode72, kMode120, kMode30);
 
-    static inline const DisplayModes kModes_30_60 = {kMode60, kMode90_G1, kMode72_G1, kMode120_G1,
-                                                     kMode30};
-    static inline const DisplayModes kModes_30_60_72_90 = {kMode60, kMode90, kMode72, kMode120_G1,
-                                                           kMode30};
-    static inline const DisplayModes kModes_30_60_90 = {kMode60, kMode90, kMode72_G1, kMode120_G1,
-                                                        kMode30};
-    static inline const DisplayModes kModes_25_30_50_60 = {kMode60,     kMode90,    kMode72_G1,
-                                                           kMode120_G1, kMode30_G1, kMode25_G1,
-                                                           kMode50};
-    static inline const DisplayModes kModes_60_120 = {kMode60, kMode120};
+    static inline const DisplayModes kModes_30_60 =
+            makeModes(kMode60, kMode90_G1, kMode72_G1, kMode120_G1, kMode30);
+    static inline const DisplayModes kModes_30_60_72_90 =
+            makeModes(kMode60, kMode90, kMode72, kMode120_G1, kMode30);
+    static inline const DisplayModes kModes_30_60_90 =
+            makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30);
+    static inline const DisplayModes kModes_25_30_50_60 =
+            makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
+    static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
 
     // This is a typical TV configuration.
-    static inline const DisplayModes kModes_24_25_30_50_60_Frac = {kMode24, kMode24Frac, kMode25,
-                                                                   kMode30, kMode30Frac, kMode50,
-                                                                   kMode60, kMode60Frac};
+    static inline const DisplayModes kModes_24_25_30_50_60_Frac =
+            makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
+                      kMode60Frac);
 };
 
 RefreshRateConfigsTest::RefreshRateConfigsTest() {
@@ -183,8 +166,8 @@
     const auto minRate = configs.getMinSupportedRefreshRate();
     const auto performanceRate = configs.getMaxSupportedRefreshRate();
 
-    EXPECT_EQ(asRefreshRate(kMode60), minRate);
-    EXPECT_EQ(asRefreshRate(kMode90), performanceRate);
+    EXPECT_EQ(kMode60, minRate);
+    EXPECT_EQ(kMode90, performanceRate);
 
     const auto minRateByPolicy = configs.getMinRefreshRateByPolicy();
     const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy();
@@ -201,19 +184,19 @@
     const auto minRate60 = configs.getMinRefreshRateByPolicy();
     const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
 
-    EXPECT_EQ(asRefreshRate(kMode60), minRate);
-    EXPECT_EQ(asRefreshRate(kMode60), minRate60);
-    EXPECT_EQ(asRefreshRate(kMode60), performanceRate60);
+    EXPECT_EQ(kMode60, minRate);
+    EXPECT_EQ(kMode60, minRate60);
+    EXPECT_EQ(kMode60, performanceRate60);
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
-    configs.setCurrentModeId(kModeId90);
+    configs.setActiveModeId(kModeId90);
 
     const auto minRate90 = configs.getMinRefreshRateByPolicy();
     const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
 
-    EXPECT_EQ(asRefreshRate(kMode90_G1), performanceRate);
-    EXPECT_EQ(asRefreshRate(kMode90_G1), minRate90);
-    EXPECT_EQ(asRefreshRate(kMode90_G1), performanceRate90);
+    EXPECT_EQ(kMode90_G1, performanceRate);
+    EXPECT_EQ(kMode90_G1, minRate90);
+    EXPECT_EQ(kMode90_G1, performanceRate90);
 }
 
 TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
@@ -224,19 +207,19 @@
     const auto minRate60 = configs.getMinRefreshRateByPolicy();
     const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
 
-    EXPECT_EQ(asRefreshRate(kMode60), minRate);
-    EXPECT_EQ(asRefreshRate(kMode60), minRate60);
-    EXPECT_EQ(asRefreshRate(kMode60), performanceRate60);
+    EXPECT_EQ(kMode60, minRate);
+    EXPECT_EQ(kMode60, minRate60);
+    EXPECT_EQ(kMode60, performanceRate60);
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
-    configs.setCurrentModeId(kModeId90);
+    configs.setActiveModeId(kModeId90);
 
     const auto minRate90 = configs.getMinRefreshRateByPolicy();
     const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
 
-    EXPECT_EQ(asRefreshRate(kMode90_4K), performanceRate);
-    EXPECT_EQ(asRefreshRate(kMode90_4K), minRate90);
-    EXPECT_EQ(asRefreshRate(kMode90_4K), performanceRate90);
+    EXPECT_EQ(kMode90_4K, performanceRate);
+    EXPECT_EQ(kMode90_4K, minRate90);
+    EXPECT_EQ(kMode90_4K, performanceRate90);
 }
 
 TEST_F(RefreshRateConfigsTest, twoModes_policyChange) {
@@ -245,35 +228,35 @@
     const auto minRate = configs.getMinRefreshRateByPolicy();
     const auto performanceRate = configs.getMaxRefreshRateByPolicy();
 
-    EXPECT_EQ(asRefreshRate(kMode60), minRate);
-    EXPECT_EQ(asRefreshRate(kMode90), performanceRate);
+    EXPECT_EQ(kMode60, minRate);
+    EXPECT_EQ(kMode90, performanceRate);
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
 
     const auto minRate60 = configs.getMinRefreshRateByPolicy();
     const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
 
-    EXPECT_EQ(asRefreshRate(kMode60), minRate60);
-    EXPECT_EQ(asRefreshRate(kMode60), performanceRate60);
+    EXPECT_EQ(kMode60, minRate60);
+    EXPECT_EQ(kMode60, performanceRate60);
 }
 
-TEST_F(RefreshRateConfigsTest, twoModes_getCurrentRefreshRate) {
+TEST_F(RefreshRateConfigsTest, twoModes_getActiveMode) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
     {
-        const auto current = configs.getCurrentRefreshRate();
-        EXPECT_EQ(current.getModeId(), kModeId60);
+        const auto mode = configs.getActiveMode();
+        EXPECT_EQ(mode->getId(), kModeId60);
     }
 
-    configs.setCurrentModeId(kModeId90);
+    configs.setActiveModeId(kModeId90);
     {
-        const auto current = configs.getCurrentRefreshRate();
-        EXPECT_EQ(current.getModeId(), kModeId90);
+        const auto mode = configs.getActiveMode();
+        EXPECT_EQ(mode->getId(), kModeId90);
     }
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
     {
-        const auto current = configs.getCurrentRefreshRate();
-        EXPECT_EQ(current.getModeId(), kModeId90);
+        const auto mode = configs.getActiveMode();
+        EXPECT_EQ(mode->getId(), kModeId90);
     }
 }
 
@@ -283,10 +266,10 @@
 
         // If there are no layers we select the default frame rate, which is the max of the primary
         // range.
-        EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate());
+        EXPECT_EQ(kMode90, configs.getBestRefreshRate());
 
         EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), NO_ERROR);
-        EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate());
+        EXPECT_EQ(kMode60, configs.getBestRefreshRate());
     }
     {
         // We select max even when this will cause a non-seamless switch.
@@ -294,7 +277,7 @@
         constexpr bool kAllowGroupSwitching = true;
         EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}),
                   NO_ERROR);
-        EXPECT_EQ(asRefreshRate(kMode90_G1), configs.getBestRefreshRate());
+        EXPECT_EQ(kMode90_G1, configs.getBestRefreshRate());
     }
 }
 
@@ -306,104 +289,104 @@
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.name = "";
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}), 0);
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
@@ -414,32 +397,32 @@
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
@@ -449,26 +432,26 @@
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
@@ -482,19 +465,19 @@
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48_Hz;
     lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
@@ -510,7 +493,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -518,7 +501,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -526,7 +509,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -534,7 +517,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -542,7 +525,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -550,7 +533,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
@@ -558,7 +541,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -566,7 +549,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -574,7 +557,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
@@ -591,7 +574,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -599,7 +582,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -607,7 +590,7 @@
     lr2.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -615,7 +598,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -623,7 +606,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -631,7 +614,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::Heuristic;
@@ -639,7 +622,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -647,7 +630,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     lr1.desiredRefreshRate = 24_Hz;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -655,7 +638,7 @@
     lr2.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
@@ -665,26 +648,26 @@
     auto& lr = layers[0];
 
     lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
@@ -695,42 +678,42 @@
 
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
-    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 90_Hz;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 45_Hz;
     lr.name = "45Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 30_Hz;
     lr.name = "30Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.name = "24Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr.desiredRefreshRate = 24_Hz;
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.name = "24Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
@@ -742,39 +725,39 @@
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Max;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 15_Hz;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 30_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
@@ -786,9 +769,9 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto refreshRate = configs.getBestRefreshRate(layers);
-        EXPECT_EQ(asRefreshRate(kMode60), refreshRate)
-                << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
+        const auto mode = configs.getBestRefreshRate(layers);
+        EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
+                                 << to_string(mode->getFps());
     }
 }
 
@@ -802,9 +785,9 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto refreshRate = configs.getBestRefreshRate(layers);
-        EXPECT_EQ(asRefreshRate(kMode60), refreshRate)
-                << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
+        const auto mode = configs.getBestRefreshRate(layers);
+        EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
+                                 << to_string(mode->getFps());
     }
 }
 
@@ -819,19 +802,19 @@
     lr1.desiredRefreshRate = 60_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90_Hz;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
@@ -843,9 +826,9 @@
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
         lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto refreshRate = configs.getBestRefreshRate(layers, {});
-        EXPECT_EQ(asRefreshRate(kMode90), refreshRate)
-                << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
+        const auto mode = configs.getBestRefreshRate(layers, {});
+        EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
+                                 << to_string(mode->getFps());
     }
 }
 
@@ -862,7 +845,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
@@ -870,14 +853,14 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30_Hz;
@@ -885,14 +868,14 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30_Hz;
     lr1.name = "30Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
@@ -907,28 +890,28 @@
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60_Hz;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     // The other layer starts to provide buffers
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -937,7 +920,7 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90_Hz;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, touchConsidered) {
@@ -1023,8 +1006,7 @@
         ss << "ExplicitDefault " << desired;
         lr.name = ss.str();
 
-        const auto refreshRate = configs.getBestRefreshRate(layers);
-        EXPECT_EQ(refreshRate.getFps(), expected);
+        EXPECT_EQ(expected, configs.getBestRefreshRate(layers)->getFps());
     }
 }
 
@@ -1035,36 +1017,36 @@
 
     // Test that 23.976 will choose 24 if 23.976 is not supported
     {
-        TestableRefreshRateConfigs configs({kMode24, kMode25, kMode30, kMode30Frac, kMode60,
-                                            kMode60Frac},
+        TestableRefreshRateConfigs configs(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).getModeId());
+        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId());
     }
 
     // Test that 24 will choose 23.976 if 24 is not supported
     {
-        TestableRefreshRateConfigs configs({kMode24Frac, kMode25, kMode30, kMode30Frac, kMode60,
-                                            kMode60Frac},
+        TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
+                                                     kMode60, kMode60Frac),
                                            kModeId60);
 
         lr.desiredRefreshRate = 24_Hz;
         lr.name = "ExplicitExactOrMultiple 24 Hz";
-        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers).getModeId());
+        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
     }
 
     // Test that 29.97 will prefer 59.94 over 60 and 30
     {
-        TestableRefreshRateConfigs configs({kMode24, kMode24Frac, kMode25, kMode30, kMode60,
-                                            kMode60Frac},
+        TestableRefreshRateConfigs configs(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).getModeId());
+        EXPECT_EQ(kModeId60Frac, configs.getBestRefreshRate(layers)->getId());
     }
 }
 
@@ -1083,32 +1065,31 @@
             ss << "ExplicitExact " << desired;
             lr.name = ss.str();
 
-            auto selectedRefreshRate = configs.getBestRefreshRate(layers);
-            EXPECT_EQ(selectedRefreshRate.getFps(), lr.desiredRefreshRate);
+            EXPECT_EQ(lr.desiredRefreshRate, configs.getBestRefreshRate(layers)->getFps());
         }
     }
 
     // Test that 23.976 will choose 24 if 23.976 is not supported
     {
-        TestableRefreshRateConfigs configs({kMode24, kMode25, kMode30, kMode30Frac, kMode60,
-                                            kMode60Frac},
+        TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
+                                                     kMode60, kMode60Frac),
                                            kModeId60);
 
         lr.vote = LayerVoteType::ExplicitExact;
         lr.desiredRefreshRate = 23.976_Hz;
         lr.name = "ExplicitExact 23.976 Hz";
-        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers).getModeId());
+        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId());
     }
 
     // Test that 24 will choose 23.976 if 24 is not supported
     {
-        TestableRefreshRateConfigs configs({kMode24Frac, kMode25, kMode30, kMode30Frac, kMode60,
-                                            kMode60Frac},
+        TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
+                                                     kMode60, kMode60Frac),
                                            kModeId60);
 
         lr.desiredRefreshRate = 24_Hz;
         lr.name = "ExplicitExact 24 Hz";
-        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers).getModeId());
+        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
     }
 }
 
@@ -1126,10 +1107,9 @@
     lr.name = "60Hz ExplicitDefault";
     lr.focused = true;
 
-    const auto [refreshRate, signals] =
-            configs.getBestRefreshRate(layers, {.touch = true, .idle = true});
+    const auto [mode, signals] = configs.getBestRefreshRate(layers, {.touch = true, .idle = true});
 
-    EXPECT_EQ(refreshRate, asRefreshRate(kMode60));
+    EXPECT_EQ(mode, kMode60);
     EXPECT_FALSE(signals.touch);
 }
 
@@ -1146,7 +1126,7 @@
     lr.desiredRefreshRate = 90_Hz;
     lr.name = "90Hz ExplicitDefault";
     lr.focused = true;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers, {.idle = true}));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.idle = true}));
 }
 
 TEST_F(RefreshRateConfigsTest,
@@ -1155,8 +1135,8 @@
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
 
-    const auto [refreshRate, signals] = configs.getBestRefreshRateAndSignals({}, {});
-    EXPECT_EQ(refreshRate, asRefreshRate(kMode90));
+    const auto [mode, signals] = configs.getBestRefreshRateAndSignals({}, {});
+    EXPECT_EQ(mode, kMode90);
     EXPECT_FALSE(signals.touch);
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1166,46 +1146,46 @@
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitExactOrMultiple";
     lr.focused = false;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::ExplicitDefault;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz ExplicitDefault";
     lr.focused = false;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Heuristic;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Heuristic";
     lr.focused = false;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Max;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Max";
     lr.focused = false;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.vote = LayerVoteType::Min;
     lr.desiredRefreshRate = 60_Hz;
     lr.name = "60Hz Min";
     lr.focused = false;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     lr.focused = true;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
@@ -1221,7 +1201,7 @@
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
@@ -1239,7 +1219,7 @@
     layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
@@ -1258,7 +1238,7 @@
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
@@ -1269,7 +1249,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
-    configs.setCurrentModeId(kModeId90);
+    configs.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}};
@@ -1279,7 +1259,7 @@
     layer.seamlessness = Seamlessness::OnlySeamless;
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
@@ -1290,7 +1270,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
-    configs.setCurrentModeId(kModeId90);
+    configs.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.
@@ -1303,7 +1283,7 @@
     layer.name = "60Hz ExplicitDefault";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
@@ -1314,7 +1294,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
-    configs.setCurrentModeId(kModeId90);
+    configs.setActiveModeId(kModeId90);
 
     // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
     // seamlessness=OnlySeamless can't change the mode group.
@@ -1332,7 +1312,7 @@
     layers[1].name = "90Hz ExplicitDefault";
     layers[1].focused = false;
 
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
@@ -1343,7 +1323,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
-    configs.setCurrentModeId(kModeId90);
+    configs.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
@@ -1365,7 +1345,7 @@
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
@@ -1376,7 +1356,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
 
-    configs.setCurrentModeId(kModeId90);
+    configs.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,
@@ -1395,7 +1375,7 @@
     layers[1].vote = LayerVoteType::ExplicitDefault;
     layers[1].name = "90Hz ExplicitDefault";
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
@@ -1415,10 +1395,10 @@
     layer.name = "60Hz ExplicitExactOrMultiple";
     layer.focused = true;
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
 
-    configs.setCurrentModeId(kModeId120);
-    EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers).getModeId());
+    configs.setActiveModeId(kModeId120);
+    EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
@@ -1443,14 +1423,14 @@
                                              .weight = 1.f,
                                              .focused = true}};
 
-    EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers)->getId());
 
     auto& seamedLayer = layers[0];
     seamedLayer.desiredRefreshRate = 30_Hz;
     seamedLayer.name = "30Hz ExplicitDefault";
-    configs.setCurrentModeId(kModeId30);
+    configs.setActiveModeId(kModeId30);
 
-    EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
@@ -1465,7 +1445,7 @@
     std::vector<LayerRequirement> layers = {
             {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
 
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
@@ -1485,12 +1465,12 @@
         layers[0].vote = voteType;
         layers[0].desiredRefreshRate = fps;
         layers[0].focused = args.focused;
-        return configs.getBestRefreshRate(layers, {.touch = args.touch}).getModeId();
+        return configs.getBestRefreshRate(layers, {.touch = args.touch})->getId();
     };
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), 0);
 
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate().getModeId());
+    EXPECT_EQ(kModeId60, configs.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));
@@ -1537,7 +1517,7 @@
 
         // Refresh rate will be chosen by either touch state or idle state.
         EXPECT_EQ(!touchActive, signals.idle);
-        return refreshRate.getModeId();
+        return refreshRate->getId();
     };
 
     EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
@@ -1555,10 +1535,10 @@
     }
 
     // With no layers, idle should still be lower priority than touch boost.
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true}).getModeId());
+    EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true})->getId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
-    configs.setCurrentModeId(kModeId90);
+    configs.setActiveModeId(kModeId90);
 
     {
         constexpr bool kTouchActive = false;
@@ -1572,7 +1552,7 @@
     }
 
     // Idle should be applied rather than the current config when there are no layers.
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true}).getModeId());
+    EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true})->getId());
 }
 
 TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
@@ -1598,13 +1578,12 @@
 
     struct Expectation {
         Fps fps;
-        const RefreshRate& refreshRate;
+        DisplayModePtr mode;
     };
 
     const std::initializer_list<Expectation> knownFrameRatesExpectations = {
-            {24_Hz, asRefreshRate(kMode60)}, {30_Hz, asRefreshRate(kMode60)},
-            {45_Hz, asRefreshRate(kMode90)}, {60_Hz, asRefreshRate(kMode60)},
-            {72_Hz, asRefreshRate(kMode90)}, {90_Hz, asRefreshRate(kMode90)},
+            {24_Hz, kMode60}, {30_Hz, kMode60}, {45_Hz, kMode90},
+            {60_Hz, kMode60}, {72_Hz, kMode90}, {90_Hz, kMode90},
     };
 
     // Make sure the test tests all the known frame rate
@@ -1620,9 +1599,9 @@
     auto& layer = layers[0];
     layer.vote = LayerVoteType::Heuristic;
 
-    for (const auto& [fps, refreshRate] : knownFrameRatesExpectations) {
+    for (const auto& [fps, mode] : knownFrameRatesExpectations) {
         layer.desiredRefreshRate = fps;
-        EXPECT_EQ(refreshRate, configs.getBestRefreshRate(layers));
+        EXPECT_EQ(mode, configs.getBestRefreshRate(layers));
     }
 }
 
@@ -1641,21 +1620,21 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode30), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
     explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
@@ -1674,21 +1653,21 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
     explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(asRefreshRate(kMode72), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(asRefreshRate(kMode90), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) {
@@ -1697,7 +1676,7 @@
     using GlobalSignals = RefreshRateConfigs::GlobalSignals;
     const auto args = std::make_pair(std::vector<LayerRequirement>{},
                                      GlobalSignals{.touch = true, .idle = true});
-    const auto result = std::make_pair(asRefreshRate(kMode90), GlobalSignals{.touch = true});
+    const auto result = std::make_pair(kMode90, GlobalSignals{.touch = true});
 
     configs.mutableGetBestRefreshRateCache() = {args, result};
 
@@ -1736,13 +1715,13 @@
     explicitExactLayer.name = "ExplicitExact";
     explicitExactLayer.desiredRefreshRate = 30_Hz;
 
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode120), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true}));
 
     explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
 
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers, {.touch = true}));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers, {.touch = true}));
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
@@ -1761,7 +1740,7 @@
     explicitDefaultLayer.name = "ExplicitDefault";
     explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
 
-    EXPECT_EQ(asRefreshRate(kMode60), configs.getBestRefreshRate(layers));
+    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 }
 
 // b/190578904
@@ -1771,17 +1750,20 @@
 
     DisplayModes displayModes;
     for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
-        displayModes.push_back(
-                createDisplayMode(DisplayModeId(fps), Fps::fromValue(static_cast<float>(fps))));
+        const DisplayModeId modeId(fps);
+        displayModes.try_emplace(modeId,
+                                 createDisplayMode(modeId,
+                                                   Fps::fromValue(static_cast<float>(fps))));
     }
 
-    const TestableRefreshRateConfigs configs(displayModes, displayModes[0]->getId());
+    const TestableRefreshRateConfigs configs(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(), configs.getBestRefreshRate(layers)->getFps().getIntValue())
                 << "Failed for " << ftl::enum_string(vote);
     };
 
@@ -1796,15 +1778,14 @@
 
 // b/190578904
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
-    const DisplayModes displayModes = {
-            createDisplayMode(DisplayModeId(0), 43_Hz),
-            createDisplayMode(DisplayModeId(1), 53_Hz),
-            createDisplayMode(DisplayModeId(2), 55_Hz),
-            createDisplayMode(DisplayModeId(3), 60_Hz),
-    };
+    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(displayModes, displayModes[0]->getId());
+    const TestableRefreshRateConfigs configs(std::move(displayModes), kActiveModeId);
 
     const std::vector<LayerRequirement> layers = {
             {
@@ -1821,13 +1802,13 @@
             },
     };
 
-    EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals).getFps());
+    EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals)->getFps());
 }
 
-TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
-    EXPECT_TRUE(asRefreshRate(kMode60) < asRefreshRate(kMode90));
-    EXPECT_FALSE(asRefreshRate(kMode60) < asRefreshRate(kMode60));
-    EXPECT_FALSE(asRefreshRate(kMode90) < asRefreshRate(kMode90));
+TEST_F(RefreshRateConfigsTest, modeComparison) {
+    EXPECT_LT(kMode60->getFps(), kMode90->getFps());
+    EXPECT_GE(kMode60->getFps(), kMode60->getFps());
+    EXPECT_GE(kMode90->getFps(), kMode90->getFps());
 }
 
 TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
@@ -1877,27 +1858,27 @@
     RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
 
     const auto frameRate = 30_Hz;
-    Fps displayRefreshRate = configs.getCurrentRefreshRate().getFps();
+    Fps displayRefreshRate = configs.getActiveMode()->getFps();
     EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setCurrentModeId(kModeId60);
-    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
+    configs.setActiveModeId(kModeId60);
+    displayRefreshRate = configs.getActiveMode()->getFps();
     EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setCurrentModeId(kModeId72);
-    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
+    configs.setActiveModeId(kModeId72);
+    displayRefreshRate = configs.getActiveMode()->getFps();
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setCurrentModeId(kModeId90);
-    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
+    configs.setActiveModeId(kModeId90);
+    displayRefreshRate = configs.getActiveMode()->getFps();
     EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setCurrentModeId(kModeId120);
-    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
+    configs.setActiveModeId(kModeId120);
+    displayRefreshRate = configs.getActiveMode()->getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    configs.setCurrentModeId(kModeId90);
-    displayRefreshRate = configs.getCurrentRefreshRate().getFps();
+    configs.setActiveModeId(kModeId90);
+    displayRefreshRate = configs.getActiveMode()->getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
 
     EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz));
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index df4a9c4..e2e3d7b 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -21,8 +21,6 @@
 #include <log/log.h>
 #include <thread>
 
-#include "DisplayHardware/DisplayMode.h"
-#include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/RefreshRateStats.h"
 #include "mock/MockTimeStats.h"
 
@@ -35,29 +33,15 @@
 
 class RefreshRateStatsTest : public testing::Test {
 protected:
-    static inline const auto CONFIG_ID_0 = DisplayModeId(0);
-    static inline const auto CONFIG_ID_1 = DisplayModeId(1);
-    static inline const auto CONFIG_GROUP_0 = 0;
-    static constexpr int64_t VSYNC_90 = 11111111;
-    static constexpr int64_t VSYNC_60 = 16666667;
-
     RefreshRateStatsTest();
     ~RefreshRateStatsTest();
 
-    void init(const DisplayModes& configs) {
-        mRefreshRateConfigs =
-                std::make_unique<RefreshRateConfigs>(configs, /*currentConfig=*/CONFIG_ID_0);
-
-        const auto currFps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
-        mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, currFps,
-                                                               /*currentPowerMode=*/PowerMode::OFF);
+    void resetStats(Fps fps) {
+        mRefreshRateStats = std::make_unique<RefreshRateStats>(mTimeStats, fps, PowerMode::OFF);
     }
 
     mock::TimeStats mTimeStats;
-    std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
     std::unique_ptr<RefreshRateStats> mRefreshRateStats;
-
-    DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod);
 };
 
 RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -72,20 +56,10 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-DisplayModePtr RefreshRateStatsTest::createDisplayMode(DisplayModeId modeId, int32_t group,
-                                                       int64_t vsyncPeriod) {
-    return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value()))
-            .setId(modeId)
-            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-            .setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
-            .setGroup(group)
-            .build();
-}
-
 namespace {
 
-TEST_F(RefreshRateStatsTest, oneConfigTest) {
-    init({createDisplayMode(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90)});
+TEST_F(RefreshRateStatsTest, oneMode) {
+    resetStats(90_Hz);
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
@@ -102,8 +76,7 @@
     EXPECT_LT(screenOff, times.get("ScreenOff")->get());
     EXPECT_FALSE(times.contains("90.00 Hz"));
 
-    const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
-    mRefreshRateStats->setRefreshRate(config0Fps);
+    mRefreshRateStats->setRefreshRate(90_Hz);
     mRefreshRateStats->setPowerMode(PowerMode::ON);
     screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
@@ -121,20 +94,18 @@
     EXPECT_LT(screenOff, times.get("ScreenOff")->get());
     EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
 
-    mRefreshRateStats->setRefreshRate(config0Fps);
+    mRefreshRateStats->setRefreshRate(90_Hz);
     screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
 
-    // Because the power mode is not PowerMode::ON, switching the config
-    // does not update refresh rates that come from the config.
+    // Stats are not updated while the screen is off.
     EXPECT_LT(screenOff, times.get("ScreenOff")->get());
     EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
 }
 
-TEST_F(RefreshRateStatsTest, twoConfigsTest) {
-    init({createDisplayMode(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90),
-          createDisplayMode(CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60)});
+TEST_F(RefreshRateStatsTest, twoModes) {
+    resetStats(90_Hz);
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
@@ -153,9 +124,7 @@
     EXPECT_FALSE(times.contains("60.00 Hz"));
     EXPECT_FALSE(times.contains("90.00 Hz"));
 
-    const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
-    const auto config1Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_1).getFps();
-    mRefreshRateStats->setRefreshRate(config0Fps);
+    mRefreshRateStats->setRefreshRate(90_Hz);
     mRefreshRateStats->setPowerMode(PowerMode::ON);
     screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
@@ -165,8 +134,7 @@
     ASSERT_TRUE(times.contains("90.00 Hz"));
     EXPECT_LT(0ms, times.get("90.00 Hz")->get());
 
-    // When power mode is normal, time for configs updates.
-    mRefreshRateStats->setRefreshRate(config1Fps);
+    mRefreshRateStats->setRefreshRate(60_Hz);
     auto ninety = mRefreshRateStats->getTotalTimes().get("90.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -176,7 +144,7 @@
     ASSERT_TRUE(times.contains("60.00 Hz"));
     EXPECT_LT(0ms, times.get("60.00 Hz")->get());
 
-    mRefreshRateStats->setRefreshRate(config0Fps);
+    mRefreshRateStats->setRefreshRate(90_Hz);
     auto sixty = mRefreshRateStats->getTotalTimes().get("60.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -185,7 +153,7 @@
     EXPECT_LT(ninety, times.get("90.00 Hz")->get());
     EXPECT_EQ(sixty, times.get("60.00 Hz")->get());
 
-    mRefreshRateStats->setRefreshRate(config1Fps);
+    mRefreshRateStats->setRefreshRate(60_Hz);
     ninety = mRefreshRateStats->getTotalTimes().get("90.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -194,10 +162,9 @@
     EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
     EXPECT_LT(sixty, times.get("60.00 Hz")->get());
 
-    // Because the power mode is not PowerMode::ON, switching the config
-    // does not update refresh rates that come from the config.
+    // Stats are not updated while the screen is off.
     mRefreshRateStats->setPowerMode(PowerMode::DOZE);
-    mRefreshRateStats->setRefreshRate(config0Fps);
+    mRefreshRateStats->setRefreshRate(90_Hz);
     sixty = mRefreshRateStats->getTotalTimes().get("60.00 Hz")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -206,7 +173,7 @@
     EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
     EXPECT_EQ(sixty, times.get("60.00 Hz")->get());
 
-    mRefreshRateStats->setRefreshRate(config1Fps);
+    mRefreshRateStats->setRefreshRate(60_Hz);
     screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index a992a91..aab2795 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -24,12 +24,15 @@
 #include "Scheduler/RefreshRateConfigs.h"
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockLayer.h"
 #include "mock/MockSchedulerCallback.h"
 
 namespace android::scheduler {
 
+using android::mock::createDisplayMode;
+
 using testing::_;
 using testing::Return;
 
@@ -55,21 +58,11 @@
 
     SchedulerTest();
 
-    const DisplayModePtr mode60 = DisplayMode::Builder(0)
-                                          .setId(DisplayModeId(0))
-                                          .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                          .setVsyncPeriod((60_Hz).getPeriodNsecs())
-                                          .setGroup(0)
-                                          .build();
-    const DisplayModePtr mode120 = DisplayMode::Builder(1)
-                                           .setId(DisplayModeId(1))
-                                           .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                           .setVsyncPeriod((120_Hz).getPeriodNsecs())
-                                           .setGroup(0)
-                                           .build();
+    static inline const DisplayModePtr kMode60 = createDisplayMode(DisplayModeId(0), 60_Hz);
+    static inline const DisplayModePtr kMode120 = createDisplayMode(DisplayModeId(1), 120_Hz);
 
     std::shared_ptr<RefreshRateConfigs> mConfigs =
-            std::make_shared<RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId());
+            std::make_shared<RefreshRateConfigs>(makeModes(kMode60), kMode60->getId());
 
     mock::SchedulerCallback mSchedulerCallback;
     TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
@@ -172,7 +165,7 @@
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
-    EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
     mScheduler->chooseRefreshRateForContent();
 }
 
@@ -182,7 +175,7 @@
     ASSERT_EQ(1u, mScheduler->layerHistorySize());
 
     mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
+            std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -221,12 +214,12 @@
 }
 
 MATCHER(Is120Hz, "") {
-    return isApproxEqual(arg.getFps(), 120_Hz);
+    return isApproxEqual(arg->getFps(), 120_Hz);
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
     mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
+            std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
 
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
@@ -239,11 +232,11 @@
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
-    EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(Is120Hz(), _)).Times(1);
     mScheduler->chooseRefreshRateForContent();
 
     // No-op if layer requirements have not changed.
-    EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
     mScheduler->chooseRefreshRateForContent();
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 3205952..32d57b5 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -43,13 +43,11 @@
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
 
         {
-            DisplayModes modes = {kDisplayMode60, kDisplayMode90, kDisplayMode120,
-                                  kDisplayMode90DifferentResolution};
-            const DisplayModeId activeModeId = kDisplayModeId60;
-            auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId);
+            DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
+            auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
 
             mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
-                               .setDisplayModes(modes, activeModeId, std::move(configs))
+                               .setDisplayModes(std::move(modes), kModeId60, std::move(configs))
                                .inject();
         }
 
@@ -68,49 +66,18 @@
     sp<DisplayDevice> mDisplay;
     mock::EventThread* mAppEventThread;
 
-    const DisplayModeId kDisplayModeId60 = DisplayModeId(0);
-    const DisplayModePtr kDisplayMode60 =
-            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId60.value()))
-                    .setId(kDisplayModeId60)
-                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
-                    .setVsyncPeriod((60_Hz).getPeriodNsecs())
-                    .setGroup(0)
-                    .setHeight(1000)
-                    .setWidth(1000)
-                    .build();
+    static constexpr DisplayModeId kModeId60{0};
+    static constexpr DisplayModeId kModeId90{1};
+    static constexpr DisplayModeId kModeId120{2};
+    static constexpr DisplayModeId kModeId90_4K{3};
 
-    const DisplayModeId kDisplayModeId90 = DisplayModeId(1);
-    const DisplayModePtr kDisplayMode90 =
-            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90.value()))
-                    .setId(kDisplayModeId90)
-                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
-                    .setVsyncPeriod((90_Hz).getPeriodNsecs())
-                    .setGroup(1)
-                    .setHeight(1000)
-                    .setWidth(1000)
-                    .build();
+    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz, 0);
+    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz, 1);
+    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz, 2);
 
-    const DisplayModeId kDisplayModeId120 = DisplayModeId(2);
-    const DisplayModePtr kDisplayMode120 =
-            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId120.value()))
-                    .setId(kDisplayModeId120)
-                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
-                    .setVsyncPeriod((120_Hz).getPeriodNsecs())
-                    .setGroup(2)
-                    .setHeight(1000)
-                    .setWidth(1000)
-                    .build();
-
-    const DisplayModeId kDisplayModeId90DifferentResolution = DisplayModeId(3);
-    const DisplayModePtr kDisplayMode90DifferentResolution =
-            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90DifferentResolution.value()))
-                    .setId(kDisplayModeId90DifferentResolution)
-                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
-                    .setVsyncPeriod((90_Hz).getPeriodNsecs())
-                    .setGroup(3)
-                    .setHeight(2000)
-                    .setWidth(2000)
-                    .build();
+    static constexpr ui::Size kResolution4K{3840, 2160};
+    static inline const DisplayModePtr kMode90_4K =
+            createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
 };
 
 void DisplayModeSwitchingTest::setupScheduler(
@@ -145,39 +112,39 @@
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
-                                        kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
+                                        false, 0.f, 120.f, 0.f, 120.f);
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
-                                               hal::HWConfigId(kDisplayModeId90.value()), _, _))
+                                               hal::HWConfigId(kModeId90.value()), _, _))
             .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
 
     mFlinger.commit();
 
     Mock::VerifyAndClearExpectations(mComposer);
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
 
     // Verify that the next commit will complete the mode change and send
     // a onModeChanged event to the framework.
 
-    EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90));
+    EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
     mFlinger.commit();
     Mock::VerifyAndClearExpectations(mAppEventThread);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
@@ -185,27 +152,27 @@
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
-                                        kDisplayModeId90.value(), true, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
+                                        true, 0.f, 120.f, 0.f, 120.f);
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
-                                               hal::HWConfigId(kDisplayModeId90.value()), _, _))
+                                               hal::HWConfigId(kModeId90.value()), _, _))
             .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
 
-    EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90));
+    EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
 
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
@@ -213,72 +180,72 @@
     // is still being processed the later call will be respected.
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
-                                        kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
+                                        false, 0.f, 120.f, 0.f, 120.f);
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
-                                               hal::HWConfigId(kDisplayModeId90.value()), _, _))
+                                               hal::HWConfigId(kModeId90.value()), _, _))
             .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
 
     mFlinger.commit();
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
-                                        kDisplayModeId120.value(), false, 0.f, 180.f, 0.f, 180.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId120.value(),
+                                        false, 0.f, 180.f, 0.f, 180.f);
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
 
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
-                                               hal::HWConfigId(kDisplayModeId120.value()), _, _))
+                                               hal::HWConfigId(kModeId120.value()), _, _))
             .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
 
     mFlinger.commit();
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
 
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId120);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId120);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
-                                        kDisplayModeId90DifferentResolution.value(), false, 0.f,
-                                        120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90_4K.value(),
+                                        false, 0.f, 120.f, 0.f, 120.f);
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90DifferentResolution);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
-                                               hal::HWConfigId(
-                                                       kDisplayModeId90DifferentResolution.value()),
-                                               _, _))
+                                               hal::HWConfigId(kModeId90_4K.value()), _, _))
             .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
 
     EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true));
 
     // Misc expecations. We don't need to enforce these method calls, but since the helper methods
     // already set expectations we should add new ones here, otherwise the test will fail.
-    EXPECT_CALL(*mConsumer, setDefaultBufferSize(2000, 2000)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mConsumer,
+                setDefaultBufferSize(static_cast<uint32_t>(kResolution4K.getWidth()),
+                                     static_cast<uint32_t>(kResolution4K.getHeight())))
+            .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(hal::Error::NONE));
 
@@ -296,7 +263,7 @@
     mDisplay = mFlinger.getDisplay(displayToken);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90DifferentResolution);
+    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90_4K);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 38dceb9..a0e078b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -240,19 +240,17 @@
         ASSERT_TRUE(hwcDisplayId);
         mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId);
         DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID)
-                                            .setWidth(Case::Display::WIDTH)
-                                            .setHeight(Case::Display::HEIGHT)
+                                            .setResolution(Case::Display::RESOLUTION)
                                             .setVsyncPeriod(DEFAULT_VSYNC_PERIOD)
                                             .setDpiX(DEFAULT_DPI)
                                             .setDpiY(DEFAULT_DPI)
                                             .setGroup(0)
                                             .build();
-        DisplayModes modes{activeMode};
         state.physical = {.id = *displayId,
                           .type = *connectionType,
                           .hwcDisplayId = *hwcDisplayId,
-                          .supportedModes = modes,
-                          .activeMode = activeMode};
+                          .supportedModes = makeModes(activeMode),
+                          .activeMode = std::move(activeMode)};
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -270,8 +268,7 @@
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
-    EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
-    EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
+    EXPECT_EQ(Case::Display::RESOLUTION, device->getSize());
     EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
     EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
     EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 6780108..bf2465f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -45,6 +45,7 @@
 #include "SurfaceInterceptor.h"
 #include "TestableScheduler.h"
 #include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
 #include "mock/MockFrameTimeline.h"
 #include "mock/MockFrameTracer.h"
 #include "mock/MockSchedulerCallback.h"
@@ -227,31 +228,24 @@
         if (std::holds_alternative<RefreshRateConfigsPtr>(modesVariant)) {
             configs = std::move(std::get<RefreshRateConfigsPtr>(modesVariant));
         } else {
-            DisplayModes modes = {DisplayMode::Builder(0)
-                                          .setId(DisplayModeId(0))
-                                          .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                          .setVsyncPeriod(16'666'667)
-                                          .setGroup(0)
-                                          .build()};
+            constexpr DisplayModeId kModeId60{0};
+            DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
 
             if (std::holds_alternative<TwoDisplayModes>(modesVariant)) {
-                modes.emplace_back(DisplayMode::Builder(1)
-                                           .setId(DisplayModeId(1))
-                                           .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                           .setVsyncPeriod(11'111'111)
-                                           .setGroup(0)
-                                           .build());
+                constexpr DisplayModeId kModeId90{1};
+                modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
             }
 
-            configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, DisplayModeId(0));
+            configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
         }
 
-        const auto currFps = configs->getCurrentRefreshRate().getFps();
-        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
+        const auto fps = configs->getActiveMode()->getFps();
+        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
         mFlinger->mRefreshRateStats =
-                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
+                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
                                                               hal::PowerMode::OFF);
 
         using Callback = scheduler::ISchedulerCallback;
@@ -572,8 +566,7 @@
     class FakeHwcDisplayInjector {
     public:
         static constexpr hal::HWDisplayId DEFAULT_HWC_DISPLAY_ID = 1000;
-        static constexpr int32_t DEFAULT_WIDTH = 1920;
-        static constexpr int32_t DEFAULT_HEIGHT = 1280;
+        static constexpr ui::Size DEFAULT_RESOLUTION{1920, 1280};
         static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667;
         static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
         static constexpr int32_t DEFAULT_DPI = 320;
@@ -589,17 +582,12 @@
             return *this;
         }
 
-        auto& setWidth(int32_t width) {
-            mWidth = width;
+        auto& setResolution(ui::Size resolution) {
+            mResolution = resolution;
             return *this;
         }
 
-        auto& setHeight(int32_t height) {
-            mHeight = height;
-            return *this;
-        }
-
-        auto& setVsyncPeriod(int32_t vsyncPeriod) {
+        auto& setVsyncPeriod(nsecs_t vsyncPeriod) {
             mVsyncPeriod = vsyncPeriod;
             return *this;
         }
@@ -660,18 +648,20 @@
 
             EXPECT_CALL(*composer,
                         getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _))
-                    .WillRepeatedly(DoAll(SetArgPointee<3>(mWidth), Return(hal::Error::NONE)));
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getWidth()),
+                                          Return(hal::Error::NONE)));
 
             EXPECT_CALL(*composer,
                         getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::HEIGHT,
                                             _))
-                    .WillRepeatedly(DoAll(SetArgPointee<3>(mHeight), Return(hal::Error::NONE)));
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getHeight()),
+                                          Return(hal::Error::NONE)));
 
             EXPECT_CALL(*composer,
                         getDisplayAttribute(mHwcDisplayId, mActiveConfig,
                                             hal::Attribute::VSYNC_PERIOD, _))
-                    .WillRepeatedly(
-                            DoAll(SetArgPointee<3>(mVsyncPeriod), Return(hal::Error::NONE)));
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(static_cast<int32_t>(mVsyncPeriod)),
+                                          Return(hal::Error::NONE)));
 
             EXPECT_CALL(*composer,
                         getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::DPI_X, _))
@@ -708,9 +698,8 @@
         const bool mIsPrimary;
 
         hal::HWDisplayId mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
-        int32_t mWidth = DEFAULT_WIDTH;
-        int32_t mHeight = DEFAULT_HEIGHT;
-        int32_t mVsyncPeriod = DEFAULT_VSYNC_PERIOD;
+        ui::Size mResolution = DEFAULT_RESOLUTION;
+        nsecs_t mVsyncPeriod = DEFAULT_VSYNC_PERIOD;
         int32_t mDpiX = DEFAULT_DPI;
         int32_t mDpiY = DEFAULT_DPI;
         int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
@@ -723,12 +712,12 @@
     class FakeDisplayDeviceInjector {
     public:
         FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
-                                  std::shared_ptr<compositionengine::Display> compositionDisplay,
+                                  std::shared_ptr<compositionengine::Display> display,
                                   std::optional<ui::DisplayConnectionType> connectionType,
                                   std::optional<hal::HWDisplayId> hwcDisplayId, bool isPrimary)
               : mFlinger(flinger),
                 mCreationArgs(flinger.mFlinger.get(), flinger.mFlinger->getHwComposer(),
-                              mDisplayToken, compositionDisplay),
+                              mDisplayToken, display),
                 mHwcDisplayId(hwcDisplayId) {
             mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
@@ -805,49 +794,52 @@
         }
 
         sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
+            const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
+
             auto& modes = mCreationArgs.supportedModes;
             auto& activeModeId = mCreationArgs.activeModeId;
 
-            if (!mCreationArgs.refreshRateConfigs) {
-                if (modes.empty()) {
-                    activeModeId = DisplayModeId(0);
-                    modes.emplace_back(
-                            DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
-                                    .setId(activeModeId)
-                                    .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
-                                    .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
-                                    .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
-                                    .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
-                                    .setDpiX(FakeHwcDisplayInjector::DEFAULT_DPI)
-                                    .setDpiY(FakeHwcDisplayInjector::DEFAULT_DPI)
-                                    .setGroup(0)
-                                    .build());
-                }
+            if (displayId && !mCreationArgs.refreshRateConfigs) {
+                if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
+                    if (modes.empty()) {
+                        constexpr DisplayModeId kModeId{0};
+                        DisplayModePtr mode =
+                                DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
+                                        .setId(kModeId)
+                                        .setPhysicalDisplayId(*physicalId)
+                                        .setResolution(FakeHwcDisplayInjector::DEFAULT_RESOLUTION)
+                                        .setVsyncPeriod(
+                                                FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
+                                        .setDpiX(FakeHwcDisplayInjector::DEFAULT_DPI)
+                                        .setDpiY(FakeHwcDisplayInjector::DEFAULT_DPI)
+                                        .setGroup(FakeHwcDisplayInjector::DEFAULT_CONFIG_GROUP)
+                                        .build();
 
-                mCreationArgs.refreshRateConfigs =
-                        std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId);
+                        modes = ftl::init::map(kModeId, std::move(mode));
+                        activeModeId = kModeId;
+                    }
+
+                    mCreationArgs.refreshRateConfigs =
+                            std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId);
+                }
             }
 
             DisplayDeviceState state;
             if (const auto type = mCreationArgs.connectionType) {
-                const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
                 LOG_ALWAYS_FATAL_IF(!displayId);
                 const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
                 LOG_ALWAYS_FATAL_IF(!physicalId);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
 
-                const auto it = std::find_if(modes.begin(), modes.end(),
-                                             [&activeModeId](const DisplayModePtr& mode) {
-                                                 return mode->getId() == activeModeId;
-                                             });
-                LOG_ALWAYS_FATAL_IF(it == modes.end());
+                const auto activeMode = modes.get(activeModeId);
+                LOG_ALWAYS_FATAL_IF(!activeMode);
 
                 state.physical = {.id = *physicalId,
                                   .type = *type,
                                   .hwcDisplayId = *mHwcDisplayId,
                                   .deviceProductInfo = {},
                                   .supportedModes = modes,
-                                  .activeMode = *it};
+                                  .activeMode = activeMode->get()};
             }
 
             state.isSecure = mCreationArgs.isSecure;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 996f835..c1d41bb 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -160,6 +160,7 @@
                  Error(Display, std::optional<DisplayDecorationSupport>*));
     MOCK_METHOD2(setIdleTimerEnabled, Error(Display, std::chrono::milliseconds));
     MOCK_METHOD2(hasDisplayIdleTimerCapability, Error(Display, bool*));
+    MOCK_METHOD2(getPhysicalDisplayOrientation, Error(Display, AidlTransform*));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
new file mode 100644
index 0000000..a83ecbc
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "DisplayHardware/DisplayMode.h"
+
+namespace android::mock {
+
+inline DisplayModePtr createDisplayMode(
+        DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
+        ui::Size resolution = ui::Size(1920, 1080),
+        PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0)) {
+    return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
+            .setId(modeId)
+            .setPhysicalDisplayId(displayId)
+            .setVsyncPeriod(refreshRate.getPeriodNsecs())
+            .setGroup(group)
+            .setResolution(resolution)
+            .build();
+}
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index d9faa06..ac2ab199c 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -101,6 +101,8 @@
             (override));
     MOCK_METHOD(hal::Error, setIdleTimerEnabled, (std::chrono::milliseconds), (override));
     MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (), (const override));
+    MOCK_METHOD(hal::Error, getPhysicalDisplayOrientation, (Hwc2::AidlTransform *),
+                (const override));
 };
 
 class Layer : public HWC2::Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index c90b8ed..5083d56 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -25,7 +25,7 @@
 struct SchedulerCallback final : ISchedulerCallback {
     MOCK_METHOD(void, scheduleComposite, (FrameHint), (override));
     MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
-    MOCK_METHOD(void, changeRefreshRate, (const RefreshRate&, DisplayModeEvent), (override));
+    MOCK_METHOD(void, requestDisplayMode, (DisplayModePtr, DisplayModeEvent), (override));
     MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
     MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
 };
@@ -33,7 +33,7 @@
 struct NoOpSchedulerCallback final : ISchedulerCallback {
     void scheduleComposite(FrameHint) override {}
     void setVsyncEnabled(bool) override {}
-    void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
+    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
 };