Merge "Add frame rate compatibility GTE logic" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index dd0ee5f..83b336c 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1035,8 +1035,6 @@
         CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
     DoRadioLogcat();
 
-    RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
-
     /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
     RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable",
                                "-v", "uid", "-d", "*:v"});
@@ -1243,7 +1241,7 @@
 
 static void DumpIpAddrAndRules() {
     /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
-    RunCommand("NETWORK INTERFACES", {"ip", "link"});
+    RunCommand("NETWORK INTERFACES", {"ip", "-s", "link"});
     RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
     RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
     RunCommand("IP RULES", {"ip", "rule", "show"});
diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs
index 6f5deb9..c30c00f 100644
--- a/cmds/evemu-record/main.rs
+++ b/cmds/evemu-record/main.rs
@@ -26,7 +26,7 @@
 use std::io::{BufRead, Write};
 use std::path::PathBuf;
 
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use nix::sys::time::TimeVal;
 
 mod evdev;
@@ -39,6 +39,19 @@
     device: Option<PathBuf>,
     /// The file to save the recording to. Defaults to standard output.
     output_file: Option<PathBuf>,
+
+    /// The base time that timestamps should be relative to (Android-specific extension)
+    #[arg(long, value_enum, default_value_t = TimestampBase::FirstEvent)]
+    timestamp_base: TimestampBase,
+}
+
+#[derive(Clone, Debug, ValueEnum)]
+enum TimestampBase {
+    /// The first event received from the device.
+    FirstEvent,
+
+    /// The time when the system booted.
+    Boot,
 }
 
 fn get_choice(max: u32) -> u32 {
@@ -149,7 +162,11 @@
     Ok(())
 }
 
