Merge "binder: recovery variant does not use libandroid_runtime_vm_headers" am: fc34af194b am: 4da1edd272 am: fd7787fe2a am: 1639fde0a7

Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1878016

Change-Id: I02ea43fca2a3c0df7b906cbf45fdcb32cf88cdd6
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 03a2709..a177027 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -101,6 +101,10 @@
     required uint32 layer_stack = 1;
 }
 
+message DisplayFlagsChange {
+    required uint32 flags = 1;
+}
+
 message HiddenFlagChange {
     required bool hidden_flag = 1;
 }
@@ -121,6 +125,7 @@
         LayerStackChange  layer_stack = 3;
         SizeChange        size        = 4;
         ProjectionChange  projection  = 5;
+        DisplayFlagsChange flags      = 6;
     }
 }
 
@@ -217,4 +222,4 @@
 message Origin {
     required int32 pid = 1;
     required int32 uid = 2;
-}
\ No newline at end of file
+}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 5fe4ea1..1e386b4 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -237,3 +237,11 @@
     src: "handheld_core_hardware.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
 }
+
+prebuilt_etc {
+    name: "android.software.app_compat_overrides.xml",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "android.software.app_compat_overrides.xml",
+    filename_from_src: true,
+}
diff --git a/data/etc/android.software.app_compat_overrides.xml b/data/etc/android.software.app_compat_overrides.xml
new file mode 100644
index 0000000..2f9726a
--- /dev/null
+++ b/data/etc/android.software.app_compat_overrides.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Feature for devices that are opted-in to receive per-app compatibility
+     overrides. -->
+<permissions>
+  <feature name="android.software.app_compat_overrides" />
+</permissions>
diff --git a/include/input/Flags.h b/include/ftl/Flags.h
similarity index 98%
rename from include/input/Flags.h
rename to include/ftl/Flags.h
index b12a9ed..27c8476 100644
--- a/include/input/Flags.h
+++ b/include/ftl/Flags.h
@@ -22,11 +22,10 @@
 #include <string>
 #include <type_traits>
 
-#include "NamedEnum.h"
+#include <ftl/NamedEnum.h>
 #include "utils/BitSet.h"
 
-#ifndef __UI_INPUT_FLAGS_H
-#define __UI_INPUT_FLAGS_H
+#pragma once
 
 namespace android {
 
@@ -279,5 +278,3 @@
 
 } // namespace flag_operators
 } // namespace android
-
-#endif // __UI_INPUT_FLAGS_H
diff --git a/include/input/NamedEnum.h b/include/ftl/NamedEnum.h
similarity index 97%
rename from include/input/NamedEnum.h
rename to include/ftl/NamedEnum.h
index 8d3f6b7..6e98fee 100644
--- a/include/input/NamedEnum.h
+++ b/include/ftl/NamedEnum.h
@@ -21,8 +21,7 @@
 #include <optional>
 #include <string>
 
-#ifndef __UI_INPUT_NAMEDENUM_H
-#define __UI_INPUT_NAMEDENUM_H
+#pragma once
 
 namespace android {
 
@@ -128,5 +127,3 @@
 };
 
 } // namespace android
-
-#endif // __UI_INPUT_NAMEDENUM_H
\ No newline at end of file
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 5e40ca7..a6213f3 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -18,8 +18,9 @@
 #define _LIBINPUT_DISPLAY_VIEWPORT_H
 
 #include <android-base/stringprintf.h>
+#include <ftl/NamedEnum.h>
+#include <gui/constants.h>
 #include <input/Input.h>
-#include <input/NamedEnum.h>
 
 #include <cinttypes>
 #include <optional>
diff --git a/include/input/Input.h b/include/input/Input.h
index dce6ccb..4adaa5b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -124,15 +124,6 @@
 constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20;
 
 enum {
-    /* Used when a motion event is not associated with any display.
-     * Typically used for non-pointer events. */
-    ADISPLAY_ID_NONE = -1,
-
-    /* The default display id. */
-    ADISPLAY_ID_DEFAULT = 0,
-};
-
-enum {
     /*
      * Indicates that an input device has switches.
      * This input source flag is hidden from the API because switches are only used by the system
@@ -354,12 +345,6 @@
  */
 constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
 
-/**
- * Invalid value for display size. Used when display size isn't available for an event or doesn't
- * matter. This is just a constant 0 so that it has no effect if unused.
- */
-constexpr int32_t AMOTION_EVENT_INVALID_DISPLAY_SIZE = 0;
-
 /*
  * Pointer coordinate data.
  */
@@ -592,6 +577,8 @@
 
     void setCursorPosition(float x, float y);
 
+    uint32_t getDisplayOrientation() const { return mDisplayOrientation; }
+
     int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; }
 
     static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
@@ -768,8 +755,8 @@
                     int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
                     MotionClassification classification, const ui::Transform& transform,
                     float xPrecision, float yPrecision, float rawXCursorPosition,
-                    float rawYCursorPosition, int32_t displayWidth, int32_t displayHeight,
-                    nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+                    float rawYCursorPosition, uint32_t displayOrientation, int32_t displayWidth,
+                    int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
                     const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
 
     void copyFrom(const MotionEvent* other, bool keepHistory);
@@ -827,6 +814,7 @@
     float mYPrecision;
     float mRawXCursorPosition;
     float mRawYCursorPosition;
+    uint32_t mDisplayOrientation;
     int32_t mDisplayWidth;
     int32_t mDisplayHeight;
     nsecs_t mDownTime;
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 360dfbf..a790b56 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -136,10 +136,10 @@
             float yPrecision;
             float xCursorPosition;
             float yCursorPosition;
+            uint32_t displayOrientation;
             int32_t displayWidth;
             int32_t displayHeight;
             uint32_t pointerCount;
-            uint32_t empty3;
             /**
              * The "pointers" field must be the last field of the struct InputMessage.
              * When we send the struct InputMessage across the socket, we are not
@@ -355,8 +355,9 @@
                                 int32_t metaState, int32_t buttonState,
                                 MotionClassification classification, const ui::Transform& transform,
                                 float xPrecision, float yPrecision, float xCursorPosition,
-                                float yCursorPosition, int32_t displayWidth, int32_t displayHeight,
-                                nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+                                float yCursorPosition, uint32_t displayOrientation,
+                                int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
+                                nsecs_t eventTime, uint32_t pointerCount,
                                 const PointerProperties* pointerProperties,
                                 const PointerCoords* pointerCoords);
 
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 97626be..2524c5f 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -14,7 +14,9 @@
         address: true,
     },
     srcs: [
+        "Flags_test.cpp",
         "future_test.cpp",
+        "NamedEnum_test.cpp",
         "small_map_test.cpp",
         "small_vector_test.cpp",
         "static_vector_test.cpp",
@@ -25,4 +27,12 @@
         "-Wextra",
         "-Wpedantic",
     ],
+
+    header_libs: [
+        "libbase_headers",
+    ],
+
+    shared_libs: [
+        "libbase",
+    ],
 }
diff --git a/libs/input/tests/Flags_test.cpp b/libs/ftl/Flags_test.cpp
similarity index 99%
rename from libs/input/tests/Flags_test.cpp
rename to libs/ftl/Flags_test.cpp
index 6de030f..8c00b52 100644
--- a/libs/input/tests/Flags_test.cpp
+++ b/libs/ftl/Flags_test.cpp
@@ -15,7 +15,7 @@
  */
 
 #include <gtest/gtest.h>
-#include <input/Flags.h>
+#include <ftl/Flags.h>
 
 #include <type_traits>
 
diff --git a/libs/input/tests/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp
similarity index 98%
rename from libs/input/tests/NamedEnum_test.cpp
rename to libs/ftl/NamedEnum_test.cpp
index 74a0044..dff2b8a 100644
--- a/libs/input/tests/NamedEnum_test.cpp
+++ b/libs/ftl/NamedEnum_test.cpp
@@ -15,7 +15,7 @@
  */
 
 #include <gtest/gtest.h>
-#include <input/NamedEnum.h>
+#include <ftl/NamedEnum.h>
 
 namespace android {
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 3190038..514988b 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -51,6 +51,60 @@
     ],
 }
 
+// AIDL files that should be exposed to java
+filegroup {
+    name: "guiconstants_aidl",
+    srcs: [
+        "android/**/DropInputMode.aidl",
+        "android/**/TouchOcclusionMode.aidl",
+    ],
+}
+
+cc_library_static {
+    name: "libgui_window_info_static",
+    vendor_available: true,
+    host_supported: true,
+    srcs: [
+        ":guiconstants_aidl",
+        "android/gui/FocusRequest.aidl",
+        "android/gui/InputApplicationInfo.aidl",
+        "android/gui/IWindowInfosListener.aidl",
+        "android/gui/IWindowInfosReportedListener.aidl",
+        "android/gui/WindowInfo.aidl",
+        "WindowInfo.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "libbinder",
+    ],
+
+    static_libs: [
+        "libui-types",
+    ],
+
+    aidl: {
+        export_aidl_headers: true
+    },
+
+    include_dirs: [
+        "frameworks/native/include",
+    ],
+
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
 filegroup {
     name: "libgui_aidl",
     srcs: ["aidl/**/*.aidl"],
@@ -77,12 +131,15 @@
         "libbinder",
     ],
 
+    static_libs: [
+        "libui-types",
+    ],
+
     aidl: {
         export_aidl_headers: true
     }
 }
 
-
 cc_library_shared {
     name: "libgui",
     vendor_available: true,
@@ -96,9 +153,11 @@
 
     static_libs: [
         "libgui_aidl_static",
+        "libgui_window_info_static",
     ],
     export_static_lib_headers: [
         "libgui_aidl_static",
+        "libgui_window_info_static",
     ],
 
     srcs: [
@@ -139,6 +198,7 @@
         "SyncFeatures.cpp",
         "TransactionTracing.cpp",
         "view/Surface.cpp",
+        "WindowInfosListenerReporter.cpp",
         "bufferqueue/1.0/B2HProducerListener.cpp",
         "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
         "bufferqueue/2.0/B2HProducerListener.cpp",
@@ -149,13 +209,11 @@
         "libbinder",
         "libbufferhub",
         "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
-        "libinput",
         "libpdx_default_transport",
     ],
 
     export_shared_lib_headers: [
         "libbinder",
-        "libinput",
     ],
 
     export_header_lib_headers: [
@@ -167,7 +225,6 @@
         vendor: {
             cflags: [
                 "-DNO_BUFFERHUB",
-                "-DNO_INPUT",
             ],
             exclude_srcs: [
                 "BufferHubConsumer.cpp",
@@ -176,7 +233,6 @@
             exclude_shared_libs: [
                 "libbufferhub",
                 "libbufferhubqueue",
-                "libinput",
                 "libpdx_default_transport",
             ],
         },
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 94e1ae1..e8e664b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -38,7 +38,7 @@
 using namespace std::chrono_literals;
 
 namespace {
-inline const char* toString(bool b) {
+inline const char* boolToString(bool b) {
     return b ? "true" : "false";
 }
 } // namespace
@@ -456,10 +456,10 @@
     // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
     incStrong((void*)transactionCallbackThunk);
 
+    const bool sizeHasChanged = mRequestedSize != mSize;
+    mSize = mRequestedSize;
+    const bool updateDestinationFrame = sizeHasChanged || !mLastBufferInfo.hasBuffer;
     Rect crop = computeCrop(bufferItem);
-    const bool updateDestinationFrame =
-            bufferItem.mScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE ||
-            !mLastBufferInfo.hasBuffer;
     mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
                            bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
                            bufferItem.mScalingMode, crop);
@@ -530,7 +530,7 @@
     BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
              " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
              " graphicBufferId=%" PRIu64 "%s transform=%d",
-             mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
+             mSize.width, mSize.height, bufferItem.mFrameNumber, boolToString(applyTransaction),
              bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
              static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(),
              bufferItem.mAutoRefresh ? " mAutoRefresh" : "", bufferItem.mTransform);
@@ -560,7 +560,7 @@
                mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
 
     BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
-             toString(nextTransactionSet));
+             boolToString(nextTransactionSet));
     processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
 }
 
@@ -586,7 +586,6 @@
 
 bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
     if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
-        mSize = mRequestedSize;
         // Only reject buffers if scaling mode is freeze.
         return false;
     }
@@ -600,7 +599,6 @@
     }
     ui::Size bufferSize(bufWidth, bufHeight);
     if (mRequestedSize != mSize && mRequestedSize == bufferSize) {
-        mSize = mRequestedSize;
         return false;
     }
 
@@ -738,14 +736,26 @@
         std::unique_lock<std::mutex> lock(mMutex);
         while (!mDone) {
             while (!mRunnables.empty()) {
-                std::function<void()> runnable = mRunnables.front();
-                mRunnables.pop_front();
-                runnable();
+                std::deque<std::function<void()>> runnables = std::move(mRunnables);
+                mRunnables.clear();
+                lock.unlock();
+                // Run outside the lock since the runnable might trigger another
+                // post to the async worker.
+                execute(runnables);
+                lock.lock();
             }
             mCv.wait(lock);
         }
     }
 
+    void execute(std::deque<std::function<void()>>& runnables) {
+        while (!runnables.empty()) {
+            std::function<void()> runnable = runnables.front();
+            runnables.pop_front();
+            runnable();
+        }
+    }
+
 public:
     AsyncWorker() : Singleton<AsyncWorker>() { mThread = std::thread(&AsyncWorker::run, this); }
 
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 5023b6b..2930154 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -34,7 +34,6 @@
 #include <gui/BufferQueueCore.h>
 #include <gui/IConsumerListener.h>
 #include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
 #include <private/gui/ComposerService.h>
 
 #include <system/window.h>
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0d7795e..1726761 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -44,6 +44,7 @@
 
 namespace android {
 
+using gui::IWindowInfosListener;
 using ui::ColorMode;
 
 class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
@@ -291,6 +292,17 @@
         return {};
     }
 
+    status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId* displayId) const override {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(remote()->transact, BnSurfaceComposer::GET_PRIMARY_PHYSICAL_DISPLAY_ID, data,
+                    &reply);
+        uint64_t rawId;
+        SAFE_PARCEL(reply.readUint64, &rawId);
+        *displayId = PhysicalDisplayId(rawId);
+        return NO_ERROR;
+    }
+
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1227,6 +1239,22 @@
 
         return reply.readInt32(buffers);
     }
+
+    status_t addWindowInfosListener(
+            const sp<IWindowInfosListener>& windowInfosListener) const override {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener));
+        return remote()->transact(BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER, data, &reply);
+    }
+
+    status_t removeWindowInfosListener(
+            const sp<IWindowInfosListener>& windowInfosListener) const override {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(windowInfosListener));
+        return remote()->transact(BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER, data, &reply);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1713,6 +1741,16 @@
                            [](PhysicalDisplayId id) { return id.value; });
             return reply->writeUint64Vector(rawIds);
         }
+        case GET_PRIMARY_PHYSICAL_DISPLAY_ID: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            PhysicalDisplayId id;
+            status_t result = getPrimaryPhysicalDisplayId(&id);
+            if (result != NO_ERROR) {
+                ALOGE("getPrimaryPhysicalDisplayId: Failed to get id");
+                return result;
+            }
+            return reply->writeUint64(id.value);
+        }
         case ADD_REGION_SAMPLING_LISTENER: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             Rect samplingArea;
@@ -2107,6 +2145,20 @@
             SAFE_PARCEL(reply->writeBool, success);
             return err;
         }
+        case ADD_WINDOW_INFOS_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IWindowInfosListener> listener;
+            SAFE_PARCEL(data.readStrongBinder, &listener);
+
+            return addWindowInfosListener(listener);
+        }
+        case REMOVE_WINDOW_INFOS_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IWindowInfosListener> listener;
+            SAFE_PARCEL(data.readStrongBinder, &listener);
+
+            return removeWindowInfosListener(listener);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 076c90d..54735fa 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -31,6 +31,9 @@
 
 namespace android {
 
+using gui::FocusRequest;
+using gui::WindowInfoHandle;
+
 layer_state_t::layer_state_t()
       : what(0),
         x(0),
@@ -67,7 +70,8 @@
         isTrustedOverlay(false),
         bufferCrop(Rect::INVALID_RECT),
         destinationFrame(Rect::INVALID_RECT),
-        releaseBufferListener(nullptr) {
+        releaseBufferListener(nullptr),
+        dropInputMode(gui::DropInputMode::NONE) {
     matrix.dsdx = matrix.dtdy = 1.0f;
     matrix.dsdy = matrix.dtdx = 0.0f;
     hdrMetadata.validTypes = 0;
@@ -95,9 +99,7 @@
     SAFE_PARCEL(output.writeFloat, color.r);
     SAFE_PARCEL(output.writeFloat, color.g);
     SAFE_PARCEL(output.writeFloat, color.b);
-#ifndef NO_INPUT
-    SAFE_PARCEL(inputHandle->writeToParcel, &output);
-#endif
+    SAFE_PARCEL(windowInfoHandle->writeToParcel, &output);
     SAFE_PARCEL(output.write, transparentRegion);
     SAFE_PARCEL(output.writeUint32, transform);
     SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
@@ -173,6 +175,8 @@
     SAFE_PARCEL(output.write, destinationFrame);
     SAFE_PARCEL(output.writeBool, isTrustedOverlay);
 
+    SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint);
+    SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode));
     return NO_ERROR;
 }
 
@@ -207,9 +211,7 @@
     color.g = tmpFloat;
     SAFE_PARCEL(input.readFloat, &tmpFloat);
     color.b = tmpFloat;
-#ifndef NO_INPUT
-    SAFE_PARCEL(inputHandle->readFromParcel, &input);
-#endif
+    SAFE_PARCEL(windowInfoHandle->readFromParcel, &input);
 
     SAFE_PARCEL(input.read, transparentRegion);
     SAFE_PARCEL(input.readUint32, &transform);
@@ -304,6 +306,11 @@
     SAFE_PARCEL(input.read, destinationFrame);
     SAFE_PARCEL(input.readBool, &isTrustedOverlay);
 
+    SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint);
+
+    uint32_t mode;
+    SAFE_PARCEL(input.readUint32, &mode);
+    dropInputMode = static_cast<gui::DropInputMode>(mode);
     return NO_ERROR;
 }
 
@@ -318,6 +325,7 @@
 DisplayState::DisplayState()
       : what(0),
         layerStack(0),
+        flags(0),
         layerStackSpaceRect(Rect::EMPTY_RECT),
         orientedDisplaySpaceRect(Rect::EMPTY_RECT),
         width(0),
@@ -328,6 +336,7 @@
     SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));
     SAFE_PARCEL(output.writeUint32, what);
     SAFE_PARCEL(output.writeUint32, layerStack);
+    SAFE_PARCEL(output.writeUint32, flags);
     SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));
     SAFE_PARCEL(output.write, layerStackSpaceRect);
     SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
@@ -344,6 +353,7 @@
 
     SAFE_PARCEL(input.readUint32, &what);
     SAFE_PARCEL(input.readUint32, &layerStack);
+    SAFE_PARCEL(input.readUint32, &flags);
     uint32_t tmpUint = 0;
     SAFE_PARCEL(input.readUint32, &tmpUint);
     orientation = ui::toRotation(tmpUint);
@@ -364,6 +374,10 @@
         what |= eLayerStackChanged;
         layerStack = other.layerStack;
     }
+    if (other.what & eFlagsChanged) {
+        what |= eFlagsChanged;
+        flags = other.flags;
+    }
     if (other.what & eDisplayProjectionChanged) {
         what |= eDisplayProjectionChanged;
         orientation = other.orientation;
@@ -455,6 +469,7 @@
     if (other.what & eBufferChanged) {
         what |= eBufferChanged;
         buffer = other.buffer;
+        releaseBufferEndpoint = other.releaseBufferEndpoint;
     }
     if (other.what & eAcquireFenceChanged) {
         what |= eAcquireFenceChanged;
@@ -487,14 +502,10 @@
     if (other.what & eHasListenerCallbacksChanged) {
         what |= eHasListenerCallbacksChanged;
     }
-
-#ifndef NO_INPUT
     if (other.what & eInputInfoChanged) {
         what |= eInputInfoChanged;
-        inputHandle = new InputWindowHandle(*other.inputHandle);
+        windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle);
     }
-#endif
-
     if (other.what & eCachedBufferChanged) {
         what |= eCachedBufferChanged;
         cachedBuffer = other.cachedBuffer;
@@ -558,6 +569,10 @@
         what |= eDestinationFrameChanged;
         destinationFrame = other.destinationFrame;
     }
+    if (other.what & eDropInputModeChanged) {
+        what |= eDropInputModeChanged;
+        dropInputMode = other.dropInputMode;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
@@ -593,11 +608,9 @@
 
 bool InputWindowCommands::merge(const InputWindowCommands& other) {
     bool changes = false;
-#ifndef NO_INPUT
     changes |= !other.focusRequests.empty();
     focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()),
                          std::make_move_iterator(other.focusRequests.end()));
-#endif
     changes |= other.syncInputWindows && !syncInputWindows;
     syncInputWindows |= other.syncInputWindows;
     return changes;
@@ -605,31 +618,23 @@
 
 bool InputWindowCommands::empty() const {
     bool empty = true;
-#ifndef NO_INPUT
     empty = focusRequests.empty() && !syncInputWindows;
-#endif
     return empty;
 }
 
 void InputWindowCommands::clear() {
-#ifndef NO_INPUT
     focusRequests.clear();
-#endif
     syncInputWindows = false;
 }
 
 status_t InputWindowCommands::write(Parcel& output) const {
-#ifndef NO_INPUT
     SAFE_PARCEL(output.writeParcelableVector, focusRequests);
-#endif
     SAFE_PARCEL(output.writeBool, syncInputWindows);
     return NO_ERROR;
 }
 
 status_t InputWindowCommands::read(const Parcel& input) {
-#ifndef NO_INPUT
     SAFE_PARCEL(input.readParcelableVector, &focusRequests);
-#endif
     SAFE_PARCEL(input.readBool, &syncInputWindows);
     return NO_ERROR;
 }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 96da8ef..922bceb 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <android/gui/IWindowInfosListener.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/SortedVector.h>
@@ -39,14 +40,11 @@
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <gui/WindowInfo.h>
 #include <private/gui/ParcelUtils.h>
 #include <ui/DisplayMode.h>
 #include <ui/DynamicDisplayInfo.h>
 
-#ifndef NO_INPUT
-#include <input/InputWindow.h>
-#endif
-
 #include <private/gui/ComposerService.h>
 
 // This server size should always be smaller than the server cache size
@@ -54,6 +52,10 @@
 
 namespace android {
 
+using gui::FocusRequest;
+using gui::WindowInfo;
+using gui::WindowInfoHandle;
+using gui::WindowInfosListener;
 using ui::ColorMode;
 // ---------------------------------------------------------------------------
 
@@ -95,6 +97,7 @@
     if (instance.mComposerService == nullptr) {
         if (ComposerService::getInstance().connectLocked()) {
             ALOGD("ComposerService reconnected");
+            WindowInfosListenerReporter::getInstance()->reconnect(instance.mComposerService);
         }
     }
     return instance.mComposerService;
@@ -144,8 +147,16 @@
     return mCallbackIdCounter++;
 }
 
+sp<TransactionCompletedListener> TransactionCompletedListener::sInstance = nullptr;
+
+void TransactionCompletedListener::setInstance(const sp<TransactionCompletedListener>& listener) {
+    sInstance = listener;
+}
+
 sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() {
-    static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener;
+    if (sInstance == nullptr) {
+        sInstance = new TransactionCompletedListener;
+    }
     return sInstance;
 }
 
@@ -183,7 +194,7 @@
 void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
                                                    sp<SurfaceControl> surfaceControl) {
     std::lock_guard<std::mutex> lock(mMutex);
-    mJankListeners.insert({surfaceControl->getHandle(), listener});
+    mJankListeners.insert({surfaceControl->getLayerId(), listener});
 }
 
 void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
@@ -212,8 +223,8 @@
 void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
         sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
     std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
-    mSurfaceStatsListeners.insert({surfaceControl->getHandle(),
-            SurfaceStatsCallbackEntry(context, cookie, listener)});
+    mSurfaceStatsListeners.insert(
+            {surfaceControl->getLayerId(), SurfaceStatsCallbackEntry(context, cookie, listener)});
 }
 
 void TransactionCompletedListener::removeSurfaceStatsListener(void* context, void* cookie) {
@@ -243,7 +254,7 @@
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
-    std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
+    std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
     {
         std::lock_guard<std::mutex> lock(mMutex);
 
@@ -341,13 +352,26 @@
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
                              surfaceControlStats);
         }
+
         for (const auto& surfaceStats : transactionStats.surfaceStats) {
+            // The callbackMap contains the SurfaceControl object, which we need to look up the
+            // layerId. Since we don't know which callback contains the SurfaceControl, iterate
+            // through all until the SC is found.
+            int32_t layerId = -1;
+            for (auto callbackId : transactionStats.callbackIds) {
+                sp<SurfaceControl> sc =
+                        callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl];
+                if (sc != nullptr) {
+                    layerId = sc->getLayerId();
+                    break;
+                }
+            }
+
             {
                 // Acquire surface stats listener lock such that we guarantee that after calling
                 // unregister, there won't be any further callback.
                 std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
-                auto listenerRange = mSurfaceStatsListeners.equal_range(
-                        surfaceStats.surfaceControl);
+                auto listenerRange = mSurfaceStatsListeners.equal_range(layerId);
                 for (auto it = listenerRange.first; it != listenerRange.second; it++) {
                     auto entry = it->second;
                     entry.callback(entry.context, transactionStats.latchTime,
@@ -356,7 +380,7 @@
             }
 
             if (surfaceStats.jankData.empty()) continue;
-            auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl);
+            auto jankRange = jankListenersMap.equal_range(layerId);
             for (auto it = jankRange.first; it != jankRange.second; it++) {
                 it->second->onJankDataAvailable(surfaceStats.jankData);
             }
@@ -913,6 +937,10 @@
     return ComposerService::getComposerService()->getPhysicalDisplayIds();
 }
 
+status_t SurfaceComposerClient::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) {
+    return ComposerService::getComposerService()->getPrimaryPhysicalDisplayId(id);
+}
+
 std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() {
     return ComposerService::getComposerService()->getInternalDisplayId();
 }
@@ -1271,6 +1299,7 @@
     removeReleaseBufferCallback(s);
     s->what |= layer_state_t::eBufferChanged;
     s->buffer = buffer;
+    s->releaseBufferEndpoint = IInterface::asBinder(TransactionCompletedListener::getIInstance());
     if (mIsAutoTimestamp) {
         mDesiredPresentTime = systemTime();
     }
@@ -1491,16 +1520,14 @@
     return *this;
 }
 
-#ifndef NO_INPUT
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
-        const sp<SurfaceControl>& sc,
-        const InputWindowInfo& info) {
+        const sp<SurfaceControl>& sc, const WindowInfo& info) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
-    s->inputHandle = new InputWindowHandle(info);
+    s->windowInfoHandle = new WindowInfoHandle(info);
     s->what |= layer_state_t::eInputInfoChanged;
     return *this;
 }
@@ -1516,8 +1543,6 @@
     return *this;
 }
 
-#endif
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform(
     const sp<SurfaceControl>& sc, const mat3& matrix, const vec3& translation) {
     layer_state_t* s = getLayerState(sc);
@@ -1718,6 +1743,21 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropInputMode(
+        const sp<SurfaceControl>& sc, gui::DropInputMode mode) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eDropInputModeChanged;
+    s->dropInputMode = mode;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1758,6 +1798,12 @@
     s.what |= DisplayState::eLayerStackChanged;
 }
 
+void SurfaceComposerClient::Transaction::setDisplayFlags(const sp<IBinder>& token, uint32_t flags) {
+    DisplayState& s(getDisplayState(token));
+    s.flags = flags;
+    s.what |= DisplayState::eFlagsChanged;
+}
+
 void SurfaceComposerClient::Transaction::setDisplayProjection(const sp<IBinder>& token,
                                                               ui::Rotation orientation,
                                                               const Rect& layerStackRect,
@@ -1779,15 +1825,10 @@
 
 // ---------------------------------------------------------------------------
 
-SurfaceComposerClient::SurfaceComposerClient()
-    : mStatus(NO_INIT)
-{
-}
+SurfaceComposerClient::SurfaceComposerClient() : mStatus(NO_INIT) {}
 
 SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& client)
-    : mStatus(NO_ERROR), mClient(client)
-{
-}
+      : mStatus(NO_ERROR), mClient(client) {}
 
 void SurfaceComposerClient::onFirstRef() {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
@@ -2153,6 +2194,18 @@
     return ComposerService::getComposerService()->getGPUContextPriority();
 }
 
+status_t SurfaceComposerClient::addWindowInfosListener(
+        const sp<WindowInfosListener>& windowInfosListener) {
+    return WindowInfosListenerReporter::getInstance()
+            ->addWindowInfosListener(windowInfosListener, ComposerService::getComposerService());
+}
+
+status_t SurfaceComposerClient::removeWindowInfosListener(
+        const sp<WindowInfosListener>& windowInfosListener) {
+    return WindowInfosListenerReporter::getInstance()
+            ->removeWindowInfosListener(windowInfosListener, ComposerService::getComposerService());
+}
+
 // ----------------------------------------------------------------------------
 
 status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
diff --git a/libs/input/InputWindow.cpp b/libs/gui/WindowInfo.cpp
similarity index 75%
rename from libs/input/InputWindow.cpp
rename to libs/gui/WindowInfo.cpp
index 9947720..b2ef7aa 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -15,50 +15,47 @@
  */
 
 #include <type_traits>
-#define LOG_TAG "InputWindow"
+#define LOG_TAG "WindowInfo"
 #define LOG_NDEBUG 0
 
 #include <android-base/stringprintf.h>
 #include <binder/Parcel.h>
-#include <input/InputTransport.h>
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
 
 #include <log/log.h>
 
-namespace android {
+namespace android::gui {
 
-
-// --- InputWindowInfo ---
-void InputWindowInfo::addTouchableRegion(const Rect& region) {
+// --- WindowInfo ---
+void WindowInfo::addTouchableRegion(const Rect& region) {
     touchableRegion.orSelf(region);
 }
 
-bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
-    return touchableRegion.contains(x,y);
+bool WindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
+    return touchableRegion.contains(x, y);
 }
 
-bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
-    return x >= frameLeft && x < frameRight
-            && y >= frameTop && y < frameBottom;
+bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
+    return x >= frameLeft && x < frameRight && y >= frameTop && y < frameBottom;
 }
 
-bool InputWindowInfo::supportsSplitTouch() const {
+bool WindowInfo::supportsSplitTouch() const {
     return flags.test(Flag::SPLIT_TOUCH);
 }
 
-bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
-    return frameLeft < other->frameRight && frameRight > other->frameLeft
-            && frameTop < other->frameBottom && frameBottom > other->frameTop;
+bool WindowInfo::overlaps(const WindowInfo* other) const {
+    return frameLeft < other->frameRight && frameRight > other->frameLeft &&
+            frameTop < other->frameBottom && frameBottom > other->frameTop;
 }
 
-bool InputWindowInfo::operator==(const InputWindowInfo& info) const {
+bool WindowInfo::operator==(const WindowInfo& info) const {
     return info.token == token && info.id == id && info.name == name && info.flags == flags &&
             info.type == type && info.dispatchingTimeout == dispatchingTimeout &&
             info.frameLeft == frameLeft && info.frameTop == frameTop &&
             info.frameRight == frameRight && info.frameBottom == frameBottom &&
             info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
-            info.transform == transform && info.displayWidth == displayWidth &&
-            info.displayHeight == displayHeight &&
+            info.transform == transform && info.displayOrientation == displayOrientation &&
+            info.displayWidth == displayWidth && info.displayHeight == displayHeight &&
             info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
             info.trustedOverlay == trustedOverlay && info.focusable == focusable &&
             info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper &&
@@ -69,7 +66,7 @@
             info.applicationInfo == applicationInfo;
 }
 
-status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const {
+status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
     if (parcel == nullptr) {
         ALOGE("%s: Null parcel", __func__);
         return BAD_VALUE;
@@ -86,7 +83,7 @@
         parcel->writeInt32(id) ?:
         parcel->writeUtf8AsUtf16(name) ?:
         parcel->writeInt32(flags.get()) ?:
-        parcel->writeInt32(static_cast<std::underlying_type_t<InputWindowInfo::Type>>(type)) ?:
+        parcel->writeInt32(static_cast<std::underlying_type_t<WindowInfo::Type>>(type)) ?:
         parcel->writeInt32(frameLeft) ?:
         parcel->writeInt32(frameTop) ?:
         parcel->writeInt32(frameRight) ?:
@@ -100,6 +97,7 @@
         parcel->writeFloat(transform.dtdy()) ?:
         parcel->writeFloat(transform.dsdy()) ?:
         parcel->writeFloat(transform.ty()) ?:
+        parcel->writeUint32(displayOrientation) ?:
         parcel->writeInt32(displayWidth) ?:
         parcel->writeInt32(displayHeight) ?:
         parcel->writeBool(visible) ?:
@@ -117,12 +115,13 @@
         applicationInfo.writeToParcel(parcel) ?:
         parcel->write(touchableRegion) ?:
         parcel->writeBool(replaceTouchableRegionWithCrop) ?:
-        parcel->writeStrongBinder(touchableRegionCropHandle.promote());
+        parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?:
+        parcel->writeStrongBinder(windowToken);
     // clang-format on
     return status;
 }
 
-status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) {
+status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
     if (parcel == nullptr) {
         ALOGE("%s: Null parcel", __func__);
         return BAD_VALUE;
@@ -156,6 +155,7 @@
         parcel->readFloat(&dtdy) ?:
         parcel->readFloat(&dsdy) ?:
         parcel->readFloat(&ty) ?:
+        parcel->readUint32(&displayOrientation) ?:
         parcel->readInt32(&displayWidth) ?:
         parcel->readInt32(&displayHeight) ?:
         parcel->readBool(&visible) ?:
@@ -176,11 +176,13 @@
     touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
 
     inputFeatures = Flags<Feature>(parcel->readInt32());
+    // clang-format off
     status = parcel->readInt32(&displayId) ?:
         parcel->readInt32(&portalToDisplayId) ?:
         applicationInfo.readFromParcel(parcel) ?:
         parcel->read(touchableRegion) ?:
         parcel->readBool(&replaceTouchableRegionWithCrop);
+    // clang-format on
 
     if (status != OK) {
         return status;
@@ -189,36 +191,37 @@
     touchableRegionCropHandle = parcel->readStrongBinder();
     transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
 
-    return OK;
+    status = parcel->readNullableStrongBinder(&windowToken);
+    return status;
 }
 
-// --- InputWindowHandle ---
+// --- WindowInfoHandle ---
 
-InputWindowHandle::InputWindowHandle() {}
+WindowInfoHandle::WindowInfoHandle() {}
 
-InputWindowHandle::~InputWindowHandle() {}
+WindowInfoHandle::~WindowInfoHandle() {}
 
-InputWindowHandle::InputWindowHandle(const InputWindowHandle& other) : mInfo(other.mInfo) {}
+WindowInfoHandle::WindowInfoHandle(const WindowInfoHandle& other) : mInfo(other.mInfo) {}
 
-InputWindowHandle::InputWindowHandle(const InputWindowInfo& other) : mInfo(other) {}
+WindowInfoHandle::WindowInfoHandle(const WindowInfo& other) : mInfo(other) {}
 
-status_t InputWindowHandle::writeToParcel(android::Parcel* parcel) const {
+status_t WindowInfoHandle::writeToParcel(android::Parcel* parcel) const {
     return mInfo.writeToParcel(parcel);
 }
 
-status_t InputWindowHandle::readFromParcel(const android::Parcel* parcel) {
+status_t WindowInfoHandle::readFromParcel(const android::Parcel* parcel) {
     return mInfo.readFromParcel(parcel);
 }
 
-void InputWindowHandle::releaseChannel() {
+void WindowInfoHandle::releaseChannel() {
     mInfo.token.clear();
 }
 
-sp<IBinder> InputWindowHandle::getToken() const {
+sp<IBinder> WindowInfoHandle::getToken() const {
     return mInfo.token;
 }
 
-void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) {
+void WindowInfoHandle::updateFrom(sp<WindowInfoHandle> handle) {
     mInfo = handle->mInfo;
 }
-} // namespace android
+} // namespace android::gui
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
new file mode 100644
index 0000000..c00a438
--- /dev/null
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/WindowInfosListenerReporter.h>
+
+namespace android {
+
+using gui::IWindowInfosReportedListener;
+using gui::WindowInfo;
+using gui::WindowInfosListener;
+
+sp<WindowInfosListenerReporter> WindowInfosListenerReporter::getInstance() {
+    static sp<WindowInfosListenerReporter> sInstance = new WindowInfosListenerReporter;
+    return sInstance;
+}
+
+status_t WindowInfosListenerReporter::addWindowInfosListener(
+        const sp<WindowInfosListener>& windowInfosListener,
+        const sp<ISurfaceComposer>& surfaceComposer) {
+    status_t status = OK;
+    {
+        std::scoped_lock lock(mListenersMutex);
+        if (mWindowInfosListeners.empty()) {
+            status = surfaceComposer->addWindowInfosListener(this);
+        }
+
+        if (status == OK) {
+            mWindowInfosListeners.insert(windowInfosListener);
+        }
+    }
+
+    return status;
+}
+
+status_t WindowInfosListenerReporter::removeWindowInfosListener(
+        const sp<WindowInfosListener>& windowInfosListener,
+        const sp<ISurfaceComposer>& surfaceComposer) {
+    status_t status = OK;
+    {
+        std::scoped_lock lock(mListenersMutex);
+        if (mWindowInfosListeners.size() == 1) {
+            status = surfaceComposer->removeWindowInfosListener(this);
+        }
+
+        if (status == OK) {
+            mWindowInfosListeners.erase(windowInfosListener);
+        }
+    }
+
+    return status;
+}
+
+binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
+        const std::vector<WindowInfo>& windowInfos,
+        const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
+    std::unordered_set<sp<WindowInfosListener>, ISurfaceComposer::SpHash<WindowInfosListener>>
+            windowInfosListeners;
+
+    {
+        std::scoped_lock lock(mListenersMutex);
+        for (auto listener : mWindowInfosListeners) {
+            windowInfosListeners.insert(listener);
+        }
+    }
+
+    for (auto listener : windowInfosListeners) {
+        listener->onWindowInfosChanged(windowInfos);
+    }
+
+    if (windowInfosReportedListener) {
+        windowInfosReportedListener->onWindowInfosReported();
+    }
+
+    return binder::Status::ok();
+}
+
+void WindowInfosListenerReporter::reconnect(const sp<ISurfaceComposer>& composerService) {
+    std::scoped_lock lock(mListenersMutex);
+    if (!mWindowInfosListeners.empty()) {
+        composerService->addWindowInfosListener(this);
+    }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/android/gui/DropInputMode.aidl b/libs/gui/android/gui/DropInputMode.aidl
new file mode 100644
index 0000000..2b31744
--- /dev/null
+++ b/libs/gui/android/gui/DropInputMode.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+
+/**
+  * Input event drop modes: Input event drop options for windows and its children.
+  *
+  * @hide
+  */
+@Backing(type="int")
+enum DropInputMode {
+    /**
+      * Default mode, input events are sent to the target as usual.
+      */
+    NONE,
+
+    /**
+      * Window and its children will not receive any input even if it has a valid input channel.
+      * Touches and keys will be dropped. If a window is focused, it will remain focused but will
+      * not receive any keys. If the window has a touchable region and is the target of an input
+      * event, the event will be dropped and will not go to the window behind. ref: b/197296414
+      */
+    ALL,
+
+    /**
+      * Similar to DROP but input events are only dropped if the window is considered to be
+      * obscured. ref: b/197364677
+      */
+    OBSCURED
+}
diff --git a/libs/input/android/FocusRequest.aidl b/libs/gui/android/gui/FocusRequest.aidl
similarity index 98%
rename from libs/input/android/FocusRequest.aidl
rename to libs/gui/android/gui/FocusRequest.aidl
index 8812d34..9018635 100644
--- a/libs/input/android/FocusRequest.aidl
+++ b/libs/gui/android/gui/FocusRequest.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android;
+package android.gui;
 
 /** @hide */
 parcelable FocusRequest {
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
new file mode 100644
index 0000000..d4553ca
--- /dev/null
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.IWindowInfosReportedListener;
+import android.gui.WindowInfo;
+
+/** @hide */
+oneway interface IWindowInfosListener
+{
+    void onWindowInfosChanged(in WindowInfo[] windowInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+}
diff --git a/libs/input/android/os/ISetInputWindowsListener.aidl b/libs/gui/android/gui/IWindowInfosReportedListener.aidl
similarity index 85%
rename from libs/input/android/os/ISetInputWindowsListener.aidl
rename to libs/gui/android/gui/IWindowInfosReportedListener.aidl
index bb58fb6..0e4cce6 100644
--- a/libs/input/android/os/ISetInputWindowsListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosReportedListener.aidl
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.gui;
 
 /** @hide */
-oneway interface ISetInputWindowsListener
+oneway interface IWindowInfosReportedListener
 {
-    void onSetInputWindowsFinished();
+    void onWindowInfosReported();
 }
diff --git a/libs/input/android/InputApplicationInfo.aidl b/libs/gui/android/gui/InputApplicationInfo.aidl
similarity index 96%
rename from libs/input/android/InputApplicationInfo.aidl
rename to libs/gui/android/gui/InputApplicationInfo.aidl
index 9336039..c0fd666 100644
--- a/libs/input/android/InputApplicationInfo.aidl
+++ b/libs/gui/android/gui/InputApplicationInfo.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android;
+package android.gui;
 
 parcelable InputApplicationInfo {
     @nullable IBinder token;
diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/gui/android/gui/TouchOcclusionMode.aidl
similarity index 98%
rename from libs/input/android/os/TouchOcclusionMode.aidl
rename to libs/gui/android/gui/TouchOcclusionMode.aidl
index 106f159..d91d052 100644
--- a/libs/input/android/os/TouchOcclusionMode.aidl
+++ b/libs/gui/android/gui/TouchOcclusionMode.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.gui;
 
 
 /**
diff --git a/libs/input/android/InputWindowInfo.aidl b/libs/gui/android/gui/WindowInfo.aidl
similarity index 81%
rename from libs/input/android/InputWindowInfo.aidl
rename to libs/gui/android/gui/WindowInfo.aidl
index eeaf400..2c85d15 100644
--- a/libs/input/android/InputWindowInfo.aidl
+++ b/libs/gui/android/gui/WindowInfo.aidl
@@ -1,5 +1,4 @@
-/* //device/java/android/android/view/InputChannel.aidl
-**
+/*
 ** Copyright 2020, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +14,6 @@
 ** limitations under the License.
 */
 
-package android;
+package android.gui;
 
-parcelable InputWindowInfo cpp_header "input/InputWindow.h";
+parcelable WindowInfo cpp_header "gui/WindowInfo.h";
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 2a3f6a4..cd289cb 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -22,11 +22,12 @@
 #include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
 #include <android/gui/ITunnelModeEnabledListener.h>
+#include <android/gui/IWindowInfosListener.h>
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
+#include <ftl/Flags.h>
 #include <gui/FrameTimelineInfo.h>
 #include <gui/ITransactionCompletedListener.h>
-#include <input/Flags.h>
 #include <math/vec4.h>
 #include <stdint.h>
 #include <sys/types.h>
@@ -140,6 +141,8 @@
      */
     virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const = 0;
 
+    virtual status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const = 0;
+
     // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
     std::optional<PhysicalDisplayId> getInternalDisplayId() const {
         const auto displayIds = getPhysicalDisplayIds();
@@ -552,6 +555,11 @@
      * in MIN_UNDEQUEUED_BUFFERS.
      */
     virtual status_t getMaxAcquiredBufferCount(int* buffers) const = 0;
+
+    virtual status_t addWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const = 0;
+    virtual status_t removeWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -624,6 +632,9 @@
         ON_PULL_ATOM,
         ADD_TUNNEL_MODE_ENABLED_LISTENER,
         REMOVE_TUNNEL_MODE_ENABLED_LISTENER,
+        ADD_WINDOW_INFOS_LISTENER,
+        REMOVE_WINDOW_INFOS_LISTENER,
+        GET_PRIMARY_PHYSICAL_DISPLAY_ID,
         // Always append new enum to the end.
     };
 
diff --git a/include/input/InputApplication.h b/libs/gui/include/gui/InputApplication.h
similarity index 84%
rename from include/input/InputApplication.h
rename to libs/gui/include/gui/InputApplication.h
index 8e4fe79..679c2a1 100644
--- a/include/input/InputApplication.h
+++ b/libs/gui/include/gui/InputApplication.h
@@ -19,17 +19,17 @@
 
 #include <string>
 
-#include <android/InputApplicationInfo.h>
+#include <android/gui/InputApplicationInfo.h>
 
 #include <binder/IBinder.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 
-#include <input/Input.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
 namespace android {
+
 /*
  * Handle for an application that can receive input.
  *
@@ -38,13 +38,9 @@
  */
 class InputApplicationHandle {
 public:
-    inline const InputApplicationInfo* getInfo() const {
-        return &mInfo;
-    }
+    inline const gui::InputApplicationInfo* getInfo() const { return &mInfo; }
 
-    inline std::string getName() const {
-        return !mInfo.name.empty() ? mInfo.name : "<invalid>";
-    }
+    inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : "<invalid>"; }
 
     inline std::chrono::nanoseconds getDispatchingTimeout(
             std::chrono::nanoseconds defaultValue) const {
@@ -52,9 +48,7 @@
                            : defaultValue;
     }
 
-    inline sp<IBinder> getApplicationToken() const {
-        return mInfo.token;
-    }
+    inline sp<IBinder> getApplicationToken() const { return mInfo.token; }
 
     bool operator==(const InputApplicationHandle& other) const {
         return getName() == other.getName() && getApplicationToken() == other.getApplicationToken();
@@ -77,7 +71,7 @@
     InputApplicationHandle() = default;
     virtual ~InputApplicationHandle() = default;
 
-    InputApplicationInfo mInfo;
+    gui::InputApplicationInfo mInfo;
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 3e57ff6..03e4aac 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -26,14 +26,13 @@
 #include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
 
-#ifndef NO_INPUT
-#include <android/FocusRequest.h>
-#include <input/InputWindow.h>
-#endif
+#include <android/gui/DropInputMode.h>
+#include <android/gui/FocusRequest.h>
 
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerMetadata.h>
 #include <gui/SurfaceControl.h>
+#include <gui/WindowInfo.h>
 #include <math/vec3.h>
 #include <ui/BlurRegion.h>
 #include <ui/GraphicTypes.h>
@@ -119,6 +118,7 @@
         eAutoRefreshChanged = 0x1000'00000000,
         eStretchChanged = 0x2000'00000000,
         eTrustedOverlayChanged = 0x4000'00000000,
+        eDropInputModeChanged = 0x8000'00000000,
     };
 
     layer_state_t();
@@ -178,9 +178,7 @@
     mat4 colorTransform;
     std::vector<BlurRegion> blurRegions;
 
-#ifndef NO_INPUT
-    sp<InputWindowHandle> inputHandle = new InputWindowHandle();
-#endif
+    sp<gui::WindowInfoHandle> windowInfoHandle = new gui::WindowInfoHandle();
 
     client_cache_t cachedBuffer;
 
@@ -246,6 +244,14 @@
     // is used to remove the old callback from the client process map if it is
     // overwritten by another setBuffer call.
     ReleaseCallbackId releaseCallbackId;
+
+    // Stores which endpoint the release information should be sent to. We don't want to send the
+    // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
+    // was called with.
+    sp<IBinder> releaseBufferEndpoint;
+
+    // Force inputflinger to drop all input events for the layer and its children.
+    gui::DropInputMode dropInputMode;
 };
 
 struct ComposerState {
@@ -259,7 +265,8 @@
         eSurfaceChanged = 0x01,
         eLayerStackChanged = 0x02,
         eDisplayProjectionChanged = 0x04,
-        eDisplaySizeChanged = 0x08
+        eDisplaySizeChanged = 0x08,
+        eFlagsChanged = 0x10
     };
 
     DisplayState();
@@ -269,6 +276,7 @@
     sp<IBinder> token;
     sp<IGraphicBufferProducer> surface;
     uint32_t layerStack;
+    uint32_t flags;
 
     // These states define how layers are projected onto the physical display.
     //
@@ -292,9 +300,7 @@
 };
 
 struct InputWindowCommands {
-#ifndef NO_INPUT
-    std::vector<FocusRequest> focusRequests;
-#endif
+    std::vector<gui::FocusRequest> focusRequests;
     bool syncInputWindows{false};
 
     // Merges the passed in commands and returns true if there were any changes.
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index baa0567..9af9e64 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -42,6 +42,7 @@
 #include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
 #include <gui/SurfaceControl.h>
+#include <gui/WindowInfosListenerReporter.h>
 #include <math/vec3.h>
 
 namespace android {
@@ -307,6 +308,7 @@
 
     //! Get stable IDs for connected physical displays
     static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
+    static status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*);
     static std::optional<PhysicalDisplayId> getInternalDisplayId();
 
     //! Get token for a physical display given its stable ID
@@ -502,11 +504,9 @@
         // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
         Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber);
 
-#ifndef NO_INPUT
-        Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
-        Transaction& setFocusedWindow(const FocusRequest& request);
+        Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info);
+        Transaction& setFocusedWindow(const gui::FocusRequest& request);
         Transaction& syncInputWindows();
-#endif
 
         // Set a color transform matrix on the given layer on the built-in display.
         Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix,
@@ -563,12 +563,15 @@
         Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
         Transaction& setDestinationFrame(const sp<SurfaceControl>& sc,
                                          const Rect& destinationFrame);
+        Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode);
 
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
         void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
 
+        void setDisplayFlags(const sp<IBinder>& token, uint32_t flags);
+
         /* setDisplayProjection() defines the projection of layer stacks
          * to a given display.
          *
@@ -622,6 +625,9 @@
     static status_t removeTunnelModeEnabledListener(
             const sp<gui::ITunnelModeEnabledListener>& listener);
 
+    status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
+    status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
+
 private:
     virtual void onFirstRef();
 
@@ -651,8 +657,10 @@
 };
 
 class TransactionCompletedListener : public BnTransactionCompletedListener {
+public:
     TransactionCompletedListener();
 
+protected:
     int64_t getNextIdLocked() REQUIRES(mMutex);
 
     std::mutex mMutex;
@@ -682,13 +690,13 @@
 
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
             GUARDED_BY(mMutex);
-    std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
+    std::multimap<int32_t, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
     std::unordered_map<ReleaseCallbackId, ReleaseBufferCallback, ReleaseBufferCallbackIdHash>
             mReleaseBufferCallbacks GUARDED_BY(mMutex);
 
     // This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for
     // std::recursive_mutex
-    std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
+    std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
 
 public:
     static sp<TransactionCompletedListener> getInstance();
@@ -730,8 +738,12 @@
     void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint,
                          uint32_t currentMaxAcquiredBufferCount) override;
 
+    // For Testing Only
+    static void setInstance(const sp<TransactionCompletedListener>&);
+
 private:
     ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
+    static sp<TransactionCompletedListener> sInstance;
 };
 
 } // namespace android
diff --git a/include/input/InputWindow.h b/libs/gui/include/gui/WindowInfo.h
similarity index 83%
rename from include/input/InputWindow.h
rename to libs/gui/include/gui/WindowInfo.h
index 121be6d..4727740 100644
--- a/include/input/InputWindow.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _UI_INPUT_WINDOW_H
-#define _UI_INPUT_WINDOW_H
+#pragma once
 
-#include <android/os/TouchOcclusionMode.h>
+#include <android/gui/TouchOcclusionMode.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
-#include <input/Flags.h>
-#include <input/Input.h>
-#include <input/InputTransport.h>
+#include <ftl/Flags.h>
+#include <gui/constants.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -31,15 +29,13 @@
 
 #include "InputApplication.h"
 
-using android::os::TouchOcclusionMode;
-
-namespace android {
+namespace android::gui {
 
 /*
  * Describes the properties of a window that can receive input.
  */
-struct InputWindowInfo : public Parcelable {
-    InputWindowInfo() = default;
+struct WindowInfo : public Parcelable {
+    WindowInfo() = default;
 
     // Window flags from WindowManager.LayoutParams
     enum class Flag : uint32_t {
@@ -125,9 +121,11 @@
     };
 
     enum class Feature {
-        DISABLE_TOUCH_PAD_GESTURES = 0x00000001,
-        NO_INPUT_CHANNEL = 0x00000002,
-        DISABLE_USER_ACTIVITY = 0x00000004,
+        DISABLE_TOUCH_PAD_GESTURES = 1u << 0,
+        NO_INPUT_CHANNEL = 1u << 1,
+        DISABLE_USER_ACTIVITY = 1u << 2,
+        DROP_INPUT = 1u << 3,
+        DROP_INPUT_IF_OBSCURED = 1u << 4,
     };
 
     /* These values are filled in by the WM and passed through SurfaceFlinger
@@ -136,6 +134,10 @@
     // This value should NOT be used to uniquely identify the window. There may be different
     // input windows that have the same token.
     sp<IBinder> token;
+
+    // The token that identifies which client window this WindowInfo was created for.
+    sp<IBinder> windowToken;
+
     // This uniquely identifies the input window.
     int32_t id = -1;
     std::string name;
@@ -168,9 +170,12 @@
     // Transform applied to individual windows.
     ui::Transform transform;
 
+    // Display orientation as ui::Transform::RotationFlags. Used for compatibility raw coordinates.
+    uint32_t displayOrientation = ui::Transform::ROT_0;
+
     // Display size in its natural rotation. Used to rotate raw coordinates for compatibility.
-    int32_t displayWidth = AMOTION_EVENT_INVALID_DISPLAY_SIZE;
-    int32_t displayHeight = AMOTION_EVENT_INVALID_DISPLAY_SIZE;
+    int32_t displayWidth = 0;
+    int32_t displayHeight = 0;
 
     /*
      * This is filled in by the WM relative to the frame and then translated
@@ -206,9 +211,9 @@
 
     bool supportsSplitTouch() const;
 
-    bool overlaps(const InputWindowInfo* other) const;
+    bool overlaps(const WindowInfo* other) const;
 
-    bool operator==(const InputWindowInfo& inputChannel) const;
+    bool operator==(const WindowInfo& inputChannel) const;
 
     status_t writeToParcel(android::Parcel* parcel) const override;
 
@@ -221,13 +226,13 @@
  * Used by the native input dispatcher to indirectly refer to the window manager objects
  * that describe a window.
  */
-class InputWindowHandle : public RefBase {
+class WindowInfoHandle : public RefBase {
 public:
-    explicit InputWindowHandle();
-    InputWindowHandle(const InputWindowHandle& other);
-    InputWindowHandle(const InputWindowInfo& other);
+    explicit WindowInfoHandle();
+    WindowInfoHandle(const WindowInfoHandle& other);
+    WindowInfoHandle(const WindowInfo& other);
 
-    inline const InputWindowInfo* getInfo() const { return &mInfo; }
+    inline const WindowInfo* getInfo() const { return &mInfo; }
 
     sp<IBinder> getToken() const;
 
@@ -243,21 +248,9 @@
     }
 
     /**
-     * Requests that the state of this object be updated to reflect
-     * the most current available information about the application.
-     * As this class is created as RefBase object, no pure virtual function is allowed.
-     *
-     * This method should only be called from within the input dispatcher's
-     * critical section.
-     *
-     * Returns true on success, or false if the handle is no longer valid.
-     */
-    virtual bool updateInfo() { return false; }
-
-    /**
      * Updates from another input window handle.
      */
-    void updateFrom(const sp<InputWindowHandle> handle);
+    void updateFrom(const sp<WindowInfoHandle> handle);
 
     /**
      * Releases the channel used by the associated information when it is
@@ -270,10 +263,8 @@
     status_t writeToParcel(android::Parcel* parcel) const;
 
 protected:
-    virtual ~InputWindowHandle();
+    virtual ~WindowInfoHandle();
 
-    InputWindowInfo mInfo;
+    WindowInfo mInfo;
 };
-} // namespace android
-
-#endif // _UI_INPUT_WINDOW_H
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h
new file mode 100644
index 0000000..8a70b9b
--- /dev/null
+++ b/libs/gui/include/gui/WindowInfosListener.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/WindowInfo.h>
+#include <utils/RefBase.h>
+
+namespace android::gui {
+
+class WindowInfosListener : public virtual RefBase {
+public:
+    virtual void onWindowInfosChanged(const std::vector<WindowInfo>& /*windowInfos*/) = 0;
+};
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
new file mode 100644
index 0000000..7cb96e0
--- /dev/null
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/BnWindowInfosListener.h>
+#include <android/gui/IWindowInfosReportedListener.h>
+#include <binder/IBinder.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/WindowInfosListener.h>
+#include <utils/Mutex.h>
+#include <unordered_set>
+
+namespace android {
+class ISurfaceComposer;
+
+class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
+public:
+    static sp<WindowInfosListenerReporter> getInstance();
+    binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos,
+                                        const sp<gui::IWindowInfosReportedListener>&) override;
+
+    status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
+                                    const sp<ISurfaceComposer>&);
+    status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
+                                       const sp<ISurfaceComposer>&);
+    void reconnect(const sp<ISurfaceComposer>&);
+
+private:
+    std::mutex mListenersMutex;
+    std::unordered_set<sp<gui::WindowInfosListener>,
+                       ISurfaceComposer::SpHash<gui::WindowInfosListener>>
+            mWindowInfosListeners GUARDED_BY(mListenersMutex);
+};
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/include/gui/constants.h b/libs/gui/include/gui/constants.h
new file mode 100644
index 0000000..8eab378
--- /dev/null
+++ b/libs/gui/include/gui/constants.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+namespace android {
+
+/**
+ * Invalid value for display size. Used when display size isn't available.
+ */
+constexpr int32_t INVALID_DISPLAY_SIZE = 0;
+
+enum {
+    /* Used when an event is not associated with any display.
+     * Typically used for non-pointer events. */
+    ADISPLAY_ID_NONE = -1,
+
+    /* The default display id. */
+    ADISPLAY_ID_DEFAULT = 0,
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index c801c62..3d26c3d 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -43,6 +43,7 @@
         "SurfaceTextureMultiContextGL_test.cpp",
         "Surface_test.cpp",
         "TextureRenderer.cpp",
+        "WindowInfo_test.cpp",
     ],
 
     shared_libs: [
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9082d27..99777e9 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -672,6 +672,83 @@
                                                /*border*/ 0, /*outsideRegion*/ true));
 }
 
+// b/196339769 verify we can can update the requested size while the in FREEZE scaling mode and
+// scale the buffer properly when the mode changes to SCALE_TO_WINDOW
+TEST_F(BLASTBufferQueueTest, ScalingModeChanges) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight / 4);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight / 4,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+        uint32_t* bufData;
+        buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+                  reinterpret_cast<void**>(&bufData));
+        fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b);
+        buf->unlock();
+
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                       HAL_DATASPACE_UNKNOWN, {},
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE);
+        igbProducer->queueBuffer(slot, input, &qbOutput);
+        adapter.waitForCallbacks();
+    }
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b,
+                               {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 4}));
+
+    // update the size to half the display and dequeue a buffer quarter of the display.
+    adapter.update(mSurfaceControl, mDisplayWidth, mDisplayHeight / 2);
+
+    {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight / 8,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+        uint32_t* bufData;
+        buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+                  reinterpret_cast<void**>(&bufData));
+        g = 255;
+        fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b);
+        buf->unlock();
+
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), true /* autotimestamp */,
+                                                       HAL_DATASPACE_UNKNOWN, {},
+                                                       NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+                                                       0, Fence::NO_FENCE);
+        igbProducer->queueBuffer(slot, input, &qbOutput);
+        adapter.waitForCallbacks();
+    }
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    // verify we still scale the buffer to the new size (half the screen height)
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b,
+                               {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
+}
+
 class TestProducerListener : public BnProducerListener {
 public:
     sp<IGraphicBufferProducer> mIgbp;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 49c44a7..78a8b42 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -24,6 +24,7 @@
 
 #include <memory>
 
+#include <android/keycodes.h>
 #include <android/native_window.h>
 
 #include <binder/Binder.h>
@@ -37,9 +38,9 @@
 #include <gui/SurfaceControl.h>
 
 #include <android/os/IInputFlinger.h>
+#include <gui/WindowInfo.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
-#include <input/InputWindow.h>
 
 #include <ui/DisplayMode.h>
 #include <ui/Rect.h>
@@ -49,6 +50,11 @@
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
 
+using android::gui::FocusRequest;
+using android::gui::InputApplicationInfo;
+using android::gui::TouchOcclusionMode;
+using android::gui::WindowInfo;
+
 namespace android::test {
 
 using Transaction = SurfaceComposerClient::Transaction;
@@ -115,8 +121,8 @@
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
-    InputEvent* consumeEvent() {
-        waitForEventAvailable();
+    InputEvent *consumeEvent(int timeoutMs = 3000) {
+        waitForEventAvailable(timeoutMs);
 
         InputEvent *ev;
         uint32_t seqId;
@@ -173,6 +179,24 @@
         EXPECT_EQ(flags, mev->getFlags() & flags);
     }
 
+    void expectKey(uint32_t keycode) {
+        InputEvent *ev = consumeEvent();
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, ev->getType());
+        KeyEvent *keyEvent = static_cast<KeyEvent *>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, keyEvent->getAction());
+        EXPECT_EQ(keycode, keyEvent->getKeyCode());
+        EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS);
+
+        ev = consumeEvent();
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, ev->getType());
+        keyEvent = static_cast<KeyEvent *>(ev);
+        EXPECT_EQ(AMOTION_EVENT_ACTION_UP, keyEvent->getAction());
+        EXPECT_EQ(keycode, keyEvent->getKeyCode());
+        EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS);
+    }
+
     virtual ~InputSurface() {
         mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
     }
@@ -210,19 +234,19 @@
     }
 
 private:
-    void waitForEventAvailable() {
+    void waitForEventAvailable(int timeoutMs) {
         struct pollfd fd;
 
         fd.fd = mClientChannel->getFd();
         fd.events = POLLIN;
-        poll(&fd, 1, 3000);
+        poll(&fd, 1, timeoutMs);
     }
 
     void populateInputInfo(int width, int height) {
         mInputInfo.token = mClientChannel->getConnectionToken();
         mInputInfo.name = "Test info";
-        mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
-        mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION;
+        mInputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL;
+        mInputInfo.type = WindowInfo::Type::BASE_APPLICATION;
         mInputInfo.dispatchingTimeout = 5s;
         mInputInfo.globalScaleFactor = 1.0;
         mInputInfo.focusable = true;
@@ -249,7 +273,7 @@
     std::shared_ptr<InputChannel> mClientChannel;
     sp<IInputFlinger> mInputFlinger;
 
-    InputWindowInfo mInputInfo;
+    WindowInfo mInputInfo;
 
     PreallocatedInputEventFactory mInputEventFactory;
     InputConsumer* mInputConsumer;
@@ -358,6 +382,14 @@
     }
 }
 
+void injectKey(uint32_t keycode) {
+    char *buf1;
+    asprintf(&buf1, "%d", keycode);
+    if (fork() == 0) {
+        execlp("input", "input", "keyevent", buf1, NULL);
+    }
+}
+
 TEST_F(InputSurfacesTest, can_receive_input) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
     surface->showAt(100, 100);
@@ -609,6 +641,9 @@
     surface->requestFocus();
 
     surface->assertFocusChange(true);
+
+    injectKey(AKEYCODE_V);
+    surface->expectKey(AKEYCODE_V);
 }
 
 TEST_F(InputSurfacesTest, rotate_surface) {
@@ -686,7 +721,7 @@
     // Add non touchable window to fully cover touchable window. Window behind gets touch, but
     // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
     nonTouchableSurface->mInputInfo.ownerUid = 22222;
     // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
     // the default obscured/untrusted touch filter introduced in S.
@@ -706,8 +741,8 @@
     // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
-    parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    parentSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
     nonTouchableSurface->mInputInfo.ownerUid = 22222;
     parentSurface->mInputInfo.ownerUid = 22222;
     nonTouchableSurface->showAt(0, 0);
@@ -730,8 +765,8 @@
     // the touchable window. Window behind gets touch with no obscured flags.
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
-    nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
-    parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    nonTouchableSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    parentSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
     nonTouchableSurface->mInputInfo.ownerUid = 22222;
     parentSurface->mInputInfo.ownerUid = 22222;
     nonTouchableSurface->showAt(0, 0);
@@ -751,7 +786,7 @@
 
     std::unique_ptr<InputSurface> bufferSurface =
             InputSurface::makeBufferInputSurface(mComposerClient, 0, 0);
-    bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    bufferSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
     bufferSurface->mInputInfo.ownerUid = 22222;
 
     surface->showAt(10, 10);
@@ -766,7 +801,7 @@
 
     std::unique_ptr<BlastInputSurface> bufferSurface =
             BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
-    bufferSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+    bufferSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
     bufferSurface->mInputInfo.ownerUid = 22222;
 
     surface->showAt(10, 10);
@@ -776,4 +811,139 @@
     surface->expectTap(1, 1);
 }
 
+TEST_F(InputSurfacesTest, strict_unobscured_input_unobscured_window) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction(
+            [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+    surface->showAt(100, 100);
+
+    injectTap(101, 101);
+
+    EXPECT_NE(surface->consumeEvent(), nullptr);
+    EXPECT_NE(surface->consumeEvent(), nullptr);
+
+    surface->requestFocus();
+    surface->assertFocusChange(true);
+    injectKey(AKEYCODE_V);
+    surface->expectKey(AKEYCODE_V);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([&](auto &t, auto &sc) {
+        t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
+        t.setMatrix(sc, 2.0, 0, 0, 2.0);
+    });
+    surface->showAt(100, 100);
+
+    injectTap(101, 101);
+
+    EXPECT_NE(surface->consumeEvent(), nullptr);
+    EXPECT_NE(surface->consumeEvent(), nullptr);
+
+    surface->requestFocus();
+    surface->assertFocusChange(true);
+    injectKey(AKEYCODE_V);
+    surface->expectKey(AKEYCODE_V);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->mInputInfo.ownerUid = 11111;
+    surface->doTransaction(
+            [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+    surface->showAt(100, 100);
+    std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
+    obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    obscuringSurface->mInputInfo.ownerUid = 22222;
+    obscuringSurface->showAt(100, 100);
+    injectTap(101, 101);
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+    surface->requestFocus();
+    surface->assertFocusChange(true);
+    injectKey(AKEYCODE_V);
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->mInputInfo.ownerUid = 11111;
+    surface->doTransaction(
+            [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+    surface->showAt(100, 100);
+    std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
+    obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
+    obscuringSurface->mInputInfo.ownerUid = 22222;
+    obscuringSurface->showAt(190, 190);
+
+    injectTap(101, 101);
+
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+    surface->requestFocus();
+    surface->assertFocusChange(true);
+    injectKey(AKEYCODE_V);
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) {
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
+    parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
+
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(100, 100);
+    surface->doTransaction([&](auto &t, auto &sc) {
+        t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
+        t.reparent(sc, parentSurface->mSurfaceControl);
+        t.setAlpha(parentSurface->mSurfaceControl, 0.9f);
+    });
+
+    injectTap(101, 101);
+
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+    surface->requestFocus();
+    surface->assertFocusChange(true);
+    injectKey(AKEYCODE_V);
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) {
+    std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
+    parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
+
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction([&](auto &t, auto &sc) {
+        t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
+        t.reparent(sc, parentSurface->mSurfaceControl);
+        t.setCrop(parentSurface->mSurfaceControl, Rect(10, 10, 100, 100));
+    });
+    surface->showAt(100, 100);
+
+    injectTap(111, 111);
+
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+    surface->requestFocus();
+    surface->assertFocusChange(true);
+    injectKey(AKEYCODE_V);
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(InputSurfacesTest, drop_input_policy) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->doTransaction(
+            [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::ALL); });
+    surface->showAt(100, 100);
+
+    injectTap(101, 101);
+
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+    surface->requestFocus();
+    surface->assertFocusChange(true);
+    injectKey(AKEYCODE_V);
+    EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
 } // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 59b0c04..c745505 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -694,6 +694,7 @@
             bool /*secure*/) override { return nullptr; }
     void destroyDisplay(const sp<IBinder>& /*display */) override {}
     std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
+    status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override { return NO_ERROR; }
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
     status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/,
                                  const Vector<ComposerState>& /*state*/,
@@ -904,6 +905,16 @@
 
     status_t getMaxAcquiredBufferCount(int* /*buffers*/) const override { return NO_ERROR; }
 
+    status_t addWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) const override {
+        return NO_ERROR;
+    }
+
+    status_t removeWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& /*windowInfosListener*/) const override {
+        return NO_ERROR;
+    }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
similarity index 87%
rename from libs/input/tests/InputWindow_test.cpp
rename to libs/gui/tests/WindowInfo_test.cpp
index 493f2f4..a4f436c 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -19,16 +19,20 @@
 #include <binder/Binder.h>
 #include <binder/Parcel.h>
 
-#include <input/InputWindow.h>
-#include <input/InputTransport.h>
+#include <gui/WindowInfo.h>
 
 using std::chrono_literals::operator""s;
 
 namespace android {
+
+using gui::InputApplicationInfo;
+using gui::TouchOcclusionMode;
+using gui::WindowInfo;
+
 namespace test {
 
-TEST(InputWindowInfo, ParcellingWithoutToken) {
-    InputWindowInfo i, i2;
+TEST(WindowInfo, ParcellingWithoutToken) {
+    WindowInfo i, i2;
     i.token = nullptr;
 
     Parcel p;
@@ -38,14 +42,15 @@
     ASSERT_TRUE(i2.token == nullptr);
 }
 
-TEST(InputWindowInfo, Parcelling) {
+TEST(WindowInfo, Parcelling) {
     sp<IBinder> touchableRegionCropHandle = new BBinder();
-    InputWindowInfo i;
+    WindowInfo i;
     i.token = new BBinder();
+    i.windowToken = new BBinder();
     i.id = 1;
     i.name = "Foobar";
-    i.flags = InputWindowInfo::Flag::SLIPPERY;
-    i.type = InputWindowInfo::Type::INPUT_METHOD;
+    i.flags = WindowInfo::Flag::SLIPPERY;
+    i.type = WindowInfo::Type::INPUT_METHOD;
     i.dispatchingTimeout = 12s;
     i.frameLeft = 93;
     i.frameTop = 34;
@@ -55,6 +60,7 @@
     i.globalScaleFactor = 0.3;
     i.alpha = 0.7;
     i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
+    i.displayOrientation = ui::Transform::ROT_0;
     i.displayWidth = 1000;
     i.displayHeight = 2000;
     i.visible = false;
@@ -65,7 +71,7 @@
     i.ownerPid = 19;
     i.ownerUid = 24;
     i.packageName = "com.example.package";
-    i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY;
+    i.inputFeatures = WindowInfo::Feature::DISABLE_USER_ACTIVITY;
     i.displayId = 34;
     i.portalToDisplayId = 2;
     i.replaceTouchableRegionWithCrop = true;
@@ -77,9 +83,10 @@
     Parcel p;
     i.writeToParcel(&p);
     p.setDataPosition(0);
-    InputWindowInfo i2;
+    WindowInfo i2;
     i2.readFromParcel(&p);
     ASSERT_EQ(i.token, i2.token);
+    ASSERT_EQ(i.windowToken, i2.windowToken);
     ASSERT_EQ(i.id, i2.id);
     ASSERT_EQ(i.name, i2.name);
     ASSERT_EQ(i.flags, i2.flags);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index a63ec8f..e73c3b8 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -30,7 +30,6 @@
         "android/os/IInputConstants.aidl",
         "android/os/InputEventInjectionResult.aidl",
         "android/os/InputEventInjectionSync.aidl",
-        "android/os/TouchOcclusionMode.aidl",
     ],
 }
 
@@ -79,16 +78,11 @@
         android: {
             srcs: [
                 "InputTransport.cpp",
-                "InputWindow.cpp",
-                "android/FocusRequest.aidl",
-                "android/InputApplicationInfo.aidl",
                 "android/os/BlockUntrustedTouchesMode.aidl",
                 "android/os/IInputConstants.aidl",
                 "android/os/IInputFlinger.aidl",
                 "android/os/InputEventInjectionResult.aidl",
                 "android/os/InputEventInjectionSync.aidl",
-                "android/os/ISetInputWindowsListener.aidl",
-                "android/os/TouchOcclusionMode.aidl",
             ],
 
             export_shared_lib_headers: ["libbinder"],
@@ -99,6 +93,14 @@
                 "libui",
             ],
 
+            static_libs: [
+                "libgui_window_info_static",
+            ],
+
+            export_static_lib_headers: [
+                "libgui_window_info_static",
+            ],
+
             sanitize: {
                 misc_undefined: ["integer"],
             },
@@ -114,26 +116,29 @@
         linux_glibc: {
             srcs: [
                 "InputTransport.cpp",
-                "InputWindow.cpp",
-                "android/FocusRequest.aidl",
-                "android/InputApplicationInfo.aidl",
                 "android/os/IInputConstants.aidl",
                 "android/os/IInputFlinger.aidl",
-                "android/os/ISetInputWindowsListener.aidl",
-                "android/os/TouchOcclusionMode.aidl",
             ],
             static_libs: [
                 "libhostgraphics",
+                "libgui_window_info_static",
             ],
             shared_libs: [
                 "libbinder",
             ],
+
+            export_static_lib_headers: [
+                "libgui_window_info_static",
+            ],
         },
     },
 
     aidl: {
         local_include_dirs: ["."],
         export_aidl_headers: true,
+        include_dirs: [
+            "frameworks/native/libs/gui",
+        ],
     },
 }
 
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index d954d23..5f440b7 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -25,6 +25,7 @@
 
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <gui/constants.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
@@ -66,23 +67,15 @@
     transformedPoint.y -= origin.y;
 
     // Derive the transformed vector's clockwise angle from vertical.
-    float result = atan2f(transformedPoint.x, -transformedPoint.y);
-    if (result < -M_PI_2) {
-        result += M_PI;
-    } else if (result > M_PI_2) {
-        result -= M_PI;
-    }
-    return result;
+    // The return value of atan2f is in range [-pi, pi] which conforms to the orientation API.
+    return atan2f(transformedPoint.x, -transformedPoint.y);
 }
 
-// Rotates the given point to the transform's orientation. If the display width and height are
+// Rotates the given point to the specified orientation. If the display width and height are
 // provided, the point is rotated in the screen space. Otherwise, the point is rotated about the
 // origin. This helper is used to avoid the extra overhead of creating new Transforms.
-vec2 rotatePoint(const ui::Transform& transform, float x, float y, int32_t displayWidth = 0,
+vec2 rotatePoint(uint32_t orientation, float x, float y, int32_t displayWidth = 0,
                  int32_t displayHeight = 0) {
-    // 0x7 encapsulates all 3 rotations (see ui::Transform::RotationFlags)
-    static const int ALL_ROTATIONS_MASK = 0x7;
-    const uint32_t orientation = (transform.getOrientation() & ALL_ROTATIONS_MASK);
     if (orientation == ui::Transform::ROT_0) {
         return {x, y};
     }
@@ -101,6 +94,19 @@
     return xy;
 }
 
+vec2 applyTransformWithoutTranslation(const ui::Transform& transform, float x, float y) {
+    const vec2 transformedXy = transform.transform(x, y);
+    const vec2 transformedOrigin = transform.transform(0, 0);
+    return transformedXy - transformedOrigin;
+}
+
+bool shouldDisregardWindowTranslation(uint32_t source) {
+    // Pointer events are the only type of events that refer to absolute coordinates on the display,
+    // so we should apply the entire window transform. For other types of events, we should make
+    // sure to not apply the window translation/offset.
+    return (source & AINPUT_SOURCE_CLASS_POINTER) == 0;
+}
+
 } // namespace
 
 const char* motionClassificationToString(MotionClassification classification) {
@@ -315,6 +321,8 @@
     scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor);
     scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor);
     scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_X, windowXScale);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale);
 }
 
 void PointerCoords::scale(float globalScaleFactor) {
@@ -383,6 +391,15 @@
     setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
     setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
 
+    if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_X) ||
+        BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_Y)) {
+        const ui::Transform rotation(transform.getOrientation());
+        const vec2 relativeXy = rotation.transform(getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+                                                   getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+        setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
+        setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
+    }
+
     if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_ORIENTATION)) {
         const float val = getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
         setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(transform, val));
@@ -410,9 +427,9 @@
                              int32_t buttonState, MotionClassification classification,
                              const ui::Transform& transform, float xPrecision, float yPrecision,
                              float rawXCursorPosition, float rawYCursorPosition,
-                             int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
-                             nsecs_t eventTime, size_t pointerCount,
-                             const PointerProperties* pointerProperties,
+                             uint32_t displayOrientation, int32_t displayWidth,
+                             int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+                             size_t pointerCount, const PointerProperties* pointerProperties,
                              const PointerCoords* pointerCoords) {
     InputEvent::initialize(id, deviceId, source, displayId, hmac);
     mAction = action;
@@ -427,6 +444,7 @@
     mYPrecision = yPrecision;
     mRawXCursorPosition = rawXCursorPosition;
     mRawYCursorPosition = rawYCursorPosition;
+    mDisplayOrientation = displayOrientation;
     mDisplayWidth = displayWidth;
     mDisplayHeight = displayHeight;
     mDownTime = downTime;
@@ -452,6 +470,7 @@
     mYPrecision = other->mYPrecision;
     mRawXCursorPosition = other->mRawXCursorPosition;
     mRawYCursorPosition = other->mRawYCursorPosition;
+    mDisplayOrientation = other->mDisplayOrientation;
     mDisplayWidth = other->mDisplayWidth;
     mDisplayHeight = other->mDisplayHeight;
     mDownTime = other->mDownTime;
@@ -521,25 +540,48 @@
     if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
         // For compatibility, convert raw coordinates into "oriented screen space". Once app
         // developers are educated about getRaw, we can consider removing this.
-        const vec2 xy = rotatePoint(mTransform, coords->getX(), coords->getY(), mDisplayWidth,
-                                    mDisplayHeight);
+        const vec2 xy = shouldDisregardWindowTranslation(mSource)
+                ? rotatePoint(mDisplayOrientation, coords->getX(), coords->getY())
+                : rotatePoint(mDisplayOrientation, coords->getX(), coords->getY(), mDisplayWidth,
+                              mDisplayHeight);
         static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
         return xy[axis];
     }
 
+    if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
+        // For compatibility, since we convert raw coordinates into "oriented screen space", we
+        // need to convert the relative axes into the same orientation for consistency.
+        const vec2 relativeXy = rotatePoint(mDisplayOrientation,
+                                            coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+                                            coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+        return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
+    }
+
     return coords->getAxisValue(axis);
 }
 
 float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
-        size_t historicalIndex) const {
+                                          size_t historicalIndex) const {
     const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
 
     if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
-        const vec2 xy = mTransform.transform(coords->getXYValue());
+        const vec2 xy = shouldDisregardWindowTranslation(mSource)
+                ? applyTransformWithoutTranslation(mTransform, coords->getX(), coords->getY())
+                : mTransform.transform(coords->getXYValue());
         static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
         return xy[axis];
     }
 
+    if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
+        const vec2 relativeXy =
+                applyTransformWithoutTranslation(mTransform,
+                                                 coords->getAxisValue(
+                                                         AMOTION_EVENT_AXIS_RELATIVE_X),
+                                                 coords->getAxisValue(
+                                                         AMOTION_EVENT_AXIS_RELATIVE_Y));
+        return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
+    }
+
     return coords->getAxisValue(axis);
 }
 
@@ -595,6 +637,13 @@
     // Apply the transformation to all samples.
     std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(),
                   [&transform](PointerCoords& c) { c.transform(transform); });
+
+    if (mRawXCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
+        mRawYCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
+        const vec2 cursor = transform.transform(mRawXCursorPosition, mRawYCursorPosition);
+        mRawXCursorPosition = cursor.x;
+        mRawYCursorPosition = cursor.y;
+    }
 }
 
 #ifdef __linux__
@@ -655,6 +704,7 @@
     mYPrecision = parcel->readFloat();
     mRawXCursorPosition = parcel->readFloat();
     mRawYCursorPosition = parcel->readFloat();
+    mDisplayOrientation = parcel->readUint32();
     mDisplayWidth = parcel->readInt32();
     mDisplayHeight = parcel->readInt32();
     mDownTime = parcel->readInt64();
@@ -716,6 +766,7 @@
     parcel->writeFloat(mYPrecision);
     parcel->writeFloat(mRawXCursorPosition);
     parcel->writeFloat(mRawYCursorPosition);
+    parcel->writeUint32(mDisplayOrientation);
     parcel->writeInt32(mDisplayWidth);
     parcel->writeInt32(mDisplayHeight);
     parcel->writeInt64(mDownTime);
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 30c42a3..220c8e1 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,9 +21,9 @@
 #include <ctype.h>
 
 #include <android-base/stringprintf.h>
+#include <ftl/NamedEnum.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
-#include <input/NamedEnum.h>
 
 using android::base::StringPrintf;
 
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index dd1c462..ea8b9a7 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -33,8 +33,8 @@
 #include <log/log.h>
 #include <utils/Trace.h>
 
+#include <ftl/NamedEnum.h>
 #include <input/InputTransport.h>
-#include <input/NamedEnum.h>
 
 using android::base::StringPrintf;
 
@@ -242,9 +242,11 @@
             msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
             // float yCursorPosition
             msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
-            // int32_t displayW
+            // uint32_t displayOrientation
+            msg->body.motion.displayOrientation = body.motion.displayOrientation;
+            // int32_t displayWidth
             msg->body.motion.displayWidth = body.motion.displayWidth;
-            // int32_t displayH
+            // int32_t displayHeight
             msg->body.motion.displayHeight = body.motion.displayHeight;
             // uint32_t pointerCount
             msg->body.motion.pointerCount = body.motion.pointerCount;
@@ -533,9 +535,10 @@
         std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
         int32_t edgeFlags, int32_t metaState, int32_t buttonState,
         MotionClassification classification, const ui::Transform& transform, float xPrecision,
-        float yPrecision, float xCursorPosition, float yCursorPosition, int32_t displayWidth,
-        int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
-        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
+        float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation,
+        int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+        uint32_t pointerCount, const PointerProperties* pointerProperties,
+        const PointerCoords* pointerCoords) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf(
                 "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
@@ -593,6 +596,7 @@
     msg.body.motion.yPrecision = yPrecision;
     msg.body.motion.xCursorPosition = xCursorPosition;
     msg.body.motion.yCursorPosition = yCursorPosition;
+    msg.body.motion.displayOrientation = displayOrientation;
     msg.body.motion.displayWidth = displayWidth;
     msg.body.motion.displayHeight = displayHeight;
     msg.body.motion.downTime = downTime;
@@ -1361,9 +1365,9 @@
                       msg->body.motion.buttonState, msg->body.motion.classification, transform,
                       msg->body.motion.xPrecision, msg->body.motion.yPrecision,
                       msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
-                      msg->body.motion.displayWidth, msg->body.motion.displayHeight,
-                      msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount,
-                      pointerProperties, pointerCoords);
+                      msg->body.motion.displayOrientation, msg->body.motion.displayWidth,
+                      msg->body.motion.displayHeight, msg->body.motion.downTime,
+                      msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
 }
 
 void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 44f3f34..3baeb55 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -28,10 +28,11 @@
 #include <input/KeyCharacterMap.h>
 #include <input/Keyboard.h>
 
-#include <utils/Log.h>
+#include <gui/constants.h>
 #include <utils/Errors.h>
-#include <utils/Tokenizer.h>
+#include <utils/Log.h>
 #include <utils/Timers.h>
+#include <utils/Tokenizer.h>
 
 // Enables debug output for the parser.
 #define DEBUG_PARSER 0
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index fa5a541..c365ab0 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -19,10 +19,10 @@
 #include <stdlib.h>
 
 #include <android/keycodes.h>
+#include <ftl/NamedEnum.h>
 #include <input/InputEventLabels.h>
 #include <input/KeyLayoutMap.h>
 #include <input/Keyboard.h>
-#include <input/NamedEnum.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
index 1771d19..00ebd4d 100644
--- a/libs/input/android/os/IInputFlinger.aidl
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -16,20 +16,13 @@
 
 package android.os;
 
-import android.FocusRequest;
 import android.InputChannel;
-import android.InputWindowInfo;
-import android.os.ISetInputWindowsListener;
+import android.gui.FocusRequest;
+import android.gui.WindowInfo;
 
 /** @hide */
 interface IInputFlinger
 {
-    // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the
-    // ordering when needed.
-    // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer
-    // shouldn't be a concern.
-    oneway void setInputWindows(in InputWindowInfo[] inputHandles,
-            in @nullable ISetInputWindowsListener setInputWindowsListener);
     InputChannel createInputChannel(in @utf8InCpp String name);
     void removeInputChannel(in IBinder connectionToken);
     /**
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 6ffc6a8..18ab1cb 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -11,26 +11,24 @@
 cc_test {
     name: "libinput_tests",
     srcs: [
-        "NamedEnum_test.cpp",
-        "Flags_test.cpp",
         "IdGenerator_test.cpp",
         "InputChannel_test.cpp",
         "InputDevice_test.cpp",
         "InputEvent_test.cpp",
         "InputPublisherAndConsumer_test.cpp",
-        "InputWindow_test.cpp",
         "TouchVideoFrame_test.cpp",
         "VelocityTracker_test.cpp",
         "VerifiedInputEvent_test.cpp",
     ],
+    static_libs: [
+        "libgui_window_info_static",
+        "libinput",
+    ],
     cflags: [
         "-Wall",
         "-Wextra",
         "-Werror",
     ],
-    static_libs: [
-        "libinput",
-    ],
     shared_libs: [
         "libbase",
         "libbinder",
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 3b76ddb..b1ef753 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -21,6 +21,7 @@
 #include <attestation/HmacKeyManager.h>
 #include <binder/Parcel.h>
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <input/Input.h>
 
 namespace android {
@@ -293,7 +294,7 @@
                       AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
                       MotionClassification::NONE, mTransform, 2.0f, 2.1f,
                       AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                      AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                      ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
                       ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
                       pointerCoords);
 
@@ -610,12 +611,12 @@
     }
     MotionEvent event;
     ui::Transform identityTransform;
-    event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID,
+    event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                      MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
                      0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
+                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
                      0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
                      pointerCoords);
     float originalRawX = 0 + 3;
@@ -658,13 +659,17 @@
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 }
 
-MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) {
+MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
+                                 const ui::Transform& transform,
+                                 uint32_t displayOrientation = ui::Transform::ROT_0) {
     std::vector<PointerProperties> pointerProperties;
     pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
     std::vector<PointerCoords> pointerCoords;
     pointerCoords.emplace_back().clear();
     pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x);
     pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+    pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, dx);
+    pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
     nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
     MotionEvent event;
     event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
@@ -672,7 +677,8 @@
                      /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
                      /* buttonState */ 0, MotionClassification::NONE, transform,
                      /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, displayOrientation,
+                     /* displayWidth */ 400,
                      /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
                      pointerProperties.data(), pointerCoords.data());
     return event;
@@ -683,26 +689,56 @@
     ui::Transform identity;
     ui::Transform xform(ui::Transform::ROT_90, 800, 400);
     xform.set(xform.tx() + 20, xform.ty() + 40);
-    MotionEvent event = createTouchDownEvent(60, 100, xform);
+    MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
     ASSERT_EQ(700, event.getRawX(0));
     ASSERT_EQ(60, event.getRawY(0));
     ASSERT_NE(event.getRawX(0), event.getX(0));
     ASSERT_NE(event.getRawY(0), event.getY(0));
+    // Relative values should be rotated but not translated.
+    ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+    ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
 
-    MotionEvent changedEvent = createTouchDownEvent(60, 100, identity);
+    MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity);
     const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
                                         xform[0][1], xform[1][1], xform[2][1],
                                         xform[0][2], xform[1][2], xform[2][2]};
     changedEvent.applyTransform(rowMajor);
 
     // transformContent effectively rotates the raw coordinates, so those should now include
-    // both rotation AND offset
+    // both rotation AND offset.
     ASSERT_EQ(720, changedEvent.getRawX(0));
     ASSERT_EQ(100, changedEvent.getRawY(0));
+    // Relative values should be rotated but not translated.
+    ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+    ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
 
-    // The transformed output should be the same then
+    // The transformed output should be the same then.
     ASSERT_NEAR(event.getX(0), changedEvent.getX(0), 0.001);
     ASSERT_NEAR(event.getY(0), changedEvent.getY(0), 0.001);
+    ASSERT_NEAR(event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0),
+                changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), 0.001);
+    ASSERT_NEAR(event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0),
+                changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001);
+}
+
+TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
+    constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL,
+                                                 AINPUT_SOURCE_MOUSE_RELATIVE,
+                                                 AINPUT_SOURCE_JOYSTICK};
+    for (uint32_t source : NON_POINTER_SOURCES) {
+        // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+        ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+        xform.set(xform.tx() + 20, xform.ty() + 40);
+        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
+        event.setSource(source);
+
+        // Since this event comes from a non-pointer source, it should include rotation but not
+        // translation/offset.
+        ASSERT_EQ(-100, event.getX(0));
+        ASSERT_EQ(60, event.getY(0));
+        ASSERT_EQ(event.getRawX(0), event.getX(0));
+        ASSERT_EQ(event.getRawY(0), event.getY(0));
+    }
 }
 
 TEST_F(MotionEventTest, RawCompatTransform) {
@@ -710,11 +746,14 @@
         // Make sure raw is raw regardless of transform translation.
         ui::Transform xform;
         xform.set(20, 40);
-        MotionEvent event = createTouchDownEvent(60, 100, xform);
+        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform);
         ASSERT_EQ(60, event.getRawX(0));
         ASSERT_EQ(100, event.getRawY(0));
         ASSERT_NE(event.getRawX(0), event.getX(0));
         ASSERT_NE(event.getRawY(0), event.getY(0));
+        // Relative values should not be modified.
+        ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+        ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
     }
 
     // Next check that getRaw contains rotation (for compatibility) but otherwise is still
@@ -723,29 +762,50 @@
         // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
         ui::Transform xform(ui::Transform::ROT_90, 800, 400);
         xform.set(xform.tx() + 20, xform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, xform);
+        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
         ASSERT_EQ(700, event.getRawX(0));
         ASSERT_EQ(60, event.getRawY(0));
         ASSERT_NE(event.getRawX(0), event.getX(0));
         ASSERT_NE(event.getRawY(0), event.getY(0));
+        // Relative values should be rotated but not translated.
+        ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+        ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
     }
 
     {
         // Same as above, but check rotate-180.
         ui::Transform xform(ui::Transform::ROT_180, 400, 800);
         xform.set(xform.tx() + 20, xform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, xform);
+        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_180);
         ASSERT_EQ(340, event.getRawX(0));
         ASSERT_EQ(700, event.getRawY(0));
+        // Relative values should be rotated but not translated.
+        ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+        ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
     }
 
     {
         // Same as above, but check rotate-270.
         ui::Transform xform(ui::Transform::ROT_270, 800, 400);
         xform.set(xform.tx() + 20, xform.ty() + 40);
-        MotionEvent event = createTouchDownEvent(60, 100, xform);
+        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_270);
         ASSERT_EQ(100, event.getRawX(0));
         ASSERT_EQ(340, event.getRawY(0));
+        // Relative values should be rotated but not translated.
+        ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+        ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
+    }
+
+    {
+        // Finally, check that raw isn't effected by transform
+        ui::Transform xform(ui::Transform::ROT_270, 800, 400);
+        xform.set(xform.tx() + 20, xform.ty() + 40);
+        MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
+        ASSERT_EQ(700, event.getRawX(0));
+        ASSERT_EQ(60, event.getRawY(0));
+        // Relative values should be rotated but not translated.
+        ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+        ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
     }
 }
 
@@ -772,9 +832,9 @@
                          DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
                          identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                         AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
-                         pointerCount, pointerProperties, pointerCoords);
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                         INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+                         0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
         ASSERT_EQ(classification, event.getClassification());
     }
 }
@@ -794,10 +854,9 @@
     event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
                      AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
-                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
-                     pointerCoords);
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
+                     pointerCount, pointerProperties, pointerCoords);
     event.offsetLocation(20, 60);
     ASSERT_EQ(280, event.getRawXCursorPosition());
     ASSERT_EQ(540, event.getRawYCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index a2cfaa1..5d1f2c3 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -23,6 +23,7 @@
 #include <attestation/HmacKeyManager.h>
 #include <cutils/ashmem.h>
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <input/InputTransport.h>
 #include <utils/StopWatch.h>
 #include <utils/Timers.h>
@@ -162,6 +163,7 @@
     constexpr float yPrecision = 0.5;
     constexpr float xCursorPosition = 1.3;
     constexpr float yCursorPosition = 50.6;
+    constexpr uint32_t displayOrientation = ui::Transform::ROT_0;
     constexpr int32_t displayWidth = 1000;
     constexpr int32_t displayHeight = 2000;
     constexpr nsecs_t downTime = 3;
@@ -192,9 +194,9 @@
     status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
                                             actionButton, flags, edgeFlags, metaState, buttonState,
                                             classification, transform, xPrecision, yPrecision,
-                                            xCursorPosition, yCursorPosition, displayWidth,
-                                            displayHeight, downTime, eventTime, pointerCount,
-                                            pointerProperties, pointerCoords);
+                                            xCursorPosition, yCursorPosition, displayOrientation,
+                                            displayWidth, displayHeight, downTime, eventTime,
+                                            pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
@@ -231,6 +233,7 @@
     EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
     EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
     EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
+    EXPECT_EQ(displayOrientation, motionEvent->getDisplayOrientation());
     EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x);
     EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y);
     EXPECT_EQ(downTime, motionEvent->getDownTime());
@@ -460,8 +463,9 @@
     status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
                                             0, 0, 0, MotionClassification::NONE, identityTransform,
                                             0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
-                                            pointerCount, pointerProperties, pointerCoords);
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
+                                            pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -476,8 +480,9 @@
     status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
                                             0, 0, 0, MotionClassification::NONE, identityTransform,
                                             0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
-                                            pointerCount, pointerProperties, pointerCoords);
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
+                                            pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -497,8 +502,9 @@
     status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
                                             0, 0, 0, MotionClassification::NONE, identityTransform,
                                             0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0,
-                                            pointerCount, pointerProperties, pointerCoords);
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
+                                            pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 5861d55..59fed1f 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -74,10 +74,10 @@
   CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
   CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
   CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
-  CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 136);
-  CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 140);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 144);
-  CHECK_OFFSET(InputMessage::Body::Motion, empty3, 148);
+  CHECK_OFFSET(InputMessage::Body::Motion, displayOrientation, 136);
+  CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 140);
+  CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 144);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 148);
   CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152);
 
   CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index aefc2ec..13e2b02 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -23,6 +23,7 @@
 #include <android-base/stringprintf.h>
 #include <attestation/HmacKeyManager.h>
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <input/VelocityTracker.h>
 
 using namespace std::chrono_literals;
@@ -183,8 +184,8 @@
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                          MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
                          0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                         AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                         INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
                          entry.eventTime.count(), pointerCount, properties, coords);
 
         events.emplace_back(event);
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index f79098c..b29c9a4 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -16,6 +16,7 @@
 
 #include <attestation/HmacKeyManager.h>
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <input/Input.h>
 
 namespace android {
@@ -46,10 +47,9 @@
                      INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
                      AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
                      MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
-                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     100 /*downTime*/, 200 /*eventTime*/, pointerCount, pointerProperties,
-                     pointerCoords);
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 100 /*downTime*/,
+                     200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
     return event;
 }
 
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 0edb213..2dd6c4f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -20,7 +20,6 @@
 #include <android-base/thread_annotations.h>
 #include <gui/DisplayEventDispatcher.h>
 #include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
 #include <jni.h>
 #include <private/android/choreographer.h>
 #include <utils/Looper.h>
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 4cb1b42..14627ce 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -71,7 +71,7 @@
     void cleanupPostRender() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
-    void onPrimaryDisplaySizeChanged(ui::Size size) override {}
+    void onActiveDisplaySizeChanged(ui::Size size) override {}
 
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 5964bc3..967cf5d 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -129,9 +129,9 @@
     // Attempt to switch RenderEngine into and out of protectedContext mode
     virtual void useProtectedContext(bool useProtectedContext) = 0;
 
-    // Notify RenderEngine of changes to the dimensions of the primary display
+    // Notify RenderEngine of changes to the dimensions of the active display
     // so that it can configure its internal caches accordingly.
-    virtual void onPrimaryDisplaySizeChanged(ui::Size size) = 0;
+    virtual void onActiveDisplaySizeChanged(ui::Size size) = 0;
 
     // Renders layers for a particular display via GPU composition. This method
     // should be called for every display that needs to be rendered via the GPU.
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index a4aa9ea..0be3ba6 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -54,7 +54,7 @@
     MOCK_METHOD0(cleanFramebufferCache, void());
     MOCK_METHOD0(getContextPriority, int());
     MOCK_METHOD0(supportsBackgroundBlur, bool());
-    MOCK_METHOD1(onPrimaryDisplaySizeChanged, void(ui::Size));
+    MOCK_METHOD1(onActiveDisplaySizeChanged, void(ui::Size));
 
 protected:
     // mock renderengine still needs to implement these, but callers should never need to call them.
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 94023e6..e42b5b9 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1413,7 +1413,7 @@
     return value;
 }
 
-void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) {
+void SkiaGLRenderEngine::onActiveDisplaySizeChanged(ui::Size size) {
     // This cache multiplier was selected based on review of cache sizes relative
     // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x),
     // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 238ad8f..e1162f5 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -67,7 +67,7 @@
     void useProtectedContext(bool useProtectedContext) override;
     bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
     void assertShadersCompiled(int numShaders) override;
-    void onPrimaryDisplaySizeChanged(ui::Size size) override;
+    void onActiveDisplaySizeChanged(ui::Size size) override;
     int reportShadersCompiled() override;
 
 protected:
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index b9dabc1..8e666d5 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -361,14 +361,14 @@
     return mRenderEngine->supportsBackgroundBlur();
 }
 
-void RenderEngineThreaded::onPrimaryDisplaySizeChanged(ui::Size size) {
+void RenderEngineThreaded::onActiveDisplaySizeChanged(ui::Size size) {
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::onPrimaryDisplaySizeChanged");
-            instance.onPrimaryDisplaySizeChanged(size);
+            ATRACE_NAME("REThreaded::onActiveDisplaySizeChanged");
+            instance.onActiveDisplaySizeChanged(size);
         });
     }
     mCondition.notify_one();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index f2f5c0f..b197df7 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -65,7 +65,7 @@
     void cleanFramebufferCache() override;
     int getContextPriority() override;
     bool supportsBackgroundBlur() override;
-    void onPrimaryDisplaySizeChanged(ui::Size size) override;
+    void onActiveDisplaySizeChanged(ui::Size size) override;
 
 protected:
     void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 9dc9beb..80f6c82 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -939,7 +939,7 @@
              << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
              << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
              << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
-             << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace)
+             << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace) << std::dec
              << ", compressed: ";
 
     if (less) {
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
index 749c568..980b08b 100644
--- a/libs/vibrator/ExternalVibrationUtils.cpp
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -56,6 +56,36 @@
     }
 }
 
+void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
+    if (scale == HapticScale::MUTE) {
+        memset(buffer, 0, length * sizeof(float));
+        return;
+    }
+    if (scale == HapticScale::NONE) {
+        return;
+    }
+    float gamma = getHapticScaleGamma(scale);
+    float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale);
+    for (size_t i = 0; i < length; i++) {
+        float sign = buffer[i] >= 0 ? 1.0 : -1.0;
+        buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+                * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+    }
+}
+
+void clipHapticData(float* buffer, size_t length, float limit) {
+    if (isnan(limit) || limit == 0) {
+        return;
+    }
+    limit = fabsf(limit);
+    for (size_t i = 0; i < length; i++) {
+        float sign = buffer[i] >= 0 ? 1.0 : -1.0;
+        if (fabsf(buffer[i]) > limit) {
+            buffer[i] = limit * sign;
+        }
+    }
+}
+
 } // namespace
 
 bool isValidHapticScale(HapticScale scale) {
@@ -71,21 +101,11 @@
     return false;
 }
 
-void scaleHapticData(float* buffer, size_t length, HapticScale scale) {
-    if (!isValidHapticScale(scale) || scale == HapticScale::NONE) {
-        return;
+void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit) {
+    if (isValidHapticScale(scale)) {
+        applyHapticScale(buffer, length, scale);
     }
-    if (scale == HapticScale::MUTE) {
-        memset(buffer, 0, length * sizeof(float));
-        return;
-    }
-    float gamma = getHapticScaleGamma(scale);
-    float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale);
-    for (size_t i = 0; i < length; i++) {
-        float sign = buffer[i] >= 0 ? 1.0 : -1.0;
-        buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
-                * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
-    }
+    clipHapticData(buffer, length, limit);
 }
 
 } // namespace android::os
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index 20045d0..84357fc 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -32,7 +32,11 @@
 
 bool isValidHapticScale(HapticScale scale);
 
-void scaleHapticData(float* buffer, size_t length, HapticScale scale);
+/* Scales the haptic data in given buffer using the selected HapticScale and ensuring no absolute
+ * value will be larger than the absolute of given limit.
+ * The limit will be ignored if it is NaN or zero.
+ */
+void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit);
 
 } // namespace android::os
 
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 6612a93..73e5749 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -73,6 +73,7 @@
         "libui",
         "lib-platform-compat-native-api",
         "server_configurable_flags",
+        "InputFlingerProperties",
     ],
     static_libs: [
         "libattestation",
@@ -95,6 +96,7 @@
         "libinputflinger_base",
         "libinputreporter",
         "libinputreader",
+        "libgui",
     ],
     static_libs: [
         "libinputdispatcher",
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index a50e5c7..7b3658d 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -31,6 +31,10 @@
 
 namespace android {
 
+using gui::FocusRequest;
+using gui::WindowInfo;
+using gui::WindowInfoHandle;
+
 static int32_t exceptionCodeFromStatusT(status_t status) {
     switch (status) {
         case OK:
@@ -110,33 +114,6 @@
     return mDispatcher;
 }
 
-class BinderWindowHandle : public InputWindowHandle {
-public:
-    BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; }
-
-    bool updateInfo() override {
-        return true;
-    }
-};
-
-binder::Status InputManager::setInputWindows(
-        const std::vector<InputWindowInfo>& infos,
-        const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay;
-
-    std::vector<sp<InputWindowHandle>> handles;
-    for (const auto& info : infos) {
-        handlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
-        handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info));
-    }
-    mDispatcher->setInputWindows(handlesPerDisplay);
-
-    if (setInputWindowsListener) {
-        setInputWindowsListener->onSetInputWindowsFinished();
-    }
-    return binder::Status::ok();
-}
-
 // Used by tests only.
 binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) {
     IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 49bea13..4c07c22 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -26,7 +26,6 @@
 
 #include <InputDispatcherInterface.h>
 #include <InputDispatcherPolicyInterface.h>
-#include <android/os/ISetInputWindowsListener.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
 
@@ -38,7 +37,6 @@
 #include <utils/Vector.h>
 
 using android::os::BnInputFlinger;
-using android::os::ISetInputWindowsListener;
 
 namespace android {
 class InputChannel;
@@ -104,13 +102,9 @@
     sp<InputDispatcherInterface> getDispatcher() override;
 
     status_t dump(int fd, const Vector<String16>& args) override;
-    binder::Status setInputWindows(
-            const std::vector<InputWindowInfo>& handles,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
-
     binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
     binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
-    binder::Status setFocusedWindow(const FocusRequest&) override;
+    binder::Status setFocusedWindow(const gui::FocusRequest&) override;
 
 private:
     sp<InputReaderInterface> mReader;
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index d34482f..05ef489 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -19,9 +19,9 @@
 //#define LOG_NDEBUG 0
 
 #include "InputReaderBase.h"
+#include <ftl/NamedEnum.h>
 #include "input/DisplayViewport.h"
 #include "input/Input.h"
-#include "input/NamedEnum.h"
 
 #include <android/log.h>
 #include <android-base/stringprintf.h>
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index aa8cc30..6ce0313 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -18,8 +18,11 @@
 
 #include <android/os/IInputConstants.h>
 #include <binder/Binder.h>
+#include <gui/constants.h>
 #include "../dispatcher/InputDispatcher.h"
 
+using android::gui::WindowInfo;
+using android::gui::WindowInfoHandle;
 using android::os::IInputConstants;
 using android::os::InputEventInjectionResult;
 using android::os::InputEventInjectionSync;
@@ -173,7 +176,7 @@
     PreallocatedInputEventFactory mEventFactory;
 };
 
-class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+class FakeWindowHandle : public WindowInfoHandle, public FakeInputReceiver {
 public:
     static const int32_t WIDTH = 200;
     static const int32_t HEIGHT = 200;
@@ -182,13 +185,14 @@
                      const sp<InputDispatcher>& dispatcher, const std::string name)
           : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
         inputApplicationHandle->updateInfo();
+        updateInfo();
         mInfo.applicationInfo = *inputApplicationHandle->getInfo();
     }
 
-    virtual bool updateInfo() override {
+    void updateInfo() {
         mInfo.token = mClientChannel->getConnectionToken();
         mInfo.name = "FakeWindowHandle";
-        mInfo.type = InputWindowInfo::Type::APPLICATION;
+        mInfo.type = WindowInfo::Type::APPLICATION;
         mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
         mInfo.frameLeft = mFrame.left;
         mInfo.frameTop = mFrame.top;
@@ -204,8 +208,6 @@
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.displayId = ADISPLAY_ID_DEFAULT;
-
-        return true;
     }
 
 protected:
@@ -234,8 +236,8 @@
                      /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
                      identityTransform, /* xPrecision */ 0,
                      /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, currentTime, currentTime,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, currentTime, currentTime,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     return event;
 }
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 1b3888b..171f2b5 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -64,9 +64,11 @@
         "libstatspull",
         "libstatssocket",
         "libui",
+        "libgui",
         "libutils",
         "lib-platform-compat-native-api",
         "server_configurable_flags",
+        "InputFlingerProperties",
     ],
     static_libs: [
         "libattestation",
diff --git a/services/inputflinger/dispatcher/DragState.cpp b/services/inputflinger/dispatcher/DragState.cpp
index 2e2df43..e1844a4 100644
--- a/services/inputflinger/dispatcher/DragState.cpp
+++ b/services/inputflinger/dispatcher/DragState.cpp
@@ -16,9 +16,7 @@
 
 #include "DragState.h"
 #include <android-base/stringprintf.h>
-#include <input/InputWindow.h>
 
-using android::InputWindowHandle;
 using android::base::StringPrintf;
 
 namespace android::inputdispatcher {
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index 06453d8..b3c5709 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -17,22 +17,22 @@
 #ifndef _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
 #define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
 
+#include <gui/WindowInfo.h>
 #include <utils/RefBase.h>
 #include <string>
 
 namespace android {
 
-class InputWindowHandle;
-
 namespace inputdispatcher {
+
 struct DragState {
-    DragState(const sp<android::InputWindowHandle>& windowHandle) : dragWindow(windowHandle) {}
+    DragState(const sp<android::gui::WindowInfoHandle>& windowHandle) : dragWindow(windowHandle) {}
     void dump(std::string& dump, const char* prefix = "");
 
     // The window being dragged.
-    const sp<InputWindowHandle> dragWindow;
+    const sp<android::gui::WindowInfoHandle> dragWindow;
     // The last drag hover window which could receive the drag event.
-    sp<InputWindowHandle> dragHoverWindowHandle;
+    sp<android::gui::WindowInfoHandle> dragHoverWindowHandle;
     // Indicates the if received first event to check for button state.
     bool isStartDrag = false;
     // Indicate if the stylus button is down at the start of the drag.
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 5c3747e..82b4fe4 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -296,12 +296,14 @@
 volatile int32_t DispatchEntry::sNextSeqAtomic;
 
 DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
-                             ui::Transform transform, float globalScaleFactor, int2 displaySize)
+                             ui::Transform transform, float globalScaleFactor,
+                             uint32_t displayOrientation, int2 displaySize)
       : seq(nextSeq()),
         eventEntry(std::move(eventEntry)),
         targetFlags(targetFlags),
         transform(transform),
         globalScaleFactor(globalScaleFactor),
+        displayOrientation(displayOrientation),
         displaySize(displaySize),
         deliveryTime(0),
         resolvedAction(0),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 6f1dfad..ffe3bb6 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -20,8 +20,8 @@
 #include "InjectionState.h"
 #include "InputTarget.h"
 
+#include <gui/InputApplication.h>
 #include <input/Input.h>
-#include <input/InputApplication.h>
 #include <stdint.h>
 #include <utils/Timers.h>
 #include <functional>
@@ -215,6 +215,7 @@
     int32_t targetFlags;
     ui::Transform transform;
     float globalScaleFactor;
+    uint32_t displayOrientation;
     int2 displaySize;
     // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
     // and will be undefined before that.
@@ -228,7 +229,8 @@
     int32_t resolvedFlags;
 
     DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
-                  ui::Transform transform, float globalScaleFactor, int2 displaySize);
+                  ui::Transform transform, float globalScaleFactor, uint32_t displayOrientation,
+                  int2 displaySize);
 
     inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
 
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index fb19435..4a75773 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -27,12 +27,15 @@
 
 #include <android-base/stringprintf.h>
 #include <binder/Binder.h>
-#include <input/InputWindow.h>
-#include <input/NamedEnum.h>
+#include <ftl/NamedEnum.h>
+#include <gui/WindowInfo.h>
 #include <log/log.h>
 
 #include "FocusResolver.h"
 
+using android::gui::FocusRequest;
+using android::gui::WindowInfoHandle;
+
 namespace android::inputdispatcher {
 
 sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
@@ -52,7 +55,7 @@
  * we will check if the previous focus request is eligible to receive focus.
  */
 std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
-        int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
+        int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
     std::string removeFocusReason;
 
     // Check if the currently focused window is still focusable.
@@ -87,7 +90,7 @@
 }
 
 std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
-        const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
+        const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) {
     const int32_t displayId = request.displayId;
     const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
     if (currentFocus == request.token) {
@@ -136,11 +139,11 @@
 }
 
 FocusResolver::Focusability FocusResolver::isTokenFocusable(
-        const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
+        const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows) {
     bool allWindowsAreFocusable = true;
     bool visibleWindowFound = false;
     bool windowFound = false;
-    for (const sp<InputWindowHandle>& window : windows) {
+    for (const sp<WindowInfoHandle>& window : windows) {
         if (window->getToken() != token) {
             continue;
         }
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index afe16b3..1d6cd9a 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -20,9 +20,9 @@
 #include <optional>
 #include <unordered_map>
 
-#include <android/FocusRequest.h>
+#include <android/gui/FocusRequest.h>
 #include <binder/Binder.h>
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
 
 namespace android::inputdispatcher {
 
@@ -58,9 +58,10 @@
         std::string reason;
     };
     std::optional<FocusResolver::FocusChanges> setInputWindows(
-            int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows);
+            int32_t displayId, const std::vector<sp<android::gui::WindowInfoHandle>>& windows);
     std::optional<FocusResolver::FocusChanges> setFocusedWindow(
-            const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows);
+            const android::gui::FocusRequest& request,
+            const std::vector<sp<android::gui::WindowInfoHandle>>& windows);
 
     // Display has been removed from the system, clean up old references.
     void displayRemoved(int32_t displayId);
@@ -87,8 +88,9 @@
     // we expect the focusability of the windows to match since its hard to reason why one window
     // can receive focus events and the other cannot when both are backed by the same input channel.
     //
-    static Focusability isTokenFocusable(const sp<IBinder>& token,
-                                         const std::vector<sp<InputWindowHandle>>& windows);
+    static Focusability isTokenFocusable(
+            const sp<IBinder>& token,
+            const std::vector<sp<android::gui::WindowInfoHandle>>& windows);
 
     // Focus tracking for keys, trackball, etc. A window token can be associated with one or
     // more InputWindowHandles. If a window is mirrored, the window and its mirror will share
@@ -99,7 +101,7 @@
 
     // This map will store the focus request per display. When the input window handles are updated,
     // the current request will be checked to see if it can be processed at that time.
-    std::unordered_map<int32_t /* displayId */, FocusRequest> mFocusRequestByDisplay;
+    std::unordered_map<int32_t /* displayId */, android::gui::FocusRequest> mFocusRequestByDisplay;
 
     // Last reason for not granting a focus request. This is used to add more debug information
     // in the event logs.
@@ -108,7 +110,7 @@
     std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
             int32_t displayId, const std::string& reason, const sp<IBinder>& token,
             const std::string& tokenName = "");
-    std::optional<FocusRequest> getFocusRequest(int32_t displayId);
+    std::optional<android::gui::FocusRequest> getFocusRequest(int32_t displayId);
 };
 
 } // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index e6495e2..dccfa99 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -47,6 +47,7 @@
 // Log debug messages about hover events.
 #define DEBUG_HOVER 0
 
+#include <InputFlingerProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -54,8 +55,8 @@
 #include <binder/Binder.h>
 #include <binder/IServiceManager.h>
 #include <com/android/internal/compat/IPlatformCompatNative.h>
+#include <gui/SurfaceComposerClient.h>
 #include <input/InputDevice.h>
-#include <input/InputWindow.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
 #include <powermanager/PowerManager.h>
@@ -81,6 +82,10 @@
 using android::base::HwTimeoutMultiplier;
 using android::base::Result;
 using android::base::StringPrintf;
+using android::gui::FocusRequest;
+using android::gui::TouchOcclusionMode;
+using android::gui::WindowInfo;
+using android::gui::WindowInfoHandle;
 using android::os::BlockUntrustedTouchesMode;
 using android::os::IInputConstants;
 using android::os::InputEventInjectionResult;
@@ -93,7 +98,8 @@
 // coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
 static bool isPerWindowInputRotationEnabled() {
     static const bool PER_WINDOW_INPUT_ROTATION =
-            base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
+            sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
+
     return PER_WINDOW_INPUT_ROTATION;
 }
 
@@ -290,7 +296,7 @@
     return it != map.end() ? it->second : V{};
 }
 
-static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
+static bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
     if (first == second) {
         return true;
     }
@@ -302,7 +308,7 @@
     return first->getToken() == second->getToken();
 }
 
-static bool haveSameApplicationToken(const InputWindowInfo* first, const InputWindowInfo* second) {
+static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
     if (first == nullptr || second == nullptr) {
         return false;
     }
@@ -319,13 +325,14 @@
                                                           int32_t inputTargetFlags) {
     if (eventEntry->type == EventEntry::Type::MOTION) {
         const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
-        if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) == 0) {
+        if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) ||
+            (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) {
             const ui::Transform identityTransform;
-            // Use identity transform for events that are not pointer events because their axes
-            // values do not represent on-screen coordinates, so they should not have any window
-            // transformations applied to them.
+            // Use identity transform for joystick and position-based (touchpad) events because they
+            // don't depend on the window transform.
             return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
                                                    1.0f /*globalScaleFactor*/,
+                                                   inputTarget.displayOrientation,
                                                    inputTarget.displaySize);
         }
     }
@@ -334,6 +341,7 @@
         const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
         return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
                                                inputTarget.globalScaleFactor,
+                                               inputTarget.displayOrientation,
                                                inputTarget.displaySize);
     }
 
@@ -386,6 +394,7 @@
     std::unique_ptr<DispatchEntry> dispatchEntry =
             std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
                                             firstPointerTransform, inputTarget.globalScaleFactor,
+                                            inputTarget.displayOrientation,
                                             inputTarget.displaySize);
     return dispatchEntry;
 }
@@ -550,6 +559,10 @@
     }
 }
 
+void InputDispatcher::onFirstRef() {
+    SurfaceComposerClient::getDefault()->addWindowInfosListener(this);
+}
+
 status_t InputDispatcher::start() {
     if (mThread) {
         return ALREADY_EXISTS;
@@ -623,7 +636,7 @@
         return; // The focused application has changed.
     }
 
-    const sp<InputWindowHandle>& focusedWindowHandle =
+    const sp<WindowInfoHandle>& focusedWindowHandle =
             getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
     if (focusedWindowHandle != nullptr) {
         return; // We now have a focused window. No need for ANR.
@@ -672,7 +685,7 @@
 }
 
 std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
-    sp<InputWindowHandle> window = getWindowHandleLocked(token);
+    sp<WindowInfoHandle> window = getWindowHandleLocked(token);
     if (window != nullptr) {
         return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
     }
@@ -884,7 +897,7 @@
                 motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
         int32_t y = static_cast<int32_t>(
                 motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-        sp<InputWindowHandle> touchedWindowHandle =
+        sp<WindowInfoHandle> touchedWindowHandle =
                 findTouchedWindowAtLocked(displayId, x, y, nullptr);
         if (touchedWindowHandle != nullptr &&
             touchedWindowHandle->getApplicationToken() !=
@@ -989,29 +1002,29 @@
     }
 }
 
-sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
-                                                                 int32_t y, TouchState* touchState,
-                                                                 bool addOutsideTargets,
-                                                                 bool addPortalWindows,
-                                                                 bool ignoreDragWindow) {
+sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
+                                                                int32_t y, TouchState* touchState,
+                                                                bool addOutsideTargets,
+                                                                bool addPortalWindows,
+                                                                bool ignoreDragWindow) {
     if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
         LOG_ALWAYS_FATAL(
                 "Must provide a valid touch state if adding portal windows or outside targets");
     }
     // Traverse windows from front to back to find touched window.
-    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
-    for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
         if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
             continue;
         }
-        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        const WindowInfo* windowInfo = windowHandle->getInfo();
         if (windowInfo->displayId == displayId) {
             auto flags = windowInfo->flags;
 
             if (windowInfo->visible) {
-                if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
-                    bool isTouchModal = !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) &&
-                            !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+                if (!flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
+                    bool isTouchModal = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
+                            !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);
                     if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                         int32_t portalToDisplayId = windowInfo->portalToDisplayId;
                         if (portalToDisplayId != ADISPLAY_ID_NONE &&
@@ -1028,7 +1041,7 @@
                     }
                 }
 
-                if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
+                if (addOutsideTargets && flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
                     touchState->addOrUpdateWindow(windowHandle,
                                                   InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
                                                   BitSet32(0));
@@ -1040,13 +1053,13 @@
 }
 
 std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
-        int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const {
+        int32_t displayId, const std::vector<sp<WindowInfoHandle>>& portalWindows) const {
     std::vector<TouchedMonitor> touchedMonitors;
 
     std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
     addGestureMonitors(monitors, touchedMonitors);
-    for (const sp<InputWindowHandle>& portalWindow : portalWindows) {
-        const InputWindowInfo* windowInfo = portalWindow->getInfo();
+    for (const sp<WindowInfoHandle>& portalWindow : portalWindows) {
+        const WindowInfo* windowInfo = portalWindow->getInfo();
         monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
         addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft,
                            -windowInfo->frameTop);
@@ -1609,7 +1622,7 @@
                 // The event has gone through these portal windows, so we add monitoring targets of
                 // the corresponding displays as well.
                 for (size_t i = 0; i < state.portalWindows.size(); i++) {
-                    const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo();
+                    const WindowInfo* windowInfo = state.portalWindows[i]->getInfo();
                     addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
                                                      -windowInfo->frameLeft, -windowInfo->frameTop);
                 }
@@ -1627,7 +1640,7 @@
     return true;
 }
 
-void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle,
+void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowHandle,
                                              bool isExiting, const MotionEntry& motionEntry) {
     // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should
     // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid.
@@ -1807,7 +1820,7 @@
     std::string reason;
 
     int32_t displayId = getTargetDisplayId(entry);
-    sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
+    sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
     std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
             getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
 
@@ -1820,6 +1833,11 @@
         return InputEventInjectionResult::FAILED;
     }
 
+    // Drop key events if requested by input feature
+    if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
+        return InputEventInjectionResult::FAILED;
+    }
+
     // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
     // Only start counting when we have a focused event to dispatch. The ANR is canceled if we
     // start interacting with another application via touch (app switch). This code can be removed
@@ -1934,8 +1952,8 @@
     // Update the touch state as needed based on the properties of the touch event.
     InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
     InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
-    sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle);
-    sp<InputWindowHandle> newTouchedWindowHandle;
+    sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle);
+    sp<WindowInfoHandle> newTouchedWindowHandle;
 
     // Copy current touch state into tempTouchState.
     // This state will be used to update mTouchStatesByDisplay at the end of this function.
@@ -2066,6 +2084,11 @@
             }
         }
 
+        // Drop touch events if requested by input feature
+        if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) {
+            newTouchedWindowHandle = nullptr;
+        }
+
         // Also don't send the new touch event to unresponsive gesture monitors
         newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
 
@@ -2128,9 +2151,16 @@
             int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
             int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
 
-            sp<InputWindowHandle> oldTouchedWindowHandle =
+            sp<WindowInfoHandle> oldTouchedWindowHandle =
                     tempTouchState.getFirstForegroundWindowHandle();
             newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
+
+            // Drop touch events if requested by input feature
+            if (newTouchedWindowHandle != nullptr &&
+                shouldDropInput(entry, newTouchedWindowHandle)) {
+                newTouchedWindowHandle = nullptr;
+            }
+
             if (oldTouchedWindowHandle != newTouchedWindowHandle &&
                 oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
                 if (DEBUG_FOCUS) {
@@ -2227,15 +2257,15 @@
     // Check whether windows listening for outside touches are owned by the same UID. If it is
     // set the policy flag that we will not reveal coordinate information to this window.
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
-        sp<InputWindowHandle> foregroundWindowHandle =
+        sp<WindowInfoHandle> foregroundWindowHandle =
                 tempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle) {
             const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
             for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
                 if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
-                    sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
-                    if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
-                        tempTouchState.addOrUpdateWindow(inputWindowHandle,
+                    sp<WindowInfoHandle> windowInfoHandle = touchedWindow.windowHandle;
+                    if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) {
+                        tempTouchState.addOrUpdateWindow(windowInfoHandle,
                                                          InputTarget::FLAG_ZERO_COORDS,
                                                          BitSet32(0));
                     }
@@ -2251,15 +2281,15 @@
     // engine only supports touch events.  We would need to add a mechanism similar
     // to View.onGenericMotionEvent to enable wallpapers to handle these events.
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
-        sp<InputWindowHandle> foregroundWindowHandle =
+        sp<WindowInfoHandle> foregroundWindowHandle =
                 tempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
-            const std::vector<sp<InputWindowHandle>>& windowHandles =
+            const std::vector<sp<WindowInfoHandle>>& windowHandles =
                     getWindowHandlesLocked(displayId);
-            for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
-                const InputWindowInfo* info = windowHandle->getInfo();
+            for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
+                const WindowInfo* info = windowHandle->getInfo();
                 if (info->displayId == displayId &&
-                    windowHandle->getInfo()->type == InputWindowInfo::Type::WALLPAPER) {
+                    windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) {
                     tempTouchState
                             .addOrUpdateWindow(windowHandle,
                                                InputTarget::FLAG_WINDOW_IS_OBSCURED |
@@ -2378,7 +2408,7 @@
 }
 
 void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
-    const sp<InputWindowHandle> dropWindow =
+    const sp<WindowInfoHandle> dropWindow =
             findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/,
                                       false /*addOutsideTargets*/, false /*addPortalWindows*/,
                                       true /*ignoreDragWindow*/);
@@ -2413,7 +2443,7 @@
             return;
         }
 
-        const sp<InputWindowHandle> hoverWindowHandle =
+        const sp<WindowInfoHandle> hoverWindowHandle =
                 findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
                                           false /*addOutsideTargets*/, false /*addPortalWindows*/,
                                           true /*ignoreDragWindow*/);
@@ -2438,7 +2468,7 @@
     }
 }
 
-void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
+void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
                                             int32_t targetFlags, BitSet32 pointerIds,
                                             std::vector<InputTarget>& inputTargets) {
     std::vector<InputTarget>::iterator it =
@@ -2448,7 +2478,7 @@
                                      windowHandle->getToken();
                          });
 
-    const InputWindowInfo* windowInfo = windowHandle->getInfo();
+    const WindowInfo* windowInfo = windowHandle->getInfo();
 
     if (it == inputTargets.end()) {
         InputTarget inputTarget;
@@ -2461,6 +2491,7 @@
         inputTarget.inputChannel = inputChannel;
         inputTarget.flags = targetFlags;
         inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+        inputTarget.displayOrientation = windowInfo->displayOrientation;
         inputTarget.displaySize =
                 int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
         inputTargets.push_back(inputTarget);
@@ -2499,7 +2530,7 @@
     inputTargets.push_back(target);
 }
 
-bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
+bool InputDispatcher::checkInjectionPermission(const sp<WindowInfoHandle>& windowHandle,
                                                const InjectionState* injectionState) {
     if (injectionState &&
         (windowHandle == nullptr ||
@@ -2524,8 +2555,8 @@
  * another window handle. We only check a few preconditions. Actually
  * checking the bounds is left to the caller.
  */
-static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle,
-                            const sp<InputWindowHandle>& otherHandle) {
+static bool canBeObscuredBy(const sp<WindowInfoHandle>& windowHandle,
+                            const sp<WindowInfoHandle>& otherHandle) {
     // Compare by token so cloned layers aren't counted
     if (haveSameToken(windowHandle, otherHandle)) {
         return false;
@@ -2534,8 +2565,7 @@
     auto otherInfo = otherHandle->getInfo();
     if (!otherInfo->visible) {
         return false;
-    } else if (otherInfo->alpha == 0 &&
-               otherInfo->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
+    } else if (otherInfo->alpha == 0 && otherInfo->flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
         // Those act as if they were invisible, so we don't need to flag them.
         // We do want to potentially flag touchable windows even if they have 0
         // opacity, since they can consume touches and alter the effects of the
@@ -2573,20 +2603,20 @@
  * If neither of those is true, then it means the touch can be allowed.
  */
 InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
-        const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
-    const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        const sp<WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const {
+    const WindowInfo* windowInfo = windowHandle->getInfo();
     int32_t displayId = windowInfo->displayId;
-    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     TouchOcclusionInfo info;
     info.hasBlockingOcclusion = false;
     info.obscuringOpacity = 0;
     info.obscuringUid = -1;
     std::map<int32_t, float> opacityByUid;
-    for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
+    for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
             break; // All future windows are below us. Exit early.
         }
-        const InputWindowInfo* otherInfo = otherHandle->getInfo();
+        const WindowInfo* otherInfo = otherHandle->getInfo();
         if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) &&
             !haveSameApplicationToken(windowInfo, otherInfo)) {
             if (DEBUG_TOUCH_OCCLUSION) {
@@ -2625,7 +2655,7 @@
     return info;
 }
 
-std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
+std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info,
                                                          bool isTouchedWindow) const {
     return StringPrintf(INDENT2
                         "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
@@ -2659,15 +2689,15 @@
     return true;
 }
 
-bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
+bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle,
                                                     int32_t x, int32_t y) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
-    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
-    for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
+    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
             break; // All future windows are below us. Exit early.
         }
-        const InputWindowInfo* otherInfo = otherHandle->getInfo();
+        const WindowInfo* otherInfo = otherHandle->getInfo();
         if (canBeObscuredBy(windowHandle, otherHandle) &&
             otherInfo->frameContainsPoint(x, y)) {
             return true;
@@ -2676,15 +2706,15 @@
     return false;
 }
 
-bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
+bool InputDispatcher::isWindowObscuredLocked(const sp<WindowInfoHandle>& windowHandle) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
-    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
-    const InputWindowInfo* windowInfo = windowHandle->getInfo();
-    for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
+    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    const WindowInfo* windowInfo = windowHandle->getInfo();
+    for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
             break; // All future windows are below us. Exit early.
         }
-        const InputWindowInfo* otherInfo = otherHandle->getInfo();
+        const WindowInfo* otherInfo = otherHandle->getInfo();
         if (canBeObscuredBy(windowHandle, otherHandle) &&
             otherInfo->overlaps(windowInfo)) {
             return true;
@@ -2694,8 +2724,7 @@
 }
 
 std::string InputDispatcher::getApplicationWindowLabel(
-        const InputApplicationHandle* applicationHandle,
-        const sp<InputWindowHandle>& windowHandle) {
+        const InputApplicationHandle* applicationHandle, const sp<WindowInfoHandle>& windowHandle) {
     if (applicationHandle != nullptr) {
         if (windowHandle != nullptr) {
             return applicationHandle->getName() + " - " + windowHandle->getName();
@@ -2718,10 +2747,10 @@
         return;
     }
     int32_t displayId = getTargetDisplayId(eventEntry);
-    sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
+    sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
     if (focusedWindowHandle != nullptr) {
-        const InputWindowInfo* info = focusedWindowHandle->getInfo();
-        if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
+        const WindowInfo* info = focusedWindowHandle->getInfo();
+        if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
 #if DEBUG_DISPATCH_CYCLE
             ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
 #endif
@@ -3176,6 +3205,7 @@
                                                      motionEntry.xPrecision, motionEntry.yPrecision,
                                                      motionEntry.xCursorPosition,
                                                      motionEntry.yCursorPosition,
+                                                     dispatchEntry->displayOrientation,
                                                      dispatchEntry->displaySize.x,
                                                      dispatchEntry->displaySize.y,
                                                      motionEntry.downTime, motionEntry.eventTime,
@@ -3492,10 +3522,10 @@
 #endif
 
     InputTarget target;
-    sp<InputWindowHandle> windowHandle =
+    sp<WindowInfoHandle> windowHandle =
             getWindowHandleLocked(connection->inputChannel->getConnectionToken());
     if (windowHandle != nullptr) {
-        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        const WindowInfo* windowInfo = windowHandle->getInfo();
         target.setDefaultPointerTransform(windowInfo->transform);
         target.globalScaleFactor = windowInfo->globalScaleFactor;
     }
@@ -3559,10 +3589,10 @@
 #endif
 
     InputTarget target;
-    sp<InputWindowHandle> windowHandle =
+    sp<WindowInfoHandle> windowHandle =
             getWindowHandleLocked(connection->inputChannel->getConnectionToken());
     if (windowHandle != nullptr) {
-        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        const WindowInfo* windowInfo = windowHandle->getInfo();
         target.setDefaultPointerTransform(windowInfo->transform);
         target.globalScaleFactor = windowInfo->globalScaleFactor;
     }
@@ -3888,8 +3918,8 @@
                              args->action, args->actionButton, args->flags, args->edgeFlags,
                              args->metaState, args->buttonState, args->classification, transform,
                              args->xPrecision, args->yPrecision, args->xCursorPosition,
-                             args->yCursorPosition, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                             AMOTION_EVENT_INVALID_DISPLAY_SIZE, args->downTime, args->eventTime,
+                             args->yCursorPosition, ui::Transform::ROT_0, INVALID_DISPLAY_SIZE,
+                             INVALID_DISPLAY_SIZE, args->downTime, args->eventTime,
                              args->pointerCount, args->pointerProperties, args->pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
@@ -4333,22 +4363,22 @@
     }
 }
 
-const std::vector<sp<InputWindowHandle>>& InputDispatcher::getWindowHandlesLocked(
+const std::vector<sp<WindowInfoHandle>>& InputDispatcher::getWindowHandlesLocked(
         int32_t displayId) const {
-    static const std::vector<sp<InputWindowHandle>> EMPTY_WINDOW_HANDLES;
+    static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES;
     auto it = mWindowHandlesByDisplay.find(displayId);
     return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
 }
 
-sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
+sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
         const sp<IBinder>& windowHandleToken) const {
     if (windowHandleToken == nullptr) {
         return nullptr;
     }
 
     for (auto& it : mWindowHandlesByDisplay) {
-        const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
-        for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+        const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
+        for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
             if (windowHandle->getToken() == windowHandleToken) {
                 return windowHandle;
             }
@@ -4357,13 +4387,13 @@
     return nullptr;
 }
 
-sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
-                                                             int displayId) const {
+sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+                                                            int displayId) const {
     if (windowHandleToken == nullptr) {
         return nullptr;
     }
 
-    for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+    for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
         if (windowHandle->getToken() == windowHandleToken) {
             return windowHandle;
         }
@@ -4371,11 +4401,11 @@
     return nullptr;
 }
 
-sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
-        const sp<InputWindowHandle>& windowHandle) const {
+sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
+        const sp<WindowInfoHandle>& windowHandle) const {
     for (auto& it : mWindowHandlesByDisplay) {
-        const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
-        for (const sp<InputWindowHandle>& handle : windowHandles) {
+        const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
+        for (const sp<WindowInfoHandle>& handle : windowHandles) {
             if (handle->getId() == windowHandle->getId() &&
                 handle->getToken() == windowHandle->getToken()) {
                 if (windowHandle->getInfo()->displayId != it.first) {
@@ -4391,15 +4421,15 @@
     return nullptr;
 }
 
-sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
     sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
     return getWindowHandleLocked(focusedToken, displayId);
 }
 
-bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const {
+bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
     sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
     const bool noInputChannel =
-            windowHandle.getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+            windowHandle.getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
     if (connection != nullptr && noInputChannel) {
         ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s",
               windowHandle.getName().c_str(), connection->inputChannel->getName().c_str());
@@ -4429,8 +4459,8 @@
 }
 
 void InputDispatcher::updateWindowHandlesForDisplayLocked(
-        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
-    if (inputWindowHandles.empty()) {
+        const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
+    if (windowInfoHandles.empty()) {
         // Remove all handles on a display if there are no windows left.
         mWindowHandlesByDisplay.erase(displayId);
         return;
@@ -4438,26 +4468,21 @@
 
     // Since we compare the pointer of input window handles across window updates, we need
     // to make sure the handle object for the same window stays unchanged across updates.
-    const std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId);
-    std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById;
-    for (const sp<InputWindowHandle>& handle : oldHandles) {
+    const std::vector<sp<WindowInfoHandle>>& oldHandles = getWindowHandlesLocked(displayId);
+    std::unordered_map<int32_t /*id*/, sp<WindowInfoHandle>> oldHandlesById;
+    for (const sp<WindowInfoHandle>& handle : oldHandles) {
         oldHandlesById[handle->getId()] = handle;
     }
 
-    std::vector<sp<InputWindowHandle>> newHandles;
-    for (const sp<InputWindowHandle>& handle : inputWindowHandles) {
-        if (!handle->updateInfo()) {
-            // handle no longer valid
-            continue;
-        }
-
-        const InputWindowInfo* info = handle->getInfo();
+    std::vector<sp<WindowInfoHandle>> newHandles;
+    for (const sp<WindowInfoHandle>& handle : windowInfoHandles) {
+        const WindowInfo* info = handle->getInfo();
         if ((getInputChannelLocked(handle->getToken()) == nullptr &&
              info->portalToDisplayId == ADISPLAY_ID_NONE)) {
             const bool noInputChannel =
-                    info->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
-            const bool canReceiveInput = !info->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE) ||
-                    !info->flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE);
+                    info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
+            const bool canReceiveInput = !info->flags.test(WindowInfo::Flag::NOT_TOUCHABLE) ||
+                    !info->flags.test(WindowInfo::Flag::NOT_FOCUSABLE);
             if (canReceiveInput && !noInputChannel) {
                 ALOGV("Window handle %s has no registered input channel",
                       handle->getName().c_str());
@@ -4473,7 +4498,7 @@
 
         if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) &&
                 (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) {
-            const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
+            const sp<WindowInfoHandle>& oldHandle = oldHandlesById.at(handle->getId());
             oldHandle->updateFrom(handle);
             newHandles.push_back(oldHandle);
         } else {
@@ -4486,7 +4511,7 @@
 }
 
 void InputDispatcher::setInputWindows(
-        const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) {
+        const std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>>& handlesPerDisplay) {
     { // acquire lock
         std::scoped_lock _l(mLock);
         for (const auto& [displayId, handles] : handlesPerDisplay) {
@@ -4505,19 +4530,19 @@
  * For removed handle, check if need to send a cancel event if already in touch.
  */
 void InputDispatcher::setInputWindowsLocked(
-        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
+        const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
     if (DEBUG_FOCUS) {
         std::string windowList;
-        for (const sp<InputWindowHandle>& iwh : inputWindowHandles) {
+        for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) {
             windowList += iwh->getName() + " ";
         }
         ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
     }
 
     // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
-    for (const sp<InputWindowHandle>& window : inputWindowHandles) {
+    for (const sp<WindowInfoHandle>& window : windowInfoHandles) {
         const bool noInputWindow =
-                window->getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+                window->getInfo()->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
         if (noInputWindow && window->getToken() != nullptr) {
             ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
                   window->getName().c_str());
@@ -4526,18 +4551,18 @@
     }
 
     // Copy old handles for release if they are no longer present.
-    const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
 
     // Save the old windows' orientation by ID before it gets updated.
     std::unordered_map<int32_t, uint32_t> oldWindowOrientations;
-    for (const sp<InputWindowHandle>& handle : oldWindowHandles) {
+    for (const sp<WindowInfoHandle>& handle : oldWindowHandles) {
         oldWindowOrientations.emplace(handle->getId(),
                                       handle->getInfo()->transform.getOrientation());
     }
 
-    updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
+    updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
 
-    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     if (mLastHoverWindowHandle &&
         std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
                 windowHandles.end()) {
@@ -4586,8 +4611,8 @@
     if (isPerWindowInputRotationEnabled()) {
         // Determine if the orientation of any of the input windows have changed, and cancel all
         // pointer events if necessary.
-        for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
-            const sp<InputWindowHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
+        for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
+            const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
             if (newWindowHandle != nullptr &&
                 newWindowHandle->getInfo()->transform.getOrientation() !=
                         oldWindowOrientations[oldWindowHandle->getId()]) {
@@ -4606,7 +4631,7 @@
     // This ensures that unused input channels are released promptly.
     // Otherwise, they might stick around until the window handle is destroyed
     // which might not happen until the next GC.
-    for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
+    for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
         if (getWindowHandleLocked(oldWindowHandle) == nullptr) {
             if (DEBUG_FOCUS) {
                 ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
@@ -4616,7 +4641,7 @@
             // check for window flags when windows are going away.
             // TODO(b/157929241) : delete this. This is only needed temporarily
             // in order to gather some data about the flag usage
-            if (oldWindowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) {
+            if (oldWindowHandle->getInfo()->flags.test(WindowInfo::Flag::SLIPPERY)) {
                 ALOGW("%s has FLAG_SLIPPERY. Please report this in b/157929241",
                       oldWindowHandle->getName().c_str());
                 if (mCompatService != nullptr) {
@@ -4794,6 +4819,18 @@
     mBlockUntrustedTouchesMode = mode;
 }
 
+std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked(
+        const sp<IBinder>& token) {
+    for (auto& [displayId, state] : mTouchStatesByDisplay) {
+        for (TouchedWindow& w : state.windows) {
+            if (w.windowHandle->getToken() == token) {
+                return std::make_pair(&state, &w);
+            }
+        }
+    }
+    return std::make_pair(nullptr, nullptr);
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                                          bool isDragDrop) {
     if (fromToken == toToken) {
@@ -4806,58 +4843,43 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
-        sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toToken);
-        if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
-            ALOGW("Cannot transfer focus because from or to window not found.");
+        // Find the target touch state and touched window by fromToken.
+        auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken);
+        if (state == nullptr || touchedWindow == nullptr) {
+            ALOGD("Focus transfer failed because from window is not being touched.");
             return false;
         }
+
+        const int32_t displayId = state->displayId;
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
+        if (toWindowHandle == nullptr) {
+            ALOGW("Cannot transfer focus because to window not found.");
+            return false;
+        }
+
         if (DEBUG_FOCUS) {
             ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
-                  fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
-        }
-        if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Cannot transfer focus because windows are on different displays.");
-            }
-            return false;
+                  touchedWindow->windowHandle->getName().c_str(),
+                  toWindowHandle->getName().c_str());
         }
 
-        bool found = false;
-        for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
-            TouchState& state = pair.second;
-            for (size_t i = 0; i < state.windows.size(); i++) {
-                const TouchedWindow& touchedWindow = state.windows[i];
-                if (touchedWindow.windowHandle == fromWindowHandle) {
-                    int32_t oldTargetFlags = touchedWindow.targetFlags;
-                    BitSet32 pointerIds = touchedWindow.pointerIds;
+        // Erase old window.
+        int32_t oldTargetFlags = touchedWindow->targetFlags;
+        BitSet32 pointerIds = touchedWindow->pointerIds;
+        state->removeWindowByToken(fromToken);
 
-                    state.windows.erase(state.windows.begin() + i);
+        // Add new window.
+        int32_t newTargetFlags = oldTargetFlags &
+                (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
+                 InputTarget::FLAG_DISPATCH_AS_IS);
+        state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
 
-                    int32_t newTargetFlags = oldTargetFlags &
-                            (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
-                             InputTarget::FLAG_DISPATCH_AS_IS);
-                    state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
-
-                    // Store the dragging window.
-                    if (isDragDrop) {
-                        mDragState = std::make_unique<DragState>(toWindowHandle);
-                    }
-
-                    found = true;
-                    goto Found;
-                }
-            }
-        }
-    Found:
-
-        if (!found) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Focus transfer failed because from window did not have focus.");
-            }
-            return false;
+        // Store the dragging window.
+        if (isDragDrop) {
+            mDragState = std::make_unique<DragState>(toWindowHandle);
         }
 
+        // Synthesize cancel for old window and down for new window.
         sp<Connection> fromConnection = getConnectionLocked(fromToken);
         sp<Connection> toConnection = getConnectionLocked(toToken);
         if (fromConnection != nullptr && toConnection != nullptr) {
@@ -4885,27 +4907,20 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+        auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(),
+                               [](const auto& pair) { return pair.second.windows.size() == 1; });
+        if (it == mTouchStatesByDisplay.end()) {
+            ALOGW("Cannot transfer touch state because there is no exact window being touched");
+            return false;
+        }
+        const int32_t displayId = it->first;
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
         if (toWindowHandle == nullptr) {
             ALOGW("Could not find window associated with token=%p", destChannelToken.get());
             return false;
         }
 
-        const int32_t displayId = toWindowHandle->getInfo()->displayId;
-
-        auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-        if (touchStateIt == mTouchStatesByDisplay.end()) {
-            ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
-                  displayId);
-            return false;
-        }
-
-        TouchState& state = touchStateIt->second;
-        if (state.windows.size() != 1) {
-            ALOGW("Cannot transfer touch state because there are %zu windows being touched",
-                  state.windows.size());
-            return false;
-        }
+        TouchState& state = it->second;
         const TouchedWindow& touchedWindow = state.windows[0];
         fromToken = touchedWindow.windowHandle->getToken();
     } // release lock
@@ -4952,7 +4967,7 @@
 
     std::string windowName = "None";
     if (mWindowTokenWithPointerCapture) {
-        const sp<InputWindowHandle> captureWindowHandle =
+        const sp<WindowInfoHandle> captureWindowHandle =
                 getWindowHandleLocked(mWindowTokenWithPointerCapture);
         windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
                                          : "token has capture without window";
@@ -5008,7 +5023,7 @@
             if (!state.portalWindows.empty()) {
                 dump += INDENT3 "Portal windows:\n";
                 for (size_t i = 0; i < state.portalWindows.size(); i++) {
-                    const sp<InputWindowHandle> portalWindowHandle = state.portalWindows[i];
+                    const sp<WindowInfoHandle> portalWindowHandle = state.portalWindows[i];
                     dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i,
                                          portalWindowHandle->getName().c_str());
                 }
@@ -5025,13 +5040,13 @@
 
     if (!mWindowHandlesByDisplay.empty()) {
         for (auto& it : mWindowHandlesByDisplay) {
-            const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+            const std::vector<sp<WindowInfoHandle>> windowHandles = it.second;
             dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
             if (!windowHandles.empty()) {
                 dump += INDENT2 "Windows:\n";
                 for (size_t i = 0; i < windowHandles.size(); i++) {
-                    const sp<InputWindowHandle>& windowHandle = windowHandles[i];
-                    const InputWindowInfo* windowInfo = windowHandle->getInfo();
+                    const sp<WindowInfoHandle>& windowHandle = windowHandles[i];
+                    const WindowInfo* windowInfo = windowHandle->getInfo();
 
                     dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
                                                  "portalToDisplayId=%d, paused=%s, focusable=%s, "
@@ -5059,12 +5074,13 @@
                                          windowInfo->inputFeatures.string().c_str());
                     dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
                                          "ms, trustedOverlay=%s, hasToken=%s, "
-                                         "touchOcclusionMode=%s\n",
+                                         "touchOcclusionMode=%s, displayOrientation=%d\n",
                                          windowInfo->ownerPid, windowInfo->ownerUid,
                                          millis(windowInfo->dispatchingTimeout),
                                          toString(windowInfo->trustedOverlay),
                                          toString(windowInfo->token != nullptr),
-                                         toString(windowInfo->touchOcclusionMode).c_str());
+                                         toString(windowInfo->touchOcclusionMode).c_str(),
+                                         windowInfo->displayOrientation);
                     windowInfo->transform.dump(dump, "transform", INDENT4);
                 }
             } else {
@@ -5412,7 +5428,7 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
         if (DEBUG_FOCUS) {
-            const sp<InputWindowHandle> windowHandle = getWindowHandleLocked(windowToken);
+            const sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(windowToken);
             ALOGI("Request to %s Pointer Capture from: %s.", enabled ? "enable" : "disable",
                   windowHandle != nullptr ? windowHandle->getName().c_str()
                                           : "token without window");
@@ -5582,7 +5598,7 @@
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window,
+void InputDispatcher::updateLastAnrStateLocked(const sp<WindowInfoHandle>& window,
                                                const std::string& reason) {
     const std::string windowLabel = getApplicationWindowLabel(nullptr, window);
     updateLastAnrStateLocked(windowLabel, reason);
@@ -6250,4 +6266,31 @@
     mLooper->wake();
 }
 
+void InputDispatcher::onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) {
+    // The listener sends the windows as a flattened array. Separate the windows by display for
+    // more convenient parsing.
+    std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
+
+    for (const auto& info : windowInfos) {
+        handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
+        handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info));
+    }
+    setInputWindows(handlesPerDisplay);
+}
+
+bool InputDispatcher::shouldDropInput(
+        const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const {
+    if (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT) ||
+        (windowHandle->getInfo()->inputFeatures.test(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED) &&
+         isWindowObscuredLocked(windowHandle))) {
+        ALOGW("Dropping %s event targeting %s as requested by input feature %s on display "
+              "%" PRId32 ".",
+              NamedEnum::string(entry.type).c_str(), windowHandle->getName().c_str(),
+              windowHandle->getInfo()->inputFeatures.string().c_str(),
+              windowHandle->getInfo()->displayId);
+        return true;
+    }
+    return false;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 30652c6..04913d4 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -37,10 +37,10 @@
 
 #include <attestation/HmacKeyManager.h>
 #include <com/android/internal/compat/IPlatformCompatNative.h>
+#include <gui/InputApplication.h>
+#include <gui/WindowInfo.h>
 #include <input/Input.h>
-#include <input/InputApplication.h>
 #include <input/InputTransport.h>
-#include <input/InputWindow.h>
 #include <limits.h>
 #include <stddef.h>
 #include <ui/Region.h>
@@ -58,6 +58,7 @@
 
 #include <InputListener.h>
 #include <InputReporterInterface.h>
+#include <gui/WindowInfosListener.h>
 
 namespace android::inputdispatcher {
 
@@ -80,7 +81,7 @@
  *
  *     A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
  */
-class InputDispatcher : public android::InputDispatcherInterface {
+class InputDispatcher : public android::InputDispatcherInterface, public gui::WindowInfosListener {
 protected:
     ~InputDispatcher() override;
 
@@ -109,8 +110,9 @@
 
     std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
 
-    void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
-                                 handlesPerDisplay) override;
+    void setInputWindows(
+            const std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>>&
+                    handlesPerDisplay) override;
     void setFocusedApplication(
             int32_t displayId,
             const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
@@ -127,7 +129,7 @@
 
     base::Result<std::unique_ptr<InputChannel>> createInputChannel(
             const std::string& name) override;
-    void setFocusedWindow(const FocusRequest&) override;
+    void setFocusedWindow(const android::gui::FocusRequest&) override;
     base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
                                                                    bool isGestureMonitor,
                                                                    const std::string& name,
@@ -141,6 +143,8 @@
 
     void displayRemoved(int32_t displayId) override;
 
+    void onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) override;
+
 private:
     enum class DropReason {
         NOT_DROPPED,
@@ -190,8 +194,8 @@
     void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
                                  const std::string& reason) REQUIRES(mLock);
     // Enqueues a drag event.
-    void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting,
-                                const MotionEntry& motionEntry) REQUIRES(mLock);
+    void enqueueDragEventLocked(const sp<android::gui::WindowInfoHandle>& windowToken,
+                                bool isExiting, const MotionEntry& motionEntry) REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
     void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -208,11 +212,12 @@
     // to transfer focus to a new application.
     std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
 
-    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
-                                                    TouchState* touchState,
-                                                    bool addOutsideTargets = false,
-                                                    bool addPortalWindows = false,
-                                                    bool ignoreDragWindow = false) REQUIRES(mLock);
+    sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x,
+                                                                 int32_t y, TouchState* touchState,
+                                                                 bool addOutsideTargets = false,
+                                                                 bool addPortalWindows = false,
+                                                                 bool ignoreDragWindow = false)
+            REQUIRES(mLock);
 
     sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
             REQUIRES(mLock);
@@ -314,33 +319,36 @@
     float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
     android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
 
-    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
-            GUARDED_BY(mLock);
-    void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
-                               int32_t displayId) REQUIRES(mLock);
+    std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>>
+            mWindowHandlesByDisplay GUARDED_BY(mLock);
+    void setInputWindowsLocked(
+            const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
+            int32_t displayId) REQUIRES(mLock);
     // Get a reference to window handles by display, return an empty vector if not found.
-    const std::vector<sp<InputWindowHandle>>& getWindowHandlesLocked(int32_t displayId) const
-            REQUIRES(mLock);
-    sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
-            REQUIRES(mLock);
+    const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesLocked(
+            int32_t displayId) const REQUIRES(mLock);
+    sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
+            const sp<IBinder>& windowHandleToken) const REQUIRES(mLock);
 
     // Same function as above, but faster. Since displayId is provided, this avoids the need
     // to loop through all displays.
-    sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
-                                                int displayId) const REQUIRES(mLock);
-    sp<InputWindowHandle> getWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const
-            REQUIRES(mLock);
+    sp<android::gui::WindowInfoHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+                                                             int displayId) const REQUIRES(mLock);
+    sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
+            const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
     std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
             REQUIRES(mLock);
-    sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
-    bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
+    sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const
+            REQUIRES(mLock);
+    bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const
+            REQUIRES(mLock);
 
     /*
      * Validate and update InputWindowHandles for a given display.
      */
     void updateWindowHandlesForDisplayLocked(
-            const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
-            REQUIRES(mLock);
+            const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
+            int32_t displayId) REQUIRES(mLock);
 
     std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
     std::unique_ptr<DragState> mDragState GUARDED_BY(mLock);
@@ -473,7 +481,7 @@
     AnrTracker mAnrTracker GUARDED_BY(mLock);
 
     // Contains the last window which received a hover event.
-    sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
+    sp<android::gui::WindowInfoHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
 
     void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
@@ -492,20 +500,21 @@
             nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
             nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
     std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
-            int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
+            int32_t displayId,
+            const std::vector<sp<android::gui::WindowInfoHandle>>& portalWindows) const
             REQUIRES(mLock);
     std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
             const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
 
-    void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
-                               BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
-            REQUIRES(mLock);
+    void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
+                               int32_t targetFlags, BitSet32 pointerIds,
+                               std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
     void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
                                    std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
     void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
                                           float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
     void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
-    bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
+    bool checkInjectionPermission(const sp<android::gui::WindowInfoHandle>& windowHandle,
                                   const InjectionState* injectionState);
     // Enqueue a drag event if needed, and update the touch state.
     // Uses findTouchedWindowTargetsLocked to make the decision
@@ -520,15 +529,22 @@
         std::vector<std::string> debugInfo;
     };
 
-    TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle,
-                                                       int32_t x, int32_t y) const REQUIRES(mLock);
+    TouchOcclusionInfo computeTouchOcclusionInfoLocked(
+            const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const
+            REQUIRES(mLock);
     bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
-    bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
-                                       int32_t y) const REQUIRES(mLock);
-    bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
-    std::string dumpWindowForTouchOcclusion(const InputWindowInfo* info, bool isTouchWindow) const;
+    bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
+                                       int32_t x, int32_t y) const REQUIRES(mLock);
+    bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const
+            REQUIRES(mLock);
+    std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info,
+                                            bool isTouchWindow) const;
     std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
-                                          const sp<InputWindowHandle>& windowHandle);
+                                          const sp<android::gui::WindowInfoHandle>& windowHandle);
+
+    bool shouldDropInput(const EventEntry& entry,
+                         const sp<android::gui::WindowInfoHandle>& windowHandle) const
+            REQUIRES(mLock);
 
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
@@ -607,8 +623,8 @@
     void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
     void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
-    void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
-            REQUIRES(mLock);
+    void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window,
+                                  const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const InputApplicationHandle& application,
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
@@ -643,6 +659,10 @@
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
+    // Find touched state and touched window by token.
+    std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
+            REQUIRES(mLock);
+
     // Statistics gathering.
     LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
     LatencyTracker mLatencyTracker GUARDED_BY(mLock);
@@ -652,6 +672,8 @@
 
     sp<InputReporterInterface> mReporter;
     sp<com::android::internal::compat::IPlatformCompatNative> mCompatService;
+
+    void onFirstRef() override;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 1c4980b..7c463c8 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -17,6 +17,7 @@
 #ifndef _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
 #define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H
 
+#include <gui/constants.h>
 #include <input/InputTransport.h>
 #include <ui/Transform.h>
 #include <utils/BitSet.h>
@@ -100,8 +101,11 @@
     // (ignored for KeyEvents)
     float globalScaleFactor = 1.0f;
 
+    // Current display orientation
+    uint32_t displayOrientation = ui::Transform::ROT_0;
+
     // Display-size in its natural rotation. Used for compatibility transform of raw coordinates.
-    int2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+    int2 displaySize = {INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE};
 
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 81b3cf0..20b6ead 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
 
 #include "InputTarget.h"
 
 #include "TouchState.h"
 
-using android::InputWindowHandle;
+using android::gui::WindowInfo;
+using android::gui::WindowInfoHandle;
 
 namespace android::inputdispatcher {
 
@@ -51,7 +52,7 @@
     gestureMonitors = other.gestureMonitors;
 }
 
-void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
+void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags,
                                    BitSet32 pointerIds) {
     if (targetFlags & InputTarget::FLAG_SPLIT) {
         split = true;
@@ -76,7 +77,7 @@
     windows.push_back(touchedWindow);
 }
 
-void TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
+void TouchState::addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle) {
     size_t numWindows = portalWindows.size();
     for (size_t i = 0; i < numWindows; i++) {
         if (portalWindows[i] == windowHandle) {
@@ -121,7 +122,7 @@
     portalWindows.clear();
 }
 
-sp<InputWindowHandle> TouchState::getFirstForegroundWindowHandle() const {
+sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
     for (size_t i = 0; i < windows.size(); i++) {
         const TouchedWindow& window = windows[i];
         if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
@@ -137,7 +138,7 @@
     for (const TouchedWindow& window : windows) {
         if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
             if (haveSlipperyForegroundWindow ||
-                !window.windowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) {
+                !window.windowHandle->getInfo()->flags.test(WindowInfo::Flag::SLIPPERY)) {
                 return false;
             }
             haveSlipperyForegroundWindow = true;
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 623c6a8..a4e52b0 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -22,7 +22,9 @@
 
 namespace android {
 
-class InputWindowHandle;
+namespace gui {
+class WindowInfoHandle;
+}
 
 namespace inputdispatcher {
 
@@ -37,7 +39,7 @@
     // This collects the portal windows that the touch has gone through. Each portal window
     // targets a display (embedded display for most cases). With this info, we can add the
     // monitoring channels of the displays touched.
-    std::vector<sp<android::InputWindowHandle>> portalWindows;
+    std::vector<sp<android::gui::WindowInfoHandle>> portalWindows;
 
     std::vector<TouchedMonitor> gestureMonitors;
 
@@ -45,14 +47,14 @@
     ~TouchState();
     void reset();
     void copyFrom(const TouchState& other);
-    void addOrUpdateWindow(const sp<android::InputWindowHandle>& windowHandle, int32_t targetFlags,
-                           BitSet32 pointerIds);
-    void addPortalWindow(const sp<android::InputWindowHandle>& windowHandle);
+    void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
+                           int32_t targetFlags, BitSet32 pointerIds);
+    void addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle);
     void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
     void removeWindowByToken(const sp<IBinder>& token);
     void filterNonAsIsTouchWindows();
     void filterNonMonitors();
-    sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
+    sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
     bool isSlippery() const;
 };
 
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 8713aa3..4c31ec3 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -19,13 +19,15 @@
 
 namespace android {
 
-class InputWindowHandle;
+namespace gui {
+class WindowInfoHandle;
+}
 
 namespace inputdispatcher {
 
 // Focus tracking for touch.
 struct TouchedWindow {
-    sp<android::InputWindowHandle> windowHandle;
+    sp<gui::WindowInfoHandle> windowHandle;
     int32_t targetFlags;
     BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
 };
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 43428a0..a7dccf0 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -19,18 +19,16 @@
 
 #include <InputListener.h>
 #include <android-base/result.h>
-#include <android/FocusRequest.h>
+#include <android/gui/FocusRequest.h>
 #include <android/os/BlockUntrustedTouchesMode.h>
-#include <android/os/ISetInputWindowsListener.h>
 #include <android/os/InputEventInjectionResult.h>
 #include <android/os/InputEventInjectionSync.h>
-#include <input/InputApplication.h>
+#include <gui/InputApplication.h>
+#include <gui/WindowInfo.h>
 #include <input/InputDevice.h>
 #include <input/InputTransport.h>
-#include <input/InputWindow.h>
 #include <unordered_map>
 
-
 namespace android {
 
 /* Notifies the system about input events generated by the input reader.
@@ -91,7 +89,7 @@
      * This method may be called on any thread (usually by the input manager).
      */
     virtual void setInputWindows(
-            const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+            const std::unordered_map<int32_t, std::vector<sp<gui::WindowInfoHandle>>>&
                     handlesPerDisplay) = 0;
 
     /* Sets the focused application on the given display.
@@ -162,7 +160,7 @@
     /**
      * Sets focus on the specified window.
      */
-    virtual void setFocusedWindow(const FocusRequest&) = 0;
+    virtual void setFocusedWindow(const gui::FocusRequest&) = 0;
 
     /**
      * Creates an input channel that may be used as targets for input events.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index fd591e0..3c1e637 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -20,8 +20,8 @@
 #include "InputDispatcherConfiguration.h"
 
 #include <binder/IBinder.h>
+#include <gui/InputApplication.h>
 #include <input/Input.h>
-#include <input/InputApplication.h>
 #include <utils/RefBase.h>
 
 namespace android {
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 8112038..3cf1b2b 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -23,15 +23,14 @@
 #include "InputHost.h"
 
 #include <android/os/BnInputFlinger.h>
-#include <android/os/ISetInputWindowsListener.h>
 #include <binder/Binder.h>
 #include <cutils/compiler.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
 #include <utils/StrongPointer.h>
 
+using android::gui::FocusRequest;
 using android::os::BnInputFlinger;
-using android::os::ISetInputWindowsListener;
 
 namespace android {
 
@@ -44,10 +43,6 @@
     InputFlinger() ANDROID_API;
 
     status_t dump(int fd, const Vector<String16>& args) override;
-    binder::Status setInputWindows(const std::vector<InputWindowInfo>&,
-                                   const sp<ISetInputWindowsListener>&) override {
-        return binder::Status::ok();
-    }
     binder::Status createInputChannel(const std::string&, InputChannel*) override {
         return binder::Status::ok();
     }
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 7db32e3..ee7b392 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -71,6 +71,7 @@
         "libstatslog",
         "libui",
         "libutils",
+        "InputFlingerProperties",
     ],
     static_libs: [
         "libc++fs",
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 7af014c..1e9ec54 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -18,7 +18,7 @@
 
 #include "InputDevice.h"
 
-#include <input/Flags.h>
+#include <ftl/Flags.h>
 #include <algorithm>
 
 #include "CursorInputMapper.h"
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 16251ee..9c8a29a 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -19,8 +19,8 @@
 
 #include "../Macros.h"
 
+#include <ftl/NamedEnum.h>
 #include "PeripheralController.h"
-#include "input/NamedEnum.h"
 
 // Log detailed debug messages about input device lights.
 static constexpr bool DEBUG_LIGHT_DETAILS = false;
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index d88b1bd..3c3f88e 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -22,7 +22,7 @@
 #include <unordered_map>
 #include <vector>
 
-#include <input/Flags.h>
+#include <ftl/Flags.h>
 #include <filesystem>
 
 #include <batteryservice/BatteryService.h>
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 2f2eba7..f32472d 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -17,8 +17,8 @@
 #ifndef _UI_INPUTREADER_INPUT_DEVICE_H
 #define _UI_INPUTREADER_INPUT_DEVICE_H
 
+#include <ftl/Flags.h>
 #include <input/DisplayViewport.h>
-#include <input/Flags.h>
 #include <input/InputDevice.h>
 #include <input/PropertyMap.h>
 #include <stdint.h>
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index da0fea4..7347b2c 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,7 +17,7 @@
 #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 
-#include <android-base/properties.h>
+#include <InputFlingerProperties.sysprop.h>
 #include <input/DisplayViewport.h>
 #include <stdint.h>
 
@@ -33,9 +33,7 @@
 // projection are part of the input window's transform. This means InputReader should work in the
 // un-rotated coordinate space.
 static bool isPerWindowInputRotationEnabled() {
-    static const bool PER_WINDOW_INPUT_ROTATION =
-            base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
-    return PER_WINDOW_INPUT_ROTATION;
+    return sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
 }
 
 static int32_t getInverseRotation(int32_t orientation) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ae89ca1..ac5f6b6 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -18,7 +18,7 @@
 #include "../Macros.h"
 // clang-format on
 
-#include <input/NamedEnum.h>
+#include <ftl/NamedEnum.h>
 #include "TouchInputMapper.h"
 
 #include "CursorButtonAccumulator.h"
@@ -470,6 +470,23 @@
     getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
                                                          mParameters.orientationAware);
 
+    mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
+    String8 orientationString;
+    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"),
+                                                             orientationString)) {
+        if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
+            ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
+        } else if (orientationString == "ORIENTATION_90") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_90;
+        } else if (orientationString == "ORIENTATION_180") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_180;
+        } else if (orientationString == "ORIENTATION_270") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
+        } else if (orientationString != "ORIENTATION_0") {
+            ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string());
+        }
+    }
+
     mParameters.hasAssociatedDisplay = false;
     mParameters.associatedDisplayIsExternal = false;
     if (mParameters.orientationAware ||
@@ -508,6 +525,7 @@
                          toString(mParameters.associatedDisplayIsExternal),
                          mParameters.uniqueDisplayId.c_str());
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+    dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n";
 }
 
 void TouchInputMapper::configureRawPointerAxes() {
@@ -669,7 +687,13 @@
             int32_t naturalPhysicalWidth, naturalPhysicalHeight;
             int32_t naturalPhysicalLeft, naturalPhysicalTop;
             int32_t naturalDeviceWidth, naturalDeviceHeight;
-            switch (mViewport.orientation) {
+
+            // Apply the inverse of the input device orientation so that the surface is configured
+            // in the same orientation as the device. The input device orientation will be
+            // re-applied to mSurfaceOrientation.
+            const int32_t naturalSurfaceOrientation =
+                    (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
+            switch (naturalSurfaceOrientation) {
                 case DISPLAY_ORIENTATION_90:
                     naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
                     naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
@@ -752,6 +776,10 @@
                 mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
                                                                    : DISPLAY_ORIENTATION_0;
             }
+
+            // Apply the input device orientation for the device.
+            mSurfaceOrientation =
+                    (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
         } else {
             mPhysicalWidth = rawWidth;
             mPhysicalHeight = rawHeight;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 920f842..e104220 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -204,6 +204,15 @@
         bool hasAssociatedDisplay;
         bool associatedDisplayIsExternal;
         bool orientationAware;
+
+        enum class Orientation : int32_t {
+            ORIENTATION_0 = DISPLAY_ORIENTATION_0,
+            ORIENTATION_90 = DISPLAY_ORIENTATION_90,
+            ORIENTATION_180 = DISPLAY_ORIENTATION_180,
+            ORIENTATION_270 = DISPLAY_ORIENTATION_270,
+        };
+        Orientation orientation;
+
         bool hasButtonUnderPad;
         std::string uniqueDisplayId;
 
diff --git a/services/inputflinger/sysprop/Android.bp b/services/inputflinger/sysprop/Android.bp
new file mode 100644
index 0000000..b9d65ee
--- /dev/null
+++ b/services/inputflinger/sysprop/Android.bp
@@ -0,0 +1,15 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+sysprop_library {
+    name: "InputFlingerProperties",
+    srcs: ["*.sysprop"],
+    api_packages: ["android.sysprop"],
+    property_owner: "Platform",
+}
diff --git a/services/inputflinger/sysprop/InputFlingerProperties.sysprop b/services/inputflinger/sysprop/InputFlingerProperties.sysprop
new file mode 100644
index 0000000..1c7e724
--- /dev/null
+++ b/services/inputflinger/sysprop/InputFlingerProperties.sysprop
@@ -0,0 +1,27 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the License);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: "android.sysprop.InputFlingerProperties"
+owner: Platform
+
+# When per-window-input-rotation is enabled, InputReader works in the un-rotated
+# display coordinate space, and the display rotation is encoded as part of the
+# input window transform that is sent from SurfaceFlinger to InputDispatcher.
+prop {
+    api_name: "per_window_input_rotation"
+    type: Boolean
+    scope: Internal
+    access: ReadWrite
+    prop_name: "persist.debug.per_window_input_rotation"
+}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 918e1be..e686924 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -52,6 +52,7 @@
     ],
     aidl: {
         include_dirs: [
+            "frameworks/native/libs/gui",
             "frameworks/native/libs/input",
         ],
     },
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index 9051ff1..662be80 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -26,9 +26,12 @@
 
 // atest inputflinger_tests:FocusResolverTest
 
+using android::gui::FocusRequest;
+using android::gui::WindowInfoHandle;
+
 namespace android::inputdispatcher {
 
-class FakeWindowHandle : public InputWindowHandle {
+class FakeWindowHandle : public WindowInfoHandle {
 public:
     FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
                      bool visible) {
@@ -38,7 +41,6 @@
         mInfo.focusable = focusable;
     }
 
-    bool updateInfo() { return true; }
     void setFocusable(bool focusable) { mInfo.focusable = focusable; }
     void setVisible(bool visible) { mInfo.visible = visible; }
 };
@@ -47,7 +49,7 @@
     sp<IBinder> focusableWindowToken = new BBinder();
     sp<IBinder> invisibleWindowToken = new BBinder();
     sp<IBinder> unfocusableWindowToken = new BBinder();
-    std::vector<sp<InputWindowHandle>> windows;
+    std::vector<sp<WindowInfoHandle>> windows;
     windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
                                            true /* visible */));
     windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
@@ -82,7 +84,7 @@
     sp<IBinder> focusableWindowToken = new BBinder();
     sp<IBinder> invisibleWindowToken = new BBinder();
     sp<IBinder> unfocusableWindowToken = new BBinder();
-    std::vector<sp<InputWindowHandle>> windows;
+    std::vector<sp<WindowInfoHandle>> windows;
     windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
                                            true /* visible */));
     windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
@@ -120,7 +122,7 @@
 
 TEST(FocusResolverTest, SetInputWindows) {
     sp<IBinder> focusableWindowToken = new BBinder();
-    std::vector<sp<InputWindowHandle>> windows;
+    std::vector<sp<WindowInfoHandle>> windows;
     sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken,
                                                        true /* focusable */, true /* visible */);
     windows.push_back(window);
@@ -142,7 +144,7 @@
 
 TEST(FocusResolverTest, FocusRequestsCanBePending) {
     sp<IBinder> invisibleWindowToken = new BBinder();
-    std::vector<sp<InputWindowHandle>> windows;
+    std::vector<sp<WindowInfoHandle>> windows;
 
     sp<FakeWindowHandle> invisibleWindow =
             new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
@@ -166,7 +168,7 @@
 
 TEST(FocusResolverTest, FocusRequestsArePersistent) {
     sp<IBinder> windowToken = new BBinder();
-    std::vector<sp<InputWindowHandle>> windows;
+    std::vector<sp<WindowInfoHandle>> windows;
 
     sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
                                                        false /* focusable */, true /* visible */);
@@ -207,7 +209,7 @@
 
 TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) {
     sp<IBinder> hostWindowToken = new BBinder();
-    std::vector<sp<InputWindowHandle>> windows;
+    std::vector<sp<WindowInfoHandle>> windows;
 
     sp<FakeWindowHandle> hostWindow =
             new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */,
@@ -258,7 +260,7 @@
 }
 TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) {
     sp<IBinder> windowToken = new BBinder();
-    std::vector<sp<InputWindowHandle>> windows;
+    std::vector<sp<WindowInfoHandle>> windows;
 
     sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
                                                        true /* focusable */, true /* visible */);
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
index 5c8a8da..5aeb21f 100644
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-import android.FocusRequest;
 import android.InputChannel;
-import android.InputWindowInfo;
-import android.os.ISetInputWindowsListener;
+import android.gui.FocusRequest;
+import android.gui.WindowInfo;
 
 /** @hide */
 interface IInputFlingerQuery
 {
     /* Test interfaces */
-    void getInputWindows(out InputWindowInfo[] inputHandles);
     void getInputChannels(out InputChannel[] channels);
-    void getLastFocusRequest(out FocusRequest request);
     void resetInputManager();
 }
diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp
index c0ada9d..f626d56 100644
--- a/services/inputflinger/tests/InputClassifierConverter_test.cpp
+++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp
@@ -17,9 +17,9 @@
 #include "../InputClassifierConverter.h"
 
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <utils/BitSet.h>
 
-
 using namespace android::hardware::input;
 
 namespace android {
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index a72df01..3a9994e 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -16,6 +16,7 @@
 
 #include "../InputClassifier.h"
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 
 #include "TestInputListener.h"
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 7d4c638..471c5f9 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -29,9 +29,12 @@
 #include <vector>
 
 using android::base::StringPrintf;
+using android::gui::FocusRequest;
+using android::gui::TouchOcclusionMode;
+using android::gui::WindowInfo;
+using android::gui::WindowInfoHandle;
 using android::os::InputEventInjectionResult;
 using android::os::InputEventInjectionSync;
-using android::os::TouchOcclusionMode;
 using namespace android::flag_operators;
 
 namespace android::inputdispatcher {
@@ -43,7 +46,8 @@
 static const int32_t DEVICE_ID = 1;
 
 // An arbitrary display id.
-static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t SECOND_DISPLAY_ID = 1;
 
 // An arbitrary injector pid / uid pair that has permission to inject events.
 static const int32_t INJECTOR_PID = 999;
@@ -473,8 +477,8 @@
         }
     }
 
-    void setFocusedWindow(const sp<InputWindowHandle>& window,
-                          const sp<InputWindowHandle>& focusedWindow = nullptr) {
+    void setFocusedWindow(const sp<WindowInfoHandle>& window,
+                          const sp<WindowInfoHandle>& focusedWindow = nullptr) {
         FocusRequest request;
         request.token = window->getToken();
         request.windowName = window->getName();
@@ -531,8 +535,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -545,9 +549,9 @@
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
-                     pointerCoords);
+                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
                                             InputEventInjectionSync::NONE, 0ms, 0))
@@ -558,9 +562,9 @@
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
-                     pointerCoords);
+                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
                                             InputEventInjectionSync::NONE, 0ms, 0))
@@ -572,9 +576,9 @@
                              (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
-                     pointerCoords);
+                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
                                             InputEventInjectionSync::NONE, 0ms, 0))
@@ -585,9 +589,9 @@
                              (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                      0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
-                     pointerCoords);
+                     ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
                                             InputEventInjectionSync::NONE, 0ms, 0))
@@ -597,8 +601,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 0, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -608,8 +612,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -621,8 +625,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -633,8 +637,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -647,8 +651,8 @@
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
                      AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
                      identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
-                     AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 2, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -896,7 +900,7 @@
     std::string mName;
 };
 
-class FakeWindowHandle : public InputWindowHandle {
+class FakeWindowHandle : public WindowInfoHandle {
 public:
     static const int32_t WIDTH = 600;
     static const int32_t HEIGHT = 800;
@@ -918,7 +922,7 @@
         mInfo.token = *token;
         mInfo.id = sId++;
         mInfo.name = name;
-        mInfo.type = InputWindowInfo::Type::APPLICATION;
+        mInfo.type = WindowInfo::Type::APPLICATION;
         mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
         mInfo.alpha = 1.0;
         mInfo.frameLeft = 0;
@@ -938,7 +942,14 @@
         mInfo.displayId = displayId;
     }
 
-    virtual bool updateInfo() { return true; }
+    sp<FakeWindowHandle> clone(
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+            const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+        sp<FakeWindowHandle> handle =
+                new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)",
+                                     displayId, mInfo.token);
+        return handle;
+    }
 
     void setFocusable(bool focusable) { mInfo.focusable = focusable; }
 
@@ -952,9 +963,7 @@
 
     void setAlpha(float alpha) { mInfo.alpha = alpha; }
 
-    void setTouchOcclusionMode(android::os::TouchOcclusionMode mode) {
-        mInfo.touchOcclusionMode = mode;
-    }
+    void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
 
     void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
 
@@ -968,11 +977,11 @@
         mInfo.addTouchableRegion(frame);
     }
 
-    void addFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags |= flags; }
+    void addFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags |= flags; }
 
-    void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; }
+    void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; }
 
-    void setInputFeatures(InputWindowInfo::Feature features) { mInfo.inputFeatures = features; }
+    void setInputFeatures(WindowInfo::Feature features) { mInfo.inputFeatures = features; }
 
     void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
         mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
@@ -1106,7 +1115,7 @@
 
     void assertNoEvents() {
         if (mInputReceiver == nullptr &&
-            mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) {
+            mInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
             return; // Can't receive events if the window does not have input channel
         }
         ASSERT_NE(nullptr, mInputReceiver)
@@ -1271,8 +1280,9 @@
                          mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE,
                          mButtonState, MotionClassification::NONE, identityTransform,
                          /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
-                         mRawYCursorPosition, mDisplayWidth, mDisplayHeight, mEventTime, mEventTime,
-                         mPointers.size(), pointerProperties.data(), pointerCoords.data());
+                         mRawYCursorPosition, mDisplayOrientation, mDisplayWidth, mDisplayHeight,
+                         mEventTime, mEventTime, mPointers.size(), pointerProperties.data(),
+                         pointerCoords.data());
 
         return event;
     }
@@ -1287,8 +1297,9 @@
     int32_t mFlags{0};
     float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
     float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
-    int32_t mDisplayWidth{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
-    int32_t mDisplayHeight{AMOTION_EVENT_INVALID_DISPLAY_SIZE};
+    uint32_t mDisplayOrientation{ui::Transform::ROT_0};
+    int32_t mDisplayWidth{INVALID_DISPLAY_SIZE};
+    int32_t mDisplayHeight{INVALID_DISPLAY_SIZE};
 
     std::vector<PointerBuilder> mPointers;
 };
@@ -1415,7 +1426,7 @@
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
     window->setFrame(Rect(0, 0, 100, 100));
-    window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+    window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1438,7 +1449,7 @@
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
     window->setFrame(Rect(0, 0, 100, 100));
-    window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+    window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -1474,11 +1485,11 @@
     sp<FakeWindowHandle> windowLeft =
             new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
     windowLeft->setFrame(Rect(0, 0, 600, 800));
-    windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+    windowLeft->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
     sp<FakeWindowHandle> windowRight =
             new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
     windowRight->setFrame(Rect(600, 0, 1200, 800));
-    windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+    windowRight->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
@@ -1585,7 +1596,7 @@
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
     window->setFrame(Rect(0, 0, 1200, 800));
-    window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+    window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
@@ -1667,11 +1678,11 @@
     sp<FakeWindowHandle> windowLeft =
             new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
     windowLeft->setFrame(Rect(0, 0, 600, 800));
-    windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+    windowLeft->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
     sp<FakeWindowHandle> windowRight =
             new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
     windowRight->setFrame(Rect(600, 0, 1200, 800));
-    windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+    windowRight->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
@@ -1865,15 +1876,13 @@
     sp<FakeWindowHandle> firstWindow =
             new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
     firstWindow->setFrame(Rect(0, 0, 600, 400));
-    firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                          InputWindowInfo::Flag::SPLIT_TOUCH);
+    firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Create a non touch modal window that supports split touch
     sp<FakeWindowHandle> secondWindow =
             new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
     secondWindow->setFrame(Rect(0, 400, 600, 800));
-    secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                           InputWindowInfo::Flag::SPLIT_TOUCH);
+    secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -1939,15 +1948,13 @@
     sp<FakeWindowHandle> firstWindow =
             new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
     firstWindow->setFrame(Rect(0, 0, 600, 400));
-    firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                          InputWindowInfo::Flag::SPLIT_TOUCH);
+    firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Create a non touch modal window that supports split touch
     sp<FakeWindowHandle> secondWindow =
             new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
     secondWindow->setFrame(Rect(0, 400, 600, 800));
-    secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                           InputWindowInfo::Flag::SPLIT_TOUCH);
+    secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -2004,6 +2011,134 @@
     secondWindow->assertNoEvents();
 }
 
+// This case will create two windows and one mirrored window on the default display and mirror
+// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put
+// the windows info of second display before default display.
+TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> firstWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> mirrorWindowInPrimary =
+            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> firstWindowInSecondary =
+            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> secondWindowInSecondary =
+            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    // Update window info, let it find window handle of second display first.
+    mDispatcher->setInputWindows(
+            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+             {ADISPLAY_ID_DEFAULT,
+              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // Transfer touch focus
+    ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(),
+                                                secondWindowInPrimary->getToken()));
+    // The first window gets cancel.
+    firstWindowInPrimary->consumeMotionCancel();
+    secondWindowInPrimary->consumeMotionDown();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionMove();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionUp();
+}
+
+// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use
+// 'transferTouch' api.
+TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> firstWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> mirrorWindowInPrimary =
+            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> firstWindowInSecondary =
+            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> secondWindowInSecondary =
+            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    // Update window info, let it find window handle of second display first.
+    mDispatcher->setInputWindows(
+            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+             {ADISPLAY_ID_DEFAULT,
+              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+    // Touch on second display.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+    // Transfer touch focus
+    ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken()));
+
+    // The first window gets cancel.
+    firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID);
+    secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                SECOND_DISPLAY_ID, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
+}
+
 TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -2065,15 +2200,13 @@
     sp<FakeWindowHandle> firstWindow =
             new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
     firstWindow->setFrame(Rect(0, 0, 600, 400));
-    firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                          InputWindowInfo::Flag::SPLIT_TOUCH);
+    firstWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Create second non touch modal window that supports split touch
     sp<FakeWindowHandle> secondWindow =
             new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
     secondWindow->setFrame(Rect(0, 400, 600, 800));
-    secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                           InputWindowInfo::Flag::SPLIT_TOUCH);
+    secondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
@@ -2437,7 +2570,7 @@
     EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
 }
 
-TEST_F(InputDispatcherTest, NonPointerMotionEvent_NotTransformed) {
+TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickAndTouchpadNotTransformed) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
@@ -2457,20 +2590,19 @@
     // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
     window->consumeFocusEvent(true);
 
-    constexpr const std::array nonPointerSources = {AINPUT_SOURCE_TRACKBALL,
-                                                    AINPUT_SOURCE_MOUSE_RELATIVE,
-                                                    AINPUT_SOURCE_JOYSTICK};
-    for (const int source : nonPointerSources) {
-        // Notify motion with a non-pointer source.
-        NotifyMotionArgs motionArgs =
-                generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, source, ADISPLAY_ID_DEFAULT);
+    constexpr const std::array nonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
+                                                                  AMOTION_EVENT_ACTION_DOWN),
+                                                        std::pair(AINPUT_SOURCE_JOYSTICK,
+                                                                  AMOTION_EVENT_ACTION_MOVE)};
+    for (const auto& [source, action] : nonTransformedSources) {
+        const NotifyMotionArgs motionArgs = generateMotionArgs(action, source, ADISPLAY_ID_DEFAULT);
         mDispatcher->notifyMotion(&motionArgs);
 
         MotionEvent* event = window->consumeMotion();
         ASSERT_NE(event, nullptr);
 
         const MotionEvent& motionEvent = *event;
-        EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, motionEvent.getAction());
+        EXPECT_EQ(action, motionEvent.getAction());
         EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
 
         float expectedX = motionArgs.pointerCoords[0].getX();
@@ -2742,8 +2874,7 @@
 
     sp<FakeWindowHandle> slipperyExitWindow =
             new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
-    slipperyExitWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                                 InputWindowInfo::Flag::SLIPPERY);
+    slipperyExitWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SLIPPERY);
     // Make sure this one overlaps the bottom window
     slipperyExitWindow->setFrame(Rect(25, 25, 75, 75));
     // Change the owner uid/pid of the window so that it is considered to be occluding the bottom
@@ -2928,7 +3059,6 @@
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
-    static constexpr int32_t SECOND_DISPLAY_ID = 1;
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
@@ -3091,8 +3221,6 @@
 
 class InputFilterTest : public InputDispatcherTest {
 protected:
-    static constexpr int32_t SECOND_DISPLAY_ID = 1;
-
     void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
         NotifyMotionArgs motionArgs;
 
@@ -3226,9 +3354,9 @@
                          DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
                          AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE,
                          identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         0 /*AMOTION_EVENT_INVALID_DISPLAY_SIZE*/,
-                         0 /*AMOTION_EVENT_INVALID_DISPLAY_SIZE*/, eventTime, eventTime,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+                         0 /*INVALID_DISPLAY_SIZE*/, 0 /*INVALID_DISPLAY_SIZE*/, eventTime,
+                         eventTime,
                          /*pointerCount*/ 1, pointerProperties, pointerCoords);
 
         const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER;
@@ -3286,12 +3414,12 @@
         mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
         // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
         // window.
-        mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+        mUnfocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         mFocusedWindow =
                 new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
         mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
-        mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+        mFocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -3400,14 +3528,12 @@
                                         ADISPLAY_ID_DEFAULT);
         // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
         // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
-        mWindow1->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                           InputWindowInfo::Flag::SPLIT_TOUCH);
+        mWindow1->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
         mWindow1->setFrame(Rect(0, 0, 100, 100));
 
         mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
                                         ADISPLAY_ID_DEFAULT, mWindow1->getToken());
-        mWindow2->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                           InputWindowInfo::Flag::SPLIT_TOUCH);
+        mWindow2->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
         mWindow2->setFrame(Rect(100, 100, 200, 200));
 
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
@@ -3418,7 +3544,7 @@
     sp<FakeWindowHandle> mWindow2;
 
     // Helper function to convert the point from screen coordinates into the window's space
-    static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
+    static PointF getPointInWindow(const WindowInfo* windowInfo, const PointF& point) {
         vec2 vals = windowInfo->transform.transform(point.x, point.y);
         return {vals.x, vals.y};
     }
@@ -3606,7 +3732,7 @@
         mWindow->setFocusable(true);
         // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
         // window.
-        mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+        mWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
@@ -4007,16 +4133,15 @@
         // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
         // window.
         // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
-        mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                                   InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH |
-                                   InputWindowInfo::Flag::SPLIT_TOUCH);
+        mUnfocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL |
+                                   WindowInfo::Flag::WATCH_OUTSIDE_TOUCH |
+                                   WindowInfo::Flag::SPLIT_TOUCH);
 
         mFocusedWindow =
                 new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
         mFocusedWindow->setDispatchingTimeout(30ms);
         mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
-        mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
-                                 InputWindowInfo::Flag::SPLIT_TOUCH);
+        mFocusedWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
@@ -4384,7 +4509,7 @@
                                               "Window without input channel", ADISPLAY_ID_DEFAULT,
                                               std::make_optional<sp<IBinder>>(nullptr) /*token*/);
 
-        mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+        mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL);
         mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
         // It's perfectly valid for this window to not have an associated input channel
 
@@ -4426,7 +4551,7 @@
                                           "Window with input channel and NO_INPUT_CHANNEL",
                                           ADISPLAY_ID_DEFAULT);
 
-    mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL);
+    mNoInputWindow->setInputFeatures(WindowInfo::Feature::NO_INPUT_CHANNEL);
     mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}});
 
@@ -4747,10 +4872,10 @@
         mTouchWindow.clear();
     }
 
-    sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name,
-                                            os::TouchOcclusionMode mode, float alpha = 1.0f) {
+    sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name, TouchOcclusionMode mode,
+                                            float alpha = 1.0f) {
         sp<FakeWindowHandle> window = getWindow(uid, name);
-        window->setFlags(InputWindowInfo::Flag::NOT_TOUCHABLE);
+        window->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
         window->setTouchOcclusionMode(mode);
         window->setAlpha(alpha);
         return window;
@@ -4864,7 +4989,7 @@
        WindowWithZeroOpacityAndWatchOutside_ReceivesOutsideEvent) {
     const sp<FakeWindowHandle>& w =
             getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
-    w->addFlags(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+    w->addFlags(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
 
     touch();
@@ -4875,7 +5000,7 @@
 TEST_F(InputDispatcherUntrustedTouchesTest, OutsideEvent_HasZeroCoordinates) {
     const sp<FakeWindowHandle>& w =
             getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f);
-    w->addFlags(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+    w->addFlags(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
 
     touch();
@@ -5129,11 +5254,11 @@
         mApp = std::make_shared<FakeApplicationHandle>();
         mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
         mWindow->setFrame(Rect(0, 0, 100, 100));
-        mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+        mWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
         mSecondWindow->setFrame(Rect(100, 0, 200, 100));
-        mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+        mSecondWindow->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
 
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
@@ -5341,4 +5466,134 @@
     mSecondWindow->assertNoEvents();
 }
 
+class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
+
+TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    // With the flag set, window should not get any input
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    window->assertNoEvents();
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+    window->assertNoEvents();
+
+    // With the flag cleared, the window should get input
+    window->setInputFeatures({});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDropInputFeatureTest, ObscuredWindowDropsInput) {
+    std::shared_ptr<FakeApplicationHandle> obscuringApplication =
+            std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> obscuringWindow =
+            new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow",
+                                 ADISPLAY_ID_DEFAULT);
+    obscuringWindow->setFrame(Rect(0, 0, 50, 50));
+    obscuringWindow->setOwnerInfo(111, 111);
+    obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+    window->setOwnerInfo(222, 222);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+    setFocusedWindow(window);
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    // With the flag set, window should not get any input
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    window->assertNoEvents();
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+    window->assertNoEvents();
+
+    // With the flag cleared, the window should get input
+    window->setInputFeatures({});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+
+    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDropInputFeatureTest, UnobscuredWindowGetsInput) {
+    std::shared_ptr<FakeApplicationHandle> obscuringApplication =
+            std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> obscuringWindow =
+            new FakeWindowHandle(obscuringApplication, mDispatcher, "obscuringWindow",
+                                 ADISPLAY_ID_DEFAULT);
+    obscuringWindow->setFrame(Rect(0, 0, 50, 50));
+    obscuringWindow->setOwnerInfo(111, 111);
+    obscuringWindow->setFlags(WindowInfo::Flag::NOT_TOUCHABLE);
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+    window->setInputFeatures(WindowInfo::Feature::DROP_INPUT_IF_OBSCURED);
+    window->setOwnerInfo(222, 222);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+    setFocusedWindow(window);
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    // With the flag set, window should not get any input
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    window->assertNoEvents();
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+    window->assertNoEvents();
+
+    // When the window is no longer obscured because it went on top, it should get input
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, obscuringWindow}}});
+
+    keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    window->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index c368e79..454e531 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -18,9 +18,7 @@
 #include <IInputFlingerQuery.h>
 
 #include <android/os/BnInputFlinger.h>
-#include <android/os/BnSetInputWindowsListener.h>
 #include <android/os/IInputFlinger.h>
-#include <android/os/ISetInputWindowsListener.h>
 
 #include <binder/Binder.h>
 #include <binder/IPCThreadState.h>
@@ -30,7 +28,6 @@
 
 #include <input/Input.h>
 #include <input/InputTransport.h>
-#include <input/InputWindow.h>
 
 #include <gtest/gtest.h>
 #include <inttypes.h>
@@ -44,56 +41,18 @@
 
 #define TAG "InputFlingerServiceTest"
 
+using android::gui::FocusRequest;
 using android::os::BnInputFlinger;
-using android::os::BnSetInputWindowsListener;
 using android::os::IInputFlinger;
-using android::os::ISetInputWindowsListener;
 
 using std::chrono_literals::operator""ms;
 using std::chrono_literals::operator""s;
 
 namespace android {
 
-static const sp<IBinder> TestInfoToken = new BBinder();
-static const sp<IBinder> FocusedTestInfoToken = new BBinder();
-static constexpr int32_t TestInfoId = 1;
-static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo";
-static constexpr Flags<InputWindowInfo::Flag> TestInfoFlags = InputWindowInfo::Flag::NOT_FOCUSABLE;
-static constexpr InputWindowInfo::Type TestInfoType = InputWindowInfo::Type::INPUT_METHOD;
-static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms;
-static constexpr int32_t TestInfoFrameLeft = 93;
-static constexpr int32_t TestInfoFrameTop = 34;
-static constexpr int32_t TestInfoFrameRight = 16;
-static constexpr int32_t TestInfoFrameBottom = 19;
-static constexpr int32_t TestInfoSurfaceInset = 17;
-static constexpr float TestInfoGlobalScaleFactor = 0.3;
-static constexpr float TestInfoWindowXScale = 0.4;
-static constexpr float TestInfoWindowYScale = 0.5;
-static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */,
-                                                 450 /* bottom */};
-static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect);
-static constexpr bool TestInfoVisible = false;
-static constexpr bool TestInfoTrustedOverlay = true;
-static constexpr bool TestInfoFocusable = false;
-static constexpr bool TestInfoHasWallpaper = false;
-static constexpr bool TestInfoPaused = false;
-static constexpr int32_t TestInfoOwnerPid = 19;
-static constexpr int32_t TestInfoOwnerUid = 24;
-static constexpr InputWindowInfo::Feature TestInfoInputFeatures =
-        InputWindowInfo::Feature::NO_INPUT_CHANNEL;
-static constexpr int32_t TestInfoDisplayId = 34;
-static constexpr int32_t TestInfoPortalToDisplayId = 2;
-static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true;
-static const sp<IBinder> TestInfoTouchableRegionCropHandle = new BBinder();
-
-static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo";
-static const sp<IBinder> TestAppInfoToken = new BBinder();
-static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms;
-
 static const String16 kTestServiceName = String16("InputFlingerService");
 static const String16 kQueryServiceName = String16("InputFlingerQueryService");
 
-struct SetInputWindowsListener;
 // --- InputFlingerServiceTest ---
 class InputFlingerServiceTest : public testing::Test {
 public:
@@ -102,32 +61,15 @@
 
 protected:
     void InitializeInputFlinger();
-    void setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos);
-    void setFocusedWindow(const sp<IBinder> token, const sp<IBinder> focusedToken,
-                          nsecs_t timestampNanos);
-
-    void setInputWindowsFinished();
-    void verifyInputWindowInfo(const InputWindowInfo& info) const;
-    InputWindowInfo& getInfo() const { return const_cast<InputWindowInfo&>(mInfo); }
 
     sp<IInputFlinger> mService;
     sp<IInputFlingerQuery> mQuery;
 
 private:
-    sp<SetInputWindowsListener> mSetInputWindowsListener;
     std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
-    InputWindowInfo mInfo;
     std::mutex mLock;
-    std::condition_variable mSetInputWindowsFinishedCondition;
 };
 
-struct SetInputWindowsListener : BnSetInputWindowsListener {
-    explicit SetInputWindowsListener(std::function<void()> cbFunc) : mCbFunc(cbFunc) {}
-
-    binder::Status onSetInputWindowsFinished() override;
-
-    std::function<void()> mCbFunc;
-};
 
 class TestInputManager : public BnInputFlinger {
 protected:
@@ -136,16 +78,10 @@
 public:
     TestInputManager(){};
 
-    binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
     binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
-    binder::Status getLastFocusRequest(FocusRequest*);
 
     status_t dump(int fd, const Vector<String16>& args) override;
 
-    binder::Status setInputWindows(
-            const std::vector<InputWindowInfo>& handles,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
-
     binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
     binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
     binder::Status setFocusedWindow(const FocusRequest&) override;
@@ -154,63 +90,28 @@
 
 private:
     mutable Mutex mLock;
-    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
     std::vector<std::shared_ptr<InputChannel>> mInputChannels;
-    FocusRequest mFocusRequest;
 };
 
 class TestInputQuery : public BnInputFlingerQuery {
 public:
     TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
-    binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
     binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
-    binder::Status getLastFocusRequest(FocusRequest*) override;
     binder::Status resetInputManager() override;
 
 private:
     sp<android::TestInputManager> mManager;
 };
 
-binder::Status TestInputQuery::getInputWindows(
-        std::vector<::android::InputWindowInfo>* inputHandles) {
-    return mManager->getInputWindows(inputHandles);
-}
-
 binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
     return mManager->getInputChannels(channels);
 }
 
-binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) {
-    return mManager->getLastFocusRequest(request);
-}
-
 binder::Status TestInputQuery::resetInputManager() {
     mManager->reset();
     return binder::Status::ok();
 }
 
-binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
-    if (mCbFunc != nullptr) {
-        mCbFunc();
-    }
-    return binder::Status::ok();
-}
-
-binder::Status TestInputManager::setInputWindows(
-        const std::vector<InputWindowInfo>& infos,
-        const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-    AutoMutex _l(mLock);
-
-    for (const auto& info : infos) {
-        mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
-        mHandlesPerDisplay[info.displayId].push_back(new InputWindowHandle(info));
-    }
-    if (setInputWindowsListener) {
-        setInputWindowsListener->onSetInputWindowsFinished();
-    }
-    return binder::Status::ok();
-}
-
 binder::Status TestInputManager::createInputChannel(const std::string& name,
                                                     InputChannel* outChannel) {
     AutoMutex _l(mLock);
@@ -248,16 +149,6 @@
     return NO_ERROR;
 }
 
-binder::Status TestInputManager::getInputWindows(
-        std::vector<::android::InputWindowInfo>* inputInfos) {
-    for (auto& [displayId, inputHandles] : mHandlesPerDisplay) {
-        for (auto& inputHandle : inputHandles) {
-            inputInfos->push_back(*inputHandle->getInfo());
-        }
-    }
-    return binder::Status::ok();
-}
-
 binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
     channels->clear();
     for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
@@ -266,64 +157,16 @@
     return binder::Status::ok();
 }
 
-binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) {
-    *request = mFocusRequest;
-    return binder::Status::ok();
-}
-
 binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
-    mFocusRequest = request;
     return binder::Status::ok();
 }
 
 void TestInputManager::reset() {
-    mHandlesPerDisplay.clear();
     mInputChannels.clear();
-    mFocusRequest = FocusRequest();
 }
 
 void InputFlingerServiceTest::SetUp() {
-    mSetInputWindowsListener = new SetInputWindowsListener([&]() {
-        std::unique_lock<std::mutex> lock(mLock);
-        mSetInputWindowsFinishedCondition.notify_all();
-    });
     InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
-
-    mInfo.token = TestInfoToken;
-    mInfo.id = TestInfoId;
-    mInfo.name = TestInfoName;
-    mInfo.flags = TestInfoFlags;
-    mInfo.type = TestInfoType;
-    mInfo.dispatchingTimeout = TestInfoDispatchingTimeout;
-    mInfo.frameLeft = TestInfoFrameLeft;
-    mInfo.frameTop = TestInfoFrameTop;
-    mInfo.frameRight = TestInfoFrameRight;
-    mInfo.frameBottom = TestInfoFrameBottom;
-    mInfo.surfaceInset = TestInfoSurfaceInset;
-    mInfo.globalScaleFactor = TestInfoGlobalScaleFactor;
-    mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale,
-                         TestInfoFrameTop, 0, 0, 1});
-    mInfo.touchableRegion = TestInfoTouchableRegion;
-    mInfo.visible = TestInfoVisible;
-    mInfo.trustedOverlay = TestInfoTrustedOverlay;
-    mInfo.focusable = TestInfoFocusable;
-
-    mInfo.hasWallpaper = TestInfoHasWallpaper;
-    mInfo.paused = TestInfoPaused;
-    mInfo.ownerPid = TestInfoOwnerPid;
-    mInfo.ownerUid = TestInfoOwnerUid;
-    mInfo.inputFeatures = TestInfoInputFeatures;
-    mInfo.displayId = TestInfoDisplayId;
-    mInfo.portalToDisplayId = TestInfoPortalToDisplayId;
-    mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop;
-    mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle;
-
-    mInfo.applicationInfo.name = TestAppInfoName;
-    mInfo.applicationInfo.token = TestAppInfoToken;
-    mInfo.applicationInfo.dispatchingTimeoutMillis =
-            std::chrono::duration_cast<std::chrono::milliseconds>(TestAppInfoDispatchingTimeout)
-                    .count();
-
     InitializeInputFlinger();
 }
 
@@ -331,10 +174,6 @@
     mQuery->resetInputManager();
 }
 
-void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
-    EXPECT_EQ(mInfo, info);
-}
-
 void InputFlingerServiceTest::InitializeInputFlinger() {
     sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
     ASSERT_TRUE(input != nullptr);
@@ -345,40 +184,6 @@
     mQuery = interface_cast<IInputFlingerQuery>(input);
 }
 
-void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos) {
-    std::unique_lock<std::mutex> lock(mLock);
-    mService->setInputWindows(infos, mSetInputWindowsListener);
-    // Verify listener call
-    EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout);
-}
-
-void InputFlingerServiceTest::setFocusedWindow(const sp<IBinder> token,
-                                               const sp<IBinder> focusedToken,
-                                               nsecs_t timestampNanos) {
-    FocusRequest request;
-    request.token = TestInfoToken;
-    request.focusedToken = focusedToken;
-    request.timestamp = timestampNanos;
-    mService->setFocusedWindow(request);
-    // call set input windows and wait for the callback to drain the queue.
-    setInputWindowsByInfos(std::vector<InputWindowInfo>());
-}
-
-/**
- *  Test InputFlinger service interface SetInputWindows
- */
-TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) {
-    std::vector<InputWindowInfo> infos = {getInfo()};
-    setInputWindowsByInfos(infos);
-
-    // Verify input windows from service
-    std::vector<::android::InputWindowInfo> windowInfos;
-    mQuery->getInputWindows(&windowInfos);
-    for (const ::android::InputWindowInfo& windowInfo : windowInfos) {
-        verifyInputWindowInfo(windowInfo);
-    }
-}
-
 /**
  *  Test InputFlinger service interface createInputChannel
  */
@@ -397,7 +202,7 @@
     EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
 }
 
-TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) {
+TEST_F(InputFlingerServiceTest, CreateInputChannel) {
     InputChannel channel;
     ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
 
@@ -411,30 +216,6 @@
     EXPECT_EQ(channels.size(), 0UL);
 }
 
-TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now);
-
-    FocusRequest request;
-    mQuery->getLastFocusRequest(&request);
-
-    EXPECT_EQ(request.token, TestInfoToken);
-    EXPECT_EQ(request.focusedToken, nullptr);
-    EXPECT_EQ(request.timestamp, now);
-}
-
-TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) {
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now);
-
-    FocusRequest request;
-    mQuery->getLastFocusRequest(&request);
-
-    EXPECT_EQ(request.token, TestInfoToken);
-    EXPECT_EQ(request.focusedToken, FocusedTestInfoToken);
-    EXPECT_EQ(request.timestamp, now);
-}
-
 } // namespace android
 
 int main(int argc, char** argv) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 38dfe40..b419d9a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -16,6 +16,7 @@
 
 #include <CursorInputMapper.h>
 #include <InputDevice.h>
+#include <InputFlingerProperties.sysprop.h>
 #include <InputMapper.h>
 #include <InputReader.h>
 #include <InputReaderBase.h>
@@ -32,6 +33,7 @@
 #include <VibratorInputMapper.h>
 #include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <inttypes.h>
 #include <math.h>
 
@@ -94,6 +96,17 @@
                                                                   {"green", LightColor::GREEN},
                                                                   {"blue", LightColor::BLUE}};
 
+static int32_t getInverseRotation(int32_t orientation) {
+    switch (orientation) {
+        case DISPLAY_ORIENTATION_90:
+            return DISPLAY_ORIENTATION_270;
+        case DISPLAY_ORIENTATION_270:
+            return DISPLAY_ORIENTATION_90;
+        default:
+            return orientation;
+    }
+}
+
 // --- FakePointerController ---
 
 class FakePointerController : public PointerControllerInterface {
@@ -2662,6 +2675,7 @@
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const Flags<InputDeviceClass> DEVICE_CLASSES;
     static const int32_t EVENTHUB_ID;
+    static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -2678,11 +2692,19 @@
         mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
     }
 
-    void SetUp() override { SetUp(DEVICE_CLASSES); }
+    void SetUp() override {
+        // Ensure per_window_input_rotation is enabled.
+        sysprop::InputFlingerProperties::per_window_input_rotation(true);
+
+        SetUp(DEVICE_CLASSES);
+    }
 
     void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
+
+        sysprop::InputFlingerProperties::per_window_input_rotation(
+                INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE);
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
@@ -2794,6 +2816,8 @@
 const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
         Flags<InputDeviceClass>(0); // not needed for current tests
 const int32_t InputMapperTest::EVENTHUB_ID = 1;
+const std::optional<bool> InputMapperTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
+        sysprop::InputFlingerProperties::per_window_input_rotation();
 
 // --- SwitchInputMapperTest ---
 
@@ -4090,8 +4114,11 @@
     ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
 }
 
-TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
+TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) {
     addConfigurationProperty("cursor.mode", "navigation");
+    // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
+    // need to be rotated.
+    addConfigurationProperty("cursor.orientationAware", "1");
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
@@ -4105,9 +4132,10 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 }
 
-TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
+TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) {
     addConfigurationProperty("cursor.mode", "navigation");
-    addConfigurationProperty("cursor.orientationAware", "1");
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
@@ -4121,14 +4149,14 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1, -1, -1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0, -1, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1, -1,  1,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0, -1,  1,  0));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1,  1, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
 
     prepareDisplay(DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
@@ -4141,14 +4169,14 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1, -1,  1,  1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0, -1,  1,  0));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1,  1, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0, -1));
-    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1, -1, -1, -1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0, -1, -1,  0));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0,  1));
+    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
@@ -4634,6 +4662,8 @@
     void prepareLocationCalibration();
     int32_t toRawX(float displayX);
     int32_t toRawY(float displayY);
+    int32_t toRotatedRawX(float displayX);
+    int32_t toRotatedRawY(float displayY);
     float toCookedX(float rawX, float rawY);
     float toCookedY(float rawX, float rawY);
     float toDisplayX(int32_t rawX);
@@ -4716,6 +4746,14 @@
     return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT + RAW_Y_MIN);
 }
 
+int32_t TouchInputMapperTest::toRotatedRawX(float displayX) {
+    return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_HEIGHT + RAW_X_MIN);
+}
+
+int32_t TouchInputMapperTest::toRotatedRawY(float displayY) {
+    return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_WIDTH + RAW_Y_MIN);
+}
+
 float TouchInputMapperTest::toCookedX(float rawX, float rawY) {
     AFFINE_TRANSFORM.applyTo(rawX, rawY);
     return rawX;
@@ -5370,11 +5408,12 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
-TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_DoesNotRotateMotions) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
-    addConfigurationProperty("touch.orientationAware", "0");
+    // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
+    // need to be rotated. Touchscreens are orientation-aware by default.
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
@@ -5393,10 +5432,13 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 }
 
-TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
+TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_RotatesMotions) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
@@ -5418,7 +5460,7 @@
     // Rotation 90.
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
+    processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
@@ -5446,7 +5488,7 @@
     // Rotation 270.
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
+    processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
@@ -5458,6 +5500,172 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 }
 
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation0_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_0");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 0.
+    processDown(mapper, toRawX(50), toRawY(75));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation90_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_90");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 90.
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation180_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_180");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 180.
+    processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation270_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_270");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 270.
+    processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotionWithDisplay) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_90");
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    NotifyMotionArgs args;
+
+    // Orientation 90, Rotation 0.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 90.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    processDown(mapper, toRotatedRawX(50), toRotatedRawY(75));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 180.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_180);
+    processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 270.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_270);
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN,
+                RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
@@ -7808,7 +8016,7 @@
     ASSERT_EQ(std::vector<TouchVideoFrame>(), motionArgs.videoFrames);
 }
 
-TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) {
+TEST_F(MultiTouchInputMapperTest, VideoFrames_AreNotRotated) {
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7818,6 +8026,32 @@
 
     // Test all 4 orientations
     for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
+                                DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+        SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
+        clearViewports();
+        prepareDisplay(orientation);
+        std::vector<TouchVideoFrame> frames{frame};
+        mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
+        processPosition(mapper, 100, 200);
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+        ASSERT_EQ(frames, motionArgs.videoFrames);
+    }
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_AreRotated) {
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    // Unrotated video frame
+    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+    NotifyMotionArgs motionArgs;
+
+    // Test all 4 orientations
+    for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
              DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
         SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
         clearViewports();
@@ -7827,12 +8061,16 @@
         processPosition(mapper, 100, 200);
         processSync(mapper);
         ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-        frames[0].rotate(orientation);
+        // We expect the raw coordinates of the MotionEvent to be rotated in the inverse direction
+        // compared to the display. This is so that when the window transform (which contains the
+        // display rotation) is applied later by InputDispatcher, the coordinates end up in the
+        // window's coordinate space.
+        frames[0].rotate(getInverseRotation(orientation));
         ASSERT_EQ(frames, motionArgs.videoFrames);
     }
 }
 
-TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) {
+TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreNotRotated) {
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7849,8 +8087,36 @@
     processPosition(mapper, 100, 200);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    std::for_each(frames.begin(), frames.end(),
-            [](TouchVideoFrame& frame) { frame.rotate(DISPLAY_ORIENTATION_90); });
+    ASSERT_EQ(frames, motionArgs.videoFrames);
+}
+
+TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_MultipleFramesAreRotated) {
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    // Unrotated video frames. There's no rule that they must all have the same dimensions,
+    // so mix these.
+    TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
+    TouchVideoFrame frame2(3, 3, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {1, 3});
+    TouchVideoFrame frame3(2, 2, {10, 20, 10, 0}, {1, 4});
+    std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
+    NotifyMotionArgs motionArgs;
+
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
+    processPosition(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    std::for_each(frames.begin(), frames.end(), [](TouchVideoFrame& frame) {
+        // We expect the raw coordinates of the MotionEvent to be rotated in the inverse direction
+        // compared to the display. This is so that when the window transform (which contains the
+        // display rotation) is applied later by InputDispatcher, the coordinates end up in the
+        // window's coordinate space.
+        frame.rotate(getInverseRotation(DISPLAY_ORIENTATION_90));
+    });
     ASSERT_EQ(frames, motionArgs.videoFrames);
 }
 
@@ -8404,18 +8670,25 @@
     // Reset.
     mapper.reset(ARBITRARY_TIME);
 
-    // Let physical display be different to device, and make surface and physical could be 1:1.
-    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_0);
+    // Let physical display be different to device, and make surface and physical could be 1:1 in
+    // all four orientations.
+    for (int orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+                            DISPLAY_ORIENTATION_270}) {
+        halfDisplayToCenterHorizontal(orientation);
 
-    const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
-    const int32_t yExpected = y;
-    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+        const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
+        const int32_t yExpected = y;
+        processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+    }
 }
 
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90) {
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90_NotOrientationAware) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
@@ -8424,34 +8697,40 @@
     const int32_t x = DISPLAY_WIDTH / 4;
     const int32_t y = DISPLAY_HEIGHT / 2;
 
-    // expect x/y = swap x/y then reverse y.
-    const int32_t xExpected = y;
-    const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
-    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) {
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
-    // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
-    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
-
-    const int32_t x = DISPLAY_WIDTH / 4;
-    const int32_t y = DISPLAY_HEIGHT / 2;
-
     // expect x/y = swap x/y then reverse x.
     constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
     constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
     processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
 }
 
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner) {
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270_NotOrientationAware) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
+    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
+
+    const int32_t x = DISPLAY_WIDTH / 4;
+    const int32_t y = DISPLAY_HEIGHT / 2;
+
+    // expect x/y = swap x/y then reverse y.
+    const int32_t xExpected = y;
+    const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
+    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner_NotOrientationAware) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     const int32_t x = 0;
@@ -8463,16 +8742,16 @@
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    // expect x/y = swap x/y then reverse y.
-    const int32_t xExpected90 = y;
-    const int32_t yExpected90 = DISPLAY_WIDTH - 1;
+    // expect x/y = swap x/y then reverse x.
+    const int32_t xExpected90 = DISPLAY_HEIGHT - 1;
+    const int32_t yExpected90 = x;
     processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90);
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    // expect x/y = swap x/y then reverse x.
-    const int32_t xExpected270 = DISPLAY_HEIGHT - 1;
-    const int32_t yExpected270 = x;
+    // expect x/y = swap x/y then reverse y.
+    const int32_t xExpected270 = y;
+    const int32_t yExpected270 = DISPLAY_WIDTH - 1;
     processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270);
 }
 
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index c233bf0..e0511bf 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -614,6 +614,8 @@
 
 Return<void> SensorDevice::onDynamicSensorsConnected(
         const hidl_vec<SensorInfo> &dynamicSensorsAdded) {
+    std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
+
     // Allocate a sensor_t structure for each dynamic sensor added and insert
     // it into the dictionary of connected dynamic sensors keyed by handle.
     for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
@@ -629,6 +631,8 @@
                 std::make_pair(sensor->handle, sensor));
     }
 
+    mDynamicSensorsCv.notify_all();
+
     return Return<void>();
 }
 
@@ -1174,8 +1178,20 @@
         dst->dynamic_sensor_meta.connected = dyn.connected;
         dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
         if (dyn.connected) {
+            std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
+            // Give MAX_DYN_SENSOR_WAIT_SEC for onDynamicSensorsConnected to be invoked since it
+            // can be received out of order from this event due to a bug in the HIDL spec that
+            // marks it as oneway.
             auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
-            CHECK(it != mConnectedDynamicSensors.end());
+            if (it == mConnectedDynamicSensors.end()) {
+                mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT,
+                        [&, dyn]{
+                            return mConnectedDynamicSensors.find(dyn.sensorHandle)
+                                    != mConnectedDynamicSensors.end();
+                });
+                it = mConnectedDynamicSensors.find(dyn.sensorHandle);
+                CHECK(it != mConnectedDynamicSensors.end());
+            }
 
             dst->dynamic_sensor_meta.sensor = it->second;
 
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 75da7bb..bc8d20f 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -139,6 +139,14 @@
     Vector<sensor_t> mSensorList;
     std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
 
+    // A bug in the Sensors HIDL spec which marks onDynamicSensorsConnected as oneway causes dynamic
+    // meta events and onDynamicSensorsConnected to be received out of order. This mutex + CV are
+    // used to block meta event processing until onDynamicSensorsConnected is received to simplify
+    // HAL implementations.
+    std::mutex mDynamicSensorsMutex;
+    std::condition_variable mDynamicSensorsCv;
+    static constexpr std::chrono::seconds MAX_DYN_SENSOR_WAIT{5};
+
     static const nsecs_t MINIMUM_EVENTS_PERIOD =   1000000; // 1000 Hz
     mutable Mutex mLock; // protect mActivationCount[].batchParams
     // fixed-size array after construction
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index eb31e92..eeb3f3a 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -158,6 +158,7 @@
         "FrameTracer/FrameTracer.cpp",
         "FrameTracker.cpp",
         "HdrLayerInfoReporter.cpp",
+        "WindowInfosListenerInvoker.cpp",
         "Layer.cpp",
         "LayerProtoHelper.cpp",
         "LayerRejecter.cpp",
@@ -212,7 +213,6 @@
         "libcutils",
         "libdisplayservicehidl",
         "libhidlbase",
-        "libinput",
         "liblayers_proto",
         "liblog",
         "libprocessgroup",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 23779be..71db330 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -58,6 +58,8 @@
 
 namespace android {
 
+using gui::WindowInfo;
+
 static constexpr float defaultMaxLuminance = 1000.0;
 
 BufferLayer::BufferLayer(const LayerCreationArgs& args)
@@ -419,33 +421,35 @@
         mFrameTracker.setFrameReadyTime(desiredPresentTime);
     }
 
-    const Fps refreshRate = mFlinger->mRefreshRateConfigs->getCurrentRefreshRate().getFps();
-    const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
-    if (presentFence->isValid()) {
-        mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
-                                              refreshRate, renderRate,
-                                              frameRateToSetFrameRateVotePayload(
-                                                      mDrawingState.frameRate),
-                                              getGameMode());
-        mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
-                                           presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
-        mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
-    } else if (!display) {
-        // Do nothing.
-    } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
-               displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
-        // The HWC doesn't support present fences, so use the refresh
-        // timestamp instead.
-        const nsecs_t actualPresentTime = display->getRefreshTimestamp();
-        mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
-                                             refreshRate, renderRate,
-                                             frameRateToSetFrameRateVotePayload(
-                                                     mDrawingState.frameRate),
-                                             getGameMode());
-        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
-                                               actualPresentTime,
+    if (display) {
+        const Fps refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
+        const std::optional<Fps> renderRate =
+                mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+        if (presentFence->isValid()) {
+            mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
+                                                  refreshRate, renderRate,
+                                                  frameRateToSetFrameRateVotePayload(
+                                                          mDrawingState.frameRate),
+                                                  getGameMode());
+            mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               presentFence,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
-        mFrameTracker.setActualPresentTime(actualPresentTime);
+            mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
+        } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+                   displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+            // The HWC doesn't support present fences, so use the refresh
+            // timestamp instead.
+            const nsecs_t actualPresentTime = display->getRefreshTimestamp();
+            mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
+                                                 refreshRate, renderRate,
+                                                 frameRateToSetFrameRateVotePayload(
+                                                         mDrawingState.frameRate),
+                                                 getGameMode());
+            mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
+                                                   mCurrentFrameNumber, actualPresentTime,
+                                                   FrameTracer::FrameEvent::PRESENT_FENCE);
+            mFrameTracker.setActualPresentTime(actualPresentTime);
+        }
     }
 
     mFrameTracker.advanceFrame();
@@ -825,7 +829,7 @@
     wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
     SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
     wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
-    InputWindowInfo tmpInputInfo = mDrawingState.inputInfo;
+    WindowInfo tmpInputInfo = mDrawingState.inputInfo;
 
     mDrawingState = clonedFrom->mDrawingState;
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 6b6d434..99e470d 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -515,13 +515,10 @@
 }
 
 status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
-    uint32_t const maxSurfaceDims =
-          std::min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims());
-
     // never allow a surface larger than what our underlying GL implementation
     // can handle.
-    if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) {
-        ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h));
+    if (mFlinger->exceedsMaxRenderTargetSize(w, h)) {
+        ALOGE("dimensions too large %" PRIu32 " x %" PRIu32, w, h);
         return BAD_VALUE;
     }
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 8bc51df..cd531d6 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -158,7 +158,8 @@
     // transaction doesn't need a previous release fence.
     sp<CallbackHandle> ch;
     for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer) {
+        if (handle->releasePreviousBuffer &&
+            mDrawingState.releaseBufferEndpoint == handle->listener) {
             ch = handle;
             break;
         }
@@ -199,14 +200,9 @@
                 mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
     }
 
-    // If there are multiple transactions in this frame, set the previous id on the earliest
-    // transacton. We don't need to pass in the released buffer id to multiple transactions.
-    // The buffer id does not have to correspond to any particular transaction as long as the
-    // listening end point is the same but the client expects the first transaction callback that
-    // replaces the presented buffer to contain the release fence. This follows the same logic.
-    // see BufferStateLayer::onLayerDisplayed.
     for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer) {
+        if (handle->releasePreviousBuffer &&
+            mDrawingState.releaseBufferEndpoint == handle->listener) {
             handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
             break;
         }
@@ -420,7 +416,8 @@
                                  nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  const client_cache_t& clientCacheId, uint64_t frameNumber,
                                  std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
-                                 const sp<ITransactionCompletedListener>& releaseBufferListener) {
+                                 const sp<ITransactionCompletedListener>& releaseBufferListener,
+                                 const sp<IBinder>& releaseBufferEndpoint) {
     ATRACE_CALL();
 
     if (mDrawingState.buffer) {
@@ -485,6 +482,7 @@
 
     mDrawingState.width = mDrawingState.buffer->getBuffer()->getWidth();
     mDrawingState.height = mDrawingState.buffer->getBuffer()->getHeight();
+    mDrawingState.releaseBufferEndpoint = releaseBufferEndpoint;
 
     return true;
 }
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index cab4899..0a0527c 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -59,7 +59,8 @@
                    const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime,
                    bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber,
                    std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
-                   const sp<ITransactionCompletedListener>& transactionListener) override;
+                   const sp<ITransactionCompletedListener>& transactionListener,
+                   const sp<IBinder>& releaseBufferEndpoint) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 1416b1e..0ef0b99 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -166,6 +166,9 @@
     // Enables (or disables) layer caching on this output
     virtual void setLayerCachingEnabled(bool) = 0;
 
+    // Enables (or disables) layer caching texture pool on this output
+    virtual void setLayerCachingTexturePoolEnabled(bool) = 0;
+
     // Sets the projection state to use
     virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                                const Rect& orientedDisplaySpaceRect) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index f832084..ddcc907 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -41,6 +41,7 @@
     std::optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
     void setLayerCachingEnabled(bool) override;
+    void setLayerCachingTexturePoolEnabled(bool) override;
     void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                        const Rect& orientedDisplaySpaceRect) override;
     void setDisplaySize(const ui::Size&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 7534548..a040fa9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -38,35 +38,56 @@
 
 class Flattener {
 public:
-    struct CachedSetRenderSchedulingTunables {
-        // This default assumes that rendering a cached set takes about 3ms. That time is then cut
-        // in half - the next frame using the cached set would have the same workload, meaning that
-        // composition cost is the same. This is best illustrated with the following example:
-        //
-        // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If
-        // renderCachedSets costs 3ms, then two consecutive frames have timings:
-        //
-        // First frame: Start at 0ms, end at 6.8ms.
-        // renderCachedSets: Start at 6.8ms, end at 9.8ms.
-        // Second frame: Start at 9.8ms, end at 16.6ms.
-        //
-        // Now the second frame won't render a cached set afterwards, but the first frame didn't
-        // really steal time from the second frame.
-        static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration = 1500us;
+    // Collection of tunables which are backed by sysprops
+    struct Tunables {
+        // Tunables that are specific to scheduling when a cached set should be rendered
+        struct RenderScheduling {
+            // This default assumes that rendering a cached set takes about 3ms. That time is then
+            // cut in half - the next frame using the cached set would have the same workload,
+            // meaning that composition cost is the same. This is best illustrated with the
+            // following example:
+            //
+            // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If
+            // renderCachedSets costs 3ms, then two consecutive frames have timings:
+            //
+            // First frame: Start at 0ms, end at 6.8ms.
+            // renderCachedSets: Start at 6.8ms, end at 9.8ms.
+            // Second frame: Start at 9.8ms, end at 16.6ms.
+            //
+            // Now the second frame won't render a cached set afterwards, but the first frame didn't
+            // really steal time from the second frame.
+            static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration =
+                    1500us;
 
-        static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240;
+            static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240;
 
-        // Duration allocated for rendering a cached set. If we don't have enough time for rendering
-        // a cached set, then rendering is deferred to another frame.
-        const std::chrono::nanoseconds cachedSetRenderDuration;
-        // Maximum of times that we defer rendering a cached set. If we defer rendering a cached set
-        // too many times, then render it anyways so that future frames would benefit from the
-        // flattened cached set.
-        const size_t maxDeferRenderAttempts;
+            // Duration allocated for rendering a cached set. If we don't have enough time for
+            // rendering a cached set, then rendering is deferred to another frame.
+            const std::chrono::nanoseconds cachedSetRenderDuration;
+            // Maximum of times that we defer rendering a cached set. If we defer rendering a cached
+            // set too many times, then render it anyways so that future frames would benefit from
+            // the flattened cached set.
+            const size_t maxDeferRenderAttempts;
+        };
+
+        static const constexpr std::chrono::milliseconds kDefaultActiveLayerTimeout = 150ms;
+
+        static const constexpr bool kDefaultEnableHolePunch = true;
+
+        // Threshold for determing whether a layer is active. A layer whose properties, including
+        // the buffer, have not changed in at least this time is considered inactive and is
+        // therefore a candidate for flattening.
+        const std::chrono::milliseconds mActiveLayerTimeout;
+
+        // Toggles for scheduling when it's safe to render a cached set.
+        // See: RenderScheduling
+        const std::optional<RenderScheduling> mRenderScheduling;
+
+        // True if the hole punching feature should be enabled.
+        const bool mEnableHolePunch;
     };
-    Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false,
-              std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables =
-                      std::nullopt);
+
+    Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables);
 
     void setDisplaySize(ui::Size size) {
         mDisplaySize = size;
@@ -80,6 +101,8 @@
     void renderCachedSets(const OutputCompositionState& outputState,
                           std::optional<std::chrono::steady_clock::time_point> renderDeadline);
 
+    void setTexturePoolEnabled(bool enabled) { mTexturePool.setEnabled(enabled); }
+
     void dump(std::string& result) const;
     void dumpLayers(std::string& result) const;
 
@@ -175,8 +198,7 @@
     void buildCachedSets(std::chrono::steady_clock::time_point now);
 
     renderengine::RenderEngine& mRenderEngine;
-    const bool mEnableHolePunch;
-    const std::optional<CachedSetRenderSchedulingTunables> mCachedSetRenderSchedulingTunables;
+    const Tunables mTunables;
 
     TexturePool mTexturePool;
 
@@ -200,9 +222,6 @@
     size_t mCachedSetCreationCount = 0;
     size_t mCachedSetCreationCost = 0;
     std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
-    std::chrono::nanoseconds mActiveLayerTimeout = kActiveLayerTimeout;
-
-    static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms);
 };
 
 } // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index bce438f..5237527 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -21,7 +21,7 @@
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
-#include <input/Flags.h>
+#include <ftl/Flags.h>
 
 #include <string>
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index be34153..b7ebca6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -39,6 +39,9 @@
 // heuristically determining the composition strategy of the current layer stack,
 // and flattens inactive layers into an override buffer so it can be used
 // as a more efficient representation of parts of the layer stack.
+// Implicitly, layer caching must also be enabled for the Planner to have any effect
+// E.g., setprop debug.sf.enable_layer_caching 1, or
+// adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
 class Planner {
 public:
     Planner(renderengine::RenderEngine& renderengine);
@@ -64,6 +67,8 @@
     void renderCachedSets(const OutputCompositionState& outputState,
                           std::optional<std::chrono::steady_clock::time_point> renderDeadline);
 
+    void setTexturePoolEnabled(bool enabled) { mFlattener.setTexturePoolEnabled(enabled); }
+
     void dump(const Vector<String16>& args, std::string&);
 
 private:
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
index fb53ee0..d607c75 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
@@ -63,7 +63,8 @@
         sp<Fence> mFence;
     };
 
-    TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine) {}
+    TexturePool(renderengine::RenderEngine& renderEngine)
+          : mRenderEngine(renderEngine), mEnabled(false) {}
 
     virtual ~TexturePool() = default;
 
@@ -78,6 +79,12 @@
     // to the pool.
     std::shared_ptr<AutoTexture> borrowTexture();
 
+    // Enables or disables the pool. When the pool is disabled, no buffers will
+    // be held by the pool. This is useful when the active display changes.
+    void setEnabled(bool enable);
+
+    void dump(std::string& out) const;
+
 protected:
     // Proteted visibility so that they can be used for testing
     const static constexpr size_t kMinPoolSize = 3;
@@ -95,8 +102,10 @@
     // Returns a previously borrowed texture to the pool.
     void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
                        const sp<Fence>& fence);
+    void allocatePool();
     renderengine::RenderEngine& mRenderEngine;
     ui::Size mSize;
+    bool mEnabled;
 };
 
 } // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 8e777e3..8fdf3ae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -37,6 +37,7 @@
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
     MOCK_METHOD1(setLayerCachingEnabled, void(bool));
+    MOCK_METHOD1(setLayerCachingTexturePoolEnabled, void(bool));
     MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index cafcb40..95ae5e5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -146,6 +146,12 @@
     }
 }
 
+void Output::setLayerCachingTexturePoolEnabled(bool enabled) {
+    if (mPlanner) {
+        mPlanner->setTexturePoolEnabled(enabled);
+    }
+}
+
 void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                            const Rect& orientedDisplaySpaceRect) {
     auto& outputState = editState();
@@ -783,6 +789,9 @@
         if (compState->sidebandStream != nullptr) {
             return nullptr;
         }
+        if (compState->isOpaque) {
+            continue;
+        }
         if (compState->backgroundBlurRadius > 0 || compState->blurRegions.size() > 0) {
             layerRequestingBgComposition = layer;
         }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index f033279..ad5e931 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -60,19 +60,8 @@
 
 } // namespace
 
-Flattener::Flattener(
-        renderengine::RenderEngine& renderEngine, bool enableHolePunch,
-        std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables)
-      : mRenderEngine(renderEngine),
-        mEnableHolePunch(enableHolePunch),
-        mCachedSetRenderSchedulingTunables(cachedSetRenderSchedulingTunables),
-        mTexturePool(mRenderEngine) {
-    const int timeoutInMs =
-            base::GetIntProperty(std::string("debug.sf.layer_caching_active_layer_timeout_ms"), 0);
-    if (timeoutInMs != 0) {
-        mActiveLayerTimeout = std::chrono::milliseconds(timeoutInMs);
-    }
-}
+Flattener::Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables)
+      : mRenderEngine(renderEngine), mTunables(tunables), mTexturePool(mRenderEngine) {}
 
 NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
                                        NonBufferHash hash, time_point now) {
@@ -128,14 +117,14 @@
     // If we have a render deadline, and the flattener is configured to skip rendering if we don't
     // have enough time, then we skip rendering the cached set if we think that we'll steal too much
     // time from the next frame.
-    if (renderDeadline && mCachedSetRenderSchedulingTunables) {
+    if (renderDeadline && mTunables.mRenderScheduling) {
         if (const auto estimatedRenderFinish =
-                    now + mCachedSetRenderSchedulingTunables->cachedSetRenderDuration;
+                    now + mTunables.mRenderScheduling->cachedSetRenderDuration;
             estimatedRenderFinish > *renderDeadline) {
             mNewCachedSet->incrementSkipCount();
 
             if (mNewCachedSet->getSkipCount() <=
-                mCachedSetRenderSchedulingTunables->maxDeferRenderAttempts) {
+                mTunables.mRenderScheduling->maxDeferRenderAttempts) {
                 ATRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us",
                               std::chrono::duration_cast<std::chrono::microseconds>(
                                       estimatedRenderFinish - *renderDeadline)
@@ -205,6 +194,9 @@
                         durationString(lastUpdate).c_str());
 
     dumpLayers(result);
+
+    base::StringAppendF(&result, "\n");
+    mTexturePool.dump(result);
 }
 
 size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
@@ -417,8 +409,10 @@
     bool runHasFirstLayer = false;
 
     for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
-        const bool layerIsInactive = now - currentSet->getLastUpdate() > mActiveLayerTimeout;
+        const bool layerIsInactive =
+                now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
         const bool layerHasBlur = currentSet->hasBlurBehind();
+
         if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
             !currentSet->hasUnsupportedDataspace()) {
             if (isPartOfRun) {
@@ -519,7 +513,7 @@
         mNewCachedSet->addBackgroundBlurLayer(*bestRun->getBlurringLayer());
     }
 
-    if (mEnableHolePunch && bestRun->getHolePunchCandidate() &&
+    if (mTunables.mEnableHolePunch && bestRun->getHolePunchCandidate() &&
         bestRun->getHolePunchCandidate()->requiresHolePunch()) {
         // Add the pip layer to mNewCachedSet, but in a special way - it should
         // replace the buffer with a clear round rect.
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index f077470..f5b1cee 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -32,36 +32,46 @@
 
 namespace {
 
-std::optional<Flattener::CachedSetRenderSchedulingTunables> buildFlattenerTuneables() {
+std::optional<Flattener::Tunables::RenderScheduling> buildRenderSchedulingTunables() {
     if (!base::GetBoolProperty(std::string("debug.sf.enable_cached_set_render_scheduling"), true)) {
         return std::nullopt;
     }
 
-    auto renderDuration = std::chrono::nanoseconds(
+    const auto renderDuration = std::chrono::nanoseconds(
             base::GetUintProperty<uint64_t>(std::string("debug.sf.cached_set_render_duration_ns"),
-                                            Flattener::CachedSetRenderSchedulingTunables::
+                                            Flattener::Tunables::RenderScheduling::
                                                     kDefaultCachedSetRenderDuration.count()));
 
-    auto maxDeferRenderAttempts = base::GetUintProperty<
+    const auto maxDeferRenderAttempts = base::GetUintProperty<
             size_t>(std::string("debug.sf.cached_set_max_defer_render_attmpts"),
-                    Flattener::CachedSetRenderSchedulingTunables::kDefaultMaxDeferRenderAttempts);
+                    Flattener::Tunables::RenderScheduling::kDefaultMaxDeferRenderAttempts);
 
-    return std::make_optional<Flattener::CachedSetRenderSchedulingTunables>(
-            Flattener::CachedSetRenderSchedulingTunables{
+    return std::make_optional<Flattener::Tunables::RenderScheduling>(
+            Flattener::Tunables::RenderScheduling{
                     .cachedSetRenderDuration = renderDuration,
                     .maxDeferRenderAttempts = maxDeferRenderAttempts,
             });
 }
 
+Flattener::Tunables buildFlattenerTuneables() {
+    const auto activeLayerTimeout = std::chrono::milliseconds(
+            base::GetIntProperty<int32_t>(std::string(
+                                                  "debug.sf.layer_caching_active_layer_timeout_ms"),
+                                          Flattener::Tunables::kDefaultActiveLayerTimeout.count()));
+    const auto enableHolePunch =
+            base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"),
+                                  Flattener::Tunables::kDefaultEnableHolePunch);
+    return Flattener::Tunables{
+            .mActiveLayerTimeout = activeLayerTimeout,
+            .mRenderScheduling = buildRenderSchedulingTunables(),
+            .mEnableHolePunch = enableHolePunch,
+    };
+}
+
 } // namespace
 
 Planner::Planner(renderengine::RenderEngine& renderEngine)
-      // Implicitly, layer caching must also be enabled for the hole punch or
-      // predictor to have any effect.
-      // E.g., setprop debug.sf.enable_layer_caching 1, or
-      // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
       : mFlattener(renderEngine,
-                   base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true),
                    buildFlattenerTuneables()) {
     mPredictorEnabled =
             base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
index e3772a2..497c433 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -24,14 +24,22 @@
 
 namespace android::compositionengine::impl::planner {
 
+void TexturePool::allocatePool() {
+    mPool.clear();
+    if (mEnabled && mSize.isValid()) {
+        mPool.resize(kMinPoolSize);
+        std::generate_n(mPool.begin(), kMinPoolSize, [&]() {
+            return Entry{genTexture(), nullptr};
+        });
+    }
+}
+
 void TexturePool::setDisplaySize(ui::Size size) {
     if (mSize == size) {
         return;
     }
     mSize = size;
-    mPool.clear();
-    mPool.resize(kMinPoolSize);
-    std::generate_n(mPool.begin(), kMinPoolSize, [&]() { return Entry{genTexture(), nullptr}; });
+    allocatePool();
 }
 
 std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
@@ -46,7 +54,12 @@
 
 void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
                                 const sp<Fence>& fence) {
-    // Drop the texture on the floor if the pool is no longer tracking textures of the same size.
+    // Drop the texture on the floor if the pool is not enabled
+    if (!mEnabled) {
+        return;
+    }
+
+    // Or the texture on the floor if the pool is no longer tracking textures of the same size.
     if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
         static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
         ALOGV("Deallocating texture from Planner's pool - display size changed (previous: (%dx%d), "
@@ -81,4 +94,15 @@
                                                    renderengine::ExternalTexture::Usage::WRITEABLE);
 }
 
+void TexturePool::setEnabled(bool enabled) {
+    mEnabled = enabled;
+    allocatePool();
+}
+
+void TexturePool::dump(std::string& out) const {
+    base::StringAppendF(&out,
+                        "TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n",
+                        mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height);
+}
+
 } // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index a195e58..ada5adf 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -47,9 +47,7 @@
 
     MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
     MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
-    MOCK_METHOD4(allocateVirtualDisplay,
-                 bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
-                      std::optional<PhysicalDisplayId>));
+    MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
     MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
     MOCK_METHOD5(getDeviceCompositionChanges,
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ee73cfc..09f5a5e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3606,6 +3606,7 @@
       : public OutputComposeSurfacesTest_SetsExpensiveRendering {
     OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() {
         mLayer.layerFEState.backgroundBlurRadius = 10;
+        mLayer.layerFEState.isOpaque = false;
         mOutput.editState().isEnabled = true;
 
         EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
@@ -4225,6 +4226,37 @@
                                                                 kDisplayDataspace));
 }
 
+TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+
+    uint32_t z = 0;
+    // Layer requesting blur, or below, should request client composition, unless opaque.
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer,
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer,
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+
+    layer2.layerFEState.backgroundBlurRadius = 10;
+    layer2.layerFEState.isOpaque = true;
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+    mOutput->writeCompositionState(args);
+}
+
 TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) {
     InjectedLayer layer1;
     InjectedLayer layer2;
@@ -4246,6 +4278,7 @@
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
 
     layer2.layerFEState.backgroundBlurRadius = 10;
+    layer2.layerFEState.isOpaque = false;
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -4283,6 +4316,7 @@
 
     BlurRegion region;
     layer2.layerFEState.blurRegions.push_back(region);
+    layer2.layerFEState.isOpaque = false;
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index f5cfd2f..a28fb2c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -47,23 +47,24 @@
 
 class TestableFlattener : public Flattener {
 public:
-    TestableFlattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch,
-                      std::optional<Flattener::CachedSetRenderSchedulingTunables>
-                              cachedSetRenderSchedulingTunables = std::nullopt)
-          : Flattener(renderEngine, enableHolePunch, cachedSetRenderSchedulingTunables) {}
+    TestableFlattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables)
+          : Flattener(renderEngine, tunables) {}
     const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }
 };
 
 class FlattenerTest : public testing::Test {
 public:
-    FlattenerTest() : FlattenerTest(std::nullopt) {}
+    FlattenerTest()
+          : FlattenerTest(Flattener::Tunables{
+                    .mActiveLayerTimeout = 100ms,
+                    .mRenderScheduling = std::nullopt,
+                    .mEnableHolePunch = true,
+            }) {}
     void SetUp() override;
 
 protected:
-    FlattenerTest(std::optional<Flattener::CachedSetRenderSchedulingTunables>
-                          cachedSetRenderSchedulingTunables)
-          : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, true,
-                                                           cachedSetRenderSchedulingTunables)) {}
+    FlattenerTest(const Flattener::Tunables& tunables)
+          : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, tunables)) {}
     void initializeOverrideBuffer(const std::vector<const LayerState*>& layers);
     void initializeFlattener(const std::vector<const LayerState*>& layers);
     void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
@@ -899,11 +900,13 @@
 public:
     FlattenerRenderSchedulingTest()
           : FlattenerTest(
-                    Flattener::CachedSetRenderSchedulingTunables{.cachedSetRenderDuration =
+                    Flattener::Tunables{.mActiveLayerTimeout = 100ms,
+                                        .mRenderScheduling = Flattener::Tunables::
+                                                RenderScheduling{.cachedSetRenderDuration =
                                                                          kCachedSetRenderDuration,
                                                                  .maxDeferRenderAttempts =
-                                                                         kMaxDeferRenderAttempts}) {
-    }
+                                                                         kMaxDeferRenderAttempts},
+                                        .mEnableHolePunch = true}) {}
 };
 
 TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToMaxAttempts) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
index b802e51..6fc90fe 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
@@ -42,6 +42,7 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+        mTexturePool.setEnabled(true);
         mTexturePool.setDisplaySize(kDisplaySize);
     }
 
@@ -130,5 +131,44 @@
               static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
 }
 
+TEST_F(TexturePoolTest, freesBuffersWhenDisabled) {
+    EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+
+    std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
+    for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+        textures.emplace_back(mTexturePool.borrowTexture());
+    }
+
+    EXPECT_EQ(mTexturePool.getPoolSize(), 1u);
+    mTexturePool.setEnabled(false);
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+
+    textures.clear();
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+}
+
+TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) {
+    EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+    mTexturePool.setEnabled(false);
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+
+    std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
+    for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
+        textures.emplace_back(mTexturePool.borrowTexture());
+    }
+
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+    textures.clear();
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+}
+
+TEST_F(TexturePoolTest, reallocatesWhenReEnabled) {
+    EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+    mTexturePool.setEnabled(false);
+    EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+    mTexturePool.setEnabled(true);
+    EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+}
+
 } // namespace
 } // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index ca4b6ab..4445eea 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -22,6 +22,8 @@
 #undef LOG_TAG
 #define LOG_TAG "DisplayDevice"
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include <android-base/stringprintf.h>
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/Display.h>
@@ -40,6 +42,7 @@
 
 #include "DisplayDevice.h"
 #include "Layer.h"
+#include "RefreshRateOverlay.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
@@ -65,9 +68,12 @@
         mSequenceId(args.sequenceId),
         mConnectionType(args.connectionType),
         mCompositionDisplay{args.compositionDisplay},
+        mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
+        mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
         mPhysicalOrientation(args.physicalOrientation),
         mSupportedModes(std::move(args.supportedModes)),
-        mIsPrimary(args.isPrimary) {
+        mIsPrimary(args.isPrimary),
+        mRefreshRateConfigs(std::move(args.refreshRateConfigs)) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
     mCompositionDisplay->createRenderSurface(
             compositionengine::RenderSurfaceCreationArgsBuilder()
@@ -154,20 +160,29 @@
 void DisplayDevice::setActiveMode(DisplayModeId id) {
     const auto mode = getMode(id);
     LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported.");
+    ATRACE_INT(mActiveModeFPSTrace.c_str(), mode->getFps().getIntValue());
     mActiveMode = mode;
+    if (mRefreshRateConfigs) {
+        mRefreshRateConfigs->setCurrentModeId(mActiveMode->getId());
+    }
+    if (mRefreshRateOverlay) {
+        mRefreshRateOverlay->changeRefreshRate(mActiveMode->getFps());
+    }
 }
 
-status_t DisplayDevice::initiateModeChange(DisplayModeId modeId,
+status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
                                            const hal::VsyncPeriodChangeConstraints& constraints,
-                                           hal::VsyncPeriodChangeTimeline* outTimeline) const {
-    const auto mode = getMode(modeId);
-    if (!mode) {
+                                           hal::VsyncPeriodChangeTimeline* outTimeline) {
+    if (!info.mode || info.mode->getPhysicalDisplayId() != getPhysicalId()) {
         ALOGE("Trying to initiate a mode change to invalid mode %s on display %s",
-              std::to_string(modeId.value()).c_str(), to_string(getId()).c_str());
+              info.mode ? std::to_string(info.mode->getId().value()).c_str() : "null",
+              to_string(getId()).c_str());
         return BAD_VALUE;
     }
-    return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), mode->getHwcId(), constraints,
-                                                    outTimeline);
+    mUpcomingActiveMode = info;
+    ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
+    return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
+                                                    constraints, outTimeline);
 }
 
 const DisplayModePtr& DisplayDevice::getActiveMode() const {
@@ -217,12 +232,23 @@
 }
 
 void DisplayDevice::setLayerStack(ui::LayerStack stack) {
-    mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
+    mCompositionDisplay->setLayerStackFilter(stack, isInternal());
+    if (mRefreshRateOverlay) {
+        mRefreshRateOverlay->setLayerStack(stack);
+    }
+}
+
+void DisplayDevice::setFlags(uint32_t flags) {
+    mFlags = flags;
 }
 
 void DisplayDevice::setDisplaySize(int width, int height) {
     LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
-    mCompositionDisplay->setDisplaySize(ui::Size(width, height));
+    const auto size = ui::Size(width, height);
+    mCompositionDisplay->setDisplaySize(size);
+    if (mRefreshRateOverlay) {
+        mRefreshRateOverlay->setViewport(size);
+    }
 }
 
 void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
@@ -262,7 +288,7 @@
 std::string DisplayDevice::getDebugName() const {
     const char* type = "virtual";
     if (mConnectionType) {
-        type = *mConnectionType == ui::DisplayConnectionType::Internal ? "internal" : "external";
+        type = isInternal() ? "internal" : "external";
     }
 
     return base::StringPrintf("DisplayDevice{%s, %s%s, \"%s\"}", to_string(getId()).c_str(), type,
@@ -292,6 +318,10 @@
     }
     result.append("\n");
     getCompositionDisplay()->dump(result);
+
+    if (mRefreshRateConfigs) {
+        mRefreshRateConfigs->dump(result);
+    }
 }
 
 bool DisplayDevice::hasRenderIntent(ui::RenderIntent intent) const {
@@ -378,6 +408,80 @@
                            capabilities.getDesiredMinLuminance());
 }
 
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
+    if (!enable) {
+        mRefreshRateOverlay.reset();
+        return;
+    }
+
+    const auto [lowFps, highFps] = mRefreshRateConfigs->getSupportedRefreshRateRange();
+    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*mFlinger, lowFps.getIntValue(),
+                                                               highFps.getIntValue(), showSpinnner);
+    mRefreshRateOverlay->setLayerStack(getLayerStack());
+    mRefreshRateOverlay->setViewport(getSize());
+    mRefreshRateOverlay->changeRefreshRate(getActiveMode()->getFps());
+}
+
+bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
+                                         bool timerExpired) {
+    if (mRefreshRateConfigs && mRefreshRateOverlay) {
+        const auto newRefreshRate =
+                mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired);
+        if (newRefreshRate) {
+            mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void DisplayDevice::onInvalidate() {
+    if (mRefreshRateOverlay) {
+        mRefreshRateOverlay->onInvalidate();
+    }
+}
+
+bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) {
+    ATRACE_CALL();
+
+    LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided");
+    LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.mode->getPhysicalDisplayId(), "DisplayId mismatch");
+
+    ALOGV("%s(%s)", __func__, to_string(*info.mode).c_str());
+
+    std::scoped_lock lock(mActiveModeLock);
+    if (mDesiredActiveModeChanged) {
+        // If a mode change is pending, just cache the latest request in mDesiredActiveMode
+        const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event;
+        mDesiredActiveMode = info;
+        mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
+        return false;
+    }
+
+    // Check if we are already at the desired mode
+    if (getActiveMode()->getId() == info.mode->getId()) {
+        return false;
+    }
+
+    // Initiate a mode change.
+    mDesiredActiveModeChanged = true;
+    mDesiredActiveMode = info;
+    return true;
+}
+
+std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const {
+    std::scoped_lock lock(mActiveModeLock);
+    if (mDesiredActiveModeChanged) return mDesiredActiveMode;
+    return std::nullopt;
+}
+
+void DisplayDevice::clearDesiredActiveModeState() {
+    std::scoped_lock lock(mActiveModeLock);
+    mDesiredActiveMode.event = Scheduler::ModeEvent::None;
+    mDesiredActiveModeChanged = false;
+}
+
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
 
 }  // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 7e4d923..4d435c7 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -39,17 +39,24 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
+#include "MainThreadGuard.h"
+
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/Hal.h"
 #include "DisplayHardware/PowerAdvisor.h"
 
+#include "Scheduler/RefreshRateConfigs.h"
+
+#include "TracedOrdinal.h"
+
 namespace android {
 
 class Fence;
 class HWComposer;
 class IGraphicBufferProducer;
 class Layer;
+class RefreshRateOverlay;
 class SurfaceFlinger;
 
 struct CompositionInfo;
@@ -64,6 +71,7 @@
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
     constexpr static float sDefaultMaxLumiance = 500.0;
+    enum { eReceivesInput = 0x01 };
 
     explicit DisplayDevice(DisplayDeviceCreationArgs& args);
 
@@ -78,6 +86,9 @@
 
     bool isVirtual() const { return !mConnectionType; }
     bool isPrimary() const { return mIsPrimary; }
+    bool isInternal() const {
+        return !isVirtual() && mConnectionType == ui::DisplayConnectionType::Internal;
+    }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
@@ -90,6 +101,7 @@
     void setLayerStack(ui::LayerStack);
     void setDisplaySize(int width, int height);
     void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
+    void setFlags(uint32_t flags);
 
     ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
     ui::Rotation getOrientation() const { return mOrientation; }
@@ -102,6 +114,7 @@
     const Rect& getOrientedDisplaySpaceRect() const;
     bool needsFiltering() const;
     ui::LayerStack getLayerStack() const;
+    bool receivesInput() const { return mFlags & eReceivesInput; }
 
     DisplayId getId() const;
 
@@ -172,10 +185,28 @@
      * Display mode management.
      */
     const DisplayModePtr& getActiveMode() const;
-    void setActiveMode(DisplayModeId);
-    status_t initiateModeChange(DisplayModeId modeId,
+
+    struct ActiveModeInfo {
+        DisplayModePtr mode;
+        scheduler::RefreshRateConfigEvent event = scheduler::RefreshRateConfigEvent::None;
+
+        bool operator!=(const ActiveModeInfo& other) const {
+            return mode != other.mode || event != other.event;
+        }
+    };
+
+    bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
+    std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
+    void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
+    ActiveModeInfo getUpcomingActiveMode() const REQUIRES(SF_MAIN_THREAD) {
+        return mUpcomingActiveMode;
+    }
+
+    void setActiveMode(DisplayModeId) REQUIRES(SF_MAIN_THREAD);
+    status_t initiateModeChange(const ActiveModeInfo&,
                                 const hal::VsyncPeriodChangeConstraints& constraints,
-                                hal::VsyncPeriodChangeTimeline* outTimeline) const;
+                                hal::VsyncPeriodChangeTimeline* outTimeline)
+            REQUIRES(SF_MAIN_THREAD);
 
     // Return the immutable list of supported display modes. The HWC may report different modes
     // after a hotplug reconnect event, in which case the DisplayDevice object will be recreated.
@@ -187,6 +218,22 @@
     // set-top boxes after a hotplug reconnect.
     DisplayModePtr getMode(DisplayModeId) const;
 
+    // Returns the refresh rate configs for this display.
+    scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
+
+    // Returns a shared pointer to the refresh rate configs for this display.
+    // Clients can store this refresh rate configs and use it even if the DisplayDevice
+    // is destroyed.
+    std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const {
+        return mRefreshRateConfigs;
+    }
+
+    // Enables an overlay to be displayed with the current refresh rate
+    void enableRefreshRateOverlay(bool enable, bool showSpinner);
+    bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
+    bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
+    void onInvalidate();
+
     void onVsync(nsecs_t timestamp);
     nsecs_t getVsyncPeriodFromHWC() const;
     nsecs_t getRefreshTimestamp() const;
@@ -211,6 +258,8 @@
     const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
     std::string mDisplayName;
+    std::string mActiveModeFPSTrace;
+    std::string mActiveModeFPSHwcTrace;
 
     const ui::Rotation mPhysicalOrientation;
     ui::Rotation mOrientation = ui::ROTATION_0;
@@ -227,9 +276,20 @@
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
 
+    uint32_t mFlags = 0;
+
     std::optional<DeviceProductInfo> mDeviceProductInfo;
 
     std::vector<ui::Hdr> mOverrideHdrTypes;
+
+    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+
+    mutable std::mutex mActiveModeLock;
+    ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
+    TracedOrdinal<bool> mDesiredActiveModeChanged
+            GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
+    ActiveModeInfo mUpcomingActiveMode GUARDED_BY(SF_MAIN_THREAD);
 };
 
 struct DisplayDeviceState {
@@ -252,6 +312,7 @@
     std::optional<Physical> physical;
     sp<IGraphicBufferProducer> surface;
     ui::LayerStack layerStack = ui::NO_LAYER_STACK;
+    uint32_t flags = 0;
     Rect layerStackSpaceRect;
     Rect orientedDisplaySpaceRect;
     ui::Rotation orientation = ui::ROTATION_0;
@@ -274,6 +335,7 @@
     HWComposer& hwComposer;
     const wp<IBinder> displayToken;
     const std::shared_ptr<compositionengine::Display> compositionDisplay;
+    std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs;
 
     int32_t sequenceId{0};
     std::optional<ui::DisplayConnectionType> connectionType;
@@ -289,6 +351,7 @@
             hardware::graphics::composer::hal::PowerMode::ON};
     bool isPrimary{false};
     DisplayModes supportedModes;
+    DisplayModeId activeModeId;
 };
 
 // Predicates for display lookup.
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index caf0294..09734c2 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -212,7 +212,7 @@
 }
 
 Error Composer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
-                                     std::optional<Display>, Display* outDisplay) {
+                                     Display* outDisplay) {
     const uint32_t bufferSlotCount = 1;
     Error error = kDefaultError;
     if (mClient_2_2) {
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index b525e63..fe114b9 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -96,7 +96,7 @@
 
     virtual uint32_t getMaxVirtualDisplayCount() = 0;
     virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat*,
-                                       std::optional<Display> mirror, Display* outDisplay) = 0;
+                                       Display* outDisplay) = 0;
     virtual Error destroyVirtualDisplay(Display display) = 0;
 
     virtual Error acceptDisplayChanges(Display display) = 0;
@@ -342,7 +342,7 @@
 
     uint32_t getMaxVirtualDisplayCount() override;
     Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
-                               std::optional<Display> mirror, Display* outDisplay) override;
+                               Display* outDisplay) override;
     Error destroyVirtualDisplay(Display display) override;
 
     Error acceptDisplayChanges(Display display) override;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 85cc993..5de622b 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -22,6 +22,7 @@
 
 #include <android-base/stringprintf.h>
 #include <android/configuration.h>
+#include <ui/DisplayId.h>
 #include <ui/DisplayMode.h>
 #include <ui/Size.h>
 #include <utils/Timers.h>
@@ -54,6 +55,11 @@
             return *this;
         }
 
+        Builder& setPhysicalDisplayId(PhysicalDisplayId id) {
+            mDisplayMode->mPhysicalDisplayId = id;
+            return *this;
+        }
+
         Builder& setWidth(int32_t width) {
             mDisplayMode->mWidth = width;
             return *this;
@@ -112,6 +118,7 @@
 
     DisplayModeId getId() const { return mId; }
     hal::HWConfigId getHwcId() const { return mHwcId; }
+    PhysicalDisplayId getPhysicalDisplayId() const { return mPhysicalDisplayId; }
 
     int32_t getWidth() const { return mWidth; }
     int32_t getHeight() const { return mHeight; }
@@ -136,6 +143,7 @@
 
     hal::HWConfigId mHwcId;
     DisplayModeId mId;
+    PhysicalDisplayId mPhysicalDisplayId;
 
     int32_t mWidth = -1;
     int32_t mHeight = -1;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7e45dab..a790b4c 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -245,8 +245,7 @@
 }
 
 bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size resolution,
-                                        ui::PixelFormat* format,
-                                        std::optional<PhysicalDisplayId> mirror) {
+                                        ui::PixelFormat* format) {
     if (!resolution.isValid()) {
         ALOGE("%s: Invalid resolution %dx%d", __func__, resolution.width, resolution.height);
         return false;
@@ -262,14 +261,9 @@
         return false;
     }
 
-    std::optional<hal::HWDisplayId> hwcMirrorId;
-    if (mirror) {
-        hwcMirrorId = fromPhysicalDisplayId(*mirror);
-    }
-
     hal::HWDisplayId hwcDisplayId;
     const auto error = static_cast<hal::Error>(
-            mComposer->createVirtualDisplay(width, height, format, hwcMirrorId, &hwcDisplayId));
+            mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
     RETURN_IF_HWC_ERROR_FOR("createVirtualDisplay", error, displayId, false);
 
     auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b1849e8..49f96d9 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -113,11 +113,7 @@
 
     // Attempts to allocate a virtual display on the HWC. The maximum number of virtual displays
     // supported by the HWC can be queried in advance, but allocation may fail for other reasons.
-    // For virtualized compositors, the PhysicalDisplayId is a hint that this virtual display is
-    // a mirror of a physical display, and that the screen should be captured by the host rather
-    // than guest compositor.
-    virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
-                                        std::optional<PhysicalDisplayId> mirror) = 0;
+    virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0;
 
     virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
 
@@ -265,8 +261,7 @@
     size_t getMaxVirtualDisplayCount() const override;
     size_t getMaxVirtualDisplayDimension() const override;
 
-    bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*,
-                                std::optional<PhysicalDisplayId>) override;
+    bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override;
 
     // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
     void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index bb2888e..02d0658 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -166,4 +166,15 @@
     }
 }
 
+inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) {
+    switch (vsync) {
+        case hardware::graphics::composer::hal::Vsync::ENABLE:
+            return "Enable";
+        case hardware::graphics::composer::hal::Vsync::DISABLE:
+            return "Disable";
+        default:
+            return "Unknown";
+    }
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d8cead5..63f2445 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -71,7 +71,6 @@
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
-#include "input/InputWindow.h"
 
 #define DEBUG_RESIZE 0
 
@@ -83,6 +82,7 @@
 using base::StringAppendF;
 using namespace android::flag_operators;
 using PresentState = frametimeline::SurfaceFrame::PresentState;
+using gui::WindowInfo;
 
 std::atomic<int32_t> Layer::sSequence{1};
 
@@ -90,8 +90,8 @@
       : mFlinger(args.flinger),
         mName(args.name),
         mClientRef(args.client),
-        mWindowType(static_cast<InputWindowInfo::Type>(
-                args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
+        mWindowType(
+                static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
     uint32_t layerFlags = 0;
     if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
     if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -135,6 +135,7 @@
     mDrawingState.postTime = -1;
     mDrawingState.destinationFrame.makeInvalid();
     mDrawingState.isTrustedOverlay = false;
+    mDrawingState.dropInputMode = gui::DropInputMode::NONE;
 
     if (args.flags & ISurfaceComposerClient::eNoColorFill) {
         // Set an invalid color so there is no color fill.
@@ -1916,27 +1917,49 @@
 }
 
 Layer::RoundedCornerState Layer::getRoundedCornerState() const {
-    const auto& p = mDrawingParent.promote();
-    if (p != nullptr) {
-        RoundedCornerState parentState = p->getRoundedCornerState();
-        if (parentState.radius > 0) {
+    // Get parent settings
+    RoundedCornerState parentSettings;
+    const auto& parent = mDrawingParent.promote();
+    if (parent != nullptr) {
+        parentSettings = parent->getRoundedCornerState();
+        if (parentSettings.radius > 0) {
             ui::Transform t = getActiveTransform(getDrawingState());
             t = t.inverse();
-            parentState.cropRect = t.transform(parentState.cropRect);
+            parentSettings.cropRect = t.transform(parentSettings.cropRect);
             // The rounded corners shader only accepts 1 corner radius for performance reasons,
             // but a transform matrix can define horizontal and vertical scales.
             // Let's take the average between both of them and pass into the shader, practically we
             // never do this type of transformation on windows anyway.
             auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
             auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
-            parentState.radius *= (scaleX + scaleY) / 2.0f;
-            return parentState;
+            parentSettings.radius *= (scaleX + scaleY) / 2.0f;
         }
     }
+
+    // Get layer settings
+    Rect layerCropRect = getCroppedBufferSize(getDrawingState());
     const float radius = getDrawingState().cornerRadius;
-    return radius > 0 && getCroppedBufferSize(getDrawingState()).isValid()
-            ? RoundedCornerState(getCroppedBufferSize(getDrawingState()).toFloatRect(), radius)
-            : RoundedCornerState();
+    RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
+    const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid();
+
+    if (layerSettingsValid && parentSettings.radius > 0) {
+        // If the parent and the layer have rounded corner settings, use the parent settings if the
+        // parent crop is entirely inside the layer crop.
+        // This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
+        if (parentSettings.cropRect.left > layerCropRect.left &&
+            parentSettings.cropRect.top > layerCropRect.top &&
+            parentSettings.cropRect.right < layerCropRect.right &&
+            parentSettings.cropRect.bottom < layerCropRect.bottom) {
+            return parentSettings;
+        } else {
+            return layerSettings;
+        }
+    } else if (layerSettingsValid) {
+        return layerSettings;
+    } else if (parentSettings.radius > 0) {
+        return parentSettings;
+    }
+    return {};
 }
 
 void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
@@ -1982,7 +2005,7 @@
 }
 
 
-void Layer::setInputInfo(const InputWindowInfo& info) {
+void Layer::setInputInfo(const WindowInfo& info) {
     mDrawingState.inputInfo = info;
     mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
     mDrawingState.modified = true;
@@ -2021,8 +2044,8 @@
         if (buffer != nullptr) {
             LayerProtoHelper::writeToProto(buffer,
                                            [&]() { return layerInfo->mutable_active_buffer(); });
-            LayerProtoHelper::writeToProto(ui::Transform(getBufferTransform()),
-                                           layerInfo->mutable_buffer_transform());
+            LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
+                                                     layerInfo->mutable_buffer_transform());
         }
         layerInfo->set_invalidate(contentDirty);
         layerInfo->set_is_protected(isProtected());
@@ -2032,10 +2055,11 @@
         layerInfo->set_curr_frame(mCurrentFrameNumber);
         layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
 
+        layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
         layerInfo->set_corner_radius(getRoundedCornerState().radius);
         layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
         layerInfo->set_is_trusted_overlay(isTrustedOverlay());
-        LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
+        LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
         LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
                                                [&]() { return layerInfo->mutable_position(); });
         LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
@@ -2110,8 +2134,8 @@
                                        [&]() { return layerInfo->mutable_requested_color(); });
         layerInfo->set_flags(state.flags);
 
-        LayerProtoHelper::writeToProto(requestedTransform,
-                                       layerInfo->mutable_requested_transform());
+        LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
+                                                 layerInfo->mutable_requested_transform());
 
         auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
         if (parent != nullptr) {
@@ -2133,7 +2157,7 @@
     }
 
     if (traceFlags & SurfaceTracing::TRACE_INPUT) {
-        InputWindowInfo info;
+        WindowInfo info;
         if (useDrawing) {
             info = fillInputInfo({nullptr});
         } else {
@@ -2164,7 +2188,7 @@
     return getCroppedBufferSize(getDrawingState());
 }
 
-void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay) {
+void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toNonRotatedDisplay) {
     // Transform layer size to screen space and inset it by surface insets.
     // If this is a portal window, set the touchableRegion to the layerBounds.
     Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
@@ -2185,12 +2209,12 @@
     }
 
     ui::Transform layerToDisplay = getInputTransform();
-    // Transform that takes window coordinates to unrotated display coordinates
-    ui::Transform t = toPhysicalDisplay * layerToDisplay;
+    // Transform that takes window coordinates to non-rotated display coordinates
+    ui::Transform t = toNonRotatedDisplay * layerToDisplay;
     int32_t xSurfaceInset = info.surfaceInset;
     int32_t ySurfaceInset = info.surfaceInset;
-    // Bring screenBounds into unrotated space
-    Rect screenBounds = toPhysicalDisplay.transform(Rect{mScreenBounds});
+    // Bring screenBounds into non-rotated space
+    Rect screenBounds = toNonRotatedDisplay.transform(Rect{mScreenBounds});
 
     const float xScale = t.getScaleX();
     const float yScale = t.getScaleY();
@@ -2259,7 +2283,7 @@
     info.touchableRegion = inputTransform.transform(info.touchableRegion);
 }
 
-void Layer::fillTouchOcclusionMode(InputWindowInfo& info) {
+void Layer::fillTouchOcclusionMode(WindowInfo& info) {
     sp<Layer> p = this;
     while (p != nullptr && !p->hasInputInfo()) {
         p = p->mDrawingParent.promote();
@@ -2269,32 +2293,115 @@
     }
 }
 
-InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
+gui::DropInputMode Layer::getDropInputMode() const {
+    gui::DropInputMode mode = mDrawingState.dropInputMode;
+    if (mode == gui::DropInputMode::ALL) {
+        return mode;
+    }
+    sp<Layer> parent = mDrawingParent.promote();
+    if (parent) {
+        gui::DropInputMode parentMode = parent->getDropInputMode();
+        if (parentMode != gui::DropInputMode::NONE) {
+            return parentMode;
+        }
+    }
+    return mode;
+}
+
+void Layer::handleDropInputMode(gui::WindowInfo& info) const {
+    if (mDrawingState.inputInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
+        return;
+    }
+
+    // Check if we need to drop input unconditionally
+    gui::DropInputMode dropInputMode = getDropInputMode();
+    if (dropInputMode == gui::DropInputMode::ALL) {
+        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+        ALOGV("Dropping input for %s as requested by policy.", getDebugName());
+        return;
+    }
+
+    // Check if we need to check if the window is obscured by parent
+    if (dropInputMode != gui::DropInputMode::OBSCURED) {
+        return;
+    }
+
+    // Check if the parent has set an alpha on the layer
+    sp<Layer> parent = mDrawingParent.promote();
+    if (parent && parent->getAlpha() != 1.0_hf) {
+        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+        ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
+              static_cast<float>(getAlpha()));
+    }
+
+    // Check if the parent has cropped the buffer
+    Rect bufferSize = getCroppedBufferSize(getDrawingState());
+    if (!bufferSize.isValid()) {
+        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+        return;
+    }
+
+    // Screenbounds are the layer bounds cropped by parents, transformed to screenspace.
+    // To check if the layer has been cropped, we take the buffer bounds, apply the local
+    // layer crop and apply the same set of transforms to move to screenspace. If the bounds
+    // match then the layer has not been cropped by its parents.
+    Rect bufferInScreenSpace(getTransform().transform(bufferSize));
+    bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds};
+
+    if (croppedByParent) {
+        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+        ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
+              getDebugName());
+    } else {
+        // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop
+        // input if the window is obscured. This check should be done in surfaceflinger but the
+        // logic currently resides in inputflinger. So pass the if_obscured check to input to only
+        // drop input events if the window is obscured.
+        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
+    }
+}
+
+WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
     if (!hasInputInfo()) {
         mDrawingState.inputInfo.name = getName();
         mDrawingState.inputInfo.ownerUid = mOwnerUid;
         mDrawingState.inputInfo.ownerPid = mOwnerPid;
-        mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL;
-        mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
+        mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
+        mDrawingState.inputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL;
         mDrawingState.inputInfo.displayId = getLayerStack();
     }
 
-    InputWindowInfo info = mDrawingState.inputInfo;
+    WindowInfo info = mDrawingState.inputInfo;
     info.id = sequence;
+    info.displayId = getLayerStack();
 
-    if (info.displayId == ADISPLAY_ID_NONE) {
-        info.displayId = getLayerStack();
-    }
-
-    // Transform that goes from "logical(rotated)" display to physical/unrotated display.
-    // This is for when inputflinger operates in physical display-space.
-    ui::Transform toPhysicalDisplay;
+    // Transform that goes from "logical(rotated)" display to the non-rotated display.
+    ui::Transform toNonRotatedDisplay;
     if (display) {
-        toPhysicalDisplay = display->getTransform();
-        info.displayWidth = display->getWidth();
-        info.displayHeight = display->getHeight();
+        // The physical orientation is set when the orientation of the display panel is different
+        // than the default orientation of the device. We do not need to expose the physical
+        // orientation of the panel outside of SurfaceFlinger.
+        const ui::Rotation inversePhysicalOrientation =
+                ui::ROTATION_0 - display->getPhysicalOrientation();
+        auto width = display->getWidth();
+        auto height = display->getHeight();
+        if (inversePhysicalOrientation == ui::ROTATION_90 ||
+            inversePhysicalOrientation == ui::ROTATION_270) {
+            std::swap(width, height);
+        }
+        const auto rotationFlags = ui::Transform::toRotationFlags(inversePhysicalOrientation);
+        const ui::Transform undoPhysicalOrientation(rotationFlags, width, height);
+        toNonRotatedDisplay = undoPhysicalOrientation * display->getTransform();
+
+        // Send the inverse of the display orientation so that input can transform points back to
+        // the rotated display space.
+        const ui::Rotation inverseOrientation = ui::ROTATION_0 - display->getOrientation();
+        info.displayOrientation = ui::Transform::toRotationFlags(inverseOrientation);
+
+        info.displayWidth = width;
+        info.displayHeight = height;
     }
-    fillInputFrameInfo(info, toPhysicalDisplay);
+    fillInputFrameInfo(info, toNonRotatedDisplay);
 
     // For compatibility reasons we let layers which can receive input
     // receive input before they have actually submitted a buffer. Because
@@ -2307,18 +2414,19 @@
     info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
     info.alpha = getAlpha();
     fillTouchOcclusionMode(info);
+    handleDropInputMode(info);
 
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
         if (cropLayer == nullptr) {
-            info.touchableRegion = Region(toPhysicalDisplay.transform(Rect{mScreenBounds}));
+            info.touchableRegion = Region(toNonRotatedDisplay.transform(Rect{mScreenBounds}));
         } else {
             info.touchableRegion =
-                    Region(toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds}));
+                    Region(toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
         }
     } else if (cropLayer != nullptr) {
         info.touchableRegion = info.touchableRegion.intersect(
-                toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds}));
+                toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
     }
 
     // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
@@ -2331,7 +2439,7 @@
     if (isClone()) {
         sp<Layer> clonedRoot = getClonedRoot();
         if (clonedRoot != nullptr) {
-            Rect rect = toPhysicalDisplay.transform(Rect{clonedRoot->mScreenBounds});
+            Rect rect = toNonRotatedDisplay.transform(Rect{clonedRoot->mScreenBounds});
             info.touchableRegion = info.touchableRegion.intersect(rect);
         }
     }
@@ -2458,7 +2566,7 @@
     }
     // Cloned layers shouldn't handle watch outside since their z order is not determined by
     // WM or the client.
-    mDrawingState.inputInfo.flags &= ~InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH;
+    mDrawingState.inputInfo.flags &= ~WindowInfo::Flag::WATCH_OUTSIDE_TOUCH;
 }
 
 void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
@@ -2560,6 +2668,14 @@
     return handle->owner;
 }
 
+bool Layer::setDropInputMode(gui::DropInputMode mode) {
+    if (mDrawingState.dropInputMode == mode) {
+        return false;
+    }
+    mDrawingState.dropInputMode = mode;
+    return true;
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8905548..b107f8e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -17,11 +17,12 @@
 
 #pragma once
 
+#include <android/gui/DropInputMode.h>
 #include <compositionengine/LayerFE.h>
 #include <gui/BufferQueue.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/vec4.h>
 #include <renderengine/Mesh.h>
@@ -186,7 +187,7 @@
         float cornerRadius;
         int backgroundBlurRadius;
 
-        InputWindowInfo inputInfo;
+        gui::WindowInfo inputInfo;
         wp<Layer> touchableRegionCrop;
 
         // dataspace is only used by BufferStateLayer and EffectLayer
@@ -280,6 +281,10 @@
 
         Rect bufferCrop;
         Rect destinationFrame;
+
+        sp<IBinder> releaseBufferEndpoint;
+
+        gui::DropInputMode dropInputMode;
     };
 
     /*
@@ -422,7 +427,8 @@
                            const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */,
                            std::optional<nsecs_t> /* dequeueTime */,
                            const FrameTimelineInfo& /*info*/,
-                           const sp<ITransactionCompletedListener>& /* releaseBufferListener */) {
+                           const sp<ITransactionCompletedListener>& /* releaseBufferListener */,
+                           const sp<IBinder>& /* releaseBufferEndpoint */) {
         return false;
     };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -444,6 +450,8 @@
     virtual bool setFrameRateSelectionPriority(int32_t priority);
     virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
     virtual void setAutoRefresh(bool /* autoRefresh */) {}
+    bool setDropInputMode(gui::DropInputMode);
+
     //  If the variable is not set on the layer, it traverses up the tree to inherit the frame
     //  rate priority from its parent.
     virtual int32_t getFrameRateSelectionPriority() const;
@@ -603,10 +611,8 @@
     virtual bool getTransformToDisplayInverse() const { return false; }
 
     // Returns how rounded corners should be drawn for this layer.
-    // This will traverse the hierarchy until it reaches its root, finding topmost rounded
-    // corner definition and converting it into current layer's coordinates.
-    // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
-    // ignored.
+    // A layer can override its parent's rounded corner settings if the parent's rounded
+    // corner crop does not intersect with its own rounded corner crop.
     virtual RoundedCornerState getRoundedCornerState() const;
 
     bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
@@ -695,7 +701,7 @@
     void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet,
                                  uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
 
-    InputWindowInfo::Type getWindowType() const { return mWindowType; }
+    gui::WindowInfo::Type getWindowType() const { return mWindowType; }
 
     bool getPrimaryDisplayOnly() const;
 
@@ -846,9 +852,9 @@
     sp<IBinder> getHandle();
     const std::string& getName() const { return mName; }
     bool getPremultipledAlpha() const;
-    void setInputInfo(const InputWindowInfo& info);
+    void setInputInfo(const gui::WindowInfo& info);
 
-    InputWindowInfo fillInputInfo(const sp<DisplayDevice>& display);
+    gui::WindowInfo fillInputInfo(const sp<DisplayDevice>& display);
     /**
      * Returns whether this layer has an explicitly set input-info.
      */
@@ -1013,7 +1019,7 @@
     wp<Layer> mDrawingParent;
 
     // Window types from WindowManager.LayoutParams
-    const InputWindowInfo::Type mWindowType;
+    const gui::WindowInfo::Type mWindowType;
 
     // The owner of the layer. If created from a non system process, it will be the calling uid.
     // If created from a system process, the value can be passed in.
@@ -1056,6 +1062,8 @@
     bool setFrameRateForLayerTree(FrameRate);
     void setZOrderRelativeOf(const wp<Layer>& relativeOf);
     bool isTrustedOverlay() const;
+    gui::DropInputMode getDropInputMode() const;
+    void handleDropInputMode(gui::WindowInfo& info) const;
 
     // Find the root of the cloned hierarchy, this means the first non cloned parent.
     // This will return null if first non cloned parent is not found.
@@ -1067,10 +1075,10 @@
 
     // Fills in the touch occlusion mode of the first parent (including this layer) that
     // hasInputInfo() or no-op if no such parent is found.
-    void fillTouchOcclusionMode(InputWindowInfo& info);
+    void fillTouchOcclusionMode(gui::WindowInfo& info);
 
-    // Fills in the frame and transform info for the InputWindowInfo
-    void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
+    // Fills in the frame and transform info for the gui::WindowInfo
+    void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toNonRotatedDisplay);
 
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index b1db6d3..1062126 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -22,6 +22,9 @@
 #include "LayerProtoHelper.h"
 
 namespace android {
+
+using gui::WindowInfo;
+
 namespace surfaceflinger {
 
 void LayerProtoHelper::writePositionToProto(const float x, const float y,
@@ -95,8 +98,8 @@
     }
 }
 
-void LayerProtoHelper::writeToProto(const ui::Transform& transform,
-                                    TransformProto* transformProto) {
+void LayerProtoHelper::writeToProtoDeprecated(const ui::Transform& transform,
+                                              TransformProto* transformProto) {
     const uint32_t type = transform.getType() | (transform.getOrientation() << 8);
     transformProto->set_type(type);
 
@@ -111,6 +114,22 @@
     }
 }
 
+void LayerProtoHelper::writeTransformToProto(const ui::Transform& transform,
+                                             TransformProto* transformProto) {
+    const uint32_t type = transform.getType() | (transform.getOrientation() << 8);
+    transformProto->set_type(type);
+
+    // Rotations that are 90/180/270 have their own type so the transform matrix can be
+    // reconstructed later. All other rotation have the type UNKNOWN so we need to save the
+    // transform values in that case.
+    if (type & (ui::Transform::SCALE | ui::Transform::UNKNOWN)) {
+        transformProto->set_dsdx(transform.dsdx());
+        transformProto->set_dtdx(transform.dtdx());
+        transformProto->set_dtdy(transform.dtdy());
+        transformProto->set_dsdy(transform.dsdy());
+    }
+}
+
 void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer,
                                     std::function<ActiveBufferProto*()> getActiveBufferProto) {
     if (buffer->getWidth() != 0 || buffer->getHeight() != 0 || buffer->getStride() != 0 ||
@@ -125,7 +144,7 @@
 }
 
 void LayerProtoHelper::writeToProto(
-        const InputWindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds,
+        const WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds,
         std::function<InputWindowInfoProto*()> getInputWindowInfoProto) {
     if (inputInfo.token == nullptr) {
         return;
@@ -133,7 +152,7 @@
 
     InputWindowInfoProto* proto = getInputWindowInfoProto();
     proto->set_layout_params_flags(inputInfo.flags.get());
-    using U = std::underlying_type_t<InputWindowInfo::Type>;
+    using U = std::underlying_type_t<WindowInfo::Type>;
     // TODO(b/129481165): This static assert can be safely removed once conversion warnings
     // are re-enabled.
     static_assert(std::is_same_v<U, int32_t>);
@@ -151,7 +170,7 @@
     proto->set_has_wallpaper(inputInfo.hasWallpaper);
 
     proto->set_global_scale_factor(inputInfo.globalScaleFactor);
-    LayerProtoHelper::writeToProto(inputInfo.transform, proto->mutable_transform());
+    LayerProtoHelper::writeToProtoDeprecated(inputInfo.transform, proto->mutable_transform());
     proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop);
     auto cropLayer = touchableRegionBounds.promote();
     if (cropLayer != nullptr) {
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 502238d..36e0647 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -17,7 +17,7 @@
 #include <layerproto/LayerProtoHeader.h>
 
 #include <Layer.h>
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
 #include <math/vec4.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
@@ -37,10 +37,15 @@
                              std::function<FloatRectProto*()> getFloatRectProto);
     static void writeToProto(const Region& region, std::function<RegionProto*()> getRegionProto);
     static void writeToProto(const half4 color, std::function<ColorProto*()> getColorProto);
-    static void writeToProto(const ui::Transform& transform, TransformProto* transformProto);
+    // This writeToProto for transform is incorrect, but due to backwards compatibility, we can't
+    // update Layers to use it. Use writeTransformToProto for any new transform proto data.
+    static void writeToProtoDeprecated(const ui::Transform& transform,
+                                       TransformProto* transformProto);
+    static void writeTransformToProto(const ui::Transform& transform,
+                                      TransformProto* transformProto);
     static void writeToProto(const sp<GraphicBuffer>& buffer,
                              std::function<ActiveBufferProto*()> getActiveBufferProto);
-    static void writeToProto(const InputWindowInfo& inputInfo,
+    static void writeToProto(const gui::WindowInfo& inputInfo,
                              const wp<Layer>& touchableRegionBounds,
                              std::function<InputWindowInfoProto*()> getInputWindowInfoProto);
     static void writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto);
diff --git a/services/surfaceflinger/MainThreadGuard.h b/services/surfaceflinger/MainThreadGuard.h
new file mode 100644
index 0000000..c1aa118
--- /dev/null
+++ b/services/surfaceflinger/MainThreadGuard.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Mutex.h>
+
+namespace android {
+namespace {
+
+// Helps to ensure that some functions runs on SF's main thread by using the
+// clang thread safety annotations.
+class CAPABILITY("mutex") MainThreadGuard {
+} SF_MAIN_THREAD;
+
+struct SCOPED_CAPABILITY MainThreadScopedGuard {
+public:
+    explicit MainThreadScopedGuard(MainThreadGuard& mutex) ACQUIRE(mutex) {}
+    ~MainThreadScopedGuard() RELEASE() {}
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 27a1c28..bc32a1d 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -175,10 +175,14 @@
     return buffers;
 }
 
-RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, bool showSpinner)
-      : mFlinger(flinger), mClient(new Client(&mFlinger)), mShowSpinner(showSpinner) {
+RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, uint32_t lowFps, uint32_t highFps,
+                                       bool showSpinner)
+      : mFlinger(flinger),
+        mClient(new Client(&mFlinger)),
+        mShowSpinner(showSpinner),
+        mLowFps(lowFps),
+        mHighFps(highFps) {
     createLayer();
-    reset();
 }
 
 bool RefreshRateOverlay::createLayer() {
@@ -194,7 +198,6 @@
         return false;
     }
 
-    Mutex::Autolock _l(mFlinger.mStateLock);
     mLayer = mClient->getLayerUser(mIBinder);
     mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote));
     mLayer->setIsAtRoot(true);
@@ -241,8 +244,11 @@
 }
 
 void RefreshRateOverlay::setViewport(ui::Size viewport) {
-    Rect frame((3 * viewport.width) >> 4, viewport.height >> 5);
-    frame.offsetBy(viewport.width >> 5, viewport.height >> 4);
+    constexpr int32_t kMaxWidth = 1000;
+    const auto width = std::min(kMaxWidth, std::min(viewport.width, viewport.height));
+    const auto height = 2 * width;
+    Rect frame((3 * width) >> 4, height >> 5);
+    frame.offsetBy(width >> 5, height >> 4);
 
     layer_state_t::matrix22_t matrix;
     matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
@@ -254,13 +260,18 @@
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
+void RefreshRateOverlay::setLayerStack(uint32_t stack) {
+    mLayer->setLayerStack(stack);
+    mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+}
+
 void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
     mCurrentFps = fps.getIntValue();
     auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
                       std::nullopt /* dequeueTime */, FrameTimelineInfo{},
-                      nullptr /* releaseBufferListener */);
+                      nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
@@ -274,18 +285,11 @@
     mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
                       std::nullopt /* dequeueTime */, FrameTimelineInfo{},
-                      nullptr /* releaseBufferListener */);
+                      nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
-void RefreshRateOverlay::reset() {
-    mBufferCache.clear();
-    const auto range = mFlinger.mRefreshRateConfigs->getSupportedRefreshRateRange();
-    mLowFps = range.min.getIntValue();
-    mHighFps = range.max.getIntValue();
-}
-
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index aa8329c..f9baa89 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -37,12 +37,12 @@
 
 class RefreshRateOverlay {
 public:
-    RefreshRateOverlay(SurfaceFlinger&, bool showSpinner);
+    RefreshRateOverlay(SurfaceFlinger&, uint32_t lowFps, uint32_t highFps, bool showSpinner);
 
+    void setLayerStack(uint32_t stack);
     void setViewport(ui::Size);
     void changeRefreshRate(const Fps&);
     void onInvalidate();
-    void reset();
 
 private:
     class SevenSegmentDrawer {
@@ -91,8 +91,8 @@
     const bool mShowSpinner;
 
     // Interpolate the colors between these values.
-    uint32_t mLowFps;
-    uint32_t mHighFps;
+    const uint32_t mLowFps;
+    const uint32_t mHighFps;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 2321e2d..2bdcaf6 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -118,12 +118,12 @@
     return event;
 }
 
-DisplayEventReceiver::Event makeModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
-                                            nsecs_t vsyncPeriod) {
+DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) {
     DisplayEventReceiver::Event event;
-    event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, displayId, systemTime()};
-    event.modeChange.modeId = modeId.value();
-    event.modeChange.vsyncPeriod = vsyncPeriod;
+    event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(),
+                    systemTime()};
+    event.modeChange.modeId = mode->getId().value();
+    event.modeChange.vsyncPeriod = mode->getVsyncPeriod();
     return event;
 }
 
@@ -375,11 +375,10 @@
     mCondition.notify_all();
 }
 
-void EventThread::onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
-                                nsecs_t vsyncPeriod) {
+void EventThread::onModeChanged(DisplayModePtr mode) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    mPendingEvents.push_back(makeModeChanged(displayId, modeId, vsyncPeriod));
+    mPendingEvents.push_back(makeModeChanged(mode));
     mCondition.notify_all();
 }
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 1e6793f..9265a25 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -124,8 +124,7 @@
     virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
 
     // called when SF changes the active mode and apps needs to be notified about the change
-    virtual void onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
-                               nsecs_t vsyncPeriod) = 0;
+    virtual void onModeChanged(DisplayModePtr) = 0;
 
     // called when SF updates the Frame Rate Override list
     virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
@@ -174,8 +173,7 @@
 
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
-    void onModeChanged(PhysicalDisplayId displayId, DisplayModeId modeId,
-                       nsecs_t vsyncPeriod) override;
+    void onModeChanged(DisplayModePtr) override;
 
     void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
                                      std::vector<FrameRateOverride> overrides) override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 0563795..84e3548 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -75,10 +75,9 @@
 }
 } // namespace
 
-LayerHistory::LayerHistory(const RefreshRateConfigs& refreshRateConfigs)
+LayerHistory::LayerHistory()
       : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
     LayerInfo::setTraceEnabled(mTraceEnabled);
-    LayerInfo::setRefreshRateConfigs(refreshRateConfigs);
 }
 
 LayerHistory::~LayerHistory() = default;
@@ -138,7 +137,8 @@
     }
 }
 
-LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
+LayerHistory::Summary LayerHistory::summarize(const RefreshRateConfigs& refreshRateConfigs,
+                                              nsecs_t now) {
     LayerHistory::Summary summary;
 
     std::lock_guard lock(mLock);
@@ -151,7 +151,7 @@
         ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
               layerFocused ? "" : "not");
 
-        const auto vote = info->getRefreshRateVote(now);
+        const auto vote = info->getRefreshRateVote(refreshRateConfigs, now);
         // Skip NoVote layer as those don't have any requirements
         if (vote.type == LayerHistory::LayerVoteType::NoVote) {
             continue;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 82f6c39..92236f5 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -42,7 +42,7 @@
 public:
     using LayerVoteType = RefreshRateConfigs::LayerVoteType;
 
-    LayerHistory(const RefreshRateConfigs&);
+    LayerHistory();
     ~LayerHistory();
 
     // Layers are unregistered when the weak reference expires.
@@ -67,7 +67,7 @@
     using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
-    Summary summarize(nsecs_t now);
+    Summary summarize(const RefreshRateConfigs&, nsecs_t now);
 
     void clear();
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 989bf4e..8a45b66 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -34,7 +34,6 @@
 
 namespace android::scheduler {
 
-const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
 bool LayerInfo::sTraceEnabled = false;
 
 LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid,
@@ -184,7 +183,8 @@
     return static_cast<nsecs_t>(averageFrameTime);
 }
 
-std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(nsecs_t now) {
+std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(
+        const RefreshRateConfigs& refreshRateConfigs, nsecs_t now) {
     static constexpr float MARGIN = 1.0f; // 1Hz
     if (!hasEnoughDataForHeuristic()) {
         ALOGV("Not enough data");
@@ -196,9 +196,7 @@
         const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
         const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
         if (refreshRateConsistent) {
-            const auto knownRefreshRate =
-                    sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
-
+            const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
             // To avoid oscillation, use the last calculated refresh rate if it is
             // close enough
             if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
@@ -220,7 +218,8 @@
                                                : std::nullopt;
 }
 
-LayerInfo::LayerVote LayerInfo::getRefreshRateVote(nsecs_t now) {
+LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& refreshRateConfigs,
+                                                   nsecs_t now) {
     if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
         ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
         return mLayerVote;
@@ -247,7 +246,7 @@
         clearHistory(now);
     }
 
-    auto refreshRate = calculateRefreshRateIfPossible(now);
+    auto refreshRate = calculateRefreshRateIfPossible(refreshRateConfigs, now);
     if (refreshRate.has_value()) {
         ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
         return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 34cc389..ce9783c 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -122,10 +122,6 @@
 
     static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
 
-    static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
-        sRefreshRateConfigs = &refreshRateConfigs;
-    }
-
     LayerInfo(const std::string& name, uid_t ownerUid, LayerHistory::LayerVoteType defaultVote);
 
     LayerInfo(const LayerInfo&) = delete;
@@ -161,7 +157,7 @@
 
     uid_t getOwnerUid() const { return mOwnerUid; }
 
-    LayerVote getRefreshRateVote(nsecs_t now);
+    LayerVote getRefreshRateVote(const RefreshRateConfigs&, nsecs_t now);
 
     // Return the last updated time. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
@@ -263,7 +259,7 @@
     bool isFrequent(nsecs_t now) const;
     bool isAnimating(nsecs_t now) const;
     bool hasEnoughDataForHeuristic() const;
-    std::optional<Fps> calculateRefreshRateIfPossible(nsecs_t now);
+    std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateConfigs&, nsecs_t now);
     std::optional<nsecs_t> calculateAverageFrameTime() const;
     bool isFrameTimeValid(const FrameTimeData&) const;
 
@@ -300,7 +296,6 @@
     mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
 
     // Shared for all LayerInfo instances
-    static const RefreshRateConfigs* sRefreshRateConfigs;
     static bool sTraceEnabled;
 };
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 73b7b63..ee1d730 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -22,6 +22,7 @@
 #pragma clang diagnostic ignored "-Wextra"
 
 #include "RefreshRateConfigs.h"
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <utils/Trace.h>
 #include <chrono>
@@ -113,7 +114,7 @@
         case LayerVoteType::ExplicitExactOrMultiple:
         case LayerVoteType::Heuristic:
             if (mConfig.frameRateMultipleThreshold != 0 &&
-                refreshRate.fps.greaterThanOrEqualWithMargin(
+                refreshRate.getFps().greaterThanOrEqualWithMargin(
                         Fps(mConfig.frameRateMultipleThreshold)) &&
                 layer.desiredRefreshRate.lessThanWithMargin(
                         Fps(mConfig.frameRateMultipleThreshold / 2))) {
@@ -146,8 +147,8 @@
 
     // If the layer wants Max, give higher score to the higher refresh rate
     if (layer.vote == LayerVoteType::Max) {
-        const auto ratio =
-                refreshRate.fps.getValue() / mAppRequestRefreshRates.back()->fps.getValue();
+        const auto ratio = refreshRate.getFps().getValue() /
+                mAppRequestRefreshRates.back()->getFps().getValue();
         // use ratio^2 to get a lower score the more we get further from peak
         return ratio * ratio;
     }
@@ -463,7 +464,7 @@
         }
     }();
     if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
-        bestRefreshRate->fps.lessThanWithMargin(touchRefreshRate.fps)) {
+        bestRefreshRate->getFps().lessThanWithMargin(touchRefreshRate.getFps())) {
         setTouchConsidered();
         ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
         return touchRefreshRate;
@@ -679,9 +680,31 @@
 RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
                                        Config config)
       : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
+    initializeIdleTimer();
     updateDisplayModes(modes, currentModeId);
 }
 
+void RefreshRateConfigs::initializeIdleTimer() {
+    if (mConfig.idleTimerTimeoutMs > 0) {
+        const auto getCallback = [this]() -> std::optional<IdleTimerCallbacks::Callbacks> {
+            std::scoped_lock lock(mIdleTimerCallbacksMutex);
+            if (!mIdleTimerCallbacks.has_value()) return {};
+            return mConfig.supportKernelIdleTimer ? mIdleTimerCallbacks->kernel
+                                                  : mIdleTimerCallbacks->platform;
+        };
+
+        mIdleTimer.emplace(
+                "IdleTimer", std::chrono::milliseconds(mConfig.idleTimerTimeoutMs),
+                [getCallback] {
+                    if (const auto callback = getCallback()) callback->onReset();
+                },
+                [getCallback] {
+                    if (const auto callback = getCallback()) callback->onExpired();
+                });
+        mIdleTimer->start();
+    }
+}
+
 void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
                                             DisplayModeId currentModeId) {
     std::lock_guard lock(mLock);
@@ -699,8 +722,7 @@
     for (const auto& mode : modes) {
         const auto modeId = mode->getId();
         mRefreshRates.emplace(modeId,
-                              std::make_unique<RefreshRate>(modeId, mode, mode->getFps(),
-                                                            RefreshRate::ConstructorTag(0)));
+                              std::make_unique<RefreshRate>(mode, RefreshRate::ConstructorTag(0)));
         if (modeId == currentModeId) {
             mCurrentRefreshRate = mRefreshRates.at(modeId).get();
         }
@@ -793,7 +815,7 @@
 bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const {
     std::lock_guard lock(mLock);
     for (const RefreshRate* refreshRate : mAppRequestRefreshRates) {
-        if (refreshRate->modeId == modeId) {
+        if (refreshRate->getModeId() == modeId) {
             return true;
         }
     }
@@ -808,7 +830,7 @@
     for (const auto& [type, refreshRate] : mRefreshRates) {
         if (shouldAddRefreshRate(*refreshRate)) {
             ALOGV("getSortedRefreshRateListLocked: mode %d added to list policy",
-                  refreshRate->modeId.value());
+                  refreshRate->getModeId().value());
             outRefreshRates->push_back(refreshRate.get());
         }
     }
@@ -945,6 +967,9 @@
 
     base::StringAppendF(&result, "Supports Frame Rate Override: %s\n",
                         mSupportsFrameRateOverride ? "yes" : "no");
+    base::StringAppendF(&result, "Idle timer: (%s) %s\n",
+                        mConfig.supportKernelIdleTimer ? "kernel" : "platform",
+                        mIdleTimer ? mIdleTimer->dump().c_str() : "off");
     result.append("\n");
 }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index dfd1395..2addc83 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -27,6 +27,7 @@
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/HWComposer.h"
 #include "Fps.h"
+#include "Scheduler/OneShotTimer.h"
 #include "Scheduler/SchedulerUtils.h"
 #include "Scheduler/Seamlessness.h"
 #include "Scheduler/StrongTyping.h"
@@ -64,25 +65,23 @@
         };
 
     public:
-        RefreshRate(DisplayModeId modeId, DisplayModePtr mode, Fps fps, ConstructorTag)
-              : modeId(modeId), mode(mode), fps(std::move(fps)) {}
+        RefreshRate(DisplayModePtr mode, ConstructorTag) : mode(mode) {}
 
-        DisplayModeId getModeId() const { return modeId; }
+        DisplayModeId getModeId() const { return mode->getId(); }
         nsecs_t getVsyncPeriod() const { return mode->getVsyncPeriod(); }
         int32_t getModeGroup() const { return mode->getGroup(); }
-        std::string getName() const { return to_string(fps); }
-        Fps getFps() const { return fps; }
+        std::string getName() const { return to_string(getFps()); }
+        Fps getFps() const { return mode->getFps(); }
+        DisplayModePtr getMode() const { return mode; }
 
         // Checks whether the fps of this RefreshRate struct is within a given min and max refresh
         // rate passed in. Margin of error is applied to the boundaries for approximation.
         bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
-            return minRefreshRate.lessThanOrEqualWithMargin(fps) &&
-                    fps.lessThanOrEqualWithMargin(maxRefreshRate);
+            return minRefreshRate.lessThanOrEqualWithMargin(getFps()) &&
+                    getFps().lessThanOrEqualWithMargin(maxRefreshRate);
         }
 
-        bool operator!=(const RefreshRate& other) const {
-            return modeId != other.modeId || mode != other.mode;
-        }
+        bool operator!=(const RefreshRate& other) const { return mode != other.mode; }
 
         bool operator<(const RefreshRate& other) const {
             return getFps().getValue() < other.getFps().getValue();
@@ -99,10 +98,7 @@
         friend RefreshRateConfigs;
         friend class RefreshRateConfigsTest;
 
-        const DisplayModeId modeId;
         DisplayModePtr mode;
-        // Refresh rate in frames per second
-        const Fps fps{0.0f};
     };
 
     using AllRefreshRatesMapType =
@@ -310,13 +306,19 @@
         // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
         // no threshold is set.
         int frameRateMultipleThreshold = 0;
+
+        // The Idle Timer timeout. 0 timeout means no idle timer.
+        int32_t idleTimerTimeoutMs = 0;
+
+        // Whether to use idle timer callbacks that support the kernel timer.
+        bool supportKernelIdleTimer = false;
     };
 
-    RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
+    RefreshRateConfigs(const DisplayModes&, DisplayModeId,
                        Config config = {.enableFrameRateOverride = false,
-                                        .frameRateMultipleThreshold = 0});
-
-    void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
+                                        .frameRateMultipleThreshold = 0,
+                                        .idleTimerTimeoutMs = 0,
+                                        .supportKernelIdleTimer = false});
 
     // Returns whether switching modes (refresh rate or resolution) is possible.
     // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
@@ -352,8 +354,35 @@
                                                  Fps displayFrameRate, bool touch) const
             EXCLUDES(mLock);
 
+    bool supportsKernelIdleTimer() const { return mConfig.supportKernelIdleTimer; }
+
+    void setIdleTimerCallbacks(std::function<void()> platformTimerReset,
+                               std::function<void()> platformTimerExpired,
+                               std::function<void()> kernelTimerReset,
+                               std::function<void()> kernelTimerExpired) {
+        std::scoped_lock lock(mIdleTimerCallbacksMutex);
+        mIdleTimerCallbacks.emplace();
+        mIdleTimerCallbacks->platform.onReset = platformTimerReset;
+        mIdleTimerCallbacks->platform.onExpired = platformTimerExpired;
+        mIdleTimerCallbacks->kernel.onReset = kernelTimerReset;
+        mIdleTimerCallbacks->kernel.onExpired = kernelTimerExpired;
+    }
+
+    void resetIdleTimer(bool kernelOnly) {
+        if (!mIdleTimer) {
+            return;
+        }
+        if (kernelOnly && !mConfig.supportKernelIdleTimer) {
+            return;
+        }
+        mIdleTimer->reset();
+    };
+
     void dump(std::string& result) const EXCLUDES(mLock);
 
+    RefreshRateConfigs(const RefreshRateConfigs&) = delete;
+    void operator=(const RefreshRateConfigs&) = delete;
+
 private:
     friend class RefreshRateConfigsTest;
 
@@ -405,6 +434,10 @@
     float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
                                     bool isSeamlessSwitch) const REQUIRES(mLock);
 
+    void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
+
+    void initializeIdleTimer();
+
     // The list of refresh rates, indexed by display modes ID. This may change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
@@ -448,6 +481,22 @@
     };
     mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
             GUARDED_BY(mLock);
+
+    // Timer that records time between requests for next vsync.
+    std::optional<scheduler::OneShotTimer> mIdleTimer;
+
+    struct IdleTimerCallbacks {
+        struct Callbacks {
+            std::function<void()> onReset;
+            std::function<void()> onExpired;
+        };
+
+        Callbacks platform;
+        Callbacks kernel;
+    };
+
+    std::mutex mIdleTimerCallbacksMutex;
+    std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 64ad178..1989d57 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,7 +25,7 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
 #include <system/window.h>
 #include <ui/DisplayStatInfo.h>
 #include <utils/Timers.h>
@@ -64,6 +64,8 @@
 
 namespace android {
 
+using gui::WindowInfo;
+
 namespace {
 
 std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
@@ -115,30 +117,18 @@
     }
 };
 
-Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
+Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
+                     ISchedulerCallback& callback)
       : Scheduler(configs, callback,
-                  {.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
-                   .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
+                  {.useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
 }
 
-Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
-                     Options options)
-      : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
-                  createLayerHistory(configs), options) {
+Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
+                     ISchedulerCallback& callback, Options options)
+      : Scheduler(createVsyncSchedule(configs->supportsKernelIdleTimer()), configs, callback,
+                  createLayerHistory(), options) {
     using namespace sysprop;
 
-    const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
-
-    if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
-        const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback
-                                                          : &Scheduler::idleTimerCallback;
-        mIdleTimer.emplace(
-                "IdleTimer", std::chrono::milliseconds(millis),
-                [this, callback] { std::invoke(callback, this, TimerState::Reset); },
-                [this, callback] { std::invoke(callback, this, TimerState::Expired); });
-        mIdleTimer->start();
-    }
-
     if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
         // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
         mTouchTimer.emplace(
@@ -157,18 +147,19 @@
     }
 }
 
-Scheduler::Scheduler(VsyncSchedule schedule, const scheduler::RefreshRateConfigs& configs,
+Scheduler::Scheduler(VsyncSchedule schedule,
+                     const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
                      ISchedulerCallback& schedulerCallback,
                      std::unique_ptr<LayerHistory> layerHistory, Options options)
       : mOptions(options),
         mVsyncSchedule(std::move(schedule)),
         mLayerHistory(std::move(layerHistory)),
         mSchedulerCallback(schedulerCallback),
-        mRefreshRateConfigs(configs),
         mPredictedVsyncTracer(
                 base::GetBoolProperty("debug.sf.show_predicted_vsync", false)
                         ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch)
                         : nullptr) {
+    setRefreshRateConfigs(configs);
     mSchedulerCallback.setVsyncEnabled(false);
 }
 
@@ -176,7 +167,7 @@
     // Ensure the OneShotTimer threads are joined before we start destroying state.
     mDisplayPowerTimer.reset();
     mTouchTimer.reset();
-    mIdleTimer.reset();
+    mRefreshRateConfigs.reset();
 }
 
 Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) {
@@ -192,9 +183,8 @@
     return {std::move(controller), std::move(tracker), std::move(dispatch)};
 }
 
-std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
-        const scheduler::RefreshRateConfigs& configs) {
-    return std::make_unique<scheduler::LayerHistory>(configs);
+std::unique_ptr<LayerHistory> Scheduler::createLayerHistory() {
+    return std::make_unique<scheduler::LayerHistory>();
 }
 
 std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
@@ -205,11 +195,14 @@
 }
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
-    if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
-        return std::nullopt;
+    {
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        if (!mRefreshRateConfigs->supportsFrameRateOverride()) {
+            return std::nullopt;
+        }
     }
 
-    std::lock_guard lock(mFrameRateOverridesMutex);
+    std::lock_guard lock(mFrameRateOverridesLock);
     {
         const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
         if (iter != mFrameRateOverridesFromBackdoor.end()) {
@@ -237,7 +230,8 @@
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
-    if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+    std::scoped_lock lock(mRefreshRateConfigsLock);
+    if (!mRefreshRateConfigs->supportsFrameRateOverride()) {
         return {};
     }
 
@@ -248,14 +242,18 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        nsecs_t basePeriod = mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod();
+        const auto refreshRateConfigs = holdRefreshRateConfigs();
+        nsecs_t basePeriod = refreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
         const auto frameRate = getFrameRateOverride(uid);
         if (!frameRate.has_value()) {
             return basePeriod;
         }
 
-        const auto divider = scheduler::RefreshRateConfigs::getFrameRateDivider(
-            mRefreshRateConfigs.getCurrentRefreshRate().getFps(), *frameRate);
+        const auto divider =
+                scheduler::RefreshRateConfigs::getFrameRateDivider(refreshRateConfigs
+                                                                           ->getCurrentRefreshRate()
+                                                                           .getFps(),
+                                                                   *frameRate);
         if (divider <= 1) {
             return basePeriod;
         }
@@ -326,6 +324,7 @@
         thread = mConnections[handle].thread.get();
     }
     thread->onScreenAcquired();
+    mScreenAcquired = true;
 }
 
 void Scheduler::onScreenReleased(ConnectionHandle handle) {
@@ -336,12 +335,13 @@
         thread = mConnections[handle].thread.get();
     }
     thread->onScreenReleased();
+    mScreenAcquired = false;
 }
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
     std::vector<FrameRateOverride> overrides;
     {
-        std::lock_guard lock(mFrameRateOverridesMutex);
+        std::lock_guard lock(mFrameRateOverridesLock);
         for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
             overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
         }
@@ -360,23 +360,22 @@
     thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
 }
 
-void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                            DisplayModeId modeId, nsecs_t vsyncPeriod) {
+void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         // Cache the last reported modes for primary display.
-        mFeatures.cachedModeChangedParams = {handle, displayId, modeId, vsyncPeriod};
+        mFeatures.cachedModeChangedParams = {handle, mode};
 
         // Invalidate content based refresh rate selection so it could be calculated
         // again for the new refresh rate.
         mFeatures.contentRequirements.clear();
     }
-    onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod);
+    onNonPrimaryDisplayModeChanged(handle, mode);
 }
 
 void Scheduler::dispatchCachedReportedMode() {
     // Check optional fields first.
-    if (!mFeatures.modeId.has_value()) {
+    if (!mFeatures.mode) {
         ALOGW("No mode ID found, not dispatching cached mode.");
         return;
     }
@@ -385,39 +384,32 @@
         return;
     }
 
-    const auto modeId = *mFeatures.modeId;
-    // If the modeId is not the current mode, this means that a
+    // If the mode is not the current mode, this means that a
     // mode change is in progress. In that case we shouldn't dispatch an event
     // as it will be dispatched when the current mode changes.
-    if (mRefreshRateConfigs.getCurrentRefreshRate().getModeId() != modeId) {
+    if (std::scoped_lock lock(mRefreshRateConfigsLock);
+        mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mFeatures.mode) {
         return;
     }
 
-    const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromModeId(modeId).getVsyncPeriod();
-
     // If there is no change from cached mode, there is no need to dispatch an event
-    if (modeId == mFeatures.cachedModeChangedParams->modeId &&
-        vsyncPeriod == mFeatures.cachedModeChangedParams->vsyncPeriod) {
+    if (mFeatures.mode == mFeatures.cachedModeChangedParams->mode) {
         return;
     }
 
-    mFeatures.cachedModeChangedParams->modeId = modeId;
-    mFeatures.cachedModeChangedParams->vsyncPeriod = vsyncPeriod;
+    mFeatures.cachedModeChangedParams->mode = mFeatures.mode;
     onNonPrimaryDisplayModeChanged(mFeatures.cachedModeChangedParams->handle,
-                                   mFeatures.cachedModeChangedParams->displayId,
-                                   mFeatures.cachedModeChangedParams->modeId,
-                                   mFeatures.cachedModeChangedParams->vsyncPeriod);
+                                   mFeatures.cachedModeChangedParams->mode);
 }
 
-void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                               DisplayModeId modeId, nsecs_t vsyncPeriod) {
+void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
     android::EventThread* thread;
     {
         std::lock_guard<std::mutex> lock(mConnectionsLock);
         RETURN_IF_INVALID_HANDLE(handle);
         thread = mConnections[handle].thread.get();
     }
-    thread->onModeChanged(displayId, modeId, vsyncPeriod);
+    thread->onModeChanged(mode);
 }
 
 size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
@@ -537,7 +529,11 @@
     const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod());
+        const auto vsyncPeriod = [&] {
+            std::scoped_lock lock(mRefreshRateConfigsLock);
+            return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+        }();
+        resyncToHardwareVsync(false, vsyncPeriod);
     }
 }
 
@@ -586,10 +582,9 @@
 void Scheduler::registerLayer(Layer* layer) {
     scheduler::LayerHistory::LayerVoteType voteType;
 
-    if (!mOptions.useContentDetection ||
-        layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
+    if (!mOptions.useContentDetection || layer->getWindowType() == WindowInfo::Type::STATUS_BAR) {
         voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
-    } else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
+    } else if (layer->getWindowType() == WindowInfo::Type::WALLPAPER) {
         // Running Wallpaper at Min is considered as part of content detection.
         voteType = scheduler::LayerHistory::LayerVoteType::Min;
     } else {
@@ -608,9 +603,12 @@
 
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
-    if (mRefreshRateConfigs.canSwitch()) {
-        mLayerHistory->record(layer, presentTime, systemTime(), updateType);
+    {
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        if (!mRefreshRateConfigs->canSwitch()) return;
     }
+
+    mLayerHistory->record(layer, presentTime, systemTime(), updateType);
 }
 
 void Scheduler::setModeChangePending(bool pending) {
@@ -618,25 +616,28 @@
 }
 
 void Scheduler::chooseRefreshRateForContent() {
-    if (!mRefreshRateConfigs.canSwitch()) return;
+    {
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        if (!mRefreshRateConfigs->canSwitch()) return;
+    }
 
     ATRACE_CALL();
 
-    scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
+    const auto refreshRateConfigs = holdRefreshRateConfigs();
+    scheduler::LayerHistory::Summary summary =
+            mLayerHistory->summarize(*refreshRateConfigs, systemTime());
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
-    DisplayModeId newModeId;
+    DisplayModePtr newMode;
     bool frameRateChanged;
     bool frameRateOverridesChanged;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         mFeatures.contentRequirements = summary;
 
-        newModeId = calculateRefreshRateModeId(&consideredSignals);
-        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
-        frameRateOverridesChanged =
-                updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
+        newMode = calculateRefreshRateModeId(&consideredSignals);
+        frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
 
-        if (mFeatures.modeId == newModeId) {
+        if (mFeatures.mode == newMode) {
             // We don't need to change the display mode, but we might need to send an event
             // about a mode change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
@@ -644,12 +645,12 @@
             }
             frameRateChanged = false;
         } else {
-            mFeatures.modeId = newModeId;
+            mFeatures.mode = newMode;
             frameRateChanged = true;
         }
     }
     if (frameRateChanged) {
-        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
+        auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
                                              consideredSignals.idle ? ModeEvent::None
                                                                     : ModeEvent::Changed);
@@ -660,18 +661,16 @@
 }
 
 void Scheduler::resetIdleTimer() {
-    if (mIdleTimer) {
-        mIdleTimer->reset();
-    }
+    std::scoped_lock lock(mRefreshRateConfigsLock);
+    mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false);
 }
 
 void Scheduler::notifyTouchEvent() {
     if (mTouchTimer) {
         mTouchTimer->reset();
 
-        if (mOptions.supportKernelTimer && mIdleTimer) {
-            mIdleTimer->reset();
-        }
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ true);
     }
 }
 
@@ -695,7 +694,11 @@
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const auto& refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+    const auto refreshRate = [&] {
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        return mRefreshRateConfigs->getCurrentRefreshRate();
+    }();
+
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER{65.0f};
     if (state == TimerState::Reset &&
         refreshRate.getFps().greaterThanWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
@@ -739,7 +742,6 @@
 void Scheduler::dump(std::string& result) const {
     using base::StringAppendF;
 
-    StringAppendF(&result, "+  Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
     StringAppendF(&result, "+  Touch timer: %s\n",
                   mTouchTimer ? mTouchTimer->dump().c_str() : "off");
     StringAppendF(&result, "+  Content detection: %s %s\n\n",
@@ -747,7 +749,7 @@
                   mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
 
     {
-        std::lock_guard lock(mFrameRateOverridesMutex);
+        std::lock_guard lock(mFrameRateOverridesLock);
         StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
         for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
             StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
@@ -760,6 +762,13 @@
         }
         StringAppendF(&result, "}\n");
     }
+
+    {
+        std::lock_guard lock(mHWVsyncLock);
+        StringAppendF(&result,
+                      "mScreenAcquired=%d mPrimaryHWVsyncEnabled=%d mHWVsyncAvailable=%d\n",
+                      mScreenAcquired.load(), mPrimaryHWVsyncEnabled, mHWVsyncAvailable);
+    }
 }
 
 void Scheduler::dumpVsync(std::string& s) const {
@@ -773,16 +782,17 @@
 
 bool Scheduler::updateFrameRateOverrides(
         scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
-    if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+    const auto refreshRateConfigs = holdRefreshRateConfigs();
+    if (!refreshRateConfigs->supportsFrameRateOverride()) {
         return false;
     }
 
     if (!consideredSignals.idle) {
         const auto frameRateOverrides =
-                mRefreshRateConfigs.getFrameRateOverrides(mFeatures.contentRequirements,
+                refreshRateConfigs->getFrameRateOverrides(mFeatures.contentRequirements,
                                                           displayRefreshRate,
                                                           consideredSignals.touch);
-        std::lock_guard lock(mFrameRateOverridesMutex);
+        std::lock_guard lock(mFrameRateOverridesLock);
         if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
                         frameRateOverrides.begin(), frameRateOverrides.end(),
                         [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
@@ -797,33 +807,33 @@
 
 template <class T>
 bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
-    DisplayModeId newModeId;
+    DisplayModePtr newMode;
     bool refreshRateChanged = false;
     bool frameRateOverridesChanged;
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
+    const auto refreshRateConfigs = holdRefreshRateConfigs();
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         if (*currentState == newState) {
             return false;
         }
         *currentState = newState;
-        newModeId = calculateRefreshRateModeId(&consideredSignals);
-        const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
-        frameRateOverridesChanged =
-                updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
-        if (mFeatures.modeId == newModeId) {
+        newMode = calculateRefreshRateModeId(&consideredSignals);
+        frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
+        if (mFeatures.mode == newMode) {
             // We don't need to change the display mode, but we might need to send an event
             // about a mode change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
                 dispatchCachedReportedMode();
             }
         } else {
-            mFeatures.modeId = newModeId;
+            mFeatures.mode = newMode;
             refreshRateChanged = true;
         }
     }
     if (refreshRateChanged) {
-        const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromModeId(newModeId);
+        const RefreshRate& newRefreshRate =
+                refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
 
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
                                              consideredSignals.idle ? ModeEvent::None
@@ -835,35 +845,36 @@
     return consideredSignals.touch;
 }
 
-DisplayModeId Scheduler::calculateRefreshRateModeId(
+DisplayModePtr Scheduler::calculateRefreshRateModeId(
         scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
     ATRACE_CALL();
     if (consideredSignals) *consideredSignals = {};
 
+    const auto refreshRateConfigs = holdRefreshRateConfigs();
     // If Display Power is not in normal operation we want to be in performance mode. When coming
     // back to normal mode, a grace period is given with DisplayPowerTimer.
     if (mDisplayPowerTimer &&
         (!mFeatures.isDisplayPowerStateNormal ||
          mFeatures.displayPowerTimer == TimerState::Reset)) {
-        return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getModeId();
+        return refreshRateConfigs->getMaxRefreshRateByPolicy().getMode();
     }
 
     const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
-    const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;
+    const bool idle = mFeatures.idleTimer == TimerState::Expired;
 
-    return mRefreshRateConfigs
-            .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
-                                consideredSignals)
-            .getModeId();
+    return refreshRateConfigs
+            ->getBestRefreshRate(mFeatures.contentRequirements,
+                                 {.touch = touchActive, .idle = idle}, consideredSignals)
+            .getMode();
 }
 
-std::optional<DisplayModeId> Scheduler::getPreferredModeId() {
+DisplayModePtr Scheduler::getPreferredDisplayMode() {
     std::lock_guard<std::mutex> lock(mFeatureStateLock);
     // Make sure that the default mode ID is first updated, before returned.
-    if (mFeatures.modeId.has_value()) {
-        mFeatures.modeId = calculateRefreshRateModeId();
+    if (mFeatures.mode) {
+        mFeatures.mode = calculateRefreshRateModeId();
     }
-    return mFeatures.modeId;
+    return mFeatures.mode;
 }
 
 void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
@@ -899,7 +910,7 @@
     }
 }
 
-void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
+void Scheduler::onActiveDisplayAreaChanged(uint32_t displayArea) {
     mLayerHistory->setDisplayArea(displayArea);
 }
 
@@ -908,7 +919,7 @@
         return;
     }
 
-    std::lock_guard lock(mFrameRateOverridesMutex);
+    std::lock_guard lock(mFrameRateOverridesLock);
     if (frameRateOverride.frameRateHz != 0.f) {
         mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
     } else {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 30a3253..ee519f3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -72,7 +72,7 @@
     using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
     using ModeEvent = scheduler::RefreshRateConfigEvent;
 
-    Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
+    Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&);
     ~Scheduler();
 
     using ConnectionHandle = scheduler::ConnectionHandle;
@@ -87,15 +87,13 @@
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
     void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
-    void onPrimaryDisplayModeChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId,
-                                     nsecs_t vsyncPeriod) EXCLUDES(mFeatureStateLock);
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle, PhysicalDisplayId, DisplayModeId,
-                                        nsecs_t vsyncPeriod);
+    void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mFeatureStateLock);
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr);
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
 
     void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
-            EXCLUDES(mFrameRateOverridesMutex) EXCLUDES(mConnectionsLock);
+            EXCLUDES(mFrameRateOverridesLock) EXCLUDES(mConnectionsLock);
 
     // Modifies work duration in the event thread.
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
@@ -116,7 +114,7 @@
     // no-op.
     // The period is the vsync period from the current display configuration.
     void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
-    void resync();
+    void resync() EXCLUDES(mRefreshRateConfigsLock);
 
     // Passes a vsync sample to VsyncController. periodFlushed will be true if
     // VsyncController detected that the vsync period changed, and false otherwise.
@@ -127,14 +125,14 @@
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
-    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
+    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType)
+            EXCLUDES(mRefreshRateConfigsLock);
     void setModeChangePending(bool pending);
     void deregisterLayer(Layer*);
 
     // Detects content using layer history, and selects a matching refresh rate.
-    void chooseRefreshRateForContent();
+    void chooseRefreshRateForContent() EXCLUDES(mRefreshRateConfigsLock);
 
-    bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
     void resetIdleTimer();
 
     // Function that resets the touch timer.
@@ -147,7 +145,7 @@
     // Returns true if a given vsync timestamp is considered valid vsync
     // for a given uid
     bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const
-            EXCLUDES(mFrameRateOverridesMutex);
+            EXCLUDES(mFrameRateOverridesLock);
 
     std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
 
@@ -156,7 +154,7 @@
     void dumpVsync(std::string&) const;
 
     // Get the appropriate refresh for current conditions.
-    std::optional<DisplayModeId> getPreferredModeId();
+    DisplayModePtr getPreferredDisplayMode();
 
     // Notifies the scheduler about a refresh rate timeline change.
     void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
@@ -165,7 +163,7 @@
     void onDisplayRefreshed(nsecs_t timestamp);
 
     // Notifies the scheduler when the display size has changed. Called from SF's main thread
-    void onPrimaryDisplayAreaChanged(uint32_t displayArea);
+    void onActiveDisplayAreaChanged(uint32_t displayArea);
 
     size_t getEventThreadConnectionCount(ConnectionHandle handle);
 
@@ -176,9 +174,30 @@
 
     // Stores the preferred refresh rate that an app should run at.
     // FrameRateOverride.refreshRateHz == 0 means no preference.
-    void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesMutex);
+    void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesLock);
     // Retrieves the overridden refresh rate for a given uid.
-    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mFrameRateOverridesMutex);
+    std::optional<Fps> getFrameRateOverride(uid_t uid) const
+            EXCLUDES(mRefreshRateConfigsLock, mFrameRateOverridesLock);
+
+    void setRefreshRateConfigs(std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs)
+            EXCLUDES(mRefreshRateConfigsLock) {
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        mRefreshRateConfigs = std::move(refreshRateConfigs);
+        mRefreshRateConfigs->setIdleTimerCallbacks(
+                [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Reset); },
+                [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Expired); },
+                [this] {
+                    std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Reset);
+                },
+                [this] {
+                    std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Expired);
+                });
+    }
+
+    nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+    }
 
 private:
     friend class TestableScheduler;
@@ -190,8 +209,6 @@
     enum class TouchState { Inactive, Active };
 
     struct Options {
-        // Whether to use idle timer callbacks that support the kernel timer.
-        bool supportKernelTimer;
         // Whether to use content detection at all.
         bool useContentDetection;
     };
@@ -203,14 +220,14 @@
     };
 
     // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers.
-    Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&, Options);
+    Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&, Options);
 
     // Used by tests to inject mocks.
-    Scheduler(VsyncSchedule, const scheduler::RefreshRateConfigs&, ISchedulerCallback&,
-              std::unique_ptr<LayerHistory>, Options);
+    Scheduler(VsyncSchedule, const std::shared_ptr<scheduler::RefreshRateConfigs>&,
+              ISchedulerCallback&, std::unique_ptr<LayerHistory>, Options);
 
     static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
-    static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&);
+    static std::unique_ptr<LayerHistory> createLayerHistory();
 
     // Create a connection on the given EventThread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread>);
@@ -218,7 +235,7 @@
             EventThread*, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
 
     // Update feature state machine to given state when corresponding timer resets or expires.
-    void kernelIdleTimerCallback(TimerState);
+    void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateConfigsLock);
     void idleTimerCallback(TimerState);
     void touchTimerCallback(TimerState);
     void displayPowerTimerCallback(TimerState);
@@ -232,18 +249,25 @@
     // This function checks whether individual features that are affecting the refresh rate
     // selection were initialized, prioritizes them, and calculates the DisplayModeId
     // for the suggested refresh rate.
-    DisplayModeId calculateRefreshRateModeId(
+    DisplayModePtr calculateRefreshRateModeId(
             scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
             REQUIRES(mFeatureStateLock);
 
-    void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock);
+    void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock) EXCLUDES(mRefreshRateConfigsLock);
     bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
                                   Fps displayRefreshRate) REQUIRES(mFeatureStateLock)
-            EXCLUDES(mFrameRateOverridesMutex);
+            EXCLUDES(mFrameRateOverridesLock);
 
-    impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
+    impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
+            EXCLUDES(mRefreshRateConfigsLock);
     impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
+    std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const
+            EXCLUDES(mRefreshRateConfigsLock) {
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        return mRefreshRateConfigs;
+    }
+
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
         sp<EventThreadConnection> connection;
@@ -258,7 +282,7 @@
     InjectVSyncSource* mVSyncInjector = nullptr;
     ConnectionHandle mInjectorConnectionHandle;
 
-    std::mutex mHWVsyncLock;
+    mutable std::mutex mHWVsyncLock;
     bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
     bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
 
@@ -270,8 +294,6 @@
     // Used to choose refresh rate if content detection is enabled.
     std::unique_ptr<LayerHistory> mLayerHistory;
 
-    // Timer that records time between requests for next vsync.
-    std::optional<scheduler::OneShotTimer> mIdleTimer;
     // Timer used to monitor touch events.
     std::optional<scheduler::OneShotTimer> mTouchTimer;
     // Timer used to monitor display power mode.
@@ -288,7 +310,7 @@
         TouchState touch = TouchState::Inactive;
         TimerState displayPowerTimer = TimerState::Expired;
 
-        std::optional<DisplayModeId> modeId;
+        DisplayModePtr mode;
         LayerHistory::Summary contentRequirements;
 
         bool isDisplayPowerStateNormal = true;
@@ -296,15 +318,15 @@
         // Used to cache the last parameters of onPrimaryDisplayModeChanged
         struct ModeChangedParams {
             ConnectionHandle handle;
-            PhysicalDisplayId displayId;
-            DisplayModeId modeId;
-            nsecs_t vsyncPeriod;
+            DisplayModePtr mode;
         };
 
         std::optional<ModeChangedParams> cachedModeChangedParams;
     } mFeatures GUARDED_BY(mFeatureStateLock);
 
-    const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
+    mutable std::mutex mRefreshRateConfigsLock;
+    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs
+            GUARDED_BY(mRefreshRateConfigsLock);
 
     std::mutex mVsyncTimelineLock;
     std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
@@ -315,14 +337,17 @@
 
     // The frame rate override lists need their own mutex as they are being read
     // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
-    mutable std::mutex mFrameRateOverridesMutex;
+    mutable std::mutex mFrameRateOverridesLock;
 
     // mappings between a UID and a preferred refresh rate that this app would
     // run at.
     scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
-            GUARDED_BY(mFrameRateOverridesMutex);
+            GUARDED_BY(mFrameRateOverridesLock);
     scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
-            GUARDED_BY(mFrameRateOverridesMutex);
+            GUARDED_BY(mFrameRateOverridesLock);
+
+    // Keeps track of whether the screen is acquired for debug
+    std::atomic<bool> mScreenAcquired = false;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 7b5d462..ee973f7 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -42,6 +42,7 @@
       : mClock(std::move(clock)),
         mTracker(tracker),
         mPendingLimit(pendingFenceLimit),
+        // TODO(adyabr): change mSupportKernelIdleTimer when the active display changes
         mSupportKernelIdleTimer(supportKernelIdleTimer) {}
 
 VSyncReactor::~VSyncReactor() = default;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fe43a20..29d35c2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -31,7 +31,6 @@
 #include <android/hardware/configstore/1.1/types.h>
 #include <android/hardware/power/Boost.h>
 #include <android/native_window.h>
-#include <android/os/BnSetInputWindowsListener.h>
 #include <android/os/IInputFlinger.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -115,6 +114,7 @@
 #include "FrameTracer/FrameTracer.h"
 #include "HdrLayerInfoReporter.h"
 #include "Layer.h"
+#include "LayerProtoHelper.h"
 #include "LayerRenderArea.h"
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
@@ -133,6 +133,7 @@
 #include "SurfaceInterceptor.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
+#include "WindowInfosListenerInvoker.h"
 #include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
@@ -146,6 +147,13 @@
         return (expr);                                             \
     }()
 
+#define MAIN_THREAD_GUARD(expr)                                    \
+    [&] {                                                          \
+        LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \
+        MainThreadScopedGuard lock(SF_MAIN_THREAD);                \
+        return (expr);                                             \
+    }()
+
 #undef NO_THREAD_SAFETY_ANALYSIS
 #define NO_THREAD_SAFETY_ANALYSIS \
     _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"")
@@ -160,6 +168,8 @@
 
 using android::hardware::power::Boost;
 using base::StringAppendF;
+using gui::IWindowInfosListener;
+using gui::WindowInfo;
 using ui::ColorMode;
 using ui::Dataspace;
 using ui::DisplayPrimaries;
@@ -265,23 +275,42 @@
     ROTATE_SURFACE_FLINGER = 0x2,
 };
 
-}  // namespace anonymous
-
-struct SetInputWindowsListener : os::BnSetInputWindowsListener {
-    explicit SetInputWindowsListener(std::function<void()> listenerCb) : mListenerCb(listenerCb) {}
-
-    binder::Status onSetInputWindowsFinished() override;
-
-    std::function<void()> mListenerCb;
+struct IdleTimerConfig {
+    int32_t timeoutMs;
+    bool supportKernelIdleTimer;
 };
 
-binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
-    if (mListenerCb != nullptr) {
-        mListenerCb();
+IdleTimerConfig getIdleTimerConfiguration(DisplayId displayId) {
+    // TODO(adyabr): use ro.surface_flinger.* namespace
+
+    const auto displayIdleTimerMsKey = [displayId] {
+        std::stringstream ss;
+        ss << "debug.sf.set_idle_timer_ms_" << displayId.value;
+        return ss.str();
+    }();
+
+    const auto displaySupportKernelIdleTimerKey = [displayId] {
+        std::stringstream ss;
+        ss << "debug.sf.support_kernel_idle_timer_" << displayId.value;
+        return ss.str();
+    }();
+
+    const int32_t displayIdleTimerMs = base::GetIntProperty(displayIdleTimerMsKey, 0);
+    const auto displaySupportKernelIdleTimer =
+            base::GetBoolProperty(displaySupportKernelIdleTimerKey, false);
+
+    if (displayIdleTimerMs > 0) {
+        return {displayIdleTimerMs, displaySupportKernelIdleTimer};
     }
-    return binder::Status::ok();
+
+    const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms", 0);
+    const int32_t millis = setIdleTimerMs ? setIdleTimerMs : sysprop::set_idle_timer_ms(0);
+
+    return {millis, sysprop::support_kernel_idle_timer(false)};
 }
 
+}  // namespace anonymous
+
 // ---------------------------------------------------------------------------
 
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
@@ -347,10 +376,9 @@
         mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
         mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
-        mPowerAdvisor(*this) {
+        mPowerAdvisor(*this),
+        mWindowInfosListenerInvoker(new WindowInfosListenerInvoker(this)) {
     ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
-
-    mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); });
 }
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
@@ -475,9 +503,6 @@
 
     useFrameRateApi = use_frame_rate_api(true);
 
-    mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false);
-    base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
-
     mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
 
     // Debug property overrides ro. property
@@ -590,19 +615,11 @@
     }
 }
 
-VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
-                                                       ui::LayerStack layerStack) {
+VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution,
+                                                       ui::PixelFormat format) {
     if (auto& generator = mVirtualDisplayIdGenerators.hal) {
         if (const auto id = generator->generateId()) {
-            std::optional<PhysicalDisplayId> mirror;
-
-            if (const auto display = findDisplay([layerStack](const auto& display) {
-                    return !display.isVirtual() && display.getLayerStack() == layerStack;
-                })) {
-                mirror = display->getPhysicalId();
-            }
-
-            if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format, mirror)) {
+            if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
                 return *id;
             }
 
@@ -632,20 +649,18 @@
     mVirtualDisplayIdGenerators.gpu.releaseId(*id);
 }
 
-std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const {
-    Mutex::Autolock lock(mStateLock);
-
-    const auto internalDisplayId = getInternalDisplayIdLocked();
-    if (!internalDisplayId) {
+std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
+    const auto display = getDefaultDisplayDeviceLocked();
+    if (!display) {
         return {};
     }
 
     std::vector<PhysicalDisplayId> displayIds;
     displayIds.reserve(mPhysicalDisplayTokens.size());
-    displayIds.push_back(*internalDisplayId);
+    displayIds.push_back(display->getPhysicalId());
 
     for (const auto& [id, token] : mPhysicalDisplayTokens) {
-        if (id != *internalDisplayId) {
+        if (id != display->getPhysicalId()) {
             displayIds.push_back(id);
         }
     }
@@ -653,6 +668,17 @@
     return displayIds;
 }
 
+status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const {
+    Mutex::Autolock lock(mStateLock);
+    const auto display = getInternalDisplayIdLocked();
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+
+    *id = *display;
+    return NO_ERROR;
+}
+
 sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
     Mutex::Autolock lock(mStateLock);
     return getPhysicalDisplayTokenLocked(displayId);
@@ -728,7 +754,7 @@
         mBootStage = BootStage::FINISHED;
 
         if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
-            enableRefreshRateOverlay(true);
+            ON_MAIN_THREAD(enableRefreshRateOverlay(true));
         }
     }));
 }
@@ -792,6 +818,8 @@
                                     ? renderengine::RenderEngine::ContextPriority::REALTIME
                                     : renderengine::RenderEngine::ContextPriority::MEDIUM)
                     .build()));
+    mMaxRenderTargetSize =
+            std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
 
     // Set SF main policy after initializing RenderEngine which has its own policy.
     if (!SetTaskProfiles(0, {"SFMainPolicy"})) {
@@ -837,7 +865,7 @@
         }
     }
 
-    getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
+    onActiveDisplaySizeChanged(display);
 
     // Inform native graphics APIs whether the present timestamp is supported:
 
@@ -879,14 +907,6 @@
     }
 }
 
-size_t SurfaceFlinger::getMaxTextureSize() const {
-    return getRenderEngine().getMaxTextureSize();
-}
-
-size_t SurfaceFlinger::getMaxViewportDims() const {
-    return getRenderEngine().getMaxViewportDims();
-}
-
 // ----------------------------------------------------------------------------
 
 bool SurfaceFlinger::authenticateSurfaceTexture(
@@ -1075,36 +1095,28 @@
 
 void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
     ATRACE_CALL();
-    auto refreshRate = mRefreshRateConfigs->getRefreshRateFromModeId(info.modeId);
-    ALOGV("%s(%s)", __func__, refreshRate.getName().c_str());
 
-    std::lock_guard<std::mutex> lock(mActiveModeLock);
-    if (mDesiredActiveModeChanged) {
-        // If a mode change is pending, just cache the latest request in mDesiredActiveMode
-        const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event;
-        mDesiredActiveMode = info;
-        mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
-    } else {
-        // Check if we are already at the desired mode
-        const auto display = getDefaultDisplayDeviceLocked();
-        if (!display || display->getActiveMode()->getId() == refreshRate.getModeId()) {
-            return;
-        }
+    if (!info.mode) {
+        ALOGW("requested display mode is null");
+        return;
+    }
+    auto display = getDisplayDeviceLocked(info.mode->getPhysicalDisplayId());
+    if (!display) {
+        ALOGW("%s: display is no longer valid", __func__);
+        return;
+    }
 
-        // Initiate a mode change.
-        mDesiredActiveModeChanged = true;
-        mDesiredActiveMode = info;
-
+    if (display->setDesiredActiveMode(info)) {
         // This will trigger HWC refresh without resetting the idle timer.
         repaintEverythingForHWC();
         // Start receiving vsync samples now, so that we can detect a period
         // switch.
-        mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
+        mScheduler->resyncToHardwareVsync(true, info.mode->getVsyncPeriod());
         // As we called to set period, we will call to onRefreshRateChangeCompleted once
         // VsyncController model is locked.
         modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
 
-        updatePhaseConfiguration(refreshRate.getFps());
+        updatePhaseConfiguration(info.mode->getFps());
         mScheduler->setModeChangePending(true);
     }
 }
@@ -1138,7 +1150,7 @@
         const auto fps = mode->getFps();
         // Keep the old switching type.
         const auto allowGroupSwitching =
-                mRefreshRateConfigs->getCurrentPolicy().allowGroupSwitching;
+                display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching;
         const scheduler::RefreshRateConfigs::Policy policy{mode->getId(),
                                                            allowGroupSwitching,
                                                            {fps, fps}};
@@ -1158,63 +1170,49 @@
         return;
     }
 
-    const auto upcomingMode = display->getMode(mUpcomingActiveMode.modeId);
-    if (!upcomingMode) {
-        ALOGW("Upcoming active mode is no longer supported. Mode ID = %d",
-              mUpcomingActiveMode.modeId.value());
-        // TODO(b/159590486) Handle the error better. Some parts of SurfaceFlinger may
-        // have been already updated with the upcoming active mode.
+    const auto upcomingModeInfo = MAIN_THREAD_GUARD(display->getUpcomingActiveMode());
+    if (!upcomingModeInfo.mode) {
+        // There is no pending mode change. This can happen if the active
+        // display changed and the mode change happened on a different display.
         return;
     }
 
-    if (display->getActiveMode()->getSize() != upcomingMode->getSize()) {
+    if (display->getActiveMode()->getSize() != upcomingModeInfo.mode->getSize()) {
         auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
         // We need to generate new sequenceId in order to recreate the display (and this
         // way the framebuffer).
         state.sequenceId = DisplayDeviceState{}.sequenceId;
-        state.physical->activeMode = upcomingMode;
+        state.physical->activeMode = upcomingModeInfo.mode;
         processDisplayChangesLocked();
 
         // processDisplayChangesLocked will update all necessary components so we're done here.
         return;
     }
 
-    std::lock_guard<std::mutex> lock(mActiveModeLock);
-    mRefreshRateConfigs->setCurrentModeId(mUpcomingActiveMode.modeId);
-    display->setActiveMode(mUpcomingActiveMode.modeId);
+    // We just created this display so we can call even if we are not on
+    // the main thread
+    MainThreadScopedGuard fakeMainThreadGuard(SF_MAIN_THREAD);
+    display->setActiveMode(upcomingModeInfo.mode->getId());
 
-    const Fps refreshRate = upcomingMode->getFps();
-
+    const Fps refreshRate = upcomingModeInfo.mode->getFps();
     mRefreshRateStats->setRefreshRate(refreshRate);
-
     updatePhaseConfiguration(refreshRate);
-    ATRACE_INT("ActiveConfigFPS", refreshRate.getValue());
 
-    if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(upcomingMode->getFps());
-    }
-
-    if (mUpcomingActiveMode.event != Scheduler::ModeEvent::None) {
-        const nsecs_t vsyncPeriod = refreshRate.getPeriodNsecs();
-        const auto physicalId = display->getPhysicalId();
-        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, physicalId,
-                                                mUpcomingActiveMode.modeId, vsyncPeriod);
+    if (upcomingModeInfo.event != Scheduler::ModeEvent::None) {
+        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
     }
 }
 
-void SurfaceFlinger::clearDesiredActiveModeState() {
-    std::lock_guard<std::mutex> lock(mActiveModeLock);
-    mDesiredActiveMode.event = Scheduler::ModeEvent::None;
-    mDesiredActiveModeChanged = false;
-    mScheduler->setModeChangePending(false);
+void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) {
+    display->clearDesiredActiveModeState();
+    if (isDisplayActiveLocked(display)) {
+        mScheduler->setModeChangePending(false);
+    }
 }
 
-void SurfaceFlinger::desiredActiveModeChangeDone() {
-    const auto modeId = getDesiredActiveMode()->modeId;
-
-    clearDesiredActiveModeState();
-
-    const auto refreshRate = getDefaultDisplayDeviceLocked()->getMode(modeId)->getFps();
+void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) {
+    const auto refreshRate = display->getDesiredActiveMode()->mode->getFps();
+    clearDesiredActiveModeState(display);
     mScheduler->resyncToHardwareVsync(true, refreshRate.getPeriodNsecs());
     updatePhaseConfiguration(refreshRate);
 }
@@ -1222,63 +1220,75 @@
 void SurfaceFlinger::performSetActiveMode() {
     ATRACE_CALL();
     ALOGV("%s", __FUNCTION__);
-    // Store the local variable to release the lock.
-    const auto desiredActiveMode = getDesiredActiveMode();
-    if (!desiredActiveMode) {
-        // No desired active mode pending to be applied
-        return;
+
+    for (const auto& iter : mDisplays) {
+        const auto& display = iter.second;
+        if (!display || !display->isInternal()) {
+            continue;
+        }
+
+        // Store the local variable to release the lock.
+        const auto desiredActiveMode = display->getDesiredActiveMode();
+        if (!desiredActiveMode) {
+            // No desired active mode pending to be applied
+            continue;
+        }
+
+        if (!isDisplayActiveLocked(display)) {
+            // display is no longer the active display, so abort the mode change
+            clearDesiredActiveModeState(display);
+            continue;
+        }
+
+        const auto desiredMode = display->getMode(desiredActiveMode->mode->getId());
+        if (!desiredMode) {
+            ALOGW("Desired display mode is no longer supported. Mode ID = %d",
+                  desiredActiveMode->mode->getId().value());
+            clearDesiredActiveModeState(display);
+            continue;
+        }
+
+        const auto refreshRate = desiredMode->getFps();
+        ALOGV("%s changing active mode to %d(%s) for display %s", __func__,
+              desiredMode->getId().value(), to_string(refreshRate).c_str(),
+              to_string(display->getId()).c_str());
+
+        if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+            // display is not valid or we are already in the requested mode
+            // on both cases there is nothing left to do
+            desiredActiveModeChangeDone(display);
+            continue;
+        }
+
+        // Desired active mode was set, it is different than the mode currently in use, however
+        // allowed modes might have changed by the time we process the refresh.
+        // Make sure the desired mode is still allowed
+        const auto displayModeAllowed =
+                display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId());
+        if (!displayModeAllowed) {
+            desiredActiveModeChangeDone(display);
+            continue;
+        }
+
+        // TODO(b/142753666) use constrains
+        hal::VsyncPeriodChangeConstraints constraints;
+        constraints.desiredTimeNanos = systemTime();
+        constraints.seamlessRequired = false;
+        hal::VsyncPeriodChangeTimeline outTimeline;
+
+        const auto status = MAIN_THREAD_GUARD(
+                display->initiateModeChange(*desiredActiveMode, constraints, &outTimeline));
+        if (status != NO_ERROR) {
+            // initiateModeChange may fail if a hotplug event is just about
+            // to be sent. We just log the error in this case.
+            ALOGW("initiateModeChange failed: %d", status);
+            continue;
+        }
+        mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
+
+        // Scheduler will submit an empty frame to HWC if needed.
+        mSetActiveModePending = true;
     }
-
-    const auto display = getDefaultDisplayDeviceLocked();
-    const auto desiredMode = display->getMode(desiredActiveMode->modeId);
-    if (!desiredMode) {
-        ALOGW("Desired display mode is no longer supported. Mode ID = %d",
-              desiredActiveMode->modeId.value());
-        clearDesiredActiveModeState();
-        return;
-    }
-    const auto refreshRate = desiredMode->getFps();
-    ALOGV("%s changing active mode to %d(%s)", __FUNCTION__, desiredMode->getId().value(),
-          to_string(refreshRate).c_str());
-
-    if (!display || display->getActiveMode()->getId() == desiredActiveMode->modeId) {
-        // display is not valid or we are already in the requested mode
-        // on both cases there is nothing left to do
-        desiredActiveModeChangeDone();
-        return;
-    }
-
-    // Desired active mode was set, it is different than the mode currently in use, however
-    // allowed modes might have changed by the time we process the refresh.
-    // Make sure the desired mode is still allowed
-    if (!isDisplayModeAllowed(desiredActiveMode->modeId)) {
-        desiredActiveModeChangeDone();
-        return;
-    }
-
-    mUpcomingActiveMode = *desiredActiveMode;
-
-    ATRACE_INT("ActiveModeFPS_HWC", refreshRate.getValue());
-
-    // TODO(b/142753666) use constrains
-    hal::VsyncPeriodChangeConstraints constraints;
-    constraints.desiredTimeNanos = systemTime();
-    constraints.seamlessRequired = false;
-
-    hal::VsyncPeriodChangeTimeline outTimeline;
-    const auto status =
-            display->initiateModeChange(mUpcomingActiveMode.modeId, constraints, &outTimeline);
-    if (status != NO_ERROR) {
-        // initiateModeChange may fail if a hotplug event is just about
-        // to be sent. We just log the error in this case.
-        ALOGW("initiateModeChange failed: %d", status);
-        return;
-    }
-
-    mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
-
-    // Scheduler will submit an empty frame to HWC if needed.
-    mSetActiveModePending = true;
 }
 
 void SurfaceFlinger::disableExpensiveRendering() {
@@ -1720,10 +1730,10 @@
     ATRACE_CALL();
 
     Mutex::Autolock lock(mStateLock);
-
-    if (const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId)) {
-        auto token = getPhysicalDisplayTokenLocked(*displayId);
-        auto display = getDisplayDeviceLocked(token);
+    const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
+    if (displayId) {
+        const auto token = getPhysicalDisplayTokenLocked(*displayId);
+        const auto display = getDisplayDeviceLocked(token);
         display->onVsync(timestamp);
     }
 
@@ -1731,8 +1741,10 @@
         return;
     }
 
-    if (hwcDisplayId != getHwComposer().getInternalHwcDisplayId()) {
-        // For now, we don't do anything with external display vsyncs.
+    const bool isActiveDisplay =
+            displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken;
+    if (!isActiveDisplay) {
+        // For now, we don't do anything with non active display vsyncs.
         return;
     }
 
@@ -1748,10 +1760,6 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
-bool SurfaceFlinger::isDisplayModeAllowed(DisplayModeId modeId) const {
-    return mRefreshRateConfigs->isModeAllowed(modeId);
-}
-
 void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
                                              Scheduler::ModeEvent event) {
     const auto display = getDefaultDisplayDeviceLocked();
@@ -1761,13 +1769,13 @@
     ATRACE_CALL();
 
     // Don't do any updating if the current fps is the same as the new one.
-    if (!isDisplayModeAllowed(refreshRate.getModeId())) {
+    if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) {
         ALOGV("Skipping mode %d as it is not part of allowed modes",
               refreshRate.getModeId().value());
         return;
     }
 
-    setDesiredActiveMode({refreshRate.getModeId(), event});
+    setDesiredActiveMode({refreshRate.getMode(), event});
 }
 
 void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
@@ -1816,7 +1824,7 @@
 
         if (const auto display = getDefaultDisplayDeviceLocked();
             display && display->isPoweredOn()) {
-            getHwComposer().setVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState);
+            setHWCVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState);
         }
     }));
 }
@@ -1965,8 +1973,13 @@
     }
 
     if (mRefreshRateOverlaySpinner) {
-        if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
-            mRefreshRateOverlay->onInvalidate();
+        if (Mutex::Autolock lock(mStateLock);
+            const auto display = getDefaultDisplayDeviceLocked()) {
+            if (display) {
+                display->onInvalidate();
+            } else {
+                ALOGW("%s: default display is null", __func__);
+            }
         }
     }
 
@@ -2352,7 +2365,7 @@
     mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
     mTransactionCallbackInvoker.sendCallbacks();
 
-    if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
+    if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON &&
         mPreviousPresentFences[0].fenceTime->isValid()) {
         mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime);
     }
@@ -2442,23 +2455,28 @@
     }
 }
 
-FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const {
-    return displayDevice.getLayerStackSpaceRect().toFloatRect();
-}
-
 void SurfaceFlinger::computeLayerBounds() {
+    // Find the largest width and height among all the displays.
+    int32_t maxDisplayWidth = 0;
+    int32_t maxDisplayHeight = 0;
     for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
         const auto& displayDevice = pair.second;
-        const auto display = displayDevice->getCompositionDisplay();
-        for (const auto& layer : mDrawingState.layersSortedByZ) {
-            // only consider the layers on the given layer stack
-            if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
-                continue;
-            }
-
-            layer->computeBounds(getLayerClipBoundsForDisplay(*displayDevice), ui::Transform(),
-                                 0.f /* shadowRadius */);
+        int32_t width = displayDevice->getWidth();
+        int32_t height = displayDevice->getHeight();
+        if (width > maxDisplayWidth) {
+            maxDisplayWidth = width;
         }
+        if (height > maxDisplayHeight) {
+            maxDisplayHeight = height;
+        }
+    }
+
+    // Ignore display bounds for now since they will be computed later. Use a large Rect bound
+    // to ensure it's bigger than an actual display will be.
+    FloatRect maxBounds(-maxDisplayWidth * 10, -maxDisplayHeight * 10, maxDisplayWidth * 10,
+                        maxDisplayHeight * 10);
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
+        layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
     }
 }
 
@@ -2489,7 +2507,6 @@
     // We call getTransactionFlags(), which will also clear the flags,
     // with mStateLock held to guarantee that mCurrentState won't change
     // until the transaction is committed.
-
     modulateVsync(&VsyncModulator::onTransactionCommit);
     transactionFlags = getTransactionFlags(eTransactionMask);
     handleTransactionLocked(transactionFlags);
@@ -2540,6 +2557,7 @@
     for (const auto& hwcMode : hwcModes) {
         newModes.push_back(DisplayMode::Builder(hwcMode.hwcId)
                                    .setId(DisplayModeId{nextModeId++})
+                                   .setPhysicalDisplayId(displayId)
                                    .setWidth(hwcMode.width)
                                    .setHeight(hwcMode.height)
                                    .setVsyncPeriod(hwcMode.vsyncPeriod)
@@ -2601,11 +2619,6 @@
                 sp<IBinder> token = new BBinder();
                 mCurrentState.displays.add(token, state);
                 mPhysicalDisplayTokens.emplace(displayId, std::move(token));
-
-                if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
-                    initScheduler(state);
-                }
-
                 mInterceptor->saveDisplayCreation(state);
             } else {
                 ALOGV("Recreating display %s", to_string(displayId).c_str());
@@ -2660,6 +2673,18 @@
     if (const auto& physical = state.physical) {
         creationArgs.connectionType = physical->type;
         creationArgs.supportedModes = physical->supportedModes;
+        creationArgs.activeModeId = physical->activeMode->getId();
+        const auto [idleTimerTimeoutMs, supportKernelIdleTimer] =
+                getIdleTimerConfiguration(compositionDisplay->getId());
+        scheduler::RefreshRateConfigs::Config config =
+                {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
+                 .frameRateMultipleThreshold =
+                         base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
+                 .idleTimerTimeoutMs = idleTimerTimeoutMs,
+                 .supportKernelIdleTimer = supportKernelIdleTimer};
+        creationArgs.refreshRateConfigs =
+                std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes,
+                                                                creationArgs.activeModeId, config);
     }
 
     if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
@@ -2716,7 +2741,7 @@
                                                     RenderIntent::COLORIMETRIC,
                                                     Dataspace::UNKNOWN});
     if (!state.isVirtual()) {
-        display->setActiveMode(state.physical->activeMode->getId());
+        MAIN_THREAD_GUARD(display->setActiveMode(state.physical->activeMode->getId()));
         display->setDeviceProductInfo(state.physical->deviceProductInfo);
     }
 
@@ -2756,7 +2781,7 @@
         builder.setId(physical->id);
         builder.setConnectionType(physical->type);
     } else {
-        builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.layerStack));
+        builder.setId(acquireVirtualDisplay(resolution, pixelFormat));
     }
 
     builder.setPixels(resolution);
@@ -2798,14 +2823,12 @@
     const auto display = setupNewDisplayDeviceInternal(displayToken, std::move(compositionDisplay),
                                                        state, displaySurface, producer);
     mDisplays.emplace(displayToken, display);
+    if (display->isPrimary()) {
+        initScheduler(display);
+    }
     if (!state.isVirtual()) {
         dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
     }
-
-    if (display->isPrimary()) {
-        mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight());
-        getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
-    }
 }
 
 void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
@@ -2870,16 +2893,7 @@
 
             // TODO(b/175678251) Call a listener instead.
             if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
-                mRefreshRateConfigs->updateDisplayModes(currentState.physical->supportedModes,
-                                                        currentState.physical->activeMode->getId());
-                mVsyncConfiguration->reset();
-                const Fps refreshRate = currentState.physical->activeMode->getFps();
-                updatePhaseConfiguration(refreshRate);
-                mRefreshRateStats->setRefreshRate(refreshRate);
-
-                if (mRefreshRateOverlay) {
-                    mRefreshRateOverlay->reset();
-                }
+                updateInternalDisplayVsyncLocked(display);
             }
         }
         return;
@@ -2889,29 +2903,34 @@
         if (currentState.layerStack != drawingState.layerStack) {
             display->setLayerStack(currentState.layerStack);
         }
+        if (currentState.flags != drawingState.flags) {
+            display->setFlags(currentState.flags);
+        }
         if ((currentState.orientation != drawingState.orientation) ||
             (currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
             (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
             display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
                                    currentState.orientedDisplaySpaceRect);
-            if (display->isPrimary()) {
-                mDefaultDisplayTransformHint = display->getTransformHint();
+            if (isDisplayActiveLocked(display)) {
+                mActiveDisplayTransformHint = display->getTransformHint();
             }
         }
         if (currentState.width != drawingState.width ||
             currentState.height != drawingState.height) {
             display->setDisplaySize(currentState.width, currentState.height);
 
-            if (display->isPrimary()) {
-                mScheduler->onPrimaryDisplayAreaChanged(currentState.width * currentState.height);
-            }
-
-            if (mRefreshRateOverlay) {
-                mRefreshRateOverlay->setViewport(display->getSize());
+            if (isDisplayActiveLocked(display)) {
+                onActiveDisplaySizeChanged(display);
             }
         }
     }
 }
+void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
+    mVsyncConfiguration->reset();
+    const Fps refreshRate = activeDisplay->refreshRateConfigs().getCurrentRefreshRate().getFps();
+    updatePhaseConfiguration(refreshRate);
+    mRefreshRateStats->setRefreshRate(refreshRate);
+}
 
 void SurfaceFlinger::processDisplayChangesLocked() {
     // here we take advantage of Vector's copy-on-write semantics to
@@ -3073,11 +3092,11 @@
 
     if (mVisibleRegionsDirty || mInputInfoChanged) {
         mInputInfoChanged = false;
-        updateInputWindowInfo();
+        notifyWindowInfos();
     } else if (mInputWindowCommands.syncInputWindows) {
         // If the caller requested to sync input windows, but there are no
         // changes to input windows, notify immediately.
-        setInputWindowsFinished();
+        windowInfosReported();
     }
 
     for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
@@ -3092,31 +3111,20 @@
     return value;
 }
 
-void SurfaceFlinger::updateInputWindowInfo() {
-    std::vector<InputWindowInfo> inputInfos;
+void SurfaceFlinger::notifyWindowInfos() {
+    std::vector<WindowInfo> windowInfos;
 
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (!layer->needsInputInfo()) return;
-        sp<DisplayDevice> display;
-        if (enablePerWindowInputRotation()) {
-            for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
-                const auto& displayDevice = pair.second;
-                if (!displayDevice->getCompositionDisplay()
-                             ->belongsInOutput(layer->getLayerStack(),
-                                               layer->getPrimaryDisplayOnly())) {
-                    continue;
-                }
-                display = displayDevice;
-            }
-        }
+        sp<DisplayDevice> display = enablePerWindowInputRotation()
+                ? ON_MAIN_THREAD(getDisplayWithInputByLayer(layer))
+                : nullptr;
         // When calculating the screen bounds we ignore the transparent region since it may
         // result in an unwanted offset.
-        inputInfos.push_back(layer->fillInputInfo(display));
+        windowInfos.push_back(layer->fillInputInfo(display));
     });
-
-    mInputFlinger->setInputWindows(inputInfos,
-                               mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
-                                                                     : nullptr);
+    mWindowInfosListenerInvoker->windowInfosChanged(windowInfos,
+                                                    mInputWindowCommands.syncInputWindows);
 }
 
 void SurfaceFlinger::updateCursorAsync() {
@@ -3148,24 +3156,16 @@
     mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
 }
 
-void SurfaceFlinger::initScheduler(const DisplayDeviceState& displayState) {
+void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {
     if (mScheduler) {
-        // In practice it's not allowed to hotplug in/out the primary display once it's been
-        // connected during startup, but some tests do it, so just warn and return.
-        ALOGW("Can't re-init scheduler");
+        // If the scheduler is already initialized, this means that we received
+        // a hotplug(connected) on the primary display. In that case we should
+        // update the scheduler with the most recent display information.
+        ALOGW("Scheduler already initialized, updating instead");
+        mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
         return;
     }
-    const auto displayId = displayState.physical->id;
-    scheduler::RefreshRateConfigs::Config config =
-            {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
-             .frameRateMultipleThreshold =
-                     base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0)};
-    mRefreshRateConfigs =
-            std::make_unique<scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
-                                                            displayState.physical->activeMode
-                                                                    ->getId(),
-                                                            config);
-    const auto currRefreshRate = displayState.physical->activeMode->getFps();
+    const auto currRefreshRate = display->getActiveMode()->getFps();
     mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
                                                                       hal::PowerMode::OFF);
 
@@ -3173,7 +3173,7 @@
     mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
 
     // start the EventThread
-    mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
+    mScheduler = getFactory().createScheduler(display->holdRefreshRateConfigs(), *this);
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
     const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
     mAppConnectionHandle =
@@ -3202,9 +3202,7 @@
     // This is a bit hacky, but this avoids a back-pointer into the main SF
     // classes from EventThread, and there should be no run-time binder cost
     // anyway since there are no connected apps at this point.
-    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, displayId,
-                                            displayState.physical->activeMode->getId(),
-                                            vsyncPeriod);
+    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode());
     static auto ignorePresentFences =
             base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
     mScheduler->setIgnorePresentFences(
@@ -3418,9 +3416,9 @@
     composerState.state.surface = handle;
     states.add(composerState);
 
-    lbc->updateTransformHint(mDefaultDisplayTransformHint);
+    lbc->updateTransformHint(mActiveDisplayTransformHint);
     if (outTransformHint) {
-        *outTransformHint = mDefaultDisplayTransformHint;
+        *outTransformHint = mActiveDisplayTransformHint;
     }
     // attach this layer to the client
     client->attachLayer(handle, lbc);
@@ -3857,6 +3855,12 @@
             flags |= eDisplayTransactionNeeded;
         }
     }
+    if (what & DisplayState::eFlagsChanged) {
+        if (state.flags != s.flags) {
+            state.flags = s.flags;
+            flags |= eDisplayTransactionNeeded;
+        }
+    }
     if (what & DisplayState::eDisplayProjectionChanged) {
         if (state.orientation != s.orientation) {
             state.orientation = s.orientation;
@@ -4115,10 +4119,10 @@
     }
     if (what & layer_state_t::eInputInfoChanged) {
         if (privileged) {
-            layer->setInputInfo(*s.inputHandle->getInfo());
+            layer->setInputInfo(*s.windowInfoHandle->getInfo());
             flags |= eTraversalNeeded;
         } else {
-            ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER");
+            ALOGE("Attempt to update WindowInfo without permission ACCESS_SURFACE_FLINGER");
         }
     }
     std::optional<nsecs_t> dequeueBufferTimestamp;
@@ -4191,6 +4195,16 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eDropInputModeChanged) {
+        if (privileged) {
+            if (layer->setDropInputMode(s.dropInputMode)) {
+                flags |= eTraversalNeeded;
+                mInputInfoChanged = true;
+            }
+        } else {
+            ALOGE("Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER");
+        }
+    }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
     // the same transaction, then we have to make sure we reparent the children first so we do not
@@ -4216,17 +4230,30 @@
     }
     bool bufferChanged = what & layer_state_t::eBufferChanged;
     bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
+    bool bufferSizeExceedsLimit = false;
     std::shared_ptr<renderengine::ExternalTexture> buffer;
     if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
-        ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
-        buffer = ClientCache::getInstance().get(s.cachedBuffer);
+        bufferSizeExceedsLimit =
+                exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
+        if (!bufferSizeExceedsLimit) {
+            ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+            buffer = ClientCache::getInstance().get(s.cachedBuffer);
+        }
     } else if (cacheIdChanged) {
         buffer = ClientCache::getInstance().get(s.cachedBuffer);
     } else if (bufferChanged && s.buffer != nullptr) {
-        buffer = std::make_shared<
-                renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
-                                               renderengine::ExternalTexture::Usage::READABLE);
+        bufferSizeExceedsLimit =
+                exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
+        if (!bufferSizeExceedsLimit) {
+            buffer = std::make_shared<
+                    renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
+                                                   renderengine::ExternalTexture::Usage::READABLE);
+        }
     }
+    ALOGE_IF(bufferSizeExceedsLimit,
+             "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
+             "limit.",
+             layer->getDebugName());
     if (buffer) {
         const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
         const uint64_t frameNumber = frameNumberChanged
@@ -4235,7 +4262,7 @@
 
         if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
                              s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo,
-                             s.releaseBufferListener)) {
+                             s.releaseBufferListener, s.releaseBufferEndpoint)) {
             flags |= eTraversalNeeded;
         }
     } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
@@ -4509,9 +4536,10 @@
                           {}, getpid(), getuid(), 0 /* Undefined transactionId */);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
-    const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+    const nsecs_t vsyncPeriod =
+            display->refreshRateConfigs().getCurrentRefreshRate().getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
-    mDefaultDisplayTransformHint = display->getTransformHint();
+    mActiveDisplayTransformHint = display->getTransformHint();
     // Use phase of 0 since phase is not known.
     // Use latency of 0, which will snap to the ideal latency.
     DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
@@ -4523,6 +4551,26 @@
     static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
 }
 
+sp<DisplayDevice> SurfaceFlinger::getDisplayWithInputByLayer(Layer* layer) const {
+    sp<DisplayDevice> display;
+    for (const auto& pair : mDisplays) {
+        const auto& displayDevice = pair.second;
+        if (!displayDevice->receivesInput() ||
+            !displayDevice->getCompositionDisplay()
+                     ->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+            continue;
+        }
+        // Don't return immediately so that we can log duplicates.
+        if (display) {
+            ALOGE("Multiple display devices claim to accept input for the same layerstack: %d",
+                  layer->getLayerStack());
+            continue;
+        }
+        display = displayDevice;
+    }
+    return display;
+}
+
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
     if (display->isVirtual()) {
         ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
@@ -4537,13 +4585,23 @@
         return;
     }
 
+    const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken);
+    if (activeDisplay != display && display->isInternal() && activeDisplay &&
+        activeDisplay->isPoweredOn()) {
+        ALOGW("Trying to change power mode on non active display while the active display is ON");
+    }
+
     display->setPowerMode(mode);
 
     if (mInterceptor->isEnabled()) {
         mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
     }
-    const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+    const auto vsyncPeriod = display->refreshRateConfigs().getCurrentRefreshRate().getVsyncPeriod();
     if (currentMode == hal::PowerMode::OFF) {
+        // Turn on the display
+        if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
+            onActiveDisplayChangedLocked(display);
+        }
         // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
         // We can merge the syscall later.
         if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
@@ -4553,8 +4611,8 @@
             ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
         }
         getHwComposer().setPowerMode(displayId, mode);
-        if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
-            getHwComposer().setVsyncEnabled(displayId, mHWCVsyncPendingState);
+        if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) {
+            setHWCVsyncEnabled(displayId, mHWCVsyncPendingState);
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
         }
@@ -4570,13 +4628,13 @@
         if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
             ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
         }
-        if (display->isPrimary() && currentMode != hal::PowerMode::DOZE_SUSPEND) {
+        if (isDisplayActiveLocked(display) && currentMode != hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
 
         // Make sure HWVsync is disabled before turning off the display
-        getHwComposer().setVsyncEnabled(displayId, hal::Vsync::DISABLE);
+        setHWCVsyncEnabled(displayId, hal::Vsync::DISABLE);
 
         getHwComposer().setPowerMode(displayId, mode);
         mVisibleRegionsDirty = true;
@@ -4584,13 +4642,13 @@
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
         getHwComposer().setPowerMode(displayId, mode);
-        if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) {
+        if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
         }
     } else if (mode == hal::PowerMode::DOZE_SUSPEND) {
         // Leave display going to doze
-        if (display->isPrimary()) {
+        if (isDisplayActiveLocked(display)) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4600,7 +4658,7 @@
         getHwComposer().setPowerMode(displayId, mode);
     }
 
-    if (display->isPrimary()) {
+    if (isDisplayActiveLocked(display)) {
         mTimeStats->setPowerMode(mode);
         mRefreshRateStats->setPowerMode(mode);
         mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
@@ -4670,12 +4728,17 @@
         }
 
         if (dumpLayers) {
-            const LayersProto layersProto = dumpProtoFromMainThread();
+            LayersTraceFileProto traceFileProto = SurfaceTracing::createLayersTraceFileProto();
+            LayersTraceProto* layersTrace = traceFileProto.add_entry();
+            LayersProto layersProto = dumpProtoFromMainThread();
+            layersTrace->mutable_layers()->Swap(&layersProto);
+            dumpDisplayProto(*layersTrace);
+
             if (asProto) {
-                result.append(layersProto.SerializeAsString());
+                result.append(traceFileProto.SerializeAsString());
             } else {
                 // Dump info that we need to access from the main thread
-                const auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+                const auto layerTree = LayerProtoParser::generateLayerTree(layersTrace->layers());
                 result.append(LayerProtoParser::layerTreeToString(layerTree));
                 result.append("\n");
                 dumpOffscreenLayers(result);
@@ -4769,13 +4832,13 @@
                   "      present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
                   dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
 
-    mRefreshRateConfigs->dump(result);
-
     StringAppendF(&result, "(mode override by backdoor: %s)\n\n",
                   mDebugDisplayModeSetByBackdoor ? "yes" : "no");
 
     mScheduler->dump(mAppConnectionHandle, result);
     mScheduler->dumpVsync(result);
+    StringAppendF(&result, "mHWCVsyncPendingState=%s mLastHWCVsyncState=%s\n",
+                  to_string(mHWCVsyncPendingState).c_str(), to_string(mLastHWCVsyncState).c_str());
 }
 
 void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const {
@@ -4946,6 +5009,22 @@
     return layersProto;
 }
 
+void SurfaceFlinger::dumpDisplayProto(LayersTraceProto& layersTraceProto) const {
+    for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+        DisplayProto* displayProto = layersTraceProto.add_displays();
+        displayProto->set_id(display->getId().value);
+        displayProto->set_name(display->getDisplayName());
+        displayProto->set_layer_stack(display->getLayerStack());
+        LayerProtoHelper::writeSizeToProto(display->getWidth(), display->getHeight(),
+                                           [&]() { return displayProto->mutable_size(); });
+        LayerProtoHelper::writeToProto(display->getLayerStackSpaceRect(), [&]() {
+            return displayProto->mutable_layer_stack_space_rect();
+        });
+        LayerProtoHelper::writeTransformToProto(display->getTransform(),
+                                                displayProto->mutable_transform());
+    }
+}
+
 void SurfaceFlinger::dumpHwc(std::string& result) const {
     getHwComposer().dump(result);
 }
@@ -5134,7 +5213,8 @@
             continue;
         }
 
-        StringAppendF(&result, "Display %s HWC layers:\n", to_string(*displayId).c_str());
+        StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
+                      (isDisplayActiveLocked(display) ? "active" : "inactive"));
         Layer::miniDumpHeader(result);
 
         const DisplayDevice& ref = *display;
@@ -5226,6 +5306,7 @@
         case REMOVE_TUNNEL_MODE_ENABLED_LISTENER:
         case NOTIFY_POWER_BOOST:
         case SET_GLOBAL_SHADOW_SETTINGS:
+        case GET_PRIMARY_PHYSICAL_DISPLAY_ID:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
             // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
             // which acquire the necessary permission dynamically. Don't use the permission cache
@@ -5333,6 +5414,14 @@
             }
             return PERMISSION_DENIED;
         }
+        case ADD_WINDOW_INFOS_LISTENER:
+        case REMOVE_WINDOW_INFOS_LISTENER: {
+            const int uid = IPCThreadState::self()->getCallingUid();
+            if (uid == AID_SYSTEM || uid == AID_GRAPHICS) {
+                return OK;
+            }
+            return PERMISSION_DENIED;
+        }
     }
 
     // These codes are used for the IBinder protocol to either interrogate the recipient
@@ -5646,16 +5735,17 @@
                 return NO_ERROR;
             }
             case 1034: {
-                switch (n = data.readInt32()) {
-                    case 0:
-                    case 1:
-                        enableRefreshRateOverlay(static_cast<bool>(n));
-                        break;
-                    default: {
-                        Mutex::Autolock lock(mStateLock);
-                        reply->writeBool(mRefreshRateOverlay != nullptr);
+                schedule([&] {
+                    switch (n = data.readInt32()) {
+                        case 0:
+                        case 1:
+                            ON_MAIN_THREAD(enableRefreshRateOverlay(static_cast<bool>(n)));
+                            break;
+                        default: {
+                            reply->writeBool(ON_MAIN_THREAD(isRefreshRateOverlayEnabled()));
+                        }
                     }
-                }
+                }).get();
                 return NO_ERROR;
             }
             case 1035: {
@@ -5675,7 +5765,7 @@
                         return std::make_optional<PhysicalDisplayId>(inputDisplayId);
                     }
 
-                    return getInternalDisplayId();
+                    return getDefaultDisplayDevice()->getPhysicalId();
                 }();
 
                 if (!displayId) {
@@ -5801,22 +5891,25 @@
     static bool updateOverlay =
             property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
     if (!updateOverlay) return;
-    if (Mutex::Autolock lock(mStateLock); !mRefreshRateOverlay) return;
+    if (Mutex::Autolock lock(mStateLock); !isRefreshRateOverlayEnabled()) return;
 
     // Update the overlay on the main thread to avoid race conditions with
     // mRefreshRateConfigs->getCurrentRefreshRate()
     static_cast<void>(schedule([=] {
-        const auto desiredActiveMode = getDesiredActiveMode();
-        const std::optional<DisplayModeId> desiredModeId =
-                desiredActiveMode ? std::make_optional(desiredActiveMode->modeId) : std::nullopt;
+        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+        if (!display) {
+            ALOGW("%s: default display is null", __func__);
+            return;
+        }
+
+        const auto desiredActiveMode = display->getDesiredActiveMode();
+        const std::optional<DisplayModeId> desiredModeId = desiredActiveMode
+                ? std::make_optional(desiredActiveMode->mode->getId())
+                : std::nullopt;
 
         const bool timerExpired = mKernelIdleTimerEnabled && expired;
-        const auto newRefreshRate =
-                mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired);
-        if (newRefreshRate) {
-            if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
-                mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
-            }
+
+        if (display->onKernelTimerChanged(desiredModeId, timerExpired)) {
             mEventQueue->invalidate();
         }
     }));
@@ -5825,12 +5918,19 @@
 void SurfaceFlinger::toggleKernelIdleTimer() {
     using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
 
-    // If the support for kernel idle timer is disabled in SF code, don't do anything.
-    if (!mSupportKernelIdleTimer) {
+    const auto display = getDefaultDisplayDeviceLocked();
+    if (!display) {
+        ALOGW("%s: default display is null", __func__);
         return;
     }
-    const KernelIdleTimerAction action = mRefreshRateConfigs->getIdleTimerAction();
 
+    // If the support for kernel idle timer is disabled for the active display,
+    // don't do anything.
+    if (!display->refreshRateConfigs().supportsKernelIdleTimer()) {
+        return;
+    }
+
+    const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction();
     switch (action) {
         case KernelIdleTimerAction::TurnOff:
             if (mKernelIdleTimerEnabled) {
@@ -6186,6 +6286,13 @@
                                              const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
+    if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) {
+        ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32
+              ") that exceeds render target size limit.",
+              bufferSize.getWidth(), bufferSize.getHeight());
+        return BAD_VALUE;
+    }
+
     // Loop over all visible layers to see whether there's any protected layer. A protected layer is
     // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
     // A protected layer has no implication on whether it's secure, which is explicitly set by
@@ -6400,7 +6507,7 @@
     return NO_ERROR;
 }
 
-void SurfaceFlinger::setInputWindowsFinished() {
+void SurfaceFlinger::windowInfosReported() {
     Mutex::Autolock _l(mStateLock);
     signalSynchronousTransactions(CountDownLatch::eSyncInputWindows);
 }
@@ -6448,78 +6555,55 @@
         const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
     Mutex::Autolock lock(mStateLock);
 
-    LOG_ALWAYS_FATAL_IF(!display->isPrimary() && overridePolicy,
-                        "Can only set override policy on the primary display");
-    LOG_ALWAYS_FATAL_IF(!policy && !overridePolicy, "Can only clear the override policy");
-
     if (mDebugDisplayModeSetByBackdoor) {
         // ignore this request as mode is overridden by backdoor
         return NO_ERROR;
     }
 
-    if (!display->isPrimary()) {
-        // TODO(b/144711714): For non-primary displays we should be able to set an active mode
-        // as well. For now, just call directly to initiateModeChange but ideally
-        // it should go thru setDesiredActiveMode, similar to primary display.
-        ALOGV("%s for non-primary display", __func__);
-        const auto displayId = display->getPhysicalId();
-
-        hal::VsyncPeriodChangeConstraints constraints;
-        constraints.desiredTimeNanos = systemTime();
-        constraints.seamlessRequired = false;
-
-        hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
-        if (display->initiateModeChange(policy->defaultMode, constraints, &timeline) != NO_ERROR) {
-            return BAD_VALUE;
-        }
-        if (timeline.refreshRequired) {
-            repaintEverythingForHWC();
-        }
-
-        display->setActiveMode(policy->defaultMode);
-        const nsecs_t vsyncPeriod = display->getMode(policy->defaultMode)->getVsyncPeriod();
-        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, displayId,
-                                                   policy->defaultMode, vsyncPeriod);
-        return NO_ERROR;
-    }
-
     status_t setPolicyResult = overridePolicy
-            ? mRefreshRateConfigs->setOverridePolicy(policy)
-            : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
+            ? display->refreshRateConfigs().setOverridePolicy(policy)
+            : display->refreshRateConfigs().setDisplayManagerPolicy(*policy);
     if (setPolicyResult < 0) {
         return BAD_VALUE;
     }
     if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) {
         return NO_ERROR;
     }
-    scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
+
+    scheduler::RefreshRateConfigs::Policy currentPolicy =
+            display->refreshRateConfigs().getCurrentPolicy();
 
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
     const auto activeMode = display->getActiveMode();
-    const nsecs_t vsyncPeriod = activeMode->getVsyncPeriod();
-    const auto physicalId = display->getPhysicalId();
-    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, physicalId, activeMode->getId(),
-                                            vsyncPeriod);
-    toggleKernelIdleTimer();
+    if (isDisplayActiveLocked(display)) {
+        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
+        toggleKernelIdleTimer();
+    } else {
+        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
+    }
 
-    auto modeId = mScheduler->getPreferredModeId();
-    auto preferredRefreshRate = modeId
-            ? mRefreshRateConfigs->getRefreshRateFromModeId(*modeId)
-            // NOTE: Choose the default mode ID, if Scheduler doesn't have one in mind.
-            : mRefreshRateConfigs->getRefreshRateFromModeId(currentPolicy.defaultMode);
+    const DisplayModePtr preferredDisplayMode = [&] {
+        const auto schedulerMode = mScheduler->getPreferredDisplayMode();
+        if (schedulerMode && schedulerMode->getPhysicalDisplayId() == display->getPhysicalId()) {
+            return schedulerMode;
+        }
+
+        return display->getMode(currentPolicy.defaultMode);
+    }();
+
     ALOGV("trying to switch to Scheduler preferred mode %d (%s)",
-          preferredRefreshRate.getModeId().value(), preferredRefreshRate.getName().c_str());
+          preferredDisplayMode->getId().value(), to_string(preferredDisplayMode->getFps()).c_str());
 
-    if (isDisplayModeAllowed(preferredRefreshRate.getModeId())) {
+    if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
         ALOGV("switching to Scheduler preferred display mode %d",
-              preferredRefreshRate.getModeId().value());
-        setDesiredActiveMode({preferredRefreshRate.getModeId(), Scheduler::ModeEvent::Changed});
+              preferredDisplayMode->getId().value());
+        setDesiredActiveMode({preferredDisplayMode, Scheduler::ModeEvent::Changed});
     } else {
         LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
-                         preferredRefreshRate.getModeId().value());
+                         preferredDisplayMode->getId().value());
     }
 
     return NO_ERROR;
@@ -6579,29 +6663,19 @@
         return NAME_NOT_FOUND;
     }
 
-    if (display->isPrimary()) {
-        scheduler::RefreshRateConfigs::Policy policy =
-                mRefreshRateConfigs->getDisplayManagerPolicy();
-        *outDefaultMode = policy.defaultMode.value();
-        *outAllowGroupSwitching = policy.allowGroupSwitching;
-        *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue();
-        *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue();
-        *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue();
-        *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue();
-        return NO_ERROR;
-    } else if (display->isVirtual()) {
+    if (display->isVirtual()) {
         return INVALID_OPERATION;
-    } else {
-        const auto activeMode = display->getActiveMode();
-        *outDefaultMode = activeMode->getId().value();
-        *outAllowGroupSwitching = false;
-        auto vsyncPeriod = activeMode->getVsyncPeriod();
-        *outPrimaryRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
-        *outPrimaryRefreshRateMax = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
-        *outAppRequestRefreshRateMin = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
-        *outAppRequestRefreshRateMax = Fps::fromPeriodNsecs(vsyncPeriod).getValue();
-        return NO_ERROR;
     }
+
+    scheduler::RefreshRateConfigs::Policy policy =
+            display->refreshRateConfigs().getDisplayManagerPolicy();
+    *outDefaultMode = policy.defaultMode.value();
+    *outAllowGroupSwitching = policy.allowGroupSwitching;
+    *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue();
+    *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue();
+    *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue();
+    *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue();
+    return NO_ERROR;
 }
 
 wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const {
@@ -6720,7 +6794,8 @@
             // matter for the override policy though, since we set allowGroupSwitching to
             // true, so it's not a problem.
             scheduler::RefreshRateConfigs::Policy overridePolicy;
-            overridePolicy.defaultMode = mRefreshRateConfigs->getDisplayManagerPolicy().defaultMode;
+            overridePolicy.defaultMode =
+                    display->refreshRateConfigs().getDisplayManagerPolicy().defaultMode;
             overridePolicy.allowGroupSwitching = true;
             constexpr bool kOverridePolicy = true;
             result = setDesiredDisplayModeSpecsInternal(display, overridePolicy, kOverridePolicy);
@@ -6788,25 +6863,11 @@
 }
 
 void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
-    static_cast<void>(schedule([=] {
-        std::unique_ptr<RefreshRateOverlay> overlay;
-        if (enable) {
-            overlay = std::make_unique<RefreshRateOverlay>(*this, mRefreshRateOverlaySpinner);
+    for (const auto& [ignored, display] : mDisplays) {
+        if (display->isInternal()) {
+            display->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
         }
-
-        {
-            Mutex::Autolock lock(mStateLock);
-
-            // Destroy the layer of the current overlay, if any, outside the lock.
-            mRefreshRateOverlay.swap(overlay);
-            if (!mRefreshRateOverlay) return;
-
-            if (const auto display = getDefaultDisplayDeviceLocked()) {
-                mRefreshRateOverlay->setViewport(display->getSize());
-                mRefreshRateOverlay->changeRefreshRate(display->getActiveMode()->getFps());
-            }
-        }
-    }));
+    }
 }
 
 status_t SurfaceFlinger::addTransactionTraceListener(
@@ -6834,7 +6895,14 @@
 }
 
 status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
-    const auto maxSupportedRefreshRate = mRefreshRateConfigs->getSupportedRefreshRateRange().max;
+    const auto maxSupportedRefreshRate = [&] {
+        const auto display = getDefaultDisplayDevice();
+        if (display) {
+            return display->refreshRateConfigs().getSupportedRefreshRateRange().max;
+        }
+        ALOGW("%s: default display is null", __func__);
+        return Fps(60);
+    }();
     *buffers = getMaxAcquiredBufferCountForRefreshRate(maxSupportedRefreshRate);
     return NO_ERROR;
 }
@@ -6845,7 +6913,14 @@
         if (frameRateOverride.has_value()) {
             return frameRateOverride.value();
         }
-        return mRefreshRateConfigs->getCurrentRefreshRate().getFps();
+
+        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+        if (display) {
+            return display->refreshRateConfigs().getCurrentRefreshRate().getFps();
+        }
+
+        ALOGW("%s: default display is null", __func__);
+        return Fps(60);
     }();
     return getMaxAcquiredBufferCountForRefreshRate(refreshRate);
 }
@@ -6932,7 +7007,7 @@
         parent->addChild(layer);
     }
 
-    layer->updateTransformHint(mDefaultDisplayTransformHint);
+    layer->updateTransformHint(mActiveDisplayTransformHint);
 
     if (state->initialProducer != nullptr) {
         mGraphicBufferProducerList.insert(state->initialProducer);
@@ -6962,6 +7037,47 @@
     mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate());
 }
 
+void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) {
+    mScheduler->onActiveDisplayAreaChanged(activeDisplay->getWidth() * activeDisplay->getHeight());
+    getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
+}
+
+void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
+    ATRACE_CALL();
+
+    if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+        display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+    }
+
+    if (!activeDisplay) {
+        ALOGE("%s: activeDisplay is null", __func__);
+        return;
+    }
+    mActiveDisplayToken = activeDisplay->getDisplayToken();
+    activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
+    updateInternalDisplayVsyncLocked(activeDisplay);
+    mScheduler->setModeChangePending(false);
+    mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs());
+    onActiveDisplaySizeChanged(activeDisplay);
+    mActiveDisplayTransformHint = activeDisplay->getTransformHint();
+
+    // Update the kernel timer for the current active display, since the policy
+    // for this display might have changed when it was not the active display.
+    toggleKernelIdleTimer();
+}
+
+status_t SurfaceFlinger::addWindowInfosListener(
+        const sp<IWindowInfosListener>& windowInfosListener) const {
+    mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeWindowInfosListener(
+        const sp<IWindowInfosListener>& windowInfosListener) const {
+    mWindowInfosListenerInvoker->removeWindowInfosListener(windowInfosListener);
+    return NO_ERROR;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c3d837e..9e2e216 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -93,7 +93,6 @@
 class TunnelModeEnabledReporter;
 class HdrLayerInfoReporter;
 class HWComposer;
-struct SetInputWindowsListener;
 class IGraphicBufferProducer;
 class Layer;
 class MessageBase;
@@ -102,6 +101,7 @@
 class RenderArea;
 class TimeStats;
 class FrameTracer;
+class WindowInfosListenerInvoker;
 
 using gui::ScreenCaptureResults;
 
@@ -332,7 +332,7 @@
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
     bool mDisableClientCompositionCache = false;
-    void setInputWindowsFinished();
+    void windowInfosReported();
 
     // Disables expensive rendering for all displays
     // This is scheduled on the main thread
@@ -350,12 +350,14 @@
             REQUIRES(mStateLock);
     virtual void commitTransactionLocked();
 
-    // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
-    // root layers on a particular display in layer-coordinate space. The
-    // layers (and effectively their children) will be clipped against this
-    // rectangle. The base behavior is to clip to the visible region of the
-    // display.
-    virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
+    virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
+            REQUIRES(mStateLock);
+
+    // Returns true if any display matches a `bool(const DisplayDevice&)` predicate.
+    template <typename Predicate>
+    bool hasDisplay(Predicate p) const REQUIRES(mStateLock) {
+        return static_cast<bool>(findDisplay(p));
+    }
 
 private:
     friend class BufferLayer;
@@ -455,14 +457,7 @@
                 mCounterByLayerHandle GUARDED_BY(mLock);
     };
 
-    struct ActiveModeInfo {
-        DisplayModeId modeId;
-        Scheduler::ModeEvent event = Scheduler::ModeEvent::None;
-
-        bool operator!=(const ActiveModeInfo& other) const {
-            return modeId != other.modeId || event != other.event;
-        }
-    };
+    using ActiveModeInfo = DisplayDevice::ActiveModeInfo;
 
     enum class BootStage {
         BOOTLOADER,
@@ -593,7 +588,7 @@
               typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
     void modulateVsync(Handler handler, Args... args) {
         if (const auto config = (*mVsyncModulator.*handler)(args...)) {
-            const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+            const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateConfigs();
             setVsyncConfig(*config, vsyncPeriod);
         }
     }
@@ -612,7 +607,12 @@
     sp<ISurfaceComposerClient> createConnection() override;
     sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
     void destroyDisplay(const sp<IBinder>& displayToken) override;
-    std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override;
+    std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override EXCLUDES(mStateLock) {
+        Mutex::Autolock lock(mStateLock);
+        return getPhysicalDisplayIdsLocked();
+    }
+    status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override EXCLUDES(mStateLock);
+
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
     status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
                                  const Vector<ComposerState>& state,
@@ -720,6 +720,11 @@
 
     status_t getMaxAcquiredBufferCount(int* buffers) const override;
 
+    status_t addWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const override;
+    status_t removeWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const override;
+
     // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
@@ -750,12 +755,10 @@
     // Called when the frame rate override list changed to trigger an event.
     void triggerOnFrameRateOverridesChanged() override;
     // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
-    void toggleKernelIdleTimer();
+    void toggleKernelIdleTimer() REQUIRES(mStateLock);
     // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
     // make calls to sys prop each time.
     bool mKernelIdleTimerEnabled = false;
-    // Keeps track of whether the kernel timer is supported on the SF side.
-    bool mSupportKernelIdleTimer = false;
     // Show spinner with refresh rate overlay
     bool mRefreshRateOverlaySpinner = false;
 
@@ -779,9 +782,9 @@
     // Calls to setActiveMode on the main thread if there is a pending mode change
     // that needs to be applied.
     void performSetActiveMode() REQUIRES(mStateLock);
-    void clearDesiredActiveModeState() REQUIRES(mStateLock) EXCLUDES(mActiveModeLock);
+    void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called when active mode is no longer is progress
-    void desiredActiveModeChangeDone() REQUIRES(mStateLock);
+    void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
             REQUIRES(mStateLock);
@@ -810,11 +813,11 @@
     void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
 
     void updateInputFlinger();
-    void updateInputWindowInfo();
+    void notifyWindowInfos();
     void commitInputWindowCommands() REQUIRES(mStateLock);
     void updateCursorAsync();
 
-    void initScheduler(const DisplayDeviceState&) REQUIRES(mStateLock);
+    void initScheduler(const sp<DisplayDevice>& display) REQUIRES(mStateLock);
     void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
     void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
 
@@ -930,8 +933,9 @@
 
     void readPersistentProperties();
 
-    size_t getMaxTextureSize() const;
-    size_t getMaxViewportDims() const;
+    bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const {
+        return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize;
+    }
 
     int getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
 
@@ -953,6 +957,10 @@
 
     sp<const DisplayDevice> getDisplayDeviceLocked(PhysicalDisplayId id) const
             REQUIRES(mStateLock) {
+        return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(id);
+    }
+
+    sp<DisplayDevice> getDisplayDeviceLocked(PhysicalDisplayId id) REQUIRES(mStateLock) {
         if (const auto token = getPhysicalDisplayTokenLocked(id)) {
             return getDisplayDeviceLocked(token);
         }
@@ -964,13 +972,18 @@
     }
 
     sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
+        if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+            return display;
+        }
+        // The active display is outdated, fall back to the internal display
+        mActiveDisplayToken.clear();
         if (const auto token = getInternalDisplayTokenLocked()) {
             return getDisplayDeviceLocked(token);
         }
         return nullptr;
     }
 
-    sp<const DisplayDevice> getDefaultDisplayDevice() EXCLUDES(mStateLock) {
+    sp<const DisplayDevice> getDefaultDisplayDevice() const EXCLUDES(mStateLock) {
         Mutex::Autolock lock(mStateLock);
         return getDefaultDisplayDeviceLocked();
     }
@@ -989,10 +1002,18 @@
         return findDisplay([id](const auto& display) { return display.getId() == id; });
     }
 
+    std::vector<PhysicalDisplayId> getPhysicalDisplayIdsLocked() const REQUIRES(mStateLock);
+
     // mark a region of a layer stack dirty. this updates the dirty
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
 
+    sp<DisplayDevice> getDisplayWithInputByLayer(Layer* layer) const REQUIRES(mStateLock);
+
+    bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
+        return display->getDisplayToken() == mActiveDisplayToken;
+    }
+
     /*
      * H/W composer
      */
@@ -1030,8 +1051,6 @@
             const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
     void processDisplayChangesLocked() REQUIRES(mStateLock);
-    void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
-            REQUIRES(mStateLock);
     void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
@@ -1045,12 +1064,15 @@
      */
     nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
 
+    void setHWCVsyncEnabled(PhysicalDisplayId id, hal::Vsync enabled) {
+        mLastHWCVsyncState = enabled;
+        getHwComposer().setVsyncEnabled(id, enabled);
+    }
+
     // Sets the refresh rate by switching active configs, if they are available for
     // the desired refresh rate.
     void changeRefreshRateLocked(const RefreshRate&, Scheduler::ModeEvent) REQUIRES(mStateLock);
 
-    bool isDisplayModeAllowed(DisplayModeId) const REQUIRES(mStateLock);
-
     struct FenceWithFenceTime {
         sp<Fence> fence = Fence::NO_FENCE;
         std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
@@ -1111,10 +1133,13 @@
     void enableHalVirtualDisplays(bool);
 
     // Virtual display lifecycle for ID generation and HAL allocation.
-    VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, ui::LayerStack)
-            REQUIRES(mStateLock);
+    VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
+    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock);
+
+    void onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay);
+
     /*
      * Debugging & dumpsys
      */
@@ -1142,6 +1167,8 @@
     LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
     void dumpOffscreenLayersProto(LayersProto& layersProto,
                                   uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+    void dumpDisplayProto(LayersTraceProto& layersTraceProto) const;
+
     // Dumps state from HW Composer
     void dumpHwc(std::string& result) const;
     LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
@@ -1180,13 +1207,6 @@
     /*
      * Misc
      */
-
-    std::optional<ActiveModeInfo> getDesiredActiveMode() EXCLUDES(mActiveModeLock) {
-        std::lock_guard<std::mutex> lock(mActiveModeLock);
-        if (mDesiredActiveModeChanged) return mDesiredActiveMode;
-        return std::nullopt;
-    }
-
     std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId)
             REQUIRES(mStateLock);
 
@@ -1194,6 +1214,9 @@
                                                std::chrono::nanoseconds presentLatency);
     int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
 
+    void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
+            REQUIRES(mStateLock);
+
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
 
@@ -1378,6 +1401,9 @@
 
     SurfaceFlingerBE mBE;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
+    // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
+    // any mutex.
+    size_t mMaxRenderTargetSize{1};
 
     const std::string mHwcServiceName;
 
@@ -1396,24 +1422,14 @@
     // Optional to defer construction until PhaseConfiguration is created.
     sp<VsyncModulator> mVsyncModulator;
 
-    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
     std::atomic<nsecs_t> mExpectedPresentTime = 0;
     nsecs_t mScheduledPresentTime = 0;
     hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
-
-    std::mutex mActiveModeLock;
-    // This bit is set once we start setting the mode. We read from this bit during the
-    // process. If at the end, this bit is different than mDesiredActiveMode, we restart
-    // the process.
-    ActiveModeInfo mUpcomingActiveMode; // Always read and written on the main thread.
-    // This bit can be set at any point in time when the system wants the new mode.
-    ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
+    hal::Vsync mLastHWCVsyncState = hal::Vsync::DISABLE;
 
     // below flags are set by main thread only
-    TracedOrdinal<bool> mDesiredActiveModeChanged
-            GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
     bool mSetActiveModePending = false;
 
     bool mLumaSampling = true;
@@ -1429,15 +1445,12 @@
     // Should only be accessed by the main thread.
     InputWindowCommands mInputWindowCommands;
 
-    sp<SetInputWindowsListener> mSetInputWindowsListener;
-
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
 
     // This should only be accessed on the main thread.
     nsecs_t mFrameStartTime = 0;
 
-    void enableRefreshRateOverlay(bool enable);
-    std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay GUARDED_BY(mStateLock);
+    void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock);
 
     // Flag used to set override desired display mode from backdoor
     bool mDebugDisplayModeSetByBackdoor = false;
@@ -1488,10 +1501,21 @@
     auto getLayerCreatedState(const sp<IBinder>& handle);
     sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
 
-    std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
+    std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
 
     void scheduleRegionSamplingThread();
     void notifyRegionSamplingThread();
+
+    bool isRefreshRateOverlayEnabled() const REQUIRES(mStateLock) {
+        return std::any_of(mDisplays.begin(), mDisplays.end(),
+                           [](std::pair<wp<IBinder>, sp<DisplayDevice>> display) {
+                               return display.second->isRefreshRateOverlayEnabled();
+                           });
+    }
+
+    wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock);
+
+    const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 4a75180..89d1c4d 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -65,8 +65,9 @@
 }
 
 std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
-        const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) {
-    return std::make_unique<Scheduler>(configs, callback);
+        const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
+        ISchedulerCallback& callback) {
+    return std::make_unique<Scheduler>(std::move(refreshRateConfigs), callback);
 }
 
 sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 24148dd..b8bf2ba 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -30,8 +30,8 @@
     std::unique_ptr<MessageQueue> createMessageQueue() override;
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) override;
-    std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
-                                               ISchedulerCallback&) override;
+    std::unique_ptr<Scheduler> createScheduler(
+            const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override;
     sp<SurfaceInterceptor> createSurfaceInterceptor() override;
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 885297f..13c95dd 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -79,8 +79,8 @@
     virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
     virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) = 0;
-    virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
-                                                       ISchedulerCallback&) = 0;
+    virtual std::unique_ptr<Scheduler> createScheduler(
+            const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) = 0;
     virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 4a69c8f..e15eae8 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -34,6 +34,8 @@
 using android::hardware::graphics::common::V1_2::PixelFormat;
 using android::ui::DisplayPrimaries;
 
+// Keep logic in sync with WindowManagerService functions that query SurfaceFlinger properties.
+// Consider exposing properties via ISurfaceComposer instead.
 int64_t vsync_event_phase_offset_ns(int64_t defaultValue) {
     auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns();
     if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 837b2e8..9be3abe 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -161,6 +161,7 @@
 
     addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface);
     addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
+    addDisplayFlagsLocked(transaction, display.sequenceId, display.flags);
     addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
     addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
                                display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
@@ -481,6 +482,9 @@
     if (state.what & DisplayState::eLayerStackChanged) {
         addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack);
     }
+    if (state.what & DisplayState::eFlagsChanged) {
+        addDisplayFlagsLocked(transaction, sequenceId, state.flags);
+    }
     if (state.what & DisplayState::eDisplaySizeChanged) {
         addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
     }
@@ -572,6 +576,13 @@
     layerStackChange->set_layer_stack(layerStack);
 }
 
+void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId,
+                                               uint32_t flags) {
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
+    DisplayFlagsChange* flagsChange(dispChange->mutable_flags());
+    flagsChange->set_flags(flags);
+}
+
 void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId,
         uint32_t w, uint32_t h)
 {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index c955596..7b331b9 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -48,7 +48,7 @@
 using Increment = surfaceflinger::Increment;
 using DisplayChange = surfaceflinger::DisplayChange;
 
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
 
 class SurfaceInterceptor : public IBinder::DeathRecipient {
 public:
@@ -185,6 +185,7 @@
             const sp<const IGraphicBufferProducer>& surface);
     void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
             uint32_t layerStack);
+    void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags);
     void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
             uint32_t h);
     void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId,
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index b4d8a9a..5963737 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -137,14 +137,19 @@
     return writeToFile();
 }
 
+LayersTraceFileProto SurfaceTracing::createLayersTraceFileProto() {
+    LayersTraceFileProto fileProto;
+    fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
+                               LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+    return fileProto;
+}
+
 status_t SurfaceTracing::Runner::writeToFile() {
     ATRACE_CALL();
 
-    LayersTraceFileProto fileProto;
+    LayersTraceFileProto fileProto = createLayersTraceFileProto();
     std::string output;
 
-    fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
-                               LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
     mBuffer.flush(&fileProto);
     mBuffer.reset(mConfig.bufferSize);
 
@@ -186,7 +191,7 @@
         entry.set_excludes_composition_state(true);
     }
     entry.set_missed_entries(mMissedTraceEntries);
-
+    mFlinger.dumpDisplayProto(entry);
     return entry;
 }
 
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 15a503d..cea1a33 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -73,11 +73,12 @@
     };
     void setTraceFlags(uint32_t flags) { mConfig.flags = flags; }
     bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
+    static LayersTraceFileProto createLayersTraceFileProto();
 
 private:
     class Runner;
     static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
-    static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.pb";
+    static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
 
     SurfaceFlinger& mFlinger;
     mutable std::mutex mTraceLock;
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
new file mode 100644
index 0000000..dc2aa58
--- /dev/null
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WindowInfosListenerInvoker.h"
+#include <gui/ISurfaceComposer.h>
+#include <unordered_set>
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+using gui::IWindowInfosListener;
+using gui::WindowInfo;
+
+struct WindowInfosReportedListener : gui::BnWindowInfosReportedListener {
+    explicit WindowInfosReportedListener(std::function<void()> listenerCb)
+          : mListenerCb(listenerCb) {}
+
+    binder::Status onWindowInfosReported() override {
+        if (mListenerCb != nullptr) {
+            mListenerCb();
+        }
+        return binder::Status::ok();
+    }
+
+    std::function<void()> mListenerCb;
+};
+
+WindowInfosListenerInvoker::WindowInfosListenerInvoker(const sp<SurfaceFlinger>& sf) : mSf(sf) {
+    mWindowInfosReportedListener =
+            new WindowInfosReportedListener([&]() { windowInfosReported(); });
+}
+
+void WindowInfosListenerInvoker::addWindowInfosListener(
+        const sp<IWindowInfosListener>& windowInfosListener) {
+    sp<IBinder> asBinder = IInterface::asBinder(windowInfosListener);
+
+    asBinder->linkToDeath(this);
+    std::scoped_lock lock(mListenersMutex);
+    mWindowInfosListeners.emplace(asBinder, windowInfosListener);
+}
+
+void WindowInfosListenerInvoker::removeWindowInfosListener(
+        const sp<IWindowInfosListener>& windowInfosListener) {
+    sp<IBinder> asBinder = IInterface::asBinder(windowInfosListener);
+
+    std::scoped_lock lock(mListenersMutex);
+    asBinder->unlinkToDeath(this);
+    mWindowInfosListeners.erase(asBinder);
+}
+
+void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) {
+    std::scoped_lock lock(mListenersMutex);
+    mWindowInfosListeners.erase(who);
+}
+
+void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+                                                    bool shouldSync) {
+    std::unordered_set<sp<IWindowInfosListener>, ISurfaceComposer::SpHash<IWindowInfosListener>>
+            windowInfosListeners;
+
+    {
+        std::scoped_lock lock(mListenersMutex);
+        for (const auto& [_, listener] : mWindowInfosListeners) {
+            windowInfosListeners.insert(listener);
+        }
+    }
+
+    mCallbacksPending = windowInfosListeners.size();
+
+    for (const auto& listener : windowInfosListeners) {
+        listener->onWindowInfosChanged(windowInfos,
+                                       shouldSync ? mWindowInfosReportedListener : nullptr);
+    }
+}
+
+void WindowInfosListenerInvoker::windowInfosReported() {
+    mCallbacksPending--;
+    if (mCallbacksPending == 0) {
+        mSf->windowInfosReported();
+    }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
new file mode 100644
index 0000000..5e5796f
--- /dev/null
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/BnWindowInfosReportedListener.h>
+#include <android/gui/IWindowInfosListener.h>
+#include <android/gui/IWindowInfosReportedListener.h>
+#include <binder/IBinder.h>
+#include <utils/Mutex.h>
+#include <unordered_map>
+
+namespace android {
+
+class SurfaceFlinger;
+
+class WindowInfosListenerInvoker : public IBinder::DeathRecipient {
+public:
+    WindowInfosListenerInvoker(const sp<SurfaceFlinger>& sf);
+    void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
+    void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
+
+    void windowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, bool shouldSync);
+
+protected:
+    void binderDied(const wp<IBinder>& who) override;
+
+private:
+    void windowInfosReported();
+
+    struct WpHash {
+        size_t operator()(const wp<IBinder>& p) const {
+            return std::hash<IBinder*>()(p.unsafe_get());
+        }
+    };
+
+    const sp<SurfaceFlinger> mSf;
+    std::mutex mListenersMutex;
+    std::unordered_map<wp<IBinder>, const sp<gui::IWindowInfosListener>, WpHash>
+            mWindowInfosListeners GUARDED_BY(mListenersMutex);
+    sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener;
+    std::atomic<size_t> mCallbacksPending{0};
+};
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index c8a2b5e..973a439 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -13,8 +13,7 @@
 
     srcs: [
         "LayerProtoParser.cpp",
-        "layers.proto",
-        "layerstrace.proto",
+        "*.proto",
     ],
 
     shared_libs: [
diff --git a/services/surfaceflinger/layerproto/common.proto b/services/surfaceflinger/layerproto/common.proto
new file mode 100644
index 0000000..1c73a9f
--- /dev/null
+++ b/services/surfaceflinger/layerproto/common.proto
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+option optimize_for = LITE_RUNTIME;
+package android.surfaceflinger;
+
+message RectProto {
+  int32 left   = 1;
+  int32 top    = 2;
+  int32 right  = 3;
+  int32 bottom = 4;
+}
+
+message SizeProto {
+  int32 w = 1;
+  int32 h = 2;
+}
+
+message TransformProto {
+  float dsdx = 1;
+  float dtdx = 2;
+  float dsdy = 3;
+  float dtdy = 4;
+  int32 type = 5;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/display.proto b/services/surfaceflinger/layerproto/display.proto
new file mode 100644
index 0000000..ee8830e
--- /dev/null
+++ b/services/surfaceflinger/layerproto/display.proto
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+option optimize_for = LITE_RUNTIME;
+
+import "frameworks/native/services/surfaceflinger/layerproto/common.proto";
+
+package android.surfaceflinger;
+
+message DisplayProto {
+    uint64 id = 1;
+
+    string name = 2;
+
+    uint32 layer_stack = 3;
+
+    SizeProto size = 4;
+
+    RectProto layer_stack_space_rect = 5;
+
+    TransformProto transform = 6;
+}
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index dddc677..057eabb 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -2,6 +2,9 @@
 
 syntax = "proto3";
 option optimize_for = LITE_RUNTIME;
+
+import "frameworks/native/services/surfaceflinger/layerproto/common.proto";
+
 package android.surfaceflinger;
 
 // Contains a list of all layers.
@@ -130,6 +133,9 @@
   repeated BlurRegion blur_regions = 54;
 
   bool is_trusted_overlay = 55;
+
+  // Corner radius explicitly set on layer rather than inherited
+  float requested_corner_radius = 56;
 }
 
 message PositionProto {
@@ -137,31 +143,11 @@
   float y = 2;
 }
 
-message SizeProto {
-  int32 w = 1;
-  int32 h = 2;
-}
-
-message TransformProto {
-  float dsdx = 1;
-  float dtdx = 2;
-  float dsdy = 3;
-  float dtdy = 4;
-  int32 type = 5;
-}
-
 message RegionProto {
   reserved 1;  // Previously: uint64 id
   repeated RectProto rect = 2;
 }
 
-message RectProto {
-  int32 left   = 1;
-  int32 top    = 2;
-  int32 right  = 3;
-  int32 bottom = 4;
-}
-
 message FloatRectProto {
   float left = 1;
   float top = 2;
@@ -228,4 +214,4 @@
     int32 top = 8;
     int32 right = 9;
     int32 bottom = 10;
-}
\ No newline at end of file
+}
diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto
index 990f3cf..13647b6 100644
--- a/services/surfaceflinger/layerproto/layerstrace.proto
+++ b/services/surfaceflinger/layerproto/layerstrace.proto
@@ -18,6 +18,7 @@
 option optimize_for = LITE_RUNTIME;
 
 import "frameworks/native/services/surfaceflinger/layerproto/layers.proto";
+import "frameworks/native/services/surfaceflinger/layerproto/display.proto";
 
 package android.surfaceflinger;
 
@@ -57,4 +58,6 @@
 
     /* Number of missed entries since the last entry was recorded. */
     optional uint32 missed_entries = 6;
+
+    repeated DisplayProto displays = 7;
 }
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index a36c6ed..32ad873 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -51,6 +51,7 @@
         "Stress_test.cpp",
         "SurfaceInterceptor_test.cpp",
         "VirtualDisplay_test.cpp",
+        "WindowInfosListener_test.cpp",
     ],
     data: ["SurfaceFlinger_test.filter"],
     static_libs: [
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index af00ec7..93656f3 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -176,6 +176,15 @@
     }
 }
 
+TEST_F(EffectLayerTest, EffectLayerWithColorNoCrop) {
+    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    ui::DisplayMode mode;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
+    const ui::Size& resolution = mode.resolution;
+    auto shot = screenshot();
+    shot->expectColor(Rect(0, 0, resolution.getWidth(), resolution.getHeight()), Color::RED);
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 43d957c..25f3bb9 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -208,6 +208,37 @@
     }
 }
 
+// b/200781179 - don't round a layer without a valid crop
+// This behaviour should be fixed since we treat buffer layers differently than
+// effect or container layers.
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusInvalidCrop) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::GREEN, size, size));
+    ASSERT_NO_FATAL_FAILURE(child = createColorLayer("child", Color::RED));
+
+    Transaction().setCornerRadius(child, cornerRadius).reparent(child, parent).show(child).apply();
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        std::this_thread::sleep_for(std::chrono::seconds(5));
+        // Solid corners since we don't round a layer without a valid crop
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::RED);
+        shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::RED);
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::RED);
+        shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::RED);
+        // Solid center
+        shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+                               size / 2 + testArea / 2, size / 2 + testArea / 2),
+                          Color::RED);
+    }
+}
+
 TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) {
     sp<SurfaceControl> parent;
     sp<SurfaceControl> child;
@@ -465,6 +496,95 @@
     }
 }
 
+TEST_P(LayerTypeAndRenderTypeTransactionTest, ChildCornerRadiusTakesPrecedence) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint32_t size = 64;
+    const uint32_t parentSize = size * 3;
+    const uint32_t testLength = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize));
+    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+    Transaction()
+            .setCornerRadius(parent, cornerRadius)
+            .setCornerRadius(child, cornerRadius)
+            .reparent(child, parent)
+            .setPosition(child, size, size)
+            .apply();
+
+    {
+        const uint32_t top = size - 1;
+        const uint32_t left = size - 1;
+        const uint32_t bottom = size * 2 - 1;
+        const uint32_t right = size * 2 - 1;
+        auto shot = getScreenCapture();
+        // Edges are transparent
+        // TL
+        shot->expectColor(Rect(left, top, testLength, testLength), Color::RED);
+        // TR
+        shot->expectColor(Rect(right - testLength, top, right, testLength), Color::RED);
+        // BL
+        shot->expectColor(Rect(left, bottom - testLength, testLength, bottom - testLength),
+                          Color::RED);
+        // BR
+        shot->expectColor(Rect(right - testLength, bottom - testLength, right, bottom), Color::RED);
+        // Solid center
+        shot->expectColor(Rect(parentSize / 2 - testLength / 2, parentSize / 2 - testLength / 2,
+                               parentSize / 2 + testLength / 2, parentSize / 2 + testLength / 2),
+                          Color::GREEN);
+    }
+}
+
+// Test if ParentCornerRadiusTakesPrecedence if the parent corner radius crop is fully contained by
+// the child corner radius crop.
+TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusTakesPrecedence) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint32_t size = 64;
+    const uint32_t parentSize = size * 3;
+    const Rect parentCrop(size + 1, size + 1, size * 2 - 1, size * 2 - 1);
+    const uint32_t testLength = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize));
+    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+    Transaction()
+            .setCornerRadius(parent, cornerRadius)
+            .setCrop(parent, parentCrop)
+            .setCornerRadius(child, cornerRadius)
+            .reparent(child, parent)
+            .setPosition(child, size, size)
+            .apply();
+
+    {
+        const uint32_t top = size - 1;
+        const uint32_t left = size - 1;
+        const uint32_t bottom = size * 2 - 1;
+        const uint32_t right = size * 2 - 1;
+        auto shot = getScreenCapture();
+        // Edges are transparent
+        // TL
+        shot->expectColor(Rect(left, top, testLength, testLength), Color::BLACK);
+        // TR
+        shot->expectColor(Rect(right - testLength, top, right, testLength), Color::BLACK);
+        // BL
+        shot->expectColor(Rect(left, bottom - testLength, testLength, bottom - testLength),
+                          Color::BLACK);
+        // BR
+        shot->expectColor(Rect(right - testLength, bottom - testLength, right, bottom),
+                          Color::BLACK);
+        // Solid center
+        shot->expectColor(Rect(parentSize / 2 - testLength / 2, parentSize / 2 - testLength / 2,
+                               parentSize / 2 + testLength / 2, parentSize / 2 + testLength / 2),
+                          Color::GREEN);
+    }
+}
+
 TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
     if (!deviceSupportsBlurs()) GTEST_SKIP();
     if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
index 05858bf..fb4458a 100644
--- a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
+++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
@@ -20,6 +20,10 @@
 
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
+#include <chrono>
+
+using ::std::literals::chrono_literals::operator""ms;
+using ::std::literals::chrono_literals::operator""s;
 
 static constexpr int kRefreshRateOverlayCode = 1034;
 static constexpr int kRefreshRateOverlayEnable = 1;
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index 579a26e..c4d42fa 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -99,10 +99,10 @@
     }
 
     static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback,
-                                             const ReleaseCallbackId& expectedCallbackId) {
+                                             const ReleaseCallbackId& expectedReleaseBufferId) {
         ReleaseCallbackId actualReleaseBufferId;
         releaseCallback.getCallbackData(&actualReleaseBufferId);
-        EXPECT_EQ(expectedCallbackId, actualReleaseBufferId);
+        EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId);
         releaseCallback.verifyNoCallbacks();
     }
     static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() {
@@ -333,4 +333,60 @@
     ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
 
+TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) {
+    sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
+    sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();
+
+    CallbackHelper callback1, callback2;
+
+    TransactionCompletedListener::setInstance(firstCompletedListener);
+
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+    // Send initial buffer for the layer
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, callback1, firstBufferCallbackId,
+                 *releaseCallback);
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+
+    // Sent a second buffer to allow the first buffer to get released.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+    Transaction transaction1;
+    transaction1.setFrameNumber(layer, secondBufferCallbackId.framenumber);
+    transaction1.setBuffer(layer, secondBuffer, secondBufferCallbackId,
+                           releaseCallback->getCallback());
+    transaction1.setAcquireFence(layer, Fence::NO_FENCE);
+    transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext());
+
+    // Set a different TransactionCompletedListener to mimic a second process
+    TransactionCompletedListener::setInstance(secondCompletedListener);
+
+    // Make sure the second "process" has a callback set up.
+    Transaction transaction2;
+    transaction2.addTransactionCompletedCallback(callback2.function, callback2.getContext());
+
+    // This merging order, merge transaction1 first then transaction2, seems to ensure the listener
+    // for transaction2 is ordered first. This makes sure the wrong process is added first to the
+    // layer's vector of listeners. With the bug, only the secondCompletedListener will get the
+    // release callback id, since it's ordered first. Then firstCompletedListener would fail to get
+    // the release callback id and not invoke the release callback.
+    Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply();
+
+    expected = ExpectedResult();
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED,
+                        ExpectedResult::PreviousBuffer::RELEASED);
+    ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 6912fcf..ab2064e 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -515,7 +515,8 @@
 }
 
 TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
+                                              ISurfaceComposerClient::eFXSurfaceBufferState);
 
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
 
@@ -532,6 +533,21 @@
     ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
 }
 
+TEST_F(ScreenCaptureTest, CaptureTooLargeLayer) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60);
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+
+    Transaction().show(redLayer).setLayer(redLayer, INT32_MAX).apply(true);
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = redLayer->getHandle();
+    captureArgs.frameScaleX = INT32_MAX / 60;
+    captureArgs.frameScaleY = INT32_MAX / 60;
+
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(captureArgs, captureResults));
+}
+
 TEST_F(ScreenCaptureTest, CaptureSecureLayer) {
     sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
                                               ISurfaceComposerClient::eFXSurfaceBufferState);
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index d5890ff..a424059 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -61,7 +61,7 @@
 constexpr auto LAYER_NAME = "Layer Create and Delete Test";
 constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";
 
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
 
 // Fill an RGBA_8888 formatted surface with a single color.
 static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
new file mode 100644
index 0000000..89228d5
--- /dev/null
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/android_filesystem_config.h>
+#include <future>
+#include "utils/TransactionUtils.h"
+
+namespace android {
+using Transaction = SurfaceComposerClient::Transaction;
+using gui::WindowInfo;
+
+class WindowInfosListenerTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        seteuid(AID_SYSTEM);
+        mClient = new SurfaceComposerClient;
+        mWindowInfosListener = new SyncWindowInfosListener();
+        mClient->addWindowInfosListener(mWindowInfosListener);
+    }
+
+    void TearDown() override {
+        mClient->removeWindowInfosListener(mWindowInfosListener);
+        seteuid(AID_ROOT);
+    }
+
+    struct SyncWindowInfosListener : public gui::WindowInfosListener {
+    public:
+        void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override {
+            windowInfosPromise.set_value(windowInfos);
+        }
+
+        std::vector<WindowInfo> waitForWindowInfos() {
+            std::future<std::vector<WindowInfo>> windowInfosFuture =
+                    windowInfosPromise.get_future();
+            std::vector<WindowInfo> windowInfos = windowInfosFuture.get();
+            windowInfosPromise = std::promise<std::vector<WindowInfo>>();
+            return windowInfos;
+        }
+
+    private:
+        std::promise<std::vector<WindowInfo>> windowInfosPromise;
+    };
+
+    sp<SurfaceComposerClient> mClient;
+    sp<SyncWindowInfosListener> mWindowInfosListener;
+};
+
+std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo,
+                                                 std::vector<WindowInfo> windowInfos) {
+    std::optional<WindowInfo> foundWindowInfo = std::nullopt;
+    for (WindowInfo windowInfo : windowInfos) {
+        if (windowInfo.token == targetWindowInfo.token) {
+            foundWindowInfo = std::make_optional<>(windowInfo);
+            break;
+        }
+    }
+
+    return foundWindowInfo;
+}
+
+TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
+    std::string name = "Test Layer";
+    sp<IBinder> token = new BBinder();
+    WindowInfo windowInfo;
+    windowInfo.name = name;
+    windowInfo.token = token;
+    sp<SurfaceControl> surfaceControl =
+            mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceBufferState);
+
+    Transaction()
+            .setLayerStack(surfaceControl, 0)
+            .show(surfaceControl)
+            .setLayer(surfaceControl, INT32_MAX - 1)
+            .setInputWindowInfo(surfaceControl, windowInfo)
+            .apply();
+
+    std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
+    std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+    ASSERT_NE(std::nullopt, foundWindowInfo);
+
+    Transaction().reparent(surfaceControl, nullptr).apply();
+
+    windowInfos = mWindowInfosListener->waitForWindowInfos();
+    foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+    ASSERT_EQ(std::nullopt, foundWindowInfo);
+}
+
+TEST_F(WindowInfosListenerTest, WindowInfoChanged) {
+    std::string name = "Test Layer";
+    sp<IBinder> token = new BBinder();
+    WindowInfo windowInfo;
+    windowInfo.name = name;
+    windowInfo.token = token;
+    sp<SurfaceControl> surfaceControl =
+            mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceBufferState);
+
+    Transaction()
+            .setLayerStack(surfaceControl, 0)
+            .show(surfaceControl)
+            .setLayer(surfaceControl, INT32_MAX - 1)
+            .setInputWindowInfo(surfaceControl, windowInfo)
+            .apply();
+
+    std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
+    std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+    ASSERT_NE(std::nullopt, foundWindowInfo);
+    ASSERT_TRUE(foundWindowInfo->touchableRegion.isEmpty());
+
+    Rect touchableRegions(0, 0, 50, 50);
+    windowInfo.addTouchableRegion(Rect(0, 0, 50, 50));
+    Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
+
+    windowInfos = mWindowInfosListener->waitForWindowInfos();
+    foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+    ASSERT_NE(std::nullopt, foundWindowInfo);
+    ASSERT_TRUE(foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index b5086fa..9e704c3 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -51,6 +51,7 @@
         "DisplayIdGeneratorTest.cpp",
         "DisplayTransactionTest.cpp",
         "DisplayDevice_GetBestColorModeTest.cpp",
+        "DisplayDevice_InitiateModeChange.cpp",
         "DisplayDevice_SetProjectionTest.cpp",
         "EventThreadTest.cpp",
         "FpsReporterTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 560f139..52a36a2 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -108,6 +108,8 @@
 
         mComposer = new Hwc2::mock::Composer();
         mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+        mFlinger.mutableMaxRenderTargetSize() = 16384;
     }
 
     ~CompositionTest() {
@@ -519,8 +521,6 @@
 
     static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) {
         // TODO: Eliminate the complexity of actually creating a buffer
-        EXPECT_CALL(*test->mRenderEngine, getMaxTextureSize()).WillOnce(Return(16384));
-        EXPECT_CALL(*test->mRenderEngine, getMaxViewportDims()).WillOnce(Return(16384));
         status_t err =
                 layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT,
                                                   LayerProperties::FORMAT);
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
new file mode 100644
index 0000000..d4cfbbb
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class InitiateModeChangeTest : public DisplayTransactionTest {
+public:
+    using Event = scheduler::RefreshRateConfigEvent;
+
+    void SetUp() override {
+        injectFakeBufferQueueFactory();
+        injectFakeNativeWindowSurfaceFactory();
+
+        PrimaryDisplayVariant::setupHwcHotplugCallExpectations(this);
+        PrimaryDisplayVariant::setupFramebufferConsumerBufferQueueCallExpectations(this);
+        PrimaryDisplayVariant::setupFramebufferProducerBufferQueueCallExpectations(this);
+        PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
+        PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
+
+        mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+
+        mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+                           .setSupportedModes({kDisplayMode60, kDisplayMode90, kDisplayMode120})
+                           .setActiveMode(kDisplayModeId60)
+                           .inject();
+    }
+
+protected:
+    sp<DisplayDevice> mDisplay;
+
+    const DisplayModeId kDisplayModeId60 = DisplayModeId(0);
+    const DisplayModePtr kDisplayMode60 =
+            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId60.value()))
+                    .setId(kDisplayModeId60)
+                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+                    .setVsyncPeriod(int32_t(16'666'667))
+                    .setGroup(0)
+                    .setHeight(1000)
+                    .setWidth(1000)
+                    .build();
+
+    const DisplayModeId kDisplayModeId90 = DisplayModeId(1);
+    const DisplayModePtr kDisplayMode90 =
+            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90.value()))
+                    .setId(kDisplayModeId90)
+                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+                    .setVsyncPeriod(int32_t(11'111'111))
+                    .setGroup(0)
+                    .setHeight(1000)
+                    .setWidth(1000)
+                    .build();
+
+    const DisplayModeId kDisplayModeId120 = DisplayModeId(2);
+    const DisplayModePtr kDisplayMode120 =
+            DisplayMode::Builder(hal::HWConfigId(kDisplayModeId120.value()))
+                    .setId(kDisplayModeId120)
+                    .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+                    .setVsyncPeriod(int32_t(8'333'333))
+                    .setGroup(0)
+                    .setHeight(1000)
+                    .setWidth(1000)
+                    .build();
+};
+
+TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) {
+    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode60, Event::None}));
+    EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
+TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
+    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None}));
+    ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
+    EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+
+    // Setting another mode should be cached but return false
+    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode120, Event::None}));
+    ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
+    EXPECT_EQ(kDisplayMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+}
+
+TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) {
+    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None}));
+    ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
+
+    mDisplay->clearDesiredActiveModeState();
+    ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
+TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
+    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None}));
+    ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
+    EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+
+    hal::VsyncPeriodChangeConstraints constraints{
+            .desiredTimeNanos = systemTime(),
+            .seamlessRequired = false,
+    };
+    hal::VsyncPeriodChangeTimeline timeline;
+    EXPECT_EQ(OK,
+              mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
+                                           &timeline));
+    EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
+
+    mDisplay->clearDesiredActiveModeState();
+    ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
+TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged)
+NO_THREAD_SAFETY_ANALYSIS {
+    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kDisplayMode90, Event::None}));
+    ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
+    EXPECT_EQ(kDisplayMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+
+    hal::VsyncPeriodChangeConstraints constraints{
+            .desiredTimeNanos = systemTime(),
+            .seamlessRequired = false,
+    };
+    hal::VsyncPeriodChangeTimeline timeline;
+    EXPECT_EQ(OK,
+              mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
+                                           &timeline));
+    EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
+
+    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kDisplayMode120, Event::None}));
+    ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
+    EXPECT_EQ(kDisplayMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+
+    EXPECT_EQ(kDisplayMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
+
+    EXPECT_EQ(OK,
+              mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
+                                           &timeline));
+    EXPECT_EQ(kDisplayMode120, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
+
+    mDisplay->clearDesiredActiveModeState();
+    ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 6ce281d..de058a4 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -615,8 +615,8 @@
     }
 
     static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _, _))
-                .WillOnce(DoAll(SetArgPointee<4>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
+                .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
         EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
     }
 };
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index b4a1481..4ff7592 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -543,17 +543,34 @@
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary) {
-    mThread->onModeChanged(INTERNAL_DISPLAY_ID, DisplayModeId(7), 16666666);
+    const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
+                              .setPhysicalDisplayId(INTERNAL_DISPLAY_ID)
+                              .setId(DisplayModeId(7))
+                              .setVsyncPeriod(16666666)
+                              .build();
+
+    mThread->onModeChanged(mode);
     expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
 }
 
 TEST_F(EventThreadTest, postConfigChangedExternal) {
-    mThread->onModeChanged(EXTERNAL_DISPLAY_ID, DisplayModeId(5), 16666666);
+    const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
+                              .setPhysicalDisplayId(EXTERNAL_DISPLAY_ID)
+                              .setId(DisplayModeId(5))
+                              .setVsyncPeriod(16666666)
+                              .build();
+
+    mThread->onModeChanged(mode);
     expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
-    mThread->onModeChanged(DISPLAY_ID_64BIT, DisplayModeId(7), 16666666);
+    const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
+                              .setPhysicalDisplayId(DISPLAY_ID_64BIT)
+                              .setId(DisplayModeId(7))
+                              .setVsyncPeriod(16666666)
+                              .build();
+    mThread->onModeChanged(mode);
     expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
 }
 
@@ -562,7 +579,13 @@
     sp<MockEventThreadConnection> suppressConnection =
             createConnection(suppressConnectionEventRecorder);
 
-    mThread->onModeChanged(INTERNAL_DISPLAY_ID, DisplayModeId(9), 16666666);
+    const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
+                              .setPhysicalDisplayId(INTERNAL_DISPLAY_ID)
+                              .setId(DisplayModeId(9))
+                              .setVsyncPeriod(16666666)
+                              .build();
+
+    mThread->onModeChanged(mode);
     expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
 
     auto args = suppressConnectionEventRecorder.waitForCall();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index b67ebca..02ec7fc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -35,6 +35,7 @@
 
 using testing::_;
 using testing::Return;
+using testing::ReturnRef;
 
 namespace android {
 
@@ -62,6 +63,10 @@
     LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
     const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
 
+    LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
+        return history().summarize(*mScheduler->refreshRateConfigs(), now);
+    }
+
     size_t layerCount() const { return mScheduler->layerHistorySize(); }
     size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
 
@@ -101,7 +106,7 @@
             history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
             time += frameRate.getPeriodNsecs();
 
-            summary = history().summarize(time);
+            summary = summarizeLayerHistory(time);
         }
 
         ASSERT_EQ(1, summary.size());
@@ -110,21 +115,24 @@
                 << "Frame rate is " << frameRate;
     }
 
-    RefreshRateConfigs mConfigs{{DisplayMode::Builder(0)
-                                         .setId(DisplayModeId(0))
-                                         .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
-                                         .setGroup(0)
-                                         .build(),
-                                 DisplayMode::Builder(1)
-                                         .setId(DisplayModeId(1))
-                                         .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
-                                         .setGroup(0)
-                                         .build()},
-                                DisplayModeId(0)};
+    std::shared_ptr<RefreshRateConfigs> mConfigs = std::make_shared<
+            RefreshRateConfigs>(DisplayModes{DisplayMode::Builder(0)
+                                                     .setId(DisplayModeId(0))
+                                                     .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                                     .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
+                                                     .setGroup(0)
+                                                     .build(),
+                                             DisplayMode::Builder(1)
+                                                     .setId(DisplayModeId(1))
+                                                     .setPhysicalDisplayId(PhysicalDisplayId(0))
+                                                     .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
+                                                     .setGroup(0)
+                                                     .build()},
+                                DisplayModeId(0));
 
-    mock::NoOpSchedulerCallback mSchedulerCallback;
+    mock::SchedulerCallback mSchedulerCallback;
 
-    TestableScheduler* const mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
+    TestableScheduler* mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
 
     TestableSurfaceFlinger mFlinger;
 };
@@ -142,22 +150,22 @@
     const nsecs_t time = systemTime();
 
     // No layers returned if no layers are active.
-    EXPECT_TRUE(history().summarize(time).empty());
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
     EXPECT_EQ(0, activeLayerCount());
 
     // Max returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
         history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
-        ASSERT_EQ(1, history().summarize(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        ASSERT_EQ(1, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
     }
 
     // Max is returned since we have enough history but there is no timestamp votes.
     for (int i = 0; i < 10; i++) {
         history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
-        ASSERT_EQ(1, history().summarize(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        ASSERT_EQ(1, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
     }
 }
@@ -173,17 +181,17 @@
     nsecs_t time = systemTime();
 
     history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
-    auto summary = history().summarize(time);
-    ASSERT_EQ(1, history().summarize(time).size());
+    auto summary = summarizeLayerHistory(time);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
     // Layer is still considered inactive so we expect to get Min
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
 
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
     history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
 
-    summary = history().summarize(time);
-    EXPECT_TRUE(history().summarize(time).empty());
+    summary = summarizeLayerHistory(time);
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
     EXPECT_EQ(0, activeLayerCount());
 }
 
@@ -201,9 +209,9 @@
         time += LO_FPS_PERIOD;
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_TRUE(LO_FPS.equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+    EXPECT_TRUE(LO_FPS.equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 }
@@ -224,13 +232,13 @@
         time += HI_FPS_PERIOD;
     }
 
-    ASSERT_TRUE(history().summarize(time).empty());
+    ASSERT_TRUE(summarizeLayerHistory(time).empty());
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer became inactive
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(history().summarize(time).empty());
+    ASSERT_TRUE(summarizeLayerHistory(time).empty());
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
@@ -251,14 +259,14 @@
         time += HI_FPS_PERIOD;
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer became inactive
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(history().summarize(time).empty());
+    ASSERT_TRUE(summarizeLayerHistory(time).empty());
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
@@ -279,14 +287,14 @@
         time += LO_FPS_PERIOD;
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer became inactive
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(history().summarize(time).empty());
+    ASSERT_TRUE(summarizeLayerHistory(time).empty());
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
@@ -307,18 +315,18 @@
         time += HI_FPS_PERIOD;
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
-    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer became inactive, but the vote stays
     setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
-    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
@@ -339,20 +347,20 @@
         time += HI_FPS_PERIOD;
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              history().summarize(time)[0].vote);
-    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer became inactive, but the vote stays
     setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(1, history().summarize(time).size());
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              history().summarize(time)[0].vote);
-    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
@@ -383,7 +391,7 @@
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-        summary = history().summarize(time);
+        summary = summarizeLayerHistory(time);
     }
 
     ASSERT_EQ(1, summary.size());
@@ -395,7 +403,7 @@
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
-        summary = history().summarize(time);
+        summary = summarizeLayerHistory(time);
     }
 
     // layer1 is still active but infrequent.
@@ -404,7 +412,7 @@
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
     ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_TRUE(HI_FPS.equalsWithMargin(history().summarize(time)[1].desiredRefreshRate));
+    EXPECT_TRUE(HI_FPS.equalsWithMargin(summarizeLayerHistory(time)[1].desiredRefreshRate));
 
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
@@ -414,7 +422,7 @@
     for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
-        summary = history().summarize(time);
+        summary = summarizeLayerHistory(time);
     }
 
     ASSERT_EQ(1, summary.size());
@@ -433,7 +441,7 @@
 
         history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
-        summary = history().summarize(time);
+        summary = summarizeLayerHistory(time);
     }
 
     ASSERT_EQ(2, summary.size());
@@ -445,7 +453,7 @@
 
     // layer3 becomes recently active.
     history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    summary = history().summarize(time);
+    summary = summarizeLayerHistory(time);
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
     EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
@@ -456,7 +464,7 @@
 
     // layer1 expires.
     layer1.clear();
-    summary = history().summarize(time);
+    summary = summarizeLayerHistory(time);
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
@@ -472,7 +480,7 @@
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
-        summary = history().summarize(time);
+        summary = summarizeLayerHistory(time);
     }
 
     ASSERT_EQ(1, summary.size());
@@ -483,7 +491,7 @@
 
     // layer2 expires.
     layer2.clear();
-    summary = history().summarize(time);
+    summary = summarizeLayerHistory(time);
     EXPECT_TRUE(summary.empty());
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -493,7 +501,7 @@
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
         history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
-        summary = history().summarize(time);
+        summary = summarizeLayerHistory(time);
     }
 
     ASSERT_EQ(1, summary.size());
@@ -505,7 +513,7 @@
 
     // layer3 expires.
     layer3.clear();
-    summary = history().summarize(time);
+    summary = summarizeLayerHistory(time);
     EXPECT_TRUE(summary.empty());
     EXPECT_EQ(0, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -526,8 +534,8 @@
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
         EXPECT_EQ(1, layerCount());
-        ASSERT_EQ(1, history().summarize(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        ASSERT_EQ(1, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
         EXPECT_EQ(1, frequentLayerCount(time));
     }
@@ -537,8 +545,8 @@
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     EXPECT_EQ(1, layerCount());
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 
@@ -551,8 +559,8 @@
         time += HI_FPS_PERIOD;
 
         EXPECT_EQ(1, layerCount());
-        ASSERT_EQ(1, history().summarize(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+        ASSERT_EQ(1, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
         EXPECT_EQ(0, frequentLayerCount(time));
     }
@@ -562,8 +570,8 @@
     time += HI_FPS_PERIOD;
 
     EXPECT_EQ(1, layerCount());
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 }
@@ -590,10 +598,10 @@
                      LayerHistory::LayerUpdateType::Buffer);
 
     EXPECT_EQ(2, layerCount());
-    ASSERT_EQ(1, history().summarize(time).size());
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              history().summarize(time)[0].vote);
-    EXPECT_TRUE(Fps(60.0f).equalsWithMargin(history().summarize(time)[0].desiredRefreshRate));
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_TRUE(Fps(60.0f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
 }
@@ -617,8 +625,8 @@
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
     EXPECT_EQ(0, animatingLayerCount(time));
@@ -627,8 +635,8 @@
     history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
     EXPECT_EQ(0, animatingLayerCount(time));
@@ -637,8 +645,8 @@
     history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
     EXPECT_EQ(1, animatingLayerCount(time));
@@ -727,13 +735,13 @@
         }
 
         if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
-            ASSERT_NE(0, history().summarize(time).size());
-            ASSERT_GE(2, history().summarize(time).size());
+            ASSERT_NE(0, summarizeLayerHistory(time).size());
+            ASSERT_GE(2, summarizeLayerHistory(time).size());
 
             bool max = false;
             bool min = false;
             Fps heuristic{0.0};
-            for (const auto& layer : history().summarize(time)) {
+            for (const auto& layer : summarizeLayerHistory(time)) {
                 if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
                     heuristic = layer.desiredRefreshRate;
                 } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
@@ -746,7 +754,7 @@
             if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
                 EXPECT_TRUE(Fps(24.0f).equalsWithMargin(heuristic));
                 EXPECT_FALSE(max);
-                if (history().summarize(time).size() == 2) {
+                if (summarizeLayerHistory(time).size() == 2) {
                     EXPECT_TRUE(min);
                 }
             }
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 3b2bd81..c1dba2b 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -51,8 +51,7 @@
     ~RefreshRateConfigsTest();
 
     RefreshRate createRefreshRate(DisplayModePtr displayMode) {
-        return {displayMode->getId(), displayMode, displayMode->getFps(),
-                RefreshRate::ConstructorTag(0)};
+        return {displayMode, RefreshRate::ConstructorTag(0)};
     }
 
     Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
@@ -147,24 +146,17 @@
     DisplayModes m60_120Device = {mConfig60, mConfig120};
 
     // Expected RefreshRate objects
-    RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, Fps(60),
-                                     RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpectedAlmost60Config = {HWC_CONFIG_ID_60,
-                                           createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665),
-                                           Fps(60), RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected90Config = {HWC_CONFIG_ID_90, mConfig90, Fps(90),
-                                     RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected90DifferentGroupConfig = {HWC_CONFIG_ID_90, mConfig90DifferentGroup,
-                                                   Fps(90), RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected90DifferentResolutionConfig = {HWC_CONFIG_ID_90,
-                                                        mConfig90DifferentResolution, Fps(90),
+    RefreshRate mExpected60Config = {mConfig60, RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpectedAlmost60Config = {createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665),
+                                           RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected90Config = {mConfig90, RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected90DifferentGroupConfig = {mConfig90DifferentGroup,
+                                                   RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected90DifferentResolutionConfig = {mConfig90DifferentResolution,
                                                         RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected72Config = {HWC_CONFIG_ID_72, mConfig72, Fps(72.0f),
-                                     RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected30Config = {HWC_CONFIG_ID_30, mConfig30, Fps(30),
-                                     RefreshRate::ConstructorTag(0)};
-    RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
-                                      RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected72Config = {mConfig72, RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)};
 
 private:
     DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
@@ -189,6 +181,7 @@
                                                          int64_t vsyncPeriod, ui::Size resolution) {
     return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
             .setId(modeId)
+            .setPhysicalDisplayId(PhysicalDisplayId(0))
             .setVsyncPeriod(int32_t(vsyncPeriod))
             .setGroup(group)
             .setHeight(resolution.height)
@@ -2276,26 +2269,6 @@
     ASSERT_TRUE(frameRateOverrides.empty());
 }
 
-TEST_F(RefreshRateConfigsTest, updateDisplayModes) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_30);
-    refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(HWC_CONFIG_ID_30),
-                                                 /* allowGroupSwitching */ false,
-                                                 /* range */ {Fps(30.0f), Fps(30.0f)}});
-
-    refreshRateConfigs->updateDisplayModes(m60_90Device, HWC_CONFIG_ID_60);
-
-    const auto currentRefreshRate = refreshRateConfigs->getCurrentRefreshRate();
-    EXPECT_TRUE(currentRefreshRate.getFps().equalsWithMargin(Fps(60.0)));
-    EXPECT_EQ(currentRefreshRate.getModeId(), HWC_CONFIG_ID_60);
-
-    EXPECT_TRUE(
-            getMaxSupportedRefreshRate(*refreshRateConfigs).getFps().equalsWithMargin(Fps(90.0)));
-    EXPECT_TRUE(
-            getMinSupportedRefreshRate(*refreshRateConfigs).getFps().equalsWithMargin(Fps(60.0)));
-}
-
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index bf07106..12b155b 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -81,6 +81,7 @@
                                                        int64_t vsyncPeriod) {
     return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value()))
             .setId(modeId)
+            .setPhysicalDisplayId(PhysicalDisplayId(0))
             .setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
             .setGroup(group)
             .build();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index f680d80..5713c2f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -53,16 +53,19 @@
 
     const DisplayModePtr mode60 = DisplayMode::Builder(0)
                                           .setId(DisplayModeId(0))
+                                          .setPhysicalDisplayId(PhysicalDisplayId(0))
                                           .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
                                           .setGroup(0)
                                           .build();
     const DisplayModePtr mode120 = DisplayMode::Builder(1)
                                            .setId(DisplayModeId(1))
+                                           .setPhysicalDisplayId(PhysicalDisplayId(0))
                                            .setVsyncPeriod(Fps(120.f).getPeriodNsecs())
                                            .setGroup(0)
                                            .build();
 
-    scheduler::RefreshRateConfigs mConfigs{{mode60}, mode60->getId()};
+    std::shared_ptr<scheduler::RefreshRateConfigs> mConfigs =
+            std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId());
 
     mock::SchedulerCallback mSchedulerCallback;
 
@@ -171,7 +174,7 @@
     mScheduler->setDisplayPowerState(kPowerStateNormal);
 
     constexpr uint32_t kDisplayArea = 999'999;
-    mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
+    mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
     EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0);
     mScheduler->chooseRefreshRateForContent();
@@ -182,7 +185,9 @@
     sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
     ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize());
 
-    mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+    mScheduler->setRefreshRateConfigs(
+            std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120},
+                                                            mode60->getId()));
 
     ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -194,20 +199,21 @@
     // onModeChange is called.
     mScheduler->clearOptionalFieldsInFeatures();
     EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
-    EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
+    EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
 }
 
 TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
-    DisplayModeId modeId = DisplayModeId(111);
-    nsecs_t vsyncPeriod = 111111;
+    const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
+                              .setId(DisplayModeId(111))
+                              .setPhysicalDisplayId(PHYSICAL_DISPLAY_ID)
+                              .setVsyncPeriod(111111)
+                              .build();
 
     // If the handle is incorrect, the function should return before
     // onModeChange is called.
     Scheduler::ConnectionHandle invalidHandle = {.id = 123};
-    EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
-                                                                       PHYSICAL_DISPLAY_ID, modeId,
-                                                                       vsyncPeriod));
-    EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
+    EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
+    EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
 }
 
 TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
@@ -225,7 +231,9 @@
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
-    mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+    mScheduler->setRefreshRateConfigs(
+            std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120},
+                                                            mode60->getId()));
 
     sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
 
@@ -235,7 +243,7 @@
     mScheduler->setDisplayPowerState(kPowerStateNormal);
 
     constexpr uint32_t kDisplayArea = 999'999;
-    mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
+    mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
     EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1);
     mScheduler->chooseRefreshRateForContent();
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 2761470..a4e9d20 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -479,7 +479,9 @@
             ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
 
     const auto layerHistorySummary =
-            mFlinger.mutableScheduler().mutableLayerHistory()->summarize(0);
+            mFlinger.mutableScheduler()
+                    .mutableLayerHistory()
+                    ->summarize(*mFlinger.mutableScheduler().refreshRateConfigs(), 0);
     ASSERT_EQ(2u, layerHistorySummary.size());
     EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[0].desiredRefreshRate));
     EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[1].desiredRefreshRate));
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
index be01984..fc40818 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -224,6 +224,74 @@
     EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
 }
 
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfFlagsNotChanged) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The display has flags set
+    display.mutableCurrentDisplayState().flags = 1u;
+
+    // The incoming request sets a different layer stack
+    DisplayState state;
+    state.what = DisplayState::eFlagsChanged;
+    state.token = display.token();
+    state.flags = 1u;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags are empty
+    EXPECT_EQ(0u, flags);
+
+    // The desired display state has been set to the new value.
+    EXPECT_EQ(1u, display.getCurrentDisplayState().flags);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfFlagsChanged) {
+    using Case = SimplePrimaryDisplayCase;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // A display is set up
+    auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+    display.inject();
+
+    // The display has a layer stack set
+    display.mutableCurrentDisplayState().flags = 0u;
+
+    // The incoming request sets a different layer stack
+    DisplayState state;
+    state.what = DisplayState::eFlagsChanged;
+    state.token = display.token();
+    state.flags = 1u;
+
+    // --------------------------------------------------------------------
+    // Invocation
+
+    uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // The returned flags indicate a transaction is needed
+    EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+    // The desired display state has been set to the new value.
+    EXPECT_EQ(1u, display.getCurrentDisplayState().flags);
+}
+
 TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
     using Case = SimplePrimaryDisplayCase;
     constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 6502420..eea1002 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -260,6 +260,11 @@
         auto display = Display::makeFakeExistingDisplayInjector(test);
         display.inject();
         display.mutableDisplayDevice()->setPowerMode(mode);
+        if (display.mutableDisplayDevice()->isInternal()) {
+            test->mFlinger.mutableActiveDisplayToken() =
+                    display.mutableDisplayDevice()->getDisplayToken();
+        }
+
         return display;
     }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 41fd6e3..1d21bd4 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -32,16 +32,19 @@
 
 class TestableScheduler : public Scheduler {
 public:
-    TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
+    TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
+                      ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<mock::VsyncController>(),
-                              std::make_unique<mock::VSyncTracker>(), configs, callback) {}
+                              std::make_unique<mock::VSyncTracker>(), refreshRateConfigs,
+                              callback) {}
 
     TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
                       std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
-                      const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
-          : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs,
-                      callback, createLayerHistory(configs),
-                      {.supportKernelTimer = false, .useContentDetection = true}) {}
+                      const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
+                      ISchedulerCallback& callback)
+          : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr},
+                      refreshRateConfigs, callback, createLayerHistory(),
+                      {.useContentDetection = true}) {}
 
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -64,6 +67,8 @@
         return mutableLayerHistory()->mLayerInfos.size();
     }
 
+    auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
+
     size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
         if (!mLayerHistory) return 0;
         return mutableLayerHistory()->mActiveLayersEnd;
@@ -95,9 +100,8 @@
         mFeatures.cachedModeChangedParams.reset();
     }
 
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                        DisplayModeId modeId, nsecs_t vsyncPeriod) {
-        return Scheduler::onNonPrimaryDisplayModeChanged(handle, displayId, modeId, vsyncPeriod);
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+        return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
     ~TestableScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index cf67593..a23361e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -82,8 +82,8 @@
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
-    std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
-                                               ISchedulerCallback&) override {
+    std::unique_ptr<Scheduler> createScheduler(
+            const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override {
         return nullptr;
     }
 
@@ -209,6 +209,7 @@
                         ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) {
         DisplayModes modes{DisplayMode::Builder(0)
                                    .setId(DisplayModeId(0))
+                                   .setPhysicalDisplayId(PhysicalDisplayId(0))
                                    .setVsyncPeriod(16'666'667)
                                    .setGroup(0)
                                    .build()};
@@ -216,25 +217,24 @@
         if (hasMultipleModes) {
             modes.emplace_back(DisplayMode::Builder(1)
                                        .setId(DisplayModeId(1))
+                                       .setPhysicalDisplayId(PhysicalDisplayId(0))
                                        .setVsyncPeriod(11'111'111)
                                        .setGroup(0)
                                        .build());
         }
 
         const auto currMode = DisplayModeId(0);
-        mFlinger->mRefreshRateConfigs =
-                std::make_unique<scheduler::RefreshRateConfigs>(modes, currMode);
-        const auto currFps =
-                mFlinger->mRefreshRateConfigs->getRefreshRateFromModeId(currMode).getFps();
-        mFlinger->mRefreshRateStats =
-                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
-                                                              /*powerMode=*/hal::PowerMode::OFF);
+        mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, currMode);
+        const auto currFps = mRefreshRateConfigs->getCurrentRefreshRate().getFps();
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
+        mFlinger->mRefreshRateStats =
+                std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
+                                                              /*powerMode=*/hal::PowerMode::OFF);
 
         mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                                           *mFlinger->mRefreshRateConfigs, *(callback ?: this));
+                                           mRefreshRateConfigs, *(callback ?: this));
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -435,12 +435,14 @@
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; }
     auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
+    auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; }
 
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
     auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
     auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
+    auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
 
     auto fromHandle(const sp<IBinder>& handle) {
         return mFlinger->fromHandle(handle);
@@ -644,6 +646,7 @@
             DisplayModePtr activeMode =
                     DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
                             .setId(mActiveModeId)
+                            .setPhysicalDisplayId(PhysicalDisplayId(0))
                             .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
                             .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
                             .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
@@ -654,6 +657,7 @@
 
             DisplayModes modes{activeMode};
             mCreationArgs.supportedModes = modes;
+            mCreationArgs.refreshRateConfigs = flinger.mRefreshRateConfigs;
         }
 
         sp<IBinder> token() const { return mDisplayToken; }
@@ -723,7 +727,7 @@
             return *this;
         }
 
-        sp<DisplayDevice> inject() {
+        sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
             const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
 
             DisplayDeviceState state;
@@ -770,6 +774,7 @@
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
     TestableScheduler* mScheduler = nullptr;
+    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 7c431a0..1a50427 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -74,6 +74,7 @@
         EXPECT_CALL(*mVSyncTracker, currentPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
 
+        mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
         mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
                                 std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
                                 std::move(eventThread), std::move(sfEventThread));
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 2845d0a..a749ece 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -119,7 +119,7 @@
                                    FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
         layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache,
                          frameNumber, dequeueTime, FrameTimelineInfo{},
-                         nullptr /* releaseBufferCallback */);
+                         nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint*/);
 
         commitTransaction(layer.get());
         bool computeVisisbleRegions;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 7bf224d..2a7921f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -119,7 +119,8 @@
                                                                  1, 0),
                                                mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         acquireFence->signalForTest(12);
 
         commitTransaction(layer.get());
@@ -147,7 +148,8 @@
                                                                  1, 0),
                                                mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -160,7 +162,8 @@
                                                mRenderEngine, false);
         nsecs_t start = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         nsecs_t end = systemTime();
         acquireFence2->signalForTest(12);
 
@@ -200,7 +203,8 @@
                                                                  1, 0),
                                                mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         acquireFence->signalForTest(12);
 
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -228,7 +232,8 @@
                                                                  1, 0),
                                                mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
 
@@ -260,7 +265,8 @@
                                                                  1, 0),
                                                mRenderEngine, false);
         layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -298,7 +304,8 @@
                                                                  1, 0),
                                                mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
@@ -309,7 +316,8 @@
                                                                  1, 0),
                                                mRenderEngine, false);
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         acquireFence2->signalForTest(12);
 
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -339,7 +347,8 @@
                                                                  1, 0),
                                                mRenderEngine, false);
         layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -353,7 +362,7 @@
         auto dropStartTime1 = systemTime();
         layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
                          {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0},
-                         nullptr /* releaseBufferCallback */);
+                         nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint */);
         auto dropEndTime1 = systemTime();
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -367,7 +376,8 @@
                                                mRenderEngine, false);
         auto dropStartTime2 = systemTime();
         layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
-                         {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */);
+                         {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
+                         nullptr /* releaseBufferEndpoint */);
         auto dropEndTime2 = systemTime();
         acquireFence3->signalForTest(12);
 
@@ -411,7 +421,8 @@
                                                    mRenderEngine, false);
             layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
                              {/*vsyncId*/ 1, /*inputEventId*/ 0},
-                             nullptr /* releaseBufferCallback */);
+                             nullptr /* releaseBufferCallback */,
+                             nullptr /* releaseBufferEndpoint */);
             layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
                                                                   /*inputEventId*/ 0},
                                                                  10);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index cb3bd73..1ba3c0f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -54,8 +54,7 @@
     MOCK_METHOD0(resetCommands, void());
     MOCK_METHOD0(executeCommands, Error());
     MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
-    MOCK_METHOD5(createVirtualDisplay,
-                 Error(uint32_t, uint32_t, PixelFormat*, std::optional<Display>, Display*));
+    MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
     MOCK_METHOD1(destroyVirtualDisplay, Error(Display));
     MOCK_METHOD1(acceptDisplayChanges, Error(Display));
     MOCK_METHOD2(createLayer, Error(Display, Layer* outLayer));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 485b4ac..d25973e 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,7 +33,7 @@
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
     MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
-    MOCK_METHOD3(onModeChanged, void(PhysicalDisplayId, DisplayModeId, nsecs_t));
+    MOCK_METHOD1(onModeChanged, void(DisplayModePtr));
     MOCK_METHOD2(onFrameRateOverridesChanged,
                  void(PhysicalDisplayId, std::vector<FrameRateOverride>));
     MOCK_CONST_METHOD1(dump, void(std::string&));
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h
index 459b35c..f4a3425 100644
--- a/services/surfaceflinger/tests/utils/CallbackUtils.h
+++ b/services/surfaceflinger/tests/utils/CallbackUtils.h
@@ -20,8 +20,12 @@
 #include <gui/SurfaceControl.h>
 #include <ui/Fence.h>
 #include <utils/Timers.h>
+#include <chrono>
 #include <thread>
 
+using ::std::literals::chrono_literals::operator""ms;
+using ::std::literals::chrono_literals::operator""s;
+
 namespace android {
 
 namespace {