Merge "TouchpadInputMapper: add timer provider" into main
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 1123d4f..073d0c4 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -757,7 +757,7 @@
 binder::Status InstalldNativeService::createAppDataLocked(
         const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
         int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
-        int32_t targetSdkVersion, int64_t* _aidl_return) {
+        int32_t targetSdkVersion, int64_t* ceDataInode, int64_t* deDataInode) {
     ENFORCE_UID(AID_SYSTEM);
     ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
@@ -767,7 +767,8 @@
     const char* pkgname = packageName.c_str();
 
     // Assume invalid inode unless filled in below
-    if (_aidl_return != nullptr) *_aidl_return = -1;
+    if (ceDataInode != nullptr) *ceDataInode = -1;
+    if (deDataInode != nullptr) *deDataInode = -1;
 
     int32_t uid = multiuser_get_uid(userId, appId);
 
@@ -805,12 +806,12 @@
 
         // And return the CE inode of the top-level data directory so we can
         // clear contents while CE storage is locked
-        if (_aidl_return != nullptr) {
+        if (ceDataInode != nullptr) {
             ino_t result;
             if (get_path_inode(path, &result) != 0) {
                 return error("Failed to get_path_inode for " + path);
             }
-            *_aidl_return = static_cast<uint64_t>(result);
+            *ceDataInode = static_cast<uint64_t>(result);
         }
     }
     if (flags & FLAG_STORAGE_DE) {
@@ -829,6 +830,14 @@
         if (!prepare_app_profile_dir(packageName, appId, userId)) {
             return error("Failed to prepare profiles for " + packageName);
         }
+
+        if (deDataInode != nullptr) {
+            ino_t result;
+            if (get_path_inode(path, &result) != 0) {
+                return error("Failed to get_path_inode for " + path);
+            }
+            *deDataInode = static_cast<uint64_t>(result);
+        }
     }
 
     if (flags & FLAG_STORAGE_SDK) {
@@ -892,14 +901,14 @@
 binder::Status InstalldNativeService::createAppData(
         const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
         int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
-        int32_t targetSdkVersion, int64_t* _aidl_return) {
+        int32_t targetSdkVersion, int64_t* ceDataInode, int64_t* deDataInode) {
     ENFORCE_UID(AID_SYSTEM);
     ENFORCE_VALID_USER(userId);
     CHECK_ARGUMENT_UUID(uuid);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     LOCK_PACKAGE_USER();
     return createAppDataLocked(uuid, packageName, userId, flags, appId, previousAppId, seInfo,
-                               targetSdkVersion, _aidl_return);
+                               targetSdkVersion, ceDataInode, deDataInode);
 }
 
 binder::Status InstalldNativeService::createAppData(
@@ -910,9 +919,12 @@
     // Locking is performed depeer in the callstack.
 
     int64_t ceDataInode = -1;
+    int64_t deDataInode = -1;
     auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
-            args.previousAppId, args.seInfo, args.targetSdkVersion, &ceDataInode);
+                                args.previousAppId, args.seInfo, args.targetSdkVersion,
+                                &ceDataInode, &deDataInode);
     _aidl_return->ceDataInode = ceDataInode;
+    _aidl_return->deDataInode = deDataInode;
     _aidl_return->exceptionCode = status.exceptionCode();
     _aidl_return->exceptionMessage = status.exceptionMessage();
     return ok();
@@ -1839,7 +1851,8 @@
         }
 
         if (!createAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
-                                 appId, /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr)
+                                 appId, /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr,
+                                 nullptr)
                      .isOk()) {
             res = error("Failed to create package target");
             goto fail;
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 0f28234..1ec092d 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -68,7 +68,8 @@
     binder::Status createAppData(const std::optional<std::string>& uuid,
                                  const std::string& packageName, int32_t userId, int32_t flags,
                                  int32_t appId, int32_t previousAppId, const std::string& seInfo,
-                                 int32_t targetSdkVersion, int64_t* _aidl_return);
+                                 int32_t targetSdkVersion, int64_t* ceDataInode,
+                                 int64_t* deDataInode);
 
     binder::Status createAppData(
             const android::os::CreateAppDataArgs& args,
@@ -238,7 +239,7 @@
                                        const std::string& packageName, int32_t userId,
                                        int32_t flags, int32_t appId, int32_t previousAppId,
                                        const std::string& seInfo, int32_t targetSdkVersion,
-                                       int64_t* _aidl_return);
+                                       int64_t* ceDataInode, int64_t* deDataInode);
     binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid,
                                            const std::string& packageName, int32_t userId,
                                            int32_t flags, int32_t appId, const std::string& seInfo);
diff --git a/cmds/installd/binder/android/os/CreateAppDataResult.aidl b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
index 3b8fa6b..463489e 100644
--- a/cmds/installd/binder/android/os/CreateAppDataResult.aidl
+++ b/cmds/installd/binder/android/os/CreateAppDataResult.aidl
@@ -19,6 +19,7 @@
 /** {@hide} */
 parcelable CreateAppDataResult {
     long ceDataInode;
+    long deDataInode;
     int exceptionCode;
     @utf8InCpp String exceptionMessage;
 }
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index c4071c6..ee91d80 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -197,6 +197,7 @@
     std::string app_oat_dir_;
 
     int64_t ce_data_inode_;
+    int64_t de_data_inode_;
 
     std::string secondary_dex_ce_;
     std::string secondary_dex_ce_link_;
@@ -261,16 +262,10 @@
         }
 
         // Create the app user data.
-        binder::Status status = service_->createAppData(
-                volume_uuid_,
-                package_name_,
-                kTestUserId,
-                kAppDataFlags,
-                kTestAppUid,
-                0 /* previousAppId */,
-                se_info_,
-                kOSdkVersion,
-                &ce_data_inode_);
+        binder::Status status =
+                service_->createAppData(volume_uuid_, package_name_, kTestUserId, kAppDataFlags,
+                                        kTestAppUid, 0 /* previousAppId */, se_info_, kOSdkVersion,
+                                        &ce_data_inode_, &de_data_inode_);
         if (!status.isOk()) {
             return ::testing::AssertionFailure() << "Could not create app data: "
                                                  << status.toString8().c_str();
@@ -1350,16 +1345,10 @@
     ASSERT_EQ(0, chmod(ref_profile_dir.c_str(), 0700));
 
     // Run createAppData again which will offer to fix-up the profile directories.
-    ASSERT_BINDER_SUCCESS(service_->createAppData(
-            volume_uuid_,
-            package_name_,
-            kTestUserId,
-            kAppDataFlags,
-            kTestAppUid,
-            0 /* previousAppId */,
-            se_info_,
-            kOSdkVersion,
-            &ce_data_inode_));
+    ASSERT_BINDER_SUCCESS(service_->createAppData(volume_uuid_, package_name_, kTestUserId,
+                                                  kAppDataFlags, kTestAppUid, 0 /* previousAppId */,
+                                                  se_info_, kOSdkVersion, &ce_data_inode_,
+                                                  &de_data_inode_));
 
     // Check the file access.
     CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR);
@@ -1492,18 +1481,13 @@
     void createAppProfilesForBootMerge(size_t number_of_profiles) {
         for (size_t i = 0; i < number_of_profiles; i++) {
             int64_t ce_data_inode;
+            int64_t de_data_inode;
             std::string package_name = "dummy_test_pkg" + std::to_string(i);
             LOG(INFO) << package_name;
-            ASSERT_BINDER_SUCCESS(service_->createAppData(
-                    volume_uuid_,
-                    package_name,
-                    kTestUserId,
-                    kAppDataFlags,
-                    kTestAppUid,
-                    0 /* previousAppId */,
-                    se_info_,
-                    kOSdkVersion,
-                    &ce_data_inode));
+            ASSERT_BINDER_SUCCESS(
+                    service_->createAppData(volume_uuid_, package_name, kTestUserId, kAppDataFlags,
+                                            kTestAppUid, 0 /* previousAppId */, se_info_,
+                                            kOSdkVersion, &ce_data_inode, &de_data_inode));
             extra_apps_.push_back(package_name);
             extra_ce_data_inodes_.push_back(ce_data_inode);
             std::string profile = create_current_profile_path(
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs
index 1bbd674..896b78f 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs
@@ -35,10 +35,26 @@
 
 /// This API automatically fuzzes provided service
 pub fn fuzz_service(binder: &mut SpIBinder, fuzzer_data: &[u8]) {
-    let ptr = binder.as_native_mut() as *mut c_void;
+    let mut binders = [binder];
+    fuzz_multiple_services(&mut binders, fuzzer_data);
+}
+
+/// This API automatically fuzzes provided services
+pub fn fuzz_multiple_services(binders: &mut [&mut SpIBinder], fuzzer_data: &[u8]) {
+    let mut cppBinders = vec![];
+    for binder in binders.iter_mut() {
+        let ptr = binder.as_native_mut() as *mut c_void;
+        cppBinders.push(ptr);
+    }
+
     unsafe {
-        // Safety: `SpIBinder::as_native_mut` and `slice::as_ptr` always
+        // Safety: `Vec::as_mut_ptr` and `slice::as_ptr` always
         // return valid pointers.
-        fuzzRustService(ptr, fuzzer_data.as_ptr(), fuzzer_data.len());
+        fuzzRustService(
+            cppBinders.as_mut_ptr(),
+            cppBinders.len(),
+            fuzzer_data.as_ptr(),
+            fuzzer_data.len(),
+        );
     }
 }
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp
index 831bd56..cfdd2ab 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp
@@ -21,5 +21,5 @@
     void createRandomParcel(void* aParcel, const uint8_t* data, size_t len);
 
     // This API is used by fuzzers to automatically fuzz aidl services
-    void fuzzRustService(void* binder, const uint8_t* data, size_t len);
-}
\ No newline at end of file
+    void fuzzRustService(void** binders, size_t numBinders, const uint8_t* data, size_t len);
+}
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 93ac116..38e6f32 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -61,11 +61,11 @@
     while (provider.remaining_bytes() > 0) {
         // Most of the AIDL services will have small set of transaction codes.
         // TODO(b/295942369) : Add remaining transact codes from IBinder.h
-        uint32_t code = provider.ConsumeBool()
-                ? provider.ConsumeIntegral<uint32_t>()
-                : provider.PickValueInArray<int64_t>(
-                          {provider.ConsumeIntegralInRange<uint32_t>(0, 100),
-                           IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
+        uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
+                : provider.ConsumeBool()
+                ? provider.ConsumeIntegralInRange<uint32_t>(0, 100)
+                : provider.PickValueInArray<uint32_t>(
+                          {IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
                            IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
                            IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
                            IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
index 0b0ca34..84b9ff6 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
@@ -22,6 +22,9 @@
 // and APEX users, but we need access to it to fuzz.
 #include "../../ndk/ibinder_internal.h"
 
+using android::IBinder;
+using android::sp;
+
 namespace android {
 
 void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider) {
@@ -41,9 +44,14 @@
 
 extern "C" {
 // This API is used by fuzzers to automatically fuzz aidl services
-void fuzzRustService(void* binder, const uint8_t* data, size_t len) {
-    AIBinder* aiBinder = static_cast<AIBinder*>(binder);
+void fuzzRustService(void** binders, size_t numBinders, const uint8_t* data, size_t len) {
+    std::vector<sp<IBinder>> cppBinders;
+    for (size_t binderIndex = 0; binderIndex < numBinders; ++binderIndex) {
+        AIBinder* aiBinder = static_cast<AIBinder*>(binders[binderIndex]);
+        cppBinders.push_back(aiBinder->getBinder());
+    }
+
     FuzzedDataProvider provider(data, len);
-    android::fuzzService(aiBinder, std::move(provider));
+    android::fuzzService(cppBinders, std::move(provider));
 }
 } // extern "C"
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
index 943fb9f..33a653e 100644
--- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
@@ -54,7 +54,7 @@
 
     if (transaction.has_value()) {
         std::FILE* intermediateFile = std::tmpfile();
-        android::base::unique_fd fdForWriting(fileno(intermediateFile));
+        android::base::unique_fd fdForWriting(dup(fileno(intermediateFile)));
         auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
 
         std::fclose(intermediateFile);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 1f18b94..82ee95c 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -22,6 +22,7 @@
 
 #include <GrBackendSemaphore.h>
 #include <GrContextOptions.h>
+#include <GrTypes.h>
 #include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkColor.h>
@@ -289,12 +290,12 @@
     }
 
     if (mGrContext) {
-        mGrContext->flushAndSubmit(true);
+        mGrContext->flushAndSubmit(GrSyncCpu::kYes);
         mGrContext->abandonContext();
     }
 
     if (mProtectedGrContext) {
-        mProtectedGrContext->flushAndSubmit(true);
+        mProtectedGrContext->flushAndSubmit(GrSyncCpu::kYes);
         mProtectedGrContext->abandonContext();
     }
 }
@@ -307,7 +308,7 @@
 
     // release any scratch resources before switching into a new mode
     if (getActiveGrContext()) {
-        getActiveGrContext()->purgeUnlockedResources(true);
+        getActiveGrContext()->purgeUnlockedResources(GrPurgeResourceOptions::kScratchResourcesOnly);
     }
 
     // Backend-specific way to switch to protected context
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 7c99a1c..56a3fb4 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -41,7 +41,13 @@
 
 constexpr size_t INTERACTIONS_QUEUE_CAPACITY = 500;
 
-int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus) {
+int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus, bool isUsiStylus) {
+    if (isUsiStylus) {
+        // This is a stylus connected over the Universal Stylus Initiative (USI) protocol.
+        // For metrics purposes, we treat this protocol as a separate bus.
+        return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USI;
+    }
+
     // When adding cases to this switch, also add them to the copy of this method in
     // TouchpadInputMapper.cpp.
     // TODO(b/286394420): deduplicate this method with the one in TouchpadInputMapper.cpp.
@@ -58,11 +64,12 @@
 class : public InputDeviceMetricsLogger {
     nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
 
-    void logInputDeviceUsageReported(const InputDeviceIdentifier& identifier,
+    void logInputDeviceUsageReported(const InputDeviceInfo& info,
                                      const DeviceUsageReport& report) override {
         const int32_t durationMillis =
                 std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
         const static std::vector<int32_t> empty;
+        const auto& identifier = info.getIdentifier();
 
         ALOGD_IF(DEBUG, "Usage session reported for device: %s", identifier.name.c_str());
         ALOGD_IF(DEBUG, "    Total duration: %dms", durationMillis);
@@ -90,7 +97,9 @@
                      durMillis);
         }
         util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
-                          identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
+                          identifier.version,
+                          linuxBusToInputDeviceBusEnum(identifier.bus,
+                                                       info.getUsiVersion().has_value()),
                           durationMillis, sources, durationsPerSource, uids, durationsPerUid);
     }
 } sStatsdLogger;
@@ -295,21 +304,21 @@
         if (newDeviceInfos.count(deviceId) != 0) {
             continue;
         }
-        onInputDeviceRemoved(deviceId, info.getIdentifier());
+        onInputDeviceRemoved(deviceId, info);
     }
 
     std::swap(newDeviceInfos, mLoggedDeviceInfos);
 }
 
 void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
-                                                       const InputDeviceIdentifier& identifier) {
+                                                       const InputDeviceInfo& info) {
     auto it = mActiveUsageSessions.find(deviceId);
     if (it == mActiveUsageSessions.end()) {
         return;
     }
     // Report usage for that device if there is an active session.
     auto& [_, activeSession] = *it;
-    mLogger.logInputDeviceUsageReported(identifier, activeSession.finishSession());
+    mLogger.logInputDeviceUsageReported(info, activeSession.finishSession());
     mActiveUsageSessions.erase(it);
 
     // We don't remove this from mLoggedDeviceInfos because it will be updated in
@@ -365,8 +374,7 @@
         auto activeSessionIt = mActiveUsageSessions.find(deviceId);
         LOG_ALWAYS_FATAL_IF(activeSessionIt == mActiveUsageSessions.end());
         auto& [_, activeSession] = *activeSessionIt;
-        mLogger.logInputDeviceUsageReported(infoIt->second.getIdentifier(),
-                                            activeSession.finishSession());
+        mLogger.logInputDeviceUsageReported(infoIt->second, activeSession.finishSession());
         mActiveUsageSessions.erase(activeSessionIt);
     }
 }
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index c70e6d4..1f7c5d9 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -110,8 +110,7 @@
         UidUsageBreakdown uidBreakdown;
     };
 