-fn print_events(device: &evdev::Device, output: &mut impl Write) -> Result<(), Box<dyn Error>> {
+fn print_events(
+    device: &evdev::Device,
+    output: &mut impl Write,
+    timestamp_base: TimestampBase,
+) -> Result<(), Box<dyn Error>> {
     fn print_event(output: &mut impl Write, event: &evdev::InputEvent) -> Result<(), io::Error> {
         // TODO(b/302297266): Translate events into human-readable names and add those as comments.
         writeln!(
@@ -164,12 +181,15 @@
         Ok(())
     }
     let event = device.read_event()?;
-    // Due to a bug in the C implementation of evemu-play [0] that has since become part of the API,
-    // the timestamp of the first event in a recording shouldn't be exactly 0.0 seconds, so offset
-    // it by 1µs.
-    //
-    // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d
-    let start_time = event.time - TimeVal::new(0, 1);
+    let start_time = match timestamp_base {
+        // Due to a bug in the C implementation of evemu-play [0] that has since become part of the
+        // API, the timestamp of the first event in a recording shouldn't be exactly 0.0 seconds,
+        // so offset it by 1µs.
+        //
+        // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d
+        TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1),
+        TimestampBase::Boot => TimeVal::new(0, 0),
+    };
     print_event(output, &event.offset_time_by(start_time))?;
     loop {
         let event = device.read_event()?;
@@ -188,6 +208,6 @@
         None => Box::new(io::stdout().lock()),
     };
     print_device_description(&device, &mut output)?;
-    print_events(&device, &mut output)?;
+    print_events(&device, &mut output, args.timestamp_base)?;
     Ok(())
 }
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index c84ee1f..44a9e52 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -423,10 +423,10 @@
         "libhidlbase",
         "liblog",
         "libnativewindow",
+        "libselinux",
         "libsync",
         "libui",
         "libutils",
-        "libvndksupport",
     ],
 
     static_libs: [
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index b6a47fb..744201a 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -36,13 +36,45 @@
 #include <gui/TraceUtils.h>
 
 #include <private/gui/BufferQueueThreadState.h>
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
 #include <binder/PermissionCache.h>
-#include <vndksupport/linker.h>
+#include <selinux/android.h>
+#include <selinux/selinux.h>
 #endif
 
 #include <system/window.h>
 
+namespace {
+#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
+int selinux_log_suppress_callback(int, const char*, ...) { // NOLINT
+    // DO NOTHING
+    return 0;
+}
+
+bool hasAccessToPermissionService() {
+    char* ctx;
+
+    if (getcon(&ctx) == -1) {
+        // Failed to get current selinux context
+        return false;
+    }
+
+    union selinux_callback cb;
+
+    cb.func_log = selinux_log_suppress_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
+
+    bool hasAccess = selinux_check_access(ctx, "u:object_r:permission_service:s0",
+                                          "service_manager", "find", NULL) == 0;
+    freecon(ctx);
+    cb.func_log = hasAccess ? selinux_log_callback : selinux_vendor_log_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
+
+    return hasAccess;
+}
+#endif
+} // namespace
+
 namespace android {
 
 // Macros for include BufferQueueCore information in log messages
@@ -814,7 +846,7 @@
     // the PermissionController. We need to do a runtime check as well, since
     // the system variant of libgui can be loaded in a vendor process. For eg:
     // if a HAL uses an llndk library that depends on libgui (libmediandk etc).
-    if (!android_is_in_vendor_process()) {
+    if (hasAccessToPermissionService()) {
         const pid_t pid = BufferQueueThreadState::getCallingPid();
         if ((uid != shellUid) &&
             !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 97a82be..38fab9c 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -930,7 +930,6 @@
     SAFE_PARCEL(output->writeStrongBinder, displayToken);
     SAFE_PARCEL(output->writeUint32, width);
     SAFE_PARCEL(output->writeUint32, height);
-    SAFE_PARCEL(output->writeBool, useIdentityTransform);
     return NO_ERROR;
 }
 
@@ -940,7 +939,6 @@
     SAFE_PARCEL(input->readStrongBinder, &displayToken);
     SAFE_PARCEL(input->readUint32, &width);
     SAFE_PARCEL(input->readUint32, &height);
-    SAFE_PARCEL(input->readBool, &useIdentityTransform);
     return NO_ERROR;
 }
 
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
index 2676e0a..e29ce41 100644
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -76,7 +76,6 @@
     sp<IBinder> displayToken;
     uint32_t width{0};
     uint32_t height{0};
-    bool useIdentityTransform{false};
 
     status_t writeToParcel(Parcel* output) const override;
     status_t readFromParcel(const Parcel* input) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index ce2b96f..1f241b0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -227,6 +227,7 @@
     // Returns the bit-set of differing fields between this LayerState and another LayerState.
     // This bit-set is based on NonUniqueFields only, and excludes GraphicBuffers.
     ftl::Flags<LayerStateField> getDifferingFields(const LayerState& other) const;
+    bool isSourceCropSizeEqual(const LayerState& other) const;
 
     compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; }
     int32_t getId() const { return mId.get(); }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 13b6307..a18397d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -20,6 +20,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <android-base/properties.h>
+#include <common/FlagManager.h>
 #include <compositionengine/impl/planner/Flattener.h>
 #include <compositionengine/impl/planner/LayerState.h>
 
@@ -50,8 +51,19 @@
     for (size_t i = 0; i < incomingLayers.size(); i++) {
         // Checking the IDs here is very strict, but we do this as otherwise we may mistakenly try
         // to access destroyed OutputLayers later on.
-        if (incomingLayers[i]->getId() != existingLayers[i]->getId() ||
-            incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
+        if (incomingLayers[i]->getId() != existingLayers[i]->getId()) {
+            return false;
+        }
+
+        // Do not unflatten if source crop is only moved.
+        if (FlagManager::getInstance().cache_if_source_crop_layer_only_moved() &&
+            incomingLayers[i]->isSourceCropSizeEqual(*(existingLayers[i])) &&
+            incomingLayers[i]->getDifferingFields(*(existingLayers[i])) ==
+                    LayerStateField::SourceCrop) {
+            continue;
+        }
+
+        if (incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
             return false;
         }
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 8dab6ce..0e3fdbb 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -76,6 +76,11 @@
     return hash;
 }
 
+bool LayerState::isSourceCropSizeEqual(const LayerState& other) const {
+    return mSourceCrop.get().getWidth() == other.mSourceCrop.get().getWidth() &&
+            mSourceCrop.get().getHeight() == other.mSourceCrop.get().getHeight();
+}
+
 ftl::Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) const {
     ftl::Flags<LayerStateField> differences;
     auto myFields = getNonUniqueFields();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index d9318af..763b998 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <common/include/common/test/FlagUtils.h>
+#include "com_android_graphics_surfaceflinger_flags.h"
+
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/Flattener.h>
@@ -239,6 +242,30 @@
     expectAllLayersFlattened(layers);
 }
 
+TEST_F(FlattenerTest, unflattenLayers_onlySourceCropMoved) {
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+                              cache_if_source_crop_layer_only_moved,
+                      true);
+
+    auto& layerState1 = mTestLayers[0]->layerState;
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+    };
+
+    initializeFlattener(layers);
+
+    mTestLayers[0]->outputLayerCompositionState.sourceCrop = FloatRect{0.f, 0.f, 100.f, 100.f};
+    mTestLayers[1]->outputLayerCompositionState.sourceCrop = FloatRect{8.f, 16.f, 108.f, 116.f};
+
+    // only source crop is moved, so no flatten
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+}
+
 TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
     auto& layerState1 = mTestLayers[0]->layerState;
     auto& layerState2 = mTestLayers[1]->layerState;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index cc2f6c7..3690219 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -78,6 +78,7 @@
 
 using AidlColorTransform = aidl::android::hardware::graphics::common::ColorTransform;
 using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
+using AidlDisplayHotplugEvent = aidl::android::hardware::graphics::common::DisplayHotplugEvent;
 using AidlFRect = aidl::android::hardware::graphics::common::FRect;
 using AidlRect = aidl::android::hardware::graphics::common::Rect;
 using AidlTransform = aidl::android::hardware::graphics::common::Transform;
@@ -174,9 +175,9 @@
     AidlIComposerCallbackWrapper(HWC2::ComposerCallback& callback) : mCallback(callback) {}
 
     ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override {
-        const auto connection = in_connected ? V2_4::IComposerCallback::Connection::CONNECTED
-                                             : V2_4::IComposerCallback::Connection::DISCONNECTED;
-        mCallback.onComposerHalHotplug(translate<Display>(in_display), connection);
+        const auto event = in_connected ? AidlDisplayHotplugEvent::CONNECTED
+                                        : AidlDisplayHotplugEvent::DISCONNECTED;
+        mCallback.onComposerHalHotplugEvent(translate<Display>(in_display), event);
         return ::ndk::ScopedAStatus::ok();
     }
 
@@ -216,6 +217,12 @@
         return ::ndk::ScopedAStatus::ok();
     }
 
+    ::ndk::ScopedAStatus onHotplugEvent(int64_t in_display,
+                                        AidlDisplayHotplugEvent event) override {
+        mCallback.onComposerHalHotplugEvent(translate<Display>(in_display), event);
+        return ::ndk::ScopedAStatus::ok();
+    }
+
 private:
     HWC2::ComposerCallback& mCallback;
 };
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index e7f807f..29fe380 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,6 +38,7 @@
 #include "Hal.h"
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
 #include <aidl/android/hardware/graphics/composer3/Color.h>
@@ -64,15 +65,16 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
+using aidl::android::hardware::graphics::common::DisplayHotplugEvent;
 using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 
 // Implement this interface to receive hardware composer events.
 //
 // These callback functions will generally be called on a hwbinder thread, but
-// when first registering the callback the onComposerHalHotplug() function will
-// immediately be called on the thread calling registerCallback().
+// when first registering the callback the onComposerHalHotplugEvent() function
+// will immediately be called on the thread calling registerCallback().
 struct ComposerCallback {
-    virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0;
+    virtual void onComposerHalHotplugEvent(hal::HWDisplayId, DisplayHotplugEvent) = 0;
     virtual void onComposerHalRefresh(hal::HWDisplayId) = 0;
     virtual void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                                     std::optional<hal::VsyncPeriodNanos>) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 20f7548..31c2833 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,7 @@
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <aidl/android/hardware/graphics/common/Hdr.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
@@ -58,6 +59,7 @@
 using ContentType = IComposerClient::ContentType;
 using Capability = IComposer::Capability;
 using ClientTargetProperty = IComposerClient::ClientTargetProperty;
+using DisplayHotplugEvent = aidl::android::hardware::graphics::common::DisplayHotplugEvent;
 using DisplayRequest = IComposerClient::DisplayRequest;
 using DisplayType = IComposerClient::DisplayType;
 using HWConfigId = V2_1::Config;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 1e7c6da..ed52b95 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -25,6 +25,7 @@
 #include "HidlComposerHal.h"
 
 #include <SurfaceFlingerProperties.h>
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <android/binder_manager.h>
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
 #include <hidl/HidlTransportSupport.h>
@@ -38,6 +39,7 @@
 #include <algorithm>
 #include <cinttypes>
 
+using aidl::android::hardware::graphics::common::DisplayHotplugEvent;
 using aidl::android::hardware::graphics::common::HdrConversionCapability;
 using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using aidl::android::hardware::graphics::composer3::Capability;
@@ -64,8 +66,13 @@
     ComposerCallbackBridge(ComposerCallback& callback, bool vsyncSwitchingSupported)
           : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
 
+    // For code sharing purposes, `ComposerCallback` (implemented by SurfaceFlinger)
+    // replaced `onComposerHalHotplug` with `onComposerHalHotplugEvent` by converting
+    // from HIDL's connection into an AIDL DisplayHotplugEvent.
     Return<void> onHotplug(Display display, Connection connection) override {
-        mCallback.onComposerHalHotplug(display, connection);
+        const auto event = connection == Connection::CONNECTED ? DisplayHotplugEvent::CONNECTED
+                                                               : DisplayHotplugEvent::DISCONNECTED;
+        mCallback.onComposerHalHotplugEvent(display, event);
         return Void();
     }
 
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
index e55cd3e..55b395b 100644
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -18,41 +18,26 @@
 #include "DisplayDevice.h"
 
 namespace android {
-namespace {
-
-RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform,
-                                                 const DisplayDevice& display) {
-    if (!useIdentityTransform) {
-        return RenderArea::RotationFlags::ROT_0;
-    }
-
-    return ui::Transform::toRotationFlags(display.getOrientation());
-}
-
-} // namespace
 
 std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
                                                       const Rect& sourceCrop, ui::Size reqSize,
                                                       ui::Dataspace reqDataSpace,
-                                                      bool useIdentityTransform,
                                                       bool hintForSeamlessTransition,
                                                       bool allowSecureLayers) {
     if (auto display = displayWeak.promote()) {
         // Using new to access a private constructor.
         return std::unique_ptr<DisplayRenderArea>(
                 new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
-                                      useIdentityTransform, hintForSeamlessTransition,
-                                      allowSecureLayers));
+                                      hintForSeamlessTransition, allowSecureLayers));
     }
     return nullptr;
 }
 
 DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
                                      ui::Size reqSize, ui::Dataspace reqDataSpace,
-                                     bool useIdentityTransform, bool hintForSeamlessTransition,
-                                     bool allowSecureLayers)
+                                     bool hintForSeamlessTransition, bool allowSecureLayers)
       : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, hintForSeamlessTransition,
-                   allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
+                   allowSecureLayers),
         mDisplay(std::move(display)),
         mSourceCrop(sourceCrop) {}
 
@@ -73,17 +58,7 @@
     if (mSourceCrop.isEmpty()) {
         return mDisplay->getLayerStackSpaceRect();
     }