-    virtual void logInputDeviceUsageReported(const InputDeviceIdentifier&,
-                                             const DeviceUsageReport&) = 0;
+    virtual void logInputDeviceUsageReported(const InputDeviceInfo&, const DeviceUsageReport&) = 0;
     virtual ~InputDeviceMetricsLogger() = default;
 };
 
@@ -189,7 +188,7 @@
     std::map<DeviceId, ActiveSession> mActiveUsageSessions;
 
     void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos);
-    void onInputDeviceRemoved(DeviceId deviceId, const InputDeviceIdentifier& identifier);
+    void onInputDeviceRemoved(DeviceId deviceId, const InputDeviceInfo& info);
     using SourceProvider = std::function<std::set<InputDeviceUsageSource>(const InputDeviceInfo&)>;
     void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
                             const SourceProvider& getSources);
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 3e5e01e..c76fec7 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -129,7 +129,13 @@
     mapper->consumeGesture(gesture);
 }
 
-int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus) {
+int32_t linuxBusToInputDeviceBusEnum(int32_t linuxBus, bool isUsiStylus) {
+    if (isUsiStylus) {
+        // This is a stylus connected over the Universal Stylus Initiative (USI) protocol.
+        // For metrics purposes, we treat this protocol as a separate bus.
+        return util::INPUT_DEVICE_USAGE_REPORTED__DEVICE_BUS__USI;
+    }
+
     // When adding cases to this switch, also add them to the copy of this method in
     // InputDeviceMetricsCollector.cpp.
     // TODO(b/286394420): deduplicate this method with the one in InputDeviceMetricsCollector.cpp.
@@ -203,8 +209,8 @@
         for (auto& [id, counters] : mCounters) {
             auto [busId, vendorId, productId, versionId] = id;
             addAStatsEvent(outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId,
-                           versionId, linuxBusToInputDeviceBusEnum(busId), counters.fingers,
-                           counters.palms, counters.twoFingerSwipeGestures,
+                           versionId, linuxBusToInputDeviceBusEnum(busId, /*isUsi=*/false),
+                           counters.fingers, counters.palms, counters.twoFingerSwipeGestures,
                            counters.threeFingerSwipeGestures, counters.fourFingerSwipeGestures,
                            counters.pinchGestures);
         }
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index 2ff64c8..7ccfaca 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -53,7 +53,7 @@
 constexpr int32_t POINTER_1_DOWN =
         AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-InputDeviceIdentifier getIdentifier(int32_t id = DEVICE_ID) {
+InputDeviceIdentifier generateTestIdentifier(int32_t id = DEVICE_ID) {
     InputDeviceIdentifier identifier;
     identifier.name = DEVICE_NAME + "_" + std::to_string(id);
     identifier.location = LOCATION;
@@ -69,8 +69,8 @@
                                        uint32_t sources = TOUCHSCREEN | STYLUS,
                                        bool isAlphabetic = false) {
     auto info = InputDeviceInfo();
-    info.initialize(id, /*generation=*/1, /*controllerNumber=*/1, getIdentifier(id), "alias",
-                    /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
+    info.initialize(id, /*generation=*/1, /*controllerNumber=*/1, generateTestIdentifier(id),
+                    "alias", /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
     info.addSource(sources);
     info.setKeyboardType(isAlphabetic ? AINPUT_KEYBOARD_TYPE_ALPHABETIC
                                       : AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
@@ -81,6 +81,8 @@
         generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/true);
 const InputDeviceInfo NON_ALPHABETIC_KEYBOARD_INFO =
         generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/false);
+const InputDeviceInfo TOUCHSCREEN_STYLUS_INFO = generateTestDeviceInfo(DEVICE_ID);
+const InputDeviceInfo SECOND_TOUCHSCREEN_STYLUS_INFO = generateTestDeviceInfo(DEVICE_ID_2);
 
 std::set<gui::Uid> uids(std::initializer_list<int32_t> vals) {
     std::set<gui::Uid> set;
@@ -351,12 +353,12 @@
     TestInputListener mTestListener;
     InputDeviceMetricsCollector mMetricsCollector{mTestListener, *this, USAGE_TIMEOUT};
 
-    void assertUsageLogged(InputDeviceIdentifier identifier, nanoseconds duration,
+    void assertUsageLogged(const InputDeviceInfo& info, nanoseconds duration,
                            std::optional<SourceUsageBreakdown> sourceBreakdown = {},
                            std::optional<UidUsageBreakdown> uidBreakdown = {}) {
         ASSERT_GE(mLoggedUsageSessions.size(), 1u);
-        const auto& [loggedIdentifier, report] = *mLoggedUsageSessions.begin();
-        ASSERT_EQ(identifier, loggedIdentifier);
+        const auto& [loggedInfo, report] = *mLoggedUsageSessions.begin();
+        ASSERT_EQ(info.getIdentifier(), loggedInfo.getIdentifier());
         ASSERT_EQ(duration, report.usageDuration);
         if (sourceBreakdown) {
             ASSERT_EQ(sourceBreakdown, report.sourceBreakdown);
@@ -387,14 +389,14 @@
     }
 
 private:
-    std::vector<std::tuple<InputDeviceIdentifier, DeviceUsageReport>> mLoggedUsageSessions;
+    std::vector<std::tuple<InputDeviceInfo, DeviceUsageReport>> mLoggedUsageSessions;
     nanoseconds mCurrentTime{TIME};
 
     nanoseconds getCurrentTime() override { return mCurrentTime; }
 
-    void logInputDeviceUsageReported(const InputDeviceIdentifier& identifier,
+    void logInputDeviceUsageReported(const InputDeviceInfo& info,
                                      const DeviceUsageReport& report) override {
-        mLoggedUsageSessions.emplace_back(identifier, report);
+        mLoggedUsageSessions.emplace_back(info, report);
     }
 };
 
@@ -438,7 +440,7 @@
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, LogsSingleEventUsageSession) {
-    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}});
 
     // Device was used.
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
@@ -448,11 +450,11 @@
     setCurrentTime(TIME + USAGE_TIMEOUT);
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
     // The usage session has zero duration because it consisted of only one event.
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 0ns));
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 0ns));
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, LogsMultipleEventUsageSession) {
-    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}});
 
     // Device was used.
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
@@ -468,11 +470,11 @@
     // Device was used again after the usage timeout.
     setCurrentTime(TIME + 42ns + 2 * USAGE_TIMEOUT);
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 42ns));
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 42ns));
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, RemovingDeviceEndsUsageSession) {
-    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}});
 
     // Device was used.
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
@@ -485,12 +487,12 @@
     // The device was removed before the usage timeout expired.
     setCurrentTime(TIME + 42ns);
     mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {}});
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 21ns));
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 21ns));
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, TracksUsageFromDifferentDevicesIndependently) {
     mMetricsCollector.notifyInputDevicesChanged(
-            {/*id=*/0, {generateTestDeviceInfo(), generateTestDeviceInfo(DEVICE_ID_2)}});
+            {/*id=*/0, {TOUCHSCREEN_STYLUS_INFO, SECOND_TOUCHSCREEN_STYLUS_INFO}});
 
     // Device 1 was used.
     setCurrentTime(TIME);