-
-    // Correct for the orientation when the screen capture request contained
-    // useIdentityTransform. This will cause the rotation flag to be non 0 since
-    // it needs to rotate based on the screen orientation to allow the screenshot
-    // to be taken in the ROT_0 orientation
-    const auto flags = getRotationFlags();
-    int width = mDisplay->getLayerStackSpaceRect().getWidth();
-    int height = mDisplay->getLayerStackSpaceRect().getHeight();
-    ui::Transform rotation;
-    rotation.set(flags, width, height);
-    return rotation.transform(mSourceCrop);
+    return mSourceCrop;
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
index 9a4981c..4555a9e 100644
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -29,7 +29,6 @@
 public:
     static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
                                               ui::Size reqSize, ui::Dataspace,
-                                              bool useIdentityTransform,
                                               bool hintForSeamlessTransition,
                                               bool allowSecureLayers = true);
 
@@ -40,8 +39,7 @@
 
 private:
     DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
-                      ui::Dataspace, bool useIdentityTransform, bool hintForSeamlessTransition,
-                      bool allowSecureLayers = true);
+                      ui::Dataspace, bool hintForSeamlessTransition, bool allowSecureLayers = true);
 
     const sp<const DisplayDevice> mDisplay;
     const Rect mSourceCrop;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 8f658d5..6db39f1 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -276,13 +276,11 @@
     }
 
     const Rect sampledBounds = sampleRegion.bounds();
-    constexpr bool kUseIdentityTransform = false;
     constexpr bool kHintForSeamlessTransition = false;
 
     SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
         return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
-                                         ui::Dataspace::V0_SRGB, kUseIdentityTransform,
-                                         kHintForSeamlessTransition);
+                                         ui::Dataspace::V0_SRGB, kHintForSeamlessTransition);
     });
 
     std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 71b85bd..5de148e 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -18,20 +18,16 @@
 // physical render area.
 class RenderArea {
 public:
-    using RotationFlags = ui::Transform::RotationFlags;
-
     enum class CaptureFill {CLEAR, OPAQUE};
 
     static float getCaptureFillValue(CaptureFill captureFill);
 
     RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
-               bool hintForSeamlessTransition, bool allowSecureLayers = false,
-               RotationFlags rotation = ui::Transform::ROT_0)
+               bool hintForSeamlessTransition, bool allowSecureLayers = false)
           : mAllowSecureLayers(allowSecureLayers),
             mReqSize(reqSize),
             mReqDataSpace(reqDataSpace),
             mCaptureFill(captureFill),
-            mRotationFlags(rotation),
             mHintForSeamlessTransition(hintForSeamlessTransition) {}
 
     static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
@@ -72,9 +68,6 @@
     // on the display).
     virtual Rect getSourceCrop() const = 0;
 
-    // Returns the rotation of the source crop and the layers.
-    RotationFlags getRotationFlags() const { return mRotationFlags; }
-
     // Returns the size of the physical render area.
     int getReqWidth() const { return mReqSize.width; }
     int getReqHeight() const { return mReqSize.height; }
@@ -103,7 +96,6 @@
     const ui::Size mReqSize;
     const ui::Dataspace mReqDataSpace;
     const CaptureFill mCaptureFill;
-    const RotationFlags mRotationFlags;
     const bool mHintForSeamlessTransition;
 };
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index d3ff236..97fca39 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -276,17 +276,16 @@
 
     if (const auto averageFrameTime = calculateAverageFrameTime()) {
         const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
-        const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
-        if (refreshRateConsistent) {
-            const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate);
+        const auto closestKnownRefreshRate = mRefreshRateHistory.add(refreshRate, now, selector);
+        if (closestKnownRefreshRate.isValid()) {
             using fps_approx_ops::operator!=;
 
             // To avoid oscillation, use the last calculated refresh rate if it is close enough.
             if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
                         MARGIN &&
-                mLastRefreshRate.reported != knownRefreshRate) {
+                mLastRefreshRate.reported != closestKnownRefreshRate) {
                 mLastRefreshRate.calculated = refreshRate;
-                mLastRefreshRate.reported = knownRefreshRate;
+                mLastRefreshRate.reported = closestKnownRefreshRate;
             }
 
             ALOGV("%s %s rounded to nearest known frame rate %s", mName.c_str(),
@@ -432,7 +431,8 @@
     mRefreshRates.clear();
 }
 
-bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) {
+Fps LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now,
+                                       const RefreshRateSelector& selector) {
     mRefreshRates.push_back({refreshRate, now});
     while (mRefreshRates.size() >= HISTORY_SIZE ||
            now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
@@ -447,11 +447,11 @@
         ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue());
     }
 
-    return isConsistent();
+    return selectRefreshRate(selector);
 }
 
-bool LayerInfo::RefreshRateHistory::isConsistent() const {
-    if (mRefreshRates.empty()) return true;
+Fps LayerInfo::RefreshRateHistory::selectRefreshRate(const RefreshRateSelector& selector) const {
+    if (mRefreshRates.empty()) return Fps();
 
     const auto [min, max] =
             std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
@@ -459,8 +459,19 @@
                                     return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
                                 });
 
-    const bool consistent =
-            max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;
+    const auto maxClosestRate = selector.findClosestKnownFrameRate(max->refreshRate);
+    const bool consistent = [&](Fps maxFps, Fps minFps) {
+        if (FlagManager::getInstance().use_known_refresh_rate_for_fps_consistency()) {
+            if (maxFps.getValue() - minFps.getValue() <
+                MARGIN_CONSISTENT_FPS_FOR_CLOSEST_REFRESH_RATE) {
+                const auto minClosestRate = selector.findClosestKnownFrameRate(minFps);
+                using fps_approx_ops::operator==;
+                return maxClosestRate == minClosestRate;
+            }
+            return false;
+        }
+        return maxFps.getValue() - minFps.getValue() < MARGIN_CONSISTENT_FPS;
+    }(max->refreshRate, min->refreshRate);
 
     if (CC_UNLIKELY(sTraceEnabled)) {
         if (!mHeuristicTraceTagData.has_value()) {
@@ -472,7 +483,7 @@
         ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
     }
 
-    return consistent;
+    return consistent ? maxClosestRate : Fps();
 }
 
 FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index c6ee69e..50bb83d 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -265,8 +265,8 @@
         // Clears History
         void clear();
 
-        // Adds a new refresh rate and returns true if it is consistent
-        bool add(Fps refreshRate, nsecs_t now);
+        // Adds a new refresh rate and returns valid refresh rate if it is consistent enough
+        Fps add(Fps refreshRate, nsecs_t now, const RefreshRateSelector&);
 
     private:
         friend class LayerHistoryTest;
@@ -286,13 +286,14 @@
             std::string average;
         };
 
-        bool isConsistent() const;
+        Fps selectRefreshRate(const RefreshRateSelector&) const;
         HeuristicTraceTagData makeHeuristicTraceTagData() const;
 
         const std::string mName;
         mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
         std::deque<RefreshRateData> mRefreshRates;
         static constexpr float MARGIN_CONSISTENT_FPS = 1.0;
+        static constexpr float MARGIN_CONSISTENT_FPS_FOR_CLOSEST_REFRESH_RATE = 5.0;
     };
 
     // Represents whether we were able to determine either layer is frequent or infrequent
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index af9fc70..6eea7f1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -510,8 +510,11 @@
     std::scoped_lock lock(mDisplayLock);
     ftl::FakeGuard guard(kMainThreadContext);
 
-    for (const auto& [id, _] : mDisplays) {
-        resyncToHardwareVsyncLocked(id, allowToEnable);
+    for (const auto& [id, display] : mDisplays) {
+        if (display.powerMode != hal::PowerMode::OFF ||
+            !FlagManager::getInstance().multithreaded_present()) {
+            resyncToHardwareVsyncLocked(id, allowToEnable);
+        }
     }
 }
 
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index ef9b457..57b0d5e 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -16,6 +16,7 @@
 
 #include "ScreenCaptureOutput.h"
 #include "ScreenCaptureRenderSurface.h"
+#include "ui/Rotation.h"
 
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/DisplayColorProfileCreationArgs.h>
@@ -24,24 +25,6 @@
 
 namespace android {
 
-namespace {
-
-ui::Size getDisplaySize(ui::Rotation orientation, const Rect& sourceCrop) {
-    if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) {
-        return {sourceCrop.getHeight(), sourceCrop.getWidth()};
-    }
-    return {sourceCrop.getWidth(), sourceCrop.getHeight()};
-}
-
-Rect getOrientedDisplaySpaceRect(ui::Rotation orientation, int reqWidth, int reqHeight) {
-    if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) {
-        return {reqHeight, reqWidth};
-    }
-    return {reqWidth, reqHeight};
-}
-
-} // namespace
-
 std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
     std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
             ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
@@ -62,11 +45,10 @@
                     .Build()));
 
     const Rect& sourceCrop = args.renderArea.getSourceCrop();
-    const ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags());
-    output->setDisplaySize(getDisplaySize(orientation, sourceCrop));
+    const ui::Rotation orientation = ui::ROTATION_0;
+    output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()});
     output->setProjection(orientation, sourceCrop,
-                          getOrientedDisplaySpaceRect(orientation, args.renderArea.getReqWidth(),
-                                                      args.renderArea.getReqHeight()));
+                          {args.renderArea.getReqWidth(), args.renderArea.getReqHeight()});
 
     {
         std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput";
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f219942..85c16b7 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2101,15 +2101,28 @@
     }
 }
 
-void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
-                                          hal::Connection connection) {
-    {
-        std::lock_guard<std::mutex> lock(mHotplugMutex);
-        mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
+void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId,
+                                               DisplayHotplugEvent event) {
+    if (event == DisplayHotplugEvent::CONNECTED || event == DisplayHotplugEvent::DISCONNECTED) {
+        hal::Connection connection = (event == DisplayHotplugEvent::CONNECTED)
+                ? hal::Connection::CONNECTED
+                : hal::Connection::DISCONNECTED;
+        {
+            std::lock_guard<std::mutex> lock(mHotplugMutex);
+            mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
+        }
+
+        if (mScheduler) {
+            mScheduler->scheduleConfigure();
+        }
+
+        return;
     }
 
-    if (mScheduler) {
-        mScheduler->scheduleConfigure();
+    if (FlagManager::getInstance().hotplug2()) {
+        ALOGD("SurfaceFlinger got hotplug event=%d", static_cast<int32_t>(event));
+        // TODO(b/311403559): use enum type instead of int
+        mScheduler->onHotplugConnectionError(mAppConnectionHandle, static_cast<int32_t>(event));
     }
 }
 
@@ -7057,7 +7070,7 @@
                 const hal::HWDisplayId hwcId =
                         (Mutex::Autolock(mStateLock), getHwComposer().getPrimaryHwcDisplayId());
 
-                onComposerHalHotplug(hwcId, hal::Connection::CONNECTED);
+                onComposerHalHotplugEvent(hwcId, DisplayHotplugEvent::CONNECTED);
                 return NO_ERROR;
             }
             // Modify the max number of display frames stored within FrameTimeline
@@ -7559,8 +7572,7 @@
 
     RenderAreaFuture renderAreaFuture = ftl::defer([=] {
         return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, args.dataspace,
-                                         args.useIdentityTransform, args.hintForSeamlessTransition,
-                                         args.captureSecureLayers);
+                                         args.hintForSeamlessTransition, args.captureSecureLayers);
     });
 
     GetLayerSnapshotsFunction getLayerSnapshots;