@@ -509,7 +511,7 @@
     // Device 1 was used after its usage timeout expired. Its usage session is reported.
     setCurrentTime(TIME + 300ns + USAGE_TIMEOUT);
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(DEVICE_ID), 100ns));
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 100ns));
 
     // Device 2 was used.
     setCurrentTime(TIME + 350ns + USAGE_TIMEOUT);
@@ -525,13 +527,14 @@
     setCurrentTime(TIME + 400ns + (2 * USAGE_TIMEOUT));
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
     // Since Device 2's usage session ended, its usage should be reported.
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(DEVICE_ID_2), 150ns + USAGE_TIMEOUT));
+    ASSERT_NO_FATAL_FAILURE(
+            assertUsageLogged(SECOND_TOUCHSCREEN_STYLUS_INFO, 150ns + USAGE_TIMEOUT));
 
     ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageBySource) {
-    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}});
     InputDeviceMetricsLogger::SourceUsageBreakdown expectedSourceBreakdown;
 
     // Use touchscreen.
@@ -576,7 +579,7 @@
                                          100ns + USAGE_TIMEOUT);
     // Verify that only one usage session was logged for the device, and that session was broken
     // down by source correctly.
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(),
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO,
                                               400ns + USAGE_TIMEOUT + USAGE_TIMEOUT,
                                               expectedSourceBreakdown));
 
@@ -585,7 +588,7 @@
 
 TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageBySource_TrackSourceByDevice) {
     mMetricsCollector.notifyInputDevicesChanged(
-            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID), generateTestDeviceInfo(DEVICE_ID_2)}});
+            {/*id=*/0, {TOUCHSCREEN_STYLUS_INFO, SECOND_TOUCHSCREEN_STYLUS_INFO}});
     InputDeviceMetricsLogger::SourceUsageBreakdown expectedSourceBreakdown1;
     InputDeviceMetricsLogger::SourceUsageBreakdown expectedSourceBreakdown2;
 
@@ -602,15 +605,15 @@
     expectedSourceBreakdown1.emplace_back(InputDeviceUsageSource::TOUCHSCREEN, 100ns);
     expectedSourceBreakdown2.emplace_back(InputDeviceUsageSource::STYLUS_DIRECT, 100ns);
     ASSERT_NO_FATAL_FAILURE(
-            assertUsageLogged(getIdentifier(DEVICE_ID), 100ns, expectedSourceBreakdown1));
+            assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 100ns, expectedSourceBreakdown1));
     ASSERT_NO_FATAL_FAILURE(
-            assertUsageLogged(getIdentifier(DEVICE_ID_2), 100ns, expectedSourceBreakdown2));
+            assertUsageLogged(SECOND_TOUCHSCREEN_STYLUS_INFO, 100ns, expectedSourceBreakdown2));
 
     ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageBySource_MultiSourceEvent) {
-    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo(DEVICE_ID)}});
+    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}});
     InputDeviceMetricsLogger::SourceUsageBreakdown expectedSourceBreakdown;
 
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN | STYLUS, //
@@ -634,13 +637,13 @@
     expectedSourceBreakdown.emplace_back(InputDeviceUsageSource::STYLUS_DIRECT, 200ns);
     expectedSourceBreakdown.emplace_back(InputDeviceUsageSource::TOUCHSCREEN, 300ns);
     ASSERT_NO_FATAL_FAILURE(
-            assertUsageLogged(getIdentifier(DEVICE_ID), 400ns, expectedSourceBreakdown));
+            assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 400ns, expectedSourceBreakdown));
 
     ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, UidsNotTrackedWhenThereIsNoActiveSession) {
-    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}});
 
     // Notify interaction with UIDs before the device is used.
     mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1}));
@@ -665,14 +668,15 @@
 
     // The first usage session is logged.
     static const UidUsageBreakdown emptyBreakdown;
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 100ns, /*sourceBreakdown=*/{},
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 100ns,
+                                              /*sourceBreakdown=*/{},
                                               /*uidBreakdown=*/emptyBreakdown));
 
     ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid) {
-    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}});
     UidUsageBreakdown expectedUidBreakdown;
 
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
@@ -691,14 +695,14 @@
 
     // Remove the device to force the usage session to be logged.
     mMetricsCollector.notifyInputDevicesChanged({});
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 200ns, /*sourceBreakdown=*/{},
-                                              expectedUidBreakdown));
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 200ns,
+                                              /*sourceBreakdown=*/{}, expectedUidBreakdown));
 
     ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
 }
 
 TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid_TracksMultipleSessionsForUid) {
-    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
+    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}});
     UidUsageBreakdown expectedUidBreakdown;
 
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
@@ -742,7 +746,7 @@
     expectedUidBreakdown.emplace_back(2, 0ns);
     expectedUidBreakdown.emplace_back(3, 100ns);
     expectedUidBreakdown.emplace_back(4, 100ns);
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 500ns + USAGE_TIMEOUT,
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 500ns + USAGE_TIMEOUT,
                                               /*sourceBreakdown=*/{}, expectedUidBreakdown));
 
     ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
@@ -750,7 +754,7 @@
 
 TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid_TracksUidsByDevice) {
     mMetricsCollector.notifyInputDevicesChanged(
-            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID), generateTestDeviceInfo(DEVICE_ID_2)}});
+            {/*id=*/0, {TOUCHSCREEN_STYLUS_INFO, SECOND_TOUCHSCREEN_STYLUS_INFO}});
     UidUsageBreakdown expectedUidBreakdown1;
     UidUsageBreakdown expectedUidBreakdown2;
 
@@ -773,9 +777,9 @@
     expectedUidBreakdown2.emplace_back(1, 100ns);
     expectedUidBreakdown2.emplace_back(3, 100ns);
     mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(DEVICE_ID), 200ns,
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 200ns,
                                               /*sourceBreakdown=*/{}, expectedUidBreakdown1));
-    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(DEVICE_ID_2), 100ns,
+    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(SECOND_TOUCHSCREEN_STYLUS_INFO, 100ns,
                                               /*sourceBreakdown=*/{}, expectedUidBreakdown2));
 
     ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 70ccaf8..252ba8e 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -208,12 +208,12 @@
     return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
 }
 
-void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps renderFps) {
-    ATRACE_INT(mActiveModeFPSTrace.c_str(), displayFps.getIntValue());
+void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
+    ATRACE_INT(mActiveModeFPSTrace.c_str(), vsyncRate.getIntValue());
     ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
 
     mRefreshRateSelector->setActiveMode(modeId, renderFps);
-    updateRefreshRateOverlayRate(displayFps, renderFps);
+    updateRefreshRateOverlayRate(vsyncRate, renderFps);
 }
 
 status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
@@ -230,14 +230,14 @@
     mIsModeSetPending = true;
 
     const auto& pendingMode = *info.modeOpt->modePtr;
-    ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), pendingMode.getFps().getIntValue());
+    ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), pendingMode.getVsyncRate().getIntValue());
 
     return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), pendingMode.getHwcId(),
                                                     constraints, outTimeline);
 }
 
-void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps displayFps, Fps renderFps) {
-    setActiveMode(modeId, displayFps, renderFps);
+void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
+    setActiveMode(modeId, vsyncRate, renderFps);
     mIsModeSetPending = false;
 }
 
@@ -253,7 +253,7 @@
         return vsyncPeriod;
     }
 
-    return refreshRateSelector().getActiveMode().modePtr->getVsyncPeriod();
+    return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs();
 }
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
@@ -466,17 +466,19 @@
         features |= RefreshRateOverlay::Features::SetByHwc;
     }
 
+    // TODO(b/296636258) Update to use the render rate range in VRR mode.
     const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
-    updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps, setByHwc);
+    updateRefreshRateOverlayRate(getActiveMode().modePtr->getVsyncRate(), getActiveMode().fps,
+                                 setByHwc);
 }
 
-void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) {
+void DisplayDevice::updateRefreshRateOverlayRate(Fps vsyncRate, Fps renderFps, bool setByHwc) {
     ATRACE_CALL();
     if (mRefreshRateOverlay && (!mRefreshRateOverlay->isSetByHwc() || setByHwc)) {
-        mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
+        mRefreshRateOverlay->changeRefreshRate(vsyncRate, renderFps);
     }
 }
 
@@ -486,7 +488,7 @@
         const auto newMode =
                 mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
         if (newMode) {
-            updateRefreshRateOverlayRate(newMode->modePtr->getFps(), newMode->fps);
+            updateRefreshRateOverlayRate(newMode->modePtr->getVsyncRate(), newMode->fps);
             return true;
         }
     }
@@ -541,14 +543,14 @@
             return DesiredActiveModeAction::None;
         }
 
-        setActiveMode(desiredMode.getId(), desiredMode.getFps(), info.modeOpt->fps);
+        setActiveMode(desiredMode.getId(), desiredMode.getVsyncRate(), info.modeOpt->fps);
         return DesiredActiveModeAction::InitiateRenderRateSwitch;
     }
 
     // Set the render frame rate to the current physical refresh rate to schedule the next
     // frame as soon as possible.
-    setActiveMode(currentMode.modePtr->getId(), currentMode.modePtr->getFps(),
-                  currentMode.modePtr->getFps());
+    setActiveMode(currentMode.modePtr->getId(), currentMode.modePtr->getVsyncRate(),
+                  currentMode.modePtr->getVsyncRate());
 
     // Initiate a mode change.
     mDesiredActiveModeChanged = true;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index a3fa701..a044534 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -224,14 +224,14 @@
         return mRefreshRateSelector->getActiveMode();
     }
 
-    void setActiveMode(DisplayModeId, Fps displayFps, Fps renderFps);
+    void setActiveMode(DisplayModeId, Fps vsyncRate, Fps renderFps);
 
     status_t initiateModeChange(const ActiveModeInfo&,
                                 const hal::VsyncPeriodChangeConstraints& constraints,
                                 hal::VsyncPeriodChangeTimeline* outTimeline)
             REQUIRES(kMainThreadContext);
 
-    void finalizeModeChange(DisplayModeId, Fps displayFps, Fps renderFps)
+    void finalizeModeChange(DisplayModeId, Fps vsyncRate, Fps renderFps)
             REQUIRES(kMainThreadContext);
 
     scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
@@ -246,7 +246,7 @@
     // Enables an overlay to be displayed with the current refresh rate
     void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate,
                                   bool showInMiddle) REQUIRES(kMainThreadContext);
-    void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false);
+    void updateRefreshRateOverlayRate(Fps vsyncRate, Fps renderFps, bool setByHwc = false);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
 
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 1810925..422513b 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -32,6 +32,8 @@
 #include "DisplayHardware/Hal.h"
 #include "Scheduler/StrongTyping.h"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 namespace android {
 
 namespace hal = android::hardware::graphics::composer::hal;
@@ -49,6 +51,7 @@
 
 using DisplayModes = ftl::SmallMap<DisplayModeId, DisplayModePtr, 3>;
 using DisplayModeIterator = DisplayModes::const_iterator;
+using namespace com::android::graphics::surfaceflinger;
 
 class DisplayMode {
 public:
@@ -76,7 +79,12 @@
         }
 
         Builder& setVsyncPeriod(nsecs_t vsyncPeriod) {
-            mDisplayMode->mFps = Fps::fromPeriodNsecs(vsyncPeriod);
+            mDisplayMode->mVsyncRate = Fps::fromPeriodNsecs(vsyncPeriod);
+            return *this;
+        }
+
+        Builder& setVrrConfig(std::optional<hal::VrrConfig> vrrConfig) {
+            mDisplayMode->mVrrConfig = std::move(vrrConfig);
             return *this;
         }
 
@@ -130,8 +138,17 @@
     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(); }