@@ -7613,7 +7625,6 @@
 
     RenderAreaFuture renderAreaFuture = ftl::defer([=] {
         return DisplayRenderArea::create(displayWeak, Rect(), size, args.dataspace,
-                                         false /* useIdentityTransform */,
                                          args.hintForSeamlessTransition,
                                          false /* captureSecureLayers */);
     });
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e90f8fe..788fe73 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -106,6 +106,7 @@
 #include <vector>
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
 #include "Client.h"
 
@@ -130,6 +131,7 @@
 class ScreenCapturer;
 class WindowInfosListenerInvoker;
 
+using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
 using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 using frontend::TransactionHandler;
 using gui::CaptureArgs;
@@ -629,7 +631,7 @@
     // HWC2::ComposerCallback overrides:
     void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                             std::optional<hal::VsyncPeriodNanos>) override;
-    void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override;
+    void onComposerHalHotplugEvent(hal::HWDisplayId, DisplayHotplugEvent) override;
     void onComposerHalRefresh(hal::HWDisplayId) override;
     void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
                                                const hal::VsyncPeriodChangeTimeline&) override;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index e2a1498..a582232 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -121,6 +121,8 @@
     DUMP_READ_ONLY_FLAG(hdcp_level_hal);
     DUMP_READ_ONLY_FLAG(multithreaded_present);
     DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace);
+    DUMP_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency);
+    DUMP_READ_ONLY_FLAG(cache_if_source_crop_layer_only_moved);
 
 #undef DUMP_READ_ONLY_FLAG
 #undef DUMP_SERVER_FLAG
@@ -190,6 +192,9 @@
 FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "")
 FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
 FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "")
+FLAG_MANAGER_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency, "")
+FLAG_MANAGER_READ_ONLY_FLAG(cache_if_source_crop_layer_only_moved,
+                            "debug.sf.cache_source_crop_only_moved")
 
 /// Trunk stable server flags ///
 FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 9aabbb9..15ca345 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -60,6 +60,8 @@
     bool hdcp_level_hal() const;
     bool multithreaded_present() const;
     bool add_sf_skipped_frames_to_trace() const;
+    bool use_known_refresh_rate_for_fps_consistency() const;
+    bool cache_if_source_crop_layer_only_moved() const;
 
 protected:
     // overridden for unit tests
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
index 1a951b3..b2dc20e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
@@ -41,6 +41,7 @@
 
 namespace android::hardware::graphics::composer::hal {
 
+using aidl::android::hardware::graphics::common::DisplayHotplugEvent;
 using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -52,7 +53,9 @@
           : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
 
     Return<void> onHotplug(HWDisplayId display, Connection connection) override {
-        mCallback->onComposerHalHotplug(display, connection);
+        const auto event = connection == Connection::CONNECTED ? DisplayHotplugEvent::CONNECTED
+                                                               : DisplayHotplugEvent::DISCONNECTED;
+        mCallback->onComposerHalHotplugEvent(display, event);
         return Void();
     }
 
@@ -94,7 +97,7 @@
 
 struct TestHWC2ComposerCallback : public HWC2::ComposerCallback {
     virtual ~TestHWC2ComposerCallback() = default;
-    void onComposerHalHotplug(HWDisplayId, Connection){};
+    void onComposerHalHotplugEvent(HWDisplayId, DisplayHotplugEvent) {}
     void onComposerHalRefresh(HWDisplayId) {}
     void onComposerHalVsync(HWDisplayId, int64_t, std::optional<VsyncPeriodNanos>) {}
     void onComposerHalVsyncPeriodTimingChanged(HWDisplayId, const VsyncPeriodChangeTimeline&) {}
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 71c59b2..3fb763e 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -84,3 +84,19 @@
   description: "enable refresh rate indicator on the external display"
   bug: "301647974"
 }
+
+flag {
+  name: "use_known_refresh_rate_for_fps_consistency"
+  namespace: "core_graphics"
+  description: "Whether to use the closest known refresh rate to determine the fps consistency."
+  bug: "299201319"
+  is_fixed_read_only: true
+}
+
+flag {
+  name: "cache_if_source_crop_layer_only_moved"
+  namespace: "core_graphics"
+  description: "do not flatten layers if source crop is only moved"
+  bug: "305718400"
+  is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 0a951d4..aeff5a5 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -56,60 +56,44 @@
         ASSERT_EQ(res, NO_ERROR);
     }
 
-    void testSetAllowGroupSwitching(bool allowGroupSwitching);
+    void testSetDesiredDisplayModeSpecs(bool allowGroupSwitching = false) {
+        ui::DynamicDisplayInfo info;
+        status_t res =
+                SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
+                                                                   &info);
+        const auto& modes = info.supportedDisplayModes;
+        ASSERT_EQ(res, NO_ERROR);
+        ASSERT_GT(modes.size(), 0);
+        for (const auto& mode : modes) {
+            gui::DisplayModeSpecs setSpecs;
+            setSpecs.defaultMode = mode.id;
+            setSpecs.allowGroupSwitching = allowGroupSwitching;
+            setSpecs.primaryRanges.physical.min = mode.peakRefreshRate;
+            setSpecs.primaryRanges.physical.max = mode.peakRefreshRate;
+            setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
+            setSpecs.appRequestRanges = setSpecs.primaryRanges;
+
+            res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
+            ASSERT_EQ(res, NO_ERROR);
+            gui::DisplayModeSpecs getSpecs;
+            res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
+            ASSERT_EQ(res, NO_ERROR);
+            ASSERT_EQ(setSpecs, getSpecs);
+        }
+    }
 
     sp<IBinder> mDisplayToken;
     uint64_t mDisplayId;
 };
 
 TEST_F(RefreshRateRangeTest, setAllConfigs) {
-    ui::DynamicDisplayInfo info;
-    status_t res =
-            SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
-                                                               &info);
-    const auto& modes = info.supportedDisplayModes;
-    ASSERT_EQ(res, NO_ERROR);
-    ASSERT_GT(modes.size(), 0);
-
-    gui::DisplayModeSpecs setSpecs;
-    setSpecs.allowGroupSwitching = false;
-    for (size_t i = 0; i < modes.size(); i++) {
-        setSpecs.defaultMode = modes[i].id;
-        setSpecs.primaryRanges.physical.min = modes[i].peakRefreshRate;
-        setSpecs.primaryRanges.physical.max = modes[i].peakRefreshRate;
-        setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
-        setSpecs.appRequestRanges = setSpecs.primaryRanges;
-        res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
-        ASSERT_EQ(res, NO_ERROR);
-
-        gui::DisplayModeSpecs getSpecs;
-        res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
-        ASSERT_EQ(res, NO_ERROR);
-        ASSERT_EQ(setSpecs, getSpecs);
-    }
-}
-
-void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
-    gui::DisplayModeSpecs setSpecs;
-    setSpecs.defaultMode = 0;
-    setSpecs.allowGroupSwitching = allowGroupSwitching;
-    setSpecs.primaryRanges.physical.min = 0;
-    setSpecs.primaryRanges.physical.max = 90;
-    setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
-    setSpecs.appRequestRanges = setSpecs.primaryRanges;
-
-    status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
-    ASSERT_EQ(res, NO_ERROR);
-    gui::DisplayModeSpecs getSpecs;
-    res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
-    ASSERT_EQ(res, NO_ERROR);
-    ASSERT_EQ(setSpecs, getSpecs);
+    testSetDesiredDisplayModeSpecs();
 }
 
 TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) {
-    testSetAllowGroupSwitching(true);
-    testSetAllowGroupSwitching(false);
-    testSetAllowGroupSwitching(true);
+    testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/true);
+    testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/false);
+    testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/true);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index 2181370..15a98df 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -38,7 +38,6 @@
     args.displayToken = sp<BBinder>::make();
     args.width = 10;
     args.height = 20;
-    args.useIdentityTransform = true;
     args.grayscale = true;
 
     Parcel p;
@@ -56,7 +55,6 @@
     ASSERT_EQ(args.displayToken, args2.displayToken);
     ASSERT_EQ(args.width, args2.width);
     ASSERT_EQ(args.height, args2.height);
-    ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform);
     ASSERT_EQ(args.grayscale, args2.grayscale);
 }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 3873b0c..bd80d88 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -43,7 +43,8 @@
         PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
         PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
 
-        mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+        mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+                                           DisplayHotplugEvent::CONNECTED);
         mFlinger.configureAndCommit();
 
         mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 4a22731..d3ce4f2 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -57,6 +57,7 @@
 
 using Hwc2::Config;
 
+using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
 using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 using hal::IComposerClient;
 using ::testing::_;
@@ -555,7 +556,8 @@
 }
 
 struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
-    MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection));
+    MOCK_METHOD(void, onComposerHalHotplugEvent, (hal::HWDisplayId, DisplayHotplugEvent),
+                (override));
     MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId));
     MOCK_METHOD3(onComposerHalVsync,
                  void(hal::HWDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos>));
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index c1059d7..787fa1c 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -768,6 +768,7 @@
 }
 
 TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating) {
+    SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false);
     auto layer = createLegacyAndFrontedEndLayer(1);
 
     nsecs_t time = systemTime();
@@ -779,6 +780,20 @@
     recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
 }
 
+TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating_useKnownRefreshRate) {
+    SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true);
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
 TEST_F(LayerHistoryIntegrationTest, smallDirtyLayer) {
     auto layer = createLegacyAndFrontedEndLayer(1);
 
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index c24d397..b88ef56 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -1169,6 +1169,8 @@
 }
 
 TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