+    // Peak refresh rate represents the highest refresh rate that can be used
+    // for the presentation.
+    Fps getPeakFps() const {
+        return flags::vrr_config() && mVrrConfig
+                ? Fps::fromPeriodNsecs(mVrrConfig->minFrameIntervalNs)
+                : mVsyncRate;
+    }
+
+    Fps getVsyncRate() const { return mVsyncRate; }
+
+    std::optional<hal::VrrConfig> getVrrConfig() const { return mVrrConfig; }
 
     struct Dpi {
         float x = -1;
@@ -155,23 +172,25 @@
     PhysicalDisplayId mPhysicalDisplayId;
 
     ui::Size mResolution;
-    Fps mFps;
+    Fps mVsyncRate;
     Dpi mDpi;
     int32_t mGroup = -1;
+    std::optional<hal::VrrConfig> mVrrConfig;
 };
 
 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();
+            lhs.getVsyncRate().getPeriodNsecs() == rhs.getVsyncRate().getPeriodNsecs() &&
+            lhs.getDpi() == rhs.getDpi() && lhs.getGroup() == rhs.getGroup();
 }
 
 inline std::string to_string(const DisplayMode& mode) {
-    return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, refreshRate=%s, "
-                              "dpi=%.2fx%.2f, group=%d}",
+    return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, vsyncRate=%s, "
+                              "dpi=%.2fx%.2f, group=%d, vrrConfig=%s}",
                               mode.getId().value(), mode.getHwcId(), mode.getWidth(),
-                              mode.getHeight(), to_string(mode.getFps()).c_str(), mode.getDpi().x,
-                              mode.getDpi().y, mode.getGroup());
+                              mode.getHeight(), to_string(mode.getVsyncRate()).c_str(),
+                              mode.getDpi().x, mode.getDpi().y, mode.getGroup(),
+                              to_string(mode.getVrrConfig()).c_str());
 }
 
 template <typename... DisplayModePtrs>
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index a9bb928..812621f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -285,13 +285,12 @@
     std::vector<HWCDisplayMode> modes;
     modes.reserve(configs.size());
     for (auto config : configs) {
-        auto hwcMode = HWCDisplayMode{
-                .hwcId = static_cast<hal::HWConfigId>(config.configId),
-                .width = config.width,
-                .height = config.height,
-                .vsyncPeriod = config.vsyncPeriod,
-                .configGroup = config.configGroup,
-        };
+        auto hwcMode = HWCDisplayMode{.hwcId = static_cast<hal::HWConfigId>(config.configId),
+                                      .width = config.width,
+                                      .height = config.height,
+                                      .vsyncPeriod = config.vsyncPeriod,
+                                      .configGroup = config.configGroup,
+                                      .vrrConfig = config.vrrConfig};
 
         if (config.dpi) {
             hwcMode.dpiX = config.dpi->x;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 86f3825..b4d3d28 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -102,11 +102,13 @@
         float dpiX = -1.f;
         float dpiY = -1.f;
         int32_t configGroup = -1;
+        std::optional<hal::VrrConfig> vrrConfig;
 
         friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
             return os << "id=" << mode.hwcId << " res=" << mode.width << "x" << mode.height
                       << " vsyncPeriod=" << mode.vsyncPeriod << " dpi=" << mode.dpiX << "x"
-                      << mode.dpiY << " group=" << mode.configGroup;
+                      << mode.dpiY << " group=" << mode.configGroup
+                      << " vrrConfig=" << to_string(mode.vrrConfig).c_str();
         }
     };
 
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index e95ae89..20f7548 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -24,6 +24,7 @@
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
+#include <aidl/android/hardware/graphics/composer3/VrrConfig.h>
 
 #define ERROR_HAS_CHANGES 5
 
@@ -73,6 +74,7 @@
 using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
 using Hdr = aidl::android::hardware::graphics::common::Hdr;
 using DisplayConfiguration = V3_0::DisplayConfiguration;
+using VrrConfig = V3_0::VrrConfig;
 
 } // namespace hardware::graphics::composer::hal
 
@@ -148,6 +150,34 @@
     }
 }
 
+inline std::string to_string(
+        const std::optional<aidl::android::hardware::graphics::composer3::VrrConfig>& vrrConfig) {
+    if (vrrConfig) {
+        std::ostringstream out;
+        out << "{minFrameIntervalNs=" << vrrConfig->minFrameIntervalNs << ", ";
+        out << "frameIntervalPowerHints={";
+        if (vrrConfig->frameIntervalPowerHints) {
+            const auto& powerHint = *vrrConfig->frameIntervalPowerHints;
+            for (size_t i = 0; i < powerHint.size(); i++) {
+                if (i > 0) out << ", ";
+                out << "[frameIntervalNs=" << powerHint[i]->frameIntervalNs
+                    << ", averageRefreshPeriodNs=" << powerHint[i]->averageRefreshPeriodNs << "]";
+            }
+        }
+        out << "}, ";
+        out << "notifyExpectedPresentConfig={";
+        if (vrrConfig->notifyExpectedPresentConfig) {
+            out << "notifyExpectedPresentHeadsUpNs="
+                << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentHeadsUpNs
+                << ", notifyExpectedPresentTimeoutNs="
+                << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs;
+        }
+        out << "}}";
+        return out.str();
+    }
+    return "N/A";
+}
+
 inline std::string to_string(hardware::graphics::composer::hal::V2_4::Error error) {
     // 5 is reserved for historical reason, during validation 5 means has changes.
     if (ERROR_HAS_CHANGES == static_cast<int32_t>(error)) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2bbfa42..2684000 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3092,6 +3092,32 @@
     return true;
 }
 
+void Layer::releasePreviousBuffer() {
+    mReleasePreviousBuffer = true;
+    if (!mBufferInfo.mBuffer ||
+        (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
+         mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) {
+        // If mDrawingState has a buffer, and we are about to update again
+        // before swapping to drawing state, then the first buffer will be
+        // dropped and we should decrement the pending buffer count and
+        // call any release buffer callbacks if set.
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                  mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                  mDrawingState.acquireFence);
+        decrementPendingBufferCount();
+        if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
+            mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
+            addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX, systemTime());
+            mDrawingState.bufferSurfaceFrameTX.reset();
+        }
+    } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                  mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                  mLastClientCompositionFence);
+        mLastClientCompositionFence = nullptr;
+    }
+}
+
 void Layer::resetDrawingStateBufferInfo() {
     mDrawingState.producerId = 0;
     mDrawingState.frameNumber = 0;
@@ -3116,29 +3142,7 @@
     ATRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber);
 
     if (mDrawingState.buffer) {
-        mReleasePreviousBuffer = true;
-        if (!mBufferInfo.mBuffer ||
-            (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
-             mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) {
-            // If mDrawingState has a buffer, and we are about to update again
-            // before swapping to drawing state, then the first buffer will be
-            // dropped and we should decrement the pending buffer count and
-            // call any release buffer callbacks if set.
-            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mDrawingState.acquireFence);
-            decrementPendingBufferCount();
-            if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
-                mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
-                addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX, systemTime());
-                mDrawingState.bufferSurfaceFrameTX.reset();
-            }
-        } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
-            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mLastClientCompositionFence);
-            mLastClientCompositionFence = nullptr;
-        }
+        releasePreviousBuffer();
     } else if (buffer) {
         // if we are latching a buffer for the first time then clear the mLastLatchTime since
         // we don't want to incorrectly classify a frame if we miss the desired present time.
@@ -3156,6 +3160,12 @@
         mDrawingState.bufferSurfaceFrameTX = nullptr;
         setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
         return true;
+    } else {
+        // release sideband stream if it exists and a non null buffer is being set
+        if (mDrawingState.sidebandStream != nullptr) {
+            mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
+            mDrawingState.sidebandStream = nullptr;
+        }
     }
 
     if ((mDrawingState.producerId > bufferData.producerId) ||
@@ -3344,7 +3354,8 @@
     return true;
 }
 
-bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
+bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info,
+                              nsecs_t postTime) {
     if (mDrawingState.sidebandStream == sidebandStream) return false;
 
     if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
@@ -3355,6 +3366,12 @@
 
     mDrawingState.sidebandStream = sidebandStream;
     mDrawingState.modified = true;
+    if (sidebandStream != nullptr && mDrawingState.buffer != nullptr) {
+        releasePreviousBuffer();
+        resetDrawingStateBufferInfo();
+        mDrawingState.bufferSurfaceFrameTX = nullptr;
+        setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+    }
     setTransactionFlags(eTransactionNeeded);
     if (!mSidebandStreamChanged.exchange(true)) {
         // mSidebandStreamChanged was false
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index dc4ceb0..0b0cef5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -317,7 +317,8 @@
     bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
     bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
     bool setApi(int32_t /*api*/);
-    bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/);
+    bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/,
+                           const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */);
     bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/,
                                           bool willPresent);
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace)
@@ -1204,6 +1205,7 @@
     half4 mBorderColor;
 
     void setTransformHintLegacy(ui::Transform::RotationFlags);