+    SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false);
+
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
@@ -1182,6 +1184,23 @@
     recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
 }
 
+TEST_F(LayerHistoryTest, heuristicLayerNotOscillating_useKnownRefreshRate) {
+    SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true);
+
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
 TEST_F(LayerHistoryTest, smallDirtyLayer) {
     auto layer = createLayer();
 
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 4c6539e..e515895 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -608,6 +608,85 @@
                                              TimePoint::fromNs(4500)));
 }
 
+TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) {
+    // resyncAllToHardwareVsync will result in requesting hardware VSYNC on both displays, since
+    // they are both on.
+    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
+    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1);
+
+    mScheduler->registerDisplay(kDisplayId2,
+                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+                                                                      kDisplay2Mode60->getId()));
+    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+    mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON);
+
+    static constexpr bool kDisallow = true;
+    mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
+    mScheduler->disableHardwareVsync(kDisplayId2, kDisallow);
+
+    static constexpr bool kAllowToEnable = true;
+    mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
+}
+
+TEST_F(SchedulerTest, resyncAllDoNotAllow) FTL_FAKE_GUARD(kMainThreadContext) {
+    // Without setting allowToEnable to true, resyncAllToHardwareVsync does not
+    // result in requesting hardware VSYNC.
+    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, _)).Times(0);
+
+    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+
+    static constexpr bool kDisallow = true;
+    mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
+
+    static constexpr bool kAllowToEnable = false;
+    mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
+}
+
+TEST_F(SchedulerTest, resyncAllSkipsOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) {
+    SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+
+    // resyncAllToHardwareVsync will result in requesting hardware VSYNC on display 1, which is on,
+    // but not on display 2, which is off.
+    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
+    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, _)).Times(0);
+
+    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+
+    mScheduler->registerDisplay(kDisplayId2,
+                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+                                                                      kDisplay2Mode60->getId()));
+    ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2));
+
+    static constexpr bool kDisallow = true;
+    mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
+    mScheduler->disableHardwareVsync(kDisplayId2, kDisallow);
+
+    static constexpr bool kAllowToEnable = true;
+    mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
+}
+
+TEST_F(SchedulerTest, resyncAllLegacyAppliesToOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) {
+    SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
+
+    // In the legacy code, prior to the flag, resync applied to OFF displays.
+    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
+    EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1);
+
+    mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+
+    mScheduler->registerDisplay(kDisplayId2,
+                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+                                                                      kDisplay2Mode60->getId()));
+    ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2));
+
+    static constexpr bool kDisallow = true;
+    mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
+    mScheduler->disableHardwareVsync(kDisplayId2, kDisallow);
+
+    static constexpr bool kAllowToEnable = true;
+    mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
+}
+
 class AttachedChoreographerTest : public SchedulerTest {
 protected:
     void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 3558ba6..01c0a96 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -46,7 +46,8 @@
 
         setupScheduler(selectorPtr);
 
-        mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+        mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+                                           DisplayHotplugEvent::CONNECTED);
         mFlinger.configureAndCommit();
 
         auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 1210d0b..a270dc9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -27,10 +27,10 @@
     EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(2);
 
     constexpr HWDisplayId hwcDisplayId1 = 456;
-    mFlinger.onComposerHalHotplug(hwcDisplayId1, Connection::CONNECTED);
+    mFlinger.onComposerHalHotplugEvent(hwcDisplayId1, DisplayHotplugEvent::CONNECTED);
 
     constexpr HWDisplayId hwcDisplayId2 = 654;
-    mFlinger.onComposerHalHotplug(hwcDisplayId2, Connection::DISCONNECTED);
+    mFlinger.onComposerHalHotplugEvent(hwcDisplayId2, DisplayHotplugEvent::DISCONNECTED);
 
     const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
     ASSERT_EQ(2u, pendingEvents.size());
@@ -45,7 +45,7 @@
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     constexpr HWDisplayId displayId1 = 456;
-    mFlinger.onComposerHalHotplug(displayId1, Connection::DISCONNECTED);
+    mFlinger.onComposerHalHotplugEvent(displayId1, DisplayHotplugEvent::DISCONNECTED);
     mFlinger.configure();
 
     // The configure stage should consume the hotplug queue and produce a display transaction.
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index b036e99..2a1b88e 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -115,6 +115,15 @@
         Scheduler::setPacesetterDisplay(displayId);
     }
 
+    std::optional<hal::PowerMode> getDisplayPowerMode(PhysicalDisplayId id) {
+        ftl::FakeGuard guard1(kMainThreadContext);
+        ftl::FakeGuard guard2(mDisplayLock);
+        return mDisplays.get(id).transform(
+                [](const Display& display) { return display.powerMode; });
+    }
+
+    using Scheduler::resyncAllToHardwareVsync;
+
     auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
     auto& mutableLayerHistory() { return mLayerHistory; }
     auto& mutableAttachedChoreographers() { return mAttachedChoreographers; }
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index cf48c76..0909178 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -466,8 +466,8 @@
         mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
-    void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
-        mFlinger->onComposerHalHotplug(hwcDisplayId, connection);
+    void onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, DisplayHotplugEvent event) {
+        mFlinger->onComposerHalHotplugEvent(hwcDisplayId, event);
     }
 
     auto setDisplayStateLocked(const DisplayState& s) {