+    void releasePreviousBuffer();
     void resetDrawingStateBufferInfo();
 
     // Transform hint provided to the producer. This must be accessed holding
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 577211f..e918dc9 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -27,7 +27,7 @@
 
 namespace android {
 
-auto RefreshRateOverlay::draw(int displayFps, int renderFps, SkColor color,
+auto RefreshRateOverlay::draw(int vsyncRate, int renderFps, SkColor color,
                               ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
         -> Buffers {
     const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
@@ -70,7 +70,7 @@
         canvas->setMatrix(canvasTransform);
 
         int left = 0;
-        drawNumber(displayFps, left, color, *canvas);
+        drawNumber(vsyncRate, left, color, *canvas);
         left += 3 * (kDigitWidth + kDigitSpace);
         if (features.test(Features::Spinner)) {
             switch (i) {
@@ -153,7 +153,7 @@
             .apply();
 }
 
-auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps vsyncRate, Fps renderFps) -> const Buffers& {
     static const Buffers kNoBuffers;
     if (!mSurfaceControl) return kNoBuffers;
 
@@ -180,16 +180,16 @@
     createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
 
     BufferCache::const_iterator it =
-            mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
+            mBufferCache.find({vsyncRate.getIntValue(), renderFps.getIntValue(), transformHint});
     if (it == mBufferCache.end()) {
         // HWC minFps is not known by the framework in order
         // to consider lower rates we set minFps to 0.
         const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue();
         const int maxFps = mFpsRange.max.getIntValue();
 
-        // Clamp to the range. The current displayFps may be outside of this range if the display
+        // Clamp to the range. The current vsyncRate may be outside of this range if the display
         // has changed its set of supported refresh rates.
-        const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps);
+        const int displayIntFps = std::clamp(vsyncRate.getIntValue(), minFps, maxFps);
         const int renderIntFps = renderFps.getIntValue();
 
         // Ensure non-zero range to avoid division by zero.
@@ -242,17 +242,17 @@
     createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
 }
 
-void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
-    mDisplayFps = displayFps;
+void RefreshRateOverlay::changeRefreshRate(Fps vsyncRate, Fps renderFps) {
+    mVsyncRate = vsyncRate;
     mRenderFps = renderFps;
-    const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
+    const auto buffer = getOrCreateBuffers(vsyncRate, renderFps)[mFrame];
     createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 void RefreshRateOverlay::animate() {
-    if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return;
+    if (!mFeatures.test(Features::Spinner) || !mVsyncRate) return;
 
-    const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
+    const auto& buffers = getOrCreateBuffers(*mVsyncRate, *mRenderFps);
     mFrame = (mFrame + 1) % buffers.size();
     const auto buffer = buffers[mFrame];
     createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 65c61cb..c0fc79b 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -56,7 +56,7 @@
 private:
     using Buffers = std::vector<sp<GraphicBuffer>>;
 
-    static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+    static Buffers draw(int vsyncRate, int renderFps, SkColor, ui::Transform::RotationFlags,
                         ftl::Flags<Features>);
     static void drawNumber(int number, int left, SkColor, SkCanvas&);
 
@@ -65,12 +65,12 @@
     SurfaceComposerClient::Transaction createTransaction() const;
 
     struct Key {
-        int displayFps;
+        int vsyncRate;
         int renderFps;
         ui::Transform::RotationFlags flags;
 
         bool operator==(Key other) const {
-            return displayFps == other.displayFps && renderFps == other.renderFps &&
+            return vsyncRate == other.vsyncRate && renderFps == other.renderFps &&
                     flags == other.flags;
         }
     };
@@ -78,7 +78,7 @@
     using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
     BufferCache mBufferCache;
 
-    std::optional<Fps> mDisplayFps;
+    std::optional<Fps> mVsyncRate;
     std::optional<Fps> mRenderFps;
     size_t mFrame = 0;
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index be28e98..3ee6a4d 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -39,12 +39,16 @@
 #include "../SurfaceFlingerProperties.h"
 #include "RefreshRateSelector.h"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 #undef LOG_TAG
 #define LOG_TAG "RefreshRateSelector"
 
 namespace android::scheduler {
 namespace {
 
+using namespace com::android::graphics::surfaceflinger;
+
 struct RefreshRateScore {
     FrameRateMode frameRateMode;
     float overallScore;
@@ -69,7 +73,7 @@
 
     // Add all supported refresh rates.
     for (const auto& [id, mode] : modes) {
-        knownFrameRates.push_back(mode->getFps());
+        knownFrameRates.push_back(mode->getPeakFps());
     }
 
     // Sort and remove duplicates.
@@ -91,17 +95,17 @@
         const auto& mode1 = it1->second;
         const auto& mode2 = it2->second;
 
-        if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) {
+        if (mode1->getVsyncRate().getPeriodNsecs() == mode2->getVsyncRate().getPeriodNsecs()) {
             return mode1->getGroup() > mode2->getGroup();
         }
 
-        return mode1->getVsyncPeriod() > mode2->getVsyncPeriod();
+        return mode1->getVsyncRate().getPeriodNsecs() > mode2->getVsyncRate().getPeriodNsecs();
     });
 
     return sortedModes;
 }
 
-std::pair<unsigned, unsigned> divisorRange(Fps fps, FpsRange range,
+std::pair<unsigned, unsigned> divisorRange(Fps vsyncRate, Fps peakFps, FpsRange range,
                                            RefreshRateSelector::Config::FrameRateOverride config) {
     if (config != RefreshRateSelector::Config::FrameRateOverride::Enabled) {
         return {1, 1};
@@ -109,8 +113,14 @@
 
     using fps_approx_ops::operator/;
     // use signed type as `fps / range.max` might be 0
-    const auto start = std::max(1, static_cast<int>(fps / range.max) - 1);
-    const auto end = fps /
+    auto start = std::max(1, static_cast<int>(peakFps / range.max) - 1);
+    if (flags::vrr_config()) {
+        start = std::max(1,
+                         static_cast<int>(vsyncRate /
+                                          std::min(range.max, peakFps, fps_approx_ops::operator<)) -
+                                 1);
+    }
+    const auto end = vsyncRate /
             std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate,
                      fps_approx_ops::operator<);
 
@@ -123,7 +133,8 @@
         for (const auto it2 : sortedModes) {
             const auto& mode2 = it2->second;
 
-            if (RefreshRateSelector::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
+            if (RefreshRateSelector::getFrameRateDivisor(mode1->getPeakFps(),
+                                                         mode2->getPeakFps()) >= 2) {
                 return true;
             }
         }
@@ -176,10 +187,12 @@
         if (!filterModes(*mode)) {
             continue;
         }
+        const auto vsyncRate = mode->getVsyncRate();
+        const auto peakFps = mode->getPeakFps();
         const auto [start, end] =
-                divisorRange(mode->getFps(), renderRange, mConfig.enableFrameRateOverride);
+                divisorRange(vsyncRate, peakFps, renderRange, mConfig.enableFrameRateOverride);
         for (auto divisor = start; divisor <= end; divisor++) {
-            const auto fps = mode->getFps() / divisor;
+            const auto fps = vsyncRate / divisor;
             using fps_approx_ops::operator<;
             if (divisor > 1 && fps < kMinSupportedFrameRate) {
                 break;
@@ -199,28 +212,30 @@
             const auto [existingIter, emplaceHappened] =
                     ratesMap.try_emplace(Key{fps, mode->getGroup()}, it);
             if (emplaceHappened) {
-                ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(),
-                      to_string(mode->getFps()).c_str());
+                ALOGV("%s: including %s (%s(%s))", __func__, to_string(fps).c_str(),
+                      to_string(peakFps).c_str(), to_string(vsyncRate).c_str());
             } else {
                 // If the primary physical range is a single rate, prefer to stay in that rate
                 // even if there is a lower physical refresh rate available. This would cause more
                 // cases to stay within the primary physical range
-                const Fps existingModeFps = existingIter->second->second->getFps();
+                const Fps existingModeFps = existingIter->second->second->getPeakFps();
                 const bool existingModeIsPrimaryRange = policy.primaryRangeIsSingleRate() &&
                         policy.primaryRanges.physical.includes(existingModeFps);
                 const bool newModeIsPrimaryRange = policy.primaryRangeIsSingleRate() &&
-                        policy.primaryRanges.physical.includes(mode->getFps());
+                        policy.primaryRanges.physical.includes(mode->getPeakFps());
                 if (newModeIsPrimaryRange == existingModeIsPrimaryRange) {
                     // We might need to update the map as we found a lower refresh rate
-                    if (isStrictlyLess(mode->getFps(), existingModeFps)) {
+                    if (isStrictlyLess(mode->getPeakFps(), existingModeFps)) {
                         existingIter->second = it;
-                        ALOGV("%s: changing %s (%s) as we found a lower physical rate", __func__,
-                              to_string(fps).c_str(), to_string(mode->getFps()).c_str());
+                        ALOGV("%s: changing %s (%s(%s)) as we found a lower physical rate",
+                              __func__, to_string(fps).c_str(), to_string(peakFps).c_str(),
+                              to_string(vsyncRate).c_str());
                     }
                 } else if (newModeIsPrimaryRange) {
                     existingIter->second = it;
-                    ALOGV("%s: changing %s (%s) to stay in the primary range", __func__,
-                          to_string(fps).c_str(), to_string(mode->getFps()).c_str());
+                    ALOGV("%s: changing %s (%s(%s)) to stay in the primary range", __func__,
+                          to_string(fps).c_str(), to_string(peakFps).c_str(),
+                          to_string(vsyncRate).c_str());
                 }
             }
         }
@@ -237,8 +252,8 @@
     const auto lowestRefreshRateIt =
             std::min_element(frameRateModes.begin(), frameRateModes.end(),
                              [](const FrameRateMode& lhs, const FrameRateMode& rhs) {
-                                 return isStrictlyLess(lhs.modePtr->getFps(),
-                                                       rhs.modePtr->getFps());
+                                 return isStrictlyLess(lhs.modePtr->getVsyncRate(),
+                                                       rhs.modePtr->getVsyncRate());
                              });
     frameRateModes.erase(frameRateModes.begin(), lowestRefreshRateIt);
 
@@ -623,7 +638,7 @@
             }
 
             const bool inPrimaryPhysicalRange =
-                    policy->primaryRanges.physical.includes(modePtr->getFps());
+                    policy->primaryRanges.physical.includes(modePtr->getPeakFps());
             const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps);
             if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
                  !inPrimaryRenderRange) &&
@@ -664,21 +679,24 @@
                             Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
             if (fixedSourceLayer && layerBelowThreshold) {
                 const bool modeAboveThreshold =
-                        modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+                        modePtr->getPeakFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
                 if (modeAboveThreshold) {
-                    ALOGV("%s gives %s (%s) fixed source (above threshold) score of %.4f",
+                    ALOGV("%s gives %s (%s(%s)) fixed source (above threshold) score of %.4f",
                           formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
-                          to_string(modePtr->getFps()).c_str(), layerScore);
+                          to_string(modePtr->getPeakFps()).c_str(),
+                          to_string(modePtr->getVsyncRate()).c_str(), layerScore);
                     fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
                 } else {
-                    ALOGV("%s gives %s (%s) fixed source (below threshold) score of %.4f",
+                    ALOGV("%s gives %s (%s(%s)) fixed source (below threshold) score of %.4f",
                           formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
-                          to_string(modePtr->getFps()).c_str(), layerScore);
+                          to_string(modePtr->getPeakFps()).c_str(),
+                          to_string(modePtr->getVsyncRate()).c_str(), layerScore);
                     fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
                 }
             } else {
-                ALOGV("%s gives %s (%s) score of %.4f", formatLayerInfo(layer, weight).c_str(),
-                      to_string(fps).c_str(), to_string(modePtr->getFps()).c_str(), layerScore);
+                ALOGV("%s gives %s (%s(%s)) score of %.4f", formatLayerInfo(layer, weight).c_str(),
+                      to_string(fps).c_str(), to_string(modePtr->getPeakFps()).c_str(),
+                      to_string(modePtr->getVsyncRate()).c_str(), layerScore);
                 overallScore += weightedLayerScore;
             }
         }
@@ -699,13 +717,14 @@
                                  [](RefreshRateScore max, RefreshRateScore current) {
                                      return current.overallScore > max.overallScore;
                                  });
-        ALOGV("%s (%s) is the best refresh rate without fixed source layers. It is %s the "
+        ALOGV("%s (%s(%s)) is the best refresh rate without fixed source layers. It is %s the "
               "threshold for "
               "refresh rate multiples",
               to_string(maxScoreIt->frameRateMode.fps).c_str(),
-              to_string(maxScoreIt->frameRateMode.modePtr->getFps()).c_str(),
+              to_string(maxScoreIt->frameRateMode.modePtr->getPeakFps()).c_str(),
+              to_string(maxScoreIt->frameRateMode.modePtr->getVsyncRate()).c_str(),
               maxScoreAboveThreshold ? "above" : "below");
-        return maxScoreIt->frameRateMode.modePtr->getFps() >=
+        return maxScoreIt->frameRateMode.modePtr->getPeakFps() >=
                 Fps::fromValue(mConfig.frameRateMultipleThreshold);
     }();
 
@@ -715,8 +734,9 @@
         if (maxScoreAboveThreshold) {
             overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
         }
-        ALOGV("%s (%s) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(),
-              to_string(frameRateMode.modePtr->getFps()).c_str(), overallScore);
+        ALOGV("%s (%s(%s)) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(),
+              to_string(frameRateMode.modePtr->getPeakFps()).c_str(),
+              to_string(frameRateMode.modePtr->getVsyncRate()).c_str(), overallScore);
     }
 
     // Now that we scored all the refresh rates we need to pick the one that got the highest
@@ -842,6 +862,7 @@
 
     const auto* policyPtr = getCurrentPolicyLocked();
     // We don't want to run lower than 30fps
+    // TODO(b/297600226): revise this for dVRR
     const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess);
 
     using fps_approx_ops::operator/;
@@ -933,7 +954,7 @@
     const auto current = [&]() REQUIRES(mLock) -> FrameRateMode {
         if (desiredActiveModeId) {
             const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get();
-            return FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+            return FrameRateMode{modePtr->getPeakFps(), ftl::as_non_null(modePtr)};
         }
 
         return getActiveModeLocked();
@@ -944,7 +965,7 @@
         return {};
     }
 
-    return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current;
+    return timerExpired ? FrameRateMode{min->getPeakFps(), ftl::as_non_null(min)} : current;
 }
 
 const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
@@ -970,12 +991,12 @@
     bool maxByAnchorFound = false;
     for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) {
         using namespace fps_approx_ops;
-        if (it->modePtr->getFps() > (*max)->getFps()) {
+        if (it->modePtr->getPeakFps() > (*max)->getPeakFps()) {
             max = &it->modePtr;
         }
 
         if (anchorGroup == it->modePtr->getGroup() &&
-            it->modePtr->getFps() >= (*maxByAnchor)->getFps()) {
+            it->modePtr->getPeakFps() >= (*maxByAnchor)->getPeakFps()) {
             maxByAnchorFound = true;
             maxByAnchor = &it->modePtr;
         }
@@ -1053,8 +1074,9 @@
             return;
         }
 
-        ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
-              to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score);
+        ALOGV("%s(%s) %s (%s(%s)) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
+              to_string(frameRateMode.fps).c_str(), to_string(modePtr->getPeakFps()).c_str(),
+              to_string(modePtr->getVsyncRate()).c_str(), score);
         ranking.emplace_back(ScoredFrameRate{frameRateMode, score});
     };
 
@@ -1134,8 +1156,8 @@
     mDisplayModes = std::move(modes);
     const auto activeModeOpt = mDisplayModes.get(activeModeId);
     LOG_ALWAYS_FATAL_IF(!activeModeOpt);
-    mActiveModeOpt =
-            FrameRateMode{activeModeOpt->get()->getFps(), ftl::as_non_null(activeModeOpt->get())};
+    mActiveModeOpt = FrameRateMode{activeModeOpt->get()->getPeakFps(),
+                                   ftl::as_non_null(activeModeOpt->get())};
 
     const auto sortedModes = sortByRefreshRate(mDisplayModes);
     mMinRefreshRateModeIt = sortedModes.front();
@@ -1161,7 +1183,7 @@
     if (mConfig.enableFrameRateOverride ==
         Config::FrameRateOverride::AppOverrideNativeRefreshRates) {
         for (const auto& [_, mode] : mDisplayModes) {
-            mAppOverrideNativeRefreshRates.try_emplace(mode->getFps(), ftl::unit);
+            mAppOverrideNativeRefreshRates.try_emplace(mode->getPeakFps(), ftl::unit);
         }
     }
 
@@ -1171,7 +1193,7 @@
 bool RefreshRateSelector::isPolicyValidLocked(const Policy& policy) const {
     // defaultMode must be a valid mode, and within the given refresh rate range.
     if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
-        if (!policy.primaryRanges.physical.includes(mode->get()->getFps())) {
+        if (!policy.primaryRanges.physical.includes(mode->get()->getPeakFps())) {
             ALOGE("Default mode is not in the primary range.");
             return false;
         }
@@ -1284,8 +1306,8 @@
             return mode.getResolution() == defaultMode->getResolution() &&
                     mode.getDpi() == defaultMode->getDpi() &&
                     (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
-                    ranges.physical.includes(mode.getFps()) &&
-                    (supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
+                    ranges.physical.includes(mode.getPeakFps()) &&
+                    (supportsFrameRateOverride() || ranges.render.includes(mode.getPeakFps()));
         };
 
         auto frameRateModes = createFrameRateModes(*policy, filterModes, ranges.render);
@@ -1340,13 +1362,13 @@
 auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
     std::lock_guard lock(mLock);
 
-    const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps();
+    const Fps deviceMinFps = mMinRefreshRateModeIt->second->getPeakFps();
     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 (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) {
+    if (isStrictlyLess(deviceMinFps, minByPolicy->getPeakFps())) {
         return KernelIdleTimerAction::TurnOff;
     }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 73e1d38..9bcbc0e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -253,7 +253,8 @@
 
     FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
         std::lock_guard lock(mLock);
-        return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
+        return {mMinRefreshRateModeIt->second->getPeakFps(),
+                mMaxRefreshRateModeIt->second->getPeakFps()};
     }
 
     ftl::Optional<FrameRateMode> onKernelTimerChanged(
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 27c96f7..595550b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -494,7 +494,7 @@
 
     if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) {
         if (!refreshRate) {
-            refreshRate = display.selectorPtr->getActiveMode().modePtr->getFps();
+            refreshRate = display.selectorPtr->getActiveMode().modePtr->getVsyncRate();
         }
         if (refreshRate->isValid()) {
             constexpr bool kForce = false;
@@ -542,7 +542,7 @@
                         to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str(), id.value);
 
     ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
-          to_string(mode.modePtr->getFps()).c_str());
+          to_string(mode.modePtr->getVsyncRate()).c_str());
 
     display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
 }
@@ -717,7 +717,7 @@
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().modePtr->getFps();
+    const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().modePtr->getPeakFps();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
     using namespace fps_approx_ops;
@@ -877,7 +877,7 @@
 
         newVsyncSchedulePtr = pacesetter.schedulePtr;
 
-        const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getFps();
+        const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getVsyncRate();
         constexpr bool kForce = true;
         newVsyncSchedulePtr->startPeriodTransition(refreshRate.getPeriod(), kForce);
     }
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index a24e43f..b6cb373 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -143,7 +143,7 @@
  */
 class WorkDuration : public VsyncConfiguration {
 public:
-    explicit WorkDuration(Fps currentRefrshRate);
+    explicit WorkDuration(Fps currentRefreshRate);
 
 protected:
     // Used for unit tests
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
index db38ebe..59a6df2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -36,7 +36,8 @@
 };
 
 inline std::string to_string(const FrameRateMode& mode) {
-    return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")";
+    return to_string(mode.fps) + " (" + to_string(mode.modePtr->getPeakFps()) + "(" +
+            to_string(mode.modePtr->getVsyncRate()) + "))";
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e6a2de5..1a08244 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -498,6 +498,7 @@
     mMiscFlagValue = flags::misc1();
     mConnectedDisplayFlagValue = flags::connected_display();
     mMisc2FlagEarlyBootValue = flags::late_boot_misc2();
+    mVrrConfigFlagValue = flags::vrr_config();
 }
 
 LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -1043,8 +1044,8 @@
         outMode.xDpi = xDpi;
         outMode.yDpi = yDpi;
 
-        const nsecs_t period = mode->getVsyncPeriod();
-        outMode.refreshRate = Fps::fromPeriodNsecs(period).getValue();
+        const auto peakFps = mode->getPeakFps();
+        outMode.refreshRate = peakFps.getValue();
 
         const auto vsyncConfigSet =
                 mVsyncConfiguration->getConfigsForRefreshRate(Fps::fromValue(outMode.refreshRate));
@@ -1064,7 +1065,7 @@
         //
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
-        outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000;
+        outMode.presentationDeadline = peakFps.getPeriodNsecs() - outMode.sfVsyncOffset + 1000000;
         excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(),
                                         outMode);
         info->supportedDisplayModes.push_back(outMode);
@@ -1199,7 +1200,7 @@
             // Start receiving vsync samples now, so that we can detect a period
             // switch.
             mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */,
-                                              mode.modePtr->getFps());
+                                              mode.modePtr->getVsyncRate());
 
             // As we called to set period, we will call to onRefreshRateChangeCompleted once
             // VsyncController model is locked.
@@ -1253,7 +1254,7 @@
         const auto& snapshot = snapshotRef.get();
 
         const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
-                [](const DisplayModePtr& mode) { return mode->getFps(); });
+                [](const DisplayModePtr& mode) { return mode->getPeakFps(); });
 
         if (!fpsOpt) {
             ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
@@ -1302,7 +1303,7 @@
     }
 
     const auto& activeMode = *upcomingModeInfo.modeOpt;
-    display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getFps(),
+    display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
                                activeMode.fps);
 
     if (displayId == mActiveDisplayId) {
@@ -1327,10 +1328,10 @@
     const auto desiredActiveMode = display->getDesiredActiveMode();
     const auto& modeOpt = desiredActiveMode->modeOpt;
     const auto displayId = modeOpt->modePtr->getPhysicalDisplayId();
-    const auto displayFps = modeOpt->modePtr->getFps();
+    const auto vsyncRate = modeOpt->modePtr->getVsyncRate();
     const auto renderFps = modeOpt->fps;
     clearDesiredActiveModeState(display);
-    mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, displayFps);
+    mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, vsyncRate);
     mScheduler->setRenderRate(displayId, renderFps);
 
     if (displayId == mActiveDisplayId) {
@@ -1371,7 +1372,7 @@
         }
 
         ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
-              to_string(displayModePtrOpt->get()->getFps()).c_str(),
+              to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
               to_string(display->getId()).c_str());
 
         if (display->getActiveMode() == desiredActiveMode->modeOpt) {
@@ -3211,6 +3212,7 @@
                                      .setPhysicalDisplayId(displayId)
                                      .setResolution({hwcMode.width, hwcMode.height})
                                      .setVsyncPeriod(hwcMode.vsyncPeriod)
+                                     .setVrrConfig(hwcMode.vrrConfig)
                                      .setDpiX(hwcMode.dpiX)
                                      .setDpiY(hwcMode.dpiY)
                                      .setGroup(hwcMode.configGroup)
@@ -3434,7 +3436,7 @@
 
     if (const auto& physical = state.physical) {
         const auto& mode = *physical->activeMode;
-        display->setActiveMode(mode.getId(), mode.getFps(), mode.getFps());
+        display->setActiveMode(mode.getId(), mode.getVsyncRate(), mode.getVsyncRate());
     }
 
     display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
@@ -5142,7 +5144,8 @@
         if (layer->setApi(s.api)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eSidebandStreamChanged) {
-        if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
+        if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime))
+            flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eInputInfoChanged) {
         layer->setInputInfo(*s.windowInfoHandle->getInfo());
@@ -5386,7 +5389,8 @@
         if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eSidebandStreamChanged) {
-        if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
+        if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime))
+            flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eDataspaceChanged) {
         if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
@@ -5677,7 +5681,7 @@
 
     display->setPowerMode(mode);
 
-    const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps();
+    const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getVsyncRate();
     if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
         // Turn on the display
 
@@ -6385,6 +6389,7 @@
     StringAppendF(&result, "Misc2FlagValue: %s (%s after boot)\n",
                   mMisc2FlagLateBootValue ? "true" : "false",
                   mMisc2FlagEarlyBootValue == mMisc2FlagLateBootValue ? "stable" : "modified");
+    StringAppendF(&result, "VrrConfigFlagValue: %s\n", mVrrConfigFlagValue ? "true" : "false");
 
     getRenderEngine().dump(result);
 
@@ -6404,7 +6409,7 @@
         std::string fps, xDpi, yDpi;
         if (const auto activeModePtr =
                     display->refreshRateSelector().getActiveMode().modePtr.get()) {
-            fps = to_string(activeModePtr->getFps());
+            fps = to_string(activeModePtr->getVsyncRate());
 
             const auto dpi = activeModePtr->getDpi();
             xDpi = base::StringPrintf("%.2f", dpi.x);
@@ -7967,7 +7972,7 @@
                 return snapshot.displayModes().get(defaultModeId);
             })
             .transform([](const DisplayModePtr& modePtr) {
-                return scheduler::FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+                return scheduler::FrameRateMode{modePtr->getPeakFps(), ftl::as_non_null(modePtr)};
             });
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e22dd56..ef6b815 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1457,6 +1457,7 @@
     bool mConnectedDisplayFlagValue;
     bool mMisc2FlagEarlyBootValue;
     bool mMisc2FlagLateBootValue;
+    bool mVrrConfigFlagValue;
 };
 
 class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 437fd35..c4077df 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -666,7 +666,7 @@
         }
 
         mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
-        const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps();
+        const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getVsyncRate();
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
 
         mFlinger->mRefreshRateStats =
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 9f0bdde..39a7ee5 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -153,7 +153,8 @@
     native_handle_t* testHandle = native_handle_create(0, 1);
     const bool ownsHandle = mFdp.ConsumeBool();
     sp<NativeHandle> nativeHandle = sp<NativeHandle>::make(testHandle, ownsHandle);
-    layer->setSidebandStream(nativeHandle);
+    layer->setSidebandStream(nativeHandle, getFuzzedFrameTimelineInfo(),
+                             mFdp.ConsumeIntegral<nsecs_t>() /* postTime */);
     layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
 
     layer->fenceHasSignaled();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 4d1a5ff..cbbfa16 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -390,7 +390,7 @@
                                       PowerMode::OFF);
 
     const auto fpsOpt = displayModes.get(modeId).transform(
-            [](const DisplayModePtr& mode) { return mode->getFps(); });
+            [](const DisplayModePtr& mode) { return mode->getVsyncRate(); });
     refreshRateStats.setRefreshRate(*fpsOpt);
 
     refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes));
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 6519d3a..bfc03aa 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -22,3 +22,11 @@
   description: "This flag controls minor miscellaneous SurfaceFlinger changes. Cannot be read before boot finished!"
   bug: "297389311"
 }
+
+flag {
+  name: "vrr_config"
+  namespace: "core_graphics"
+  description: "Controls SurfaceFlinger support for VRR Configurations"
+  bug: "284845445"
+  is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 91c6239..9b46009 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -703,7 +703,7 @@
                               .setId(DisplayModeId(7))
                               .setVsyncPeriod(16666666)
                               .build();
-    const Fps fps = mode->getFps() / 2;
+    const Fps fps = mode->getPeakFps() / 2;
 
     mThread->onModeChanged({fps, ftl::as_non_null(mode)});
     expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, fps.getPeriodNsecs());
@@ -717,7 +717,7 @@
                               .setId(DisplayModeId(5))
                               .setVsyncPeriod(16666666)
                               .build();
-    const Fps fps = mode->getFps() / 2;
+    const Fps fps = mode->getPeakFps() / 2;
 
     mThread->onModeChanged({fps, ftl::as_non_null(mode)});
     expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, fps.getPeriodNsecs());
@@ -731,7 +731,7 @@
                               .setId(DisplayModeId(7))
                               .setVsyncPeriod(16666666)
                               .build();
-    const Fps fps = mode->getFps() / 2;
+    const Fps fps = mode->getPeakFps() / 2;
     mThread->onModeChanged({fps, ftl::as_non_null(mode)});
     expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, fps.getPeriodNsecs());
 }
@@ -748,7 +748,7 @@
                               .setId(DisplayModeId(9))
                               .setVsyncPeriod(16666666)
                               .build();
-    const Fps fps = mode->getFps() / 2;
+    const Fps fps = mode->getPeakFps() / 2;
 
     mThread->onModeChanged({fps, ftl::as_non_null(mode)});
     expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, fps.getPeriodNsecs());
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 50c1626..264b172 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -36,6 +36,9 @@
 
 #include "libsurfaceflinger_unittest_main.h"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
+using namespace com::android::graphics::surfaceflinger;
 using namespace std::chrono_literals;
 
 namespace android::scheduler {
@@ -48,6 +51,7 @@
 using SetPolicyResult = RefreshRateSelector::SetPolicyResult;
 
 using mock::createDisplayMode;
+using mock::createVrrDisplayMode;
 
 struct TestableRefreshRateSelector : RefreshRateSelector {
     using RefreshRateSelector::FrameRateRanking;
@@ -201,6 +205,19 @@
     static inline const ftl::NonNull<DisplayModePtr> kMode10 =
             ftl::as_non_null(createDisplayMode(kModeId10, 10_Hz));
 
+    // VRR modes
+    static inline const ftl::NonNull<DisplayModePtr> kVrrMode120TE240 = ftl::as_non_null(
+            createVrrDisplayMode(kModeId120, 240_Hz,
+                                 hal::VrrConfig{
+                                         .minFrameIntervalNs =
+                                                 static_cast<Fps>(120_Hz).getPeriodNsecs()}));
+
+    static inline const ftl::NonNull<DisplayModePtr> kVrrMode60TE120 = ftl::as_non_null(
+            createVrrDisplayMode(kModeId60, 120_Hz,
+                                 hal::VrrConfig{.minFrameIntervalNs =
+                                                        static_cast<Fps>(60_Hz).getPeriodNsecs()},
+                                 /*group=*/1));
+
     // Test configurations.
     static inline const DisplayModes kModes_60 = makeModes(kMode60);
     static inline const DisplayModes kModes_35_60_90 = makeModes(kMode35, kMode60, kMode90);
@@ -225,6 +242,11 @@
     static inline const DisplayModes kModes_1_5_10 = makeModes(kMode1, kMode5, kMode10);
     static inline const DisplayModes kModes_60_90_120 = makeModes(kMode60, kMode90, kMode120);
 
+    // VRR display modes
+    static inline const DisplayModes kVrrMode_120 = makeModes(kVrrMode120TE240);
+    static inline const DisplayModes kVrrModes_60_120 =
+            makeModes(kVrrMode60TE120, kVrrMode120TE240);
+
     // This is a typical TV configuration.
     static inline const DisplayModes kModes_24_25_30_50_60_Frac =
             makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
@@ -973,7 +995,8 @@
         lr.desiredRefreshRate = Fps::fromValue(fps);
         const auto mode = selector.getBestFrameRateMode(layers);
         EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
-                                 << to_string(mode->getFps());
+                                 << to_string(mode->getPeakFps()) << "("
+                                 << to_string(mode->getVsyncRate()) << ")";
     }
 }
 
@@ -988,7 +1011,8 @@
         lr.desiredRefreshRate = Fps::fromValue(fps);
         const auto mode = selector.getBestFrameRateMode(layers);
         EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
-                                 << to_string(mode->getFps());
+                                 << to_string(mode->getPeakFps()) << "("
+                                 << to_string(mode->getVsyncRate()) << ")";
     }
 }
 
@@ -1029,7 +1053,8 @@
         lr.desiredRefreshRate = Fps::fromValue(fps);
         const auto mode = selector.getBestFrameRateMode(layers, {});
         EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
-                                 << to_string(mode->getFps());
+                                 << to_string(mode->getPeakFps()) << "("
+                                 << to_string(mode->getVsyncRate()) << ")";
     }
 }
 
@@ -1146,9 +1171,9 @@
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
         EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
                 << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
-                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRefreshRates[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
-                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << refreshRates[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 }
 
@@ -1175,9 +1200,9 @@
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
         EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
                 << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
-                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRefreshRates[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
-                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << refreshRates[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 }
 
@@ -1207,9 +1232,9 @@
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
         EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
                 << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
-                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRefreshRates[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
-                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << refreshRates[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 }
 
@@ -1239,9 +1264,9 @@
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
         EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
                 << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
-                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRefreshRates[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
-                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << refreshRates[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 }
 
@@ -1267,9 +1292,9 @@
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
         EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
                 << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
-                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRefreshRates[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
-                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << refreshRates[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 
     std::tie(refreshRates, signals) =
@@ -1281,9 +1306,9 @@
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
         EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
                 << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
-                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRefreshRates[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
-                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << refreshRates[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1301,9 +1326,9 @@
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
         EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
                 << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
-                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRefreshRates[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
-                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << refreshRates[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 
     std::tie(refreshRates, signals) =
@@ -1326,9 +1351,9 @@
     for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
         EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
                 << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
-                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRefreshRates[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
-                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << refreshRates[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 }
 
@@ -1434,7 +1459,7 @@
             layers[1].frameRateCategory = testCase.frameRateCategory;
         }
 
-        EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getFps())
+        EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getPeakFps())
                 << "did not get expected frame rate for " << lr.name;
     }
 }
@@ -1491,7 +1516,7 @@
             layers[1].frameRateCategory = testCase.frameRateCategory;
         }
 
-        EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getFps())
+        EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getPeakFps())
                 << "did not get expected frame rate for " << lr.name;
     }
 }
@@ -1529,9 +1554,10 @@
         ss << "ExplicitDefault " << desired;
         lr.name = ss.str();
 
-        const auto bestFps = selector.getBestFrameRateMode(layers)->getFps();
-        EXPECT_EQ(expected, bestFps)
-                << "expected " << expected << " for " << desired << " but got " << bestFps;
+        const auto bestMode = selector.getBestFrameRateMode(layers);
+        EXPECT_EQ(expected, bestMode->getPeakFps())
+                << "expected " << expected << " for " << desired << " but got "
+                << bestMode->getPeakFps() << "(" << bestMode->getVsyncRate() << ")";
     }
 }
 
@@ -1608,7 +1634,7 @@
             ss << "ExplicitExact " << desired;
             lr.name = ss.str();
 
-            EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getFps());
+            EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getPeakFps());
         }
     }
 }
@@ -1713,9 +1739,9 @@
     for (size_t i = 0; i < expectedRanking.size(); ++i) {
         EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
                 << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
-                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRanking[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
-                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << actualRanking[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 
     lr1.vote = LayerVoteType::Max;
@@ -1755,9 +1781,9 @@
     for (size_t i = 0; i < expectedRanking.size(); ++i) {
         EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
                 << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
-                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRanking[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
-                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << actualRanking[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 
     lr1.vote = LayerVoteType::Heuristic;
@@ -1795,9 +1821,9 @@
     for (size_t i = 0; i < expectedRanking.size(); ++i) {
         EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
                 << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
-                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRanking[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
-                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << actualRanking[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 
     lr1.desiredRefreshRate = 120_Hz;
@@ -1838,9 +1864,9 @@
     for (size_t i = 0; i < expectedRanking.size(); ++i) {
         EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
                 << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
-                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << expectedRanking[i].modePtr->getVsyncRate().getIntValue() << ")"
                 << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
-                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+                << actualRanking[i].frameRateMode.modePtr->getVsyncRate().getIntValue() << ")";
     }
 }
 
@@ -2485,7 +2511,8 @@
     const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
         layers[0].desiredRefreshRate = fps;
         layers[0].vote = vote;
-        EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
+        EXPECT_EQ(fps.getIntValue(),
+                  selector.getBestFrameRateMode(layers)->getPeakFps().getIntValue())
                 << "Failed for " << ftl::enum_string(vote);
     };
 
@@ -2524,13 +2551,13 @@
             },
     };
 
-    EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getFps());
+    EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getPeakFps());
 }
 
 TEST_P(RefreshRateSelectorTest, modeComparison) {
-    EXPECT_LT(kMode60->getFps(), kMode90->getFps());
-    EXPECT_GE(kMode60->getFps(), kMode60->getFps());
-    EXPECT_GE(kMode90->getFps(), kMode90->getFps());
+    EXPECT_LT(kMode60->getPeakFps(), kMode90->getPeakFps());
+    EXPECT_GE(kMode60->getPeakFps(), kMode60->getPeakFps());
+    EXPECT_GE(kMode90->getPeakFps(), kMode90->getPeakFps());
 }
 
 TEST_P(RefreshRateSelectorTest, testKernelIdleTimerAction) {
@@ -2579,27 +2606,27 @@
     auto selector = createSelector(kModes_30_60_72_90_120, kModeId30);
 
     const auto frameRate = 30_Hz;
-    Fps displayRefreshRate = selector.getActiveMode().getFps();
+    Fps displayRefreshRate = selector.getActiveMode().getPeakFps();
     EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     selector.setActiveMode(kModeId60, 60_Hz);
-    displayRefreshRate = selector.getActiveMode().getFps();
+    displayRefreshRate = selector.getActiveMode().getPeakFps();
     EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     selector.setActiveMode(kModeId72, 72_Hz);
-    displayRefreshRate = selector.getActiveMode().getFps();
+    displayRefreshRate = selector.getActiveMode().getPeakFps();
     EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     selector.setActiveMode(kModeId90, 90_Hz);
-    displayRefreshRate = selector.getActiveMode().getFps();
+    displayRefreshRate = selector.getActiveMode().getPeakFps();
     EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     selector.setActiveMode(kModeId120, 120_Hz);
-    displayRefreshRate = selector.getActiveMode().getFps();
+    displayRefreshRate = selector.getActiveMode().getPeakFps();
     EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
     selector.setActiveMode(kModeId90, 90_Hz);
-    displayRefreshRate = selector.getActiveMode().getFps();
+    displayRefreshRate = selector.getActiveMode().getPeakFps();
     EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
 
     EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 25_Hz));
@@ -3029,7 +3056,7 @@
     for (size_t i = 0; i < expected.size(); i++) {
         const auto [expectedRenderRate, expectedRefreshRate] = expected[i];
         EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps);
-        EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps());
+        EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getPeakFps());
     }
 }
 
@@ -3175,6 +3202,7 @@
 }
 
 // TODO(b/266481656): Once this bug is fixed, we can remove this test
+// And test for VRR when we remove this work around for VRR.
 TEST_P(RefreshRateSelectorTest, noLowerFrameRateOnMinVote) {
     auto selector = createSelector(kModes_60_90, kModeId60);
 
@@ -3304,5 +3332,187 @@
     EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
 }
 
+// VRR tests
+TEST_P(RefreshRateSelectorTest, singleMinMaxRateForVrr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+        return;
+    }
+
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+    EXPECT_TRUE(selector.supportsFrameRateOverride());
+
+    const auto minRate = selector.getMinSupportedRefreshRate();
+    const auto performanceRate = selector.getMaxSupportedRefreshRate();
+    const auto minRateByPolicy = selector.getMinRefreshRateByPolicy();
+    const auto performanceRateByPolicy = selector.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(kVrrMode120TE240, minRate);
+    EXPECT_EQ(kVrrMode120TE240, performanceRate);
+    EXPECT_EQ(kVrrMode120TE240, minRateByPolicy);
+    EXPECT_EQ(kVrrMode120TE240, performanceRateByPolicy);
+}
+
+TEST_P(RefreshRateSelectorTest, renderRateChangesWithPolicyChangeForVrr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+        return;
+    }
+
+    auto selector = createSelector(kVrrModes_60_120, kModeId120);
+
+    const FpsRange only120 = {120_Hz, 120_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {only120, only120}, {only120, only120}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 120_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+
+    const FpsRange range120 = {0_Hz, 120_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {only120, range120}, {only120, range120}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 120_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+
+    const FpsRange range90 = {0_Hz, 90_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {only120, range90}, {only120, range90}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 80_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+
+    const FpsRange range80 = {0_Hz, 80_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {only120, range80}, {only120, range80}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 80_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+
+    const FpsRange range60 = {0_Hz, 60_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {only120, range60}, {only120, range60}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 60_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+
+    const FpsRange range48 = {0_Hz, 48_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {only120, range48}, {only120, range48}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 48_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+
+    const FpsRange range30 = {0_Hz, 30_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {only120, range30}, {only120, range30}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 30_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, modeChangesWithPolicyChangeForVrr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+        return;
+    }
+
+    auto selector = createSelector(kVrrModes_60_120, kModeId120);
+
+    const FpsRange range120 = {0_Hz, 120_Hz};
+    const FpsRange range60 = {0_Hz, 60_Hz};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {range120, range60}, {range120, range60}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 60_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId60, {range60, range60}, {range60, range60}}));
+    EXPECT_FRAME_RATE_MODE(kVrrMode60TE120, 60_Hz,
+                           selector.getBestScoredFrameRate({}).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverridesForVrr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+        return;
+    }
+
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+    // TODO(b/297600226) Run at lower than 30 Fps for dVRR
+    const std::vector<Fps> desiredRefreshRates = {30_Hz, 34.285_Hz, 40_Hz, 48_Hz,
+                                                  60_Hz, 80_Hz,     120_Hz};
+    const std::vector<LayerVoteType> layerVotes = {LayerVoteType::ExplicitDefault,
+                                                   LayerVoteType::ExplicitExactOrMultiple,
+                                                   LayerVoteType::ExplicitExact};
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].ownerUid = 1234;
+
+    for (auto desiredRefreshRate : desiredRefreshRates) {
+        layers[0].desiredRefreshRate = desiredRefreshRate;
+        for (auto vote : layerVotes) {
+            layers[0].vote = vote;
+            auto frameRateOverrides = selector.getFrameRateOverrides(layers, 240_Hz, {});
+            EXPECT_EQ(1u, frameRateOverrides.size());
+            ASSERT_EQ(1u, frameRateOverrides.count(1234));
+            EXPECT_EQ(desiredRefreshRate, frameRateOverrides.at(1234));
+        }
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRatesForVrr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+        return;
+    }
+
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+    const FpsRange only120 = {120_Hz, 120_Hz};
+    const FpsRange range120 = {0_Hz, 120_Hz};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {only120, range120}, {only120, range120}}));
+
+    std::vector<Fps> expected = {20_Hz, 21.818_Hz, 24_Hz, 26.666_Hz, 30_Hz, 34.285_Hz,
+                                 40_Hz, 48_Hz,     60_Hz, 80_Hz,     120_Hz};
+
+    auto primaryRefreshRates = selector.getPrimaryFrameRates();
+    ASSERT_EQ(expected.size(), primaryRefreshRates.size());
+
+    for (size_t i = 0; i < expected.size(); i++) {
+        EXPECT_EQ(expected[i], primaryRefreshRates[i].fps);
+        EXPECT_EQ(120_Hz, primaryRefreshRates[i].modePtr->getPeakFps());
+    }
+
+    // Render range (0,90)
+    const FpsRange range90 = {0_Hz, 90_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {range120, range90}, {range120, range90}}));
+
+    expected = {20_Hz, 21.818_Hz, 24_Hz, 26.666_Hz, 30_Hz, 34.285_Hz, 40_Hz, 48_Hz, 60_Hz, 80_Hz};
+
+    primaryRefreshRates = selector.getPrimaryFrameRates();
+    ASSERT_EQ(expected.size(), primaryRefreshRates.size());
+    for (size_t i = 0; i < expected.size(); i++) {
+        EXPECT_EQ(expected[i], primaryRefreshRates[i].fps);
+        EXPECT_EQ(120_Hz, primaryRefreshRates[i].modePtr->getPeakFps());
+    }
+
+    // Render range (0,60)
+    const FpsRange range60 = {0_Hz, 60_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId120, {range120, range60}, {range120, range60}}));
+    expected = {20_Hz, 21.818_Hz, 24_Hz, 26.666_Hz, 30_Hz, 34.285_Hz, 40_Hz, 48_Hz, 60_Hz};
+
+    primaryRefreshRates = selector.getPrimaryFrameRates();
+    ASSERT_EQ(expected.size(), primaryRefreshRates.size());
+    for (size_t i = 0; i < expected.size(); i++) {
+        EXPECT_EQ(expected[i], primaryRefreshRates[i].fps);
+        EXPECT_EQ(120_Hz, primaryRefreshRates[i].modePtr->getPeakFps());
+    }
+}
 } // namespace
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8458aa3..908c9ab 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -997,7 +997,7 @@
 
                 const auto activeMode = modes.get(activeModeId);
                 LOG_ALWAYS_FATAL_IF(!activeMode);
-                const auto fps = activeMode->get()->getFps();
+                const auto fps = activeMode->get()->getPeakFps();
 
                 state.physical = {.id = physicalId,
                                   .hwcDisplayId = *mHwcDisplayId,
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index 108151e..1cf14ae 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -127,7 +127,7 @@
     sp<NativeHandle> stream =
             NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                  false);
-    layer->setSidebandStream(stream);
+    layer->setSidebandStream(stream, FrameTimelineInfo{}, 20);
     mFlinger.mutableCurrentState().layersSortedByZ.add(layer);
     mTunnelModeEnabledReporter->updateTunnelModeStatus();
     mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
@@ -151,7 +151,7 @@
     sp<NativeHandle> stream =
             NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                  false);
-    layerWithSidebandStream->setSidebandStream(stream);
+    layerWithSidebandStream->setSidebandStream(stream, FrameTimelineInfo{}, 20);
 
     mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer);
     mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index 3b36361..cb05c00 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -20,17 +20,23 @@
 
 namespace android::mock {
 
-inline DisplayModePtr createDisplayMode(
-        DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
+inline DisplayMode::Builder createDisplayModeBuilder(
+        DisplayModeId modeId, Fps displayRefreshRate, 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())
+            .setVsyncPeriod(displayRefreshRate.getPeriodNsecs())
             .setGroup(group)
-            .setResolution(resolution)
-            .build();
+            .setResolution(resolution);
+}
+
+inline DisplayModePtr createDisplayMode(
+        DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
+        ui::Size resolution = ui::Size(1920, 1080),
+        PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0)) {
+    return createDisplayModeBuilder(modeId, refreshRate, group, resolution, displayId).build();
 }
 
 inline DisplayModePtr createDisplayMode(PhysicalDisplayId displayId, DisplayModeId modeId,
@@ -38,11 +44,19 @@
     return createDisplayMode(modeId, refreshRate, {}, {}, displayId);
 }
 
+inline DisplayModePtr createVrrDisplayMode(
+        DisplayModeId modeId, Fps displayRefreshRate, hal::VrrConfig vrrConfig, int32_t group = 0,
+        ui::Size resolution = ui::Size(1920, 1080),
+        PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0)) {
+    return createDisplayModeBuilder(modeId, displayRefreshRate, group, resolution, displayId)
+            .setVrrConfig(std::move(vrrConfig))
+            .build();
+}
 inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) {
     return DisplayMode::Builder(modePtr->getHwcId())
             .setId(modePtr->getId())
             .setPhysicalDisplayId(displayId)
-            .setVsyncPeriod(modePtr->getVsyncPeriod())
+            .setVsyncPeriod(modePtr->getVsyncRate().getPeriodNsecs())
             .setGroup(modePtr->getGroup())
             .setResolution(modePtr->getResolution())
             .build();
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
index 4cfdd58..bfcdd9b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
@@ -19,7 +19,7 @@
 #include <scheduler/FrameRateMode.h>
 
 // Use a C style macro to keep the line numbers printed in gtest
-#define EXPECT_FRAME_RATE_MODE(_modePtr, _fps, _mode)                                \
-    EXPECT_EQ((scheduler::FrameRateMode{(_fps), (_modePtr)}), (_mode))               \
-            << "Expected " << (_fps) << " (" << (_modePtr)->getFps() << ") but was " \
-            << (_mode).fps << " (" << (_mode).modePtr->getFps() << ")"
+#define EXPECT_FRAME_RATE_MODE(_modePtr, _fps, _mode)                                      \
+    EXPECT_EQ((scheduler::FrameRateMode{(_fps), (_modePtr)}), (_mode))                     \
+            << "Expected " << (_fps) << " (" << (_modePtr)->getVsyncRate() << ") but was " \
+            << (_mode).fps << " (" << (_mode).modePtr->getVsyncRate() << ")"
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 7159d83..c5870d4 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1565,22 +1565,26 @@
               query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
-    uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
-    const auto mailbox_num_images = std::max(3u, create_info->minImageCount);
-    const auto requested_images =
-        swap_interval ? create_info->minImageCount : mailbox_num_images;
-    uint32_t num_images = requested_images - 1 + min_undequeued_buffers;
+    const uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
 
     // Lower layer insists that we have at least min_undequeued_buffers + 1
     // buffers.  This is wasteful and we'd like to relax it in the shared case,
     // but not all the pieces are in place for that to work yet.  Note we only
     // lie to the lower layer--we don't want to give the app back a swapchain
     // with extra images (which they can't actually use!).
-    uint32_t min_buffer_count = min_undequeued_buffers + 1;
-    err = native_window_set_buffer_count(
-        window, std::max(min_buffer_count, num_images));
+    const uint32_t min_buffer_count = min_undequeued_buffers + 1;
+
+    uint32_t num_images;
+    if (create_info->presentMode  == VK_PRESENT_MODE_MAILBOX_KHR) {
+        num_images = std::max(3u, create_info->minImageCount);
+    } else {
+        num_images = create_info->minImageCount;
+    }
+
+    const uint32_t buffer_count = std::max(min_buffer_count, num_images);
+    err = native_window_set_buffer_count(window, buffer_count);
     if (err != android::OK) {
-        ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
+        ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", buffer_count,
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }