Merge "Vulkan: update the loader for vulkan-headers v1.2.183"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 25e6dc9..4eb601d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -176,6 +176,7 @@
 #define LINKERCONFIG_DIR "/linkerconfig"
 #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
 #define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
+#define CGROUPFS_DIR "/sys/fs/cgroup"
 
 // TODO(narayan): Since this information has to be kept in sync
 // with tombstoned, we should just put it in a common header.
@@ -1785,6 +1786,9 @@
     // Add linker configuration directory
     ds.AddDir(LINKERCONFIG_DIR, true);
 
+    /* Dump cgroupfs */
+    ds.AddDir(CGROUPFS_DIR, true);
+
     if (ds.dump_pool_) {
         WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
     } else {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 59311a0..74be7ce 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -342,8 +342,7 @@
 
     // If the initial top-level restorecon above changed the label, then go
     // back and restorecon everything recursively
-    // TODO(b/190567190, b/188141923) Remove recursive fixup of com.google.android.gsf.
-    if (strcmp(before, after) || (path.find("com.google.android.gsf") != std::string::npos)) {
+    if (strcmp(before, after)) {
         if (existing) {
             LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
                     << path << "; running recursive restorecon";
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index d6807ff..643b2c2 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -4,7 +4,6 @@
 jsharkey@android.com
 maco@google.com
 mast@google.com
-mathieuc@google.com
 narayan@google.com
 ngeoffray@google.com
 rpl@google.com
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 15f0c5b..9128542 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1231,6 +1231,14 @@
             }
         }
 
+        // On-device signing related. odsign sets the system property odsign.verification.success if
+        // AOT artifacts have the expected signatures.
+        const bool trust_art_apex_data_files =
+                ::android::base::GetBoolProperty("odsign.verification.success", false);
+        if (!trust_art_apex_data_files) {
+            AddRuntimeArg("-Xdeny-art-apex-data-files");
+        }
+
         PrepareArgs(dexoptanalyzer_bin);
     }
 
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index e847626..b661684 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -283,6 +283,13 @@
         }
     }
 
+    // On-device signing related. odsign sets the system property odsign.verification.success if
+    // AOT artifacts have the expected signatures.
+    const bool trust_art_apex_data_files = GetBoolProperty("odsign.verification.success", false);
+    if (!trust_art_apex_data_files) {
+        AddRuntimeArg("-Xdeny-art-apex-data-files");
+    }
+
     if (target_sdk_version != 0) {
         AddRuntimeArg(StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version));
     }
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 6562348..f50ff46 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 {
 
@@ -124,5 +123,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 438121b..b09ff48 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -43,6 +43,13 @@
  * Additional private constants not defined in ndk/ui/input.h.
  */
 enum {
+#ifdef __linux__
+    /* This event was generated or modified by accessibility service. */
+    AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
+            android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, // 0x800,
+#else
+    AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800,
+#endif
     /* Signifies that the key is being predispatched */
     AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000,
 
@@ -81,6 +88,16 @@
      */
     AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40,
 
+#ifdef __linux__
+    /**
+     * This event was generated or modified by accessibility service.
+     */
+    AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
+            android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, // 0x800,
+#else
+    AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800,
+#endif
+
     /* Motion event is inconsistent with previously sent motion events. */
     AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
 };
@@ -89,14 +106,15 @@
  * Allowed VerifiedKeyEvent flags. All other flags from KeyEvent do not get verified.
  * These values must be kept in sync with VerifiedKeyEvent.java
  */
-constexpr int32_t VERIFIED_KEY_EVENT_FLAGS = AKEY_EVENT_FLAG_CANCELED;
+constexpr int32_t VERIFIED_KEY_EVENT_FLAGS =
+        AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
 
 /**
  * Allowed VerifiedMotionEventFlags. All other flags from MotionEvent do not get verified.
  * These values must be kept in sync with VerifiedMotionEvent.java
  */
-constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS =
-        AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+        AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED | AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
 
 /**
  * This flag indicates that the point up event has been canceled.
@@ -106,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
@@ -222,16 +231,14 @@
     POLICY_FLAG_GESTURE = 0x00000008,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
-#ifdef __linux__
-    POLICY_FLAG_INPUTFILTER_TRUSTED = android::os::IInputConstants::POLICY_FLAG_INPUTFILTER_TRUSTED,
 
+#ifdef __linux__
     POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY =
             android::os::IInputConstants::POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
 #else
-    POLICY_FLAG_INPUTFILTER_TRUSTED = 0x10000,
-
     POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000,
 #endif
+
     /* These flags are set by the input dispatcher. */
 
     // Indicates that the input event was injected.
@@ -338,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.
  */
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 1955104..7f0324a 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -318,8 +318,6 @@
         const std::string& name, InputDeviceConfigurationFileType type);
 
 enum ReservedInputDeviceId : int32_t {
-    // Device id assigned to input events generated inside accessibility service
-    ACCESSIBILITY_DEVICE_ID = -2,
     // Device id of a special "virtual" keyboard that is always present.
     VIRTUAL_KEYBOARD_ID = -1,
     // Device id of the "built-in" keyboard if there is one.
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 0951dfe..46292d3 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -148,6 +148,7 @@
                 "libbase",
             ],
             srcs: [
+                "ServiceManagerHost.cpp",
                 "UtilsHost.cpp",
             ],
         },
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 02321cd..628381c 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -179,6 +179,17 @@
     return transact(SET_RPC_CLIENT_TRANSACTION, data, &reply);
 }
 
+void IBinder::withLock(const std::function<void()>& doWithLock) {
+    BBinder* local = localBinder();
+    if (local) {
+        local->withLock(doWithLock);
+        return;
+    }
+    BpBinder* proxy = this->remoteBinder();
+    LOG_ALWAYS_FATAL_IF(proxy == nullptr, "binder object must be either local or remote");
+    proxy->withLock(doWithLock);
+}
+
 // ---------------------------------------------------------------------------
 
 class BBinder::RpcServerLink : public IBinder::DeathRecipient {
@@ -311,15 +322,13 @@
     return NO_ERROR;
 }
 
-void BBinder::attachObject(
-    const void* objectID, void* object, void* cleanupCookie,
-    object_cleanup_func func)
-{
+void* BBinder::attachObject(const void* objectID, void* object, void* cleanupCookie,
+                            object_cleanup_func func) {
     Extras* e = getOrCreateExtras();
-    if (!e) return; // out of memory
+    LOG_ALWAYS_FATAL_IF(!e, "no memory");
 
     AutoMutex _l(e->mLock);
-    e->mObjects.attach(objectID, object, cleanupCookie, func);
+    return e->mObjects.attach(objectID, object, cleanupCookie, func);
 }
 
 void* BBinder::findObject(const void* objectID) const
@@ -331,13 +340,20 @@
     return e->mObjects.find(objectID);
 }
 
-void BBinder::detachObject(const void* objectID)
-{
+void* BBinder::detachObject(const void* objectID) {
     Extras* e = mExtras.load(std::memory_order_acquire);
-    if (!e) return;
+    if (!e) return nullptr;
 
     AutoMutex _l(e->mLock);
-    e->mObjects.detach(objectID);
+    return e->mObjects.detach(objectID);
+}
+
+void BBinder::withLock(const std::function<void()>& doWithLock) {
+    Extras* e = getOrCreateExtras();
+    LOG_ALWAYS_FATAL_IF(!e, "no memory");
+
+    AutoMutex _l(e->mLock);
+    doWithLock();
 }
 
 BBinder* BBinder::localBinder()
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 5e44a0f..3099296 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -61,22 +61,22 @@
     kill();
 }
 
-void BpBinder::ObjectManager::attach(
-    const void* objectID, void* object, void* cleanupCookie,
-    IBinder::object_cleanup_func func)
-{
+void* BpBinder::ObjectManager::attach(const void* objectID, void* object, void* cleanupCookie,
+                                      IBinder::object_cleanup_func func) {
     entry_t e;
     e.object = object;
     e.cleanupCookie = cleanupCookie;
     e.func = func;
 
-    if (mObjects.indexOfKey(objectID) >= 0) {
-        ALOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use",
-                objectID, this,  object);
-        return;
+    if (ssize_t idx = mObjects.indexOfKey(objectID); idx >= 0) {
+        ALOGI("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object "
+              "ID already in use",
+              objectID, this, object);
+        return mObjects[idx].object;
     }
 
     mObjects.add(objectID, e);
+    return nullptr;
 }
 
 void* BpBinder::ObjectManager::find(const void* objectID) const
@@ -86,9 +86,12 @@
     return mObjects.valueAt(i).object;
 }
 
-void BpBinder::ObjectManager::detach(const void* objectID)
-{
-    mObjects.removeItem(objectID);
+void* BpBinder::ObjectManager::detach(const void* objectID) {
+    ssize_t idx = mObjects.indexOfKey(objectID);
+    if (idx < 0) return nullptr;
+    void* value = mObjects[idx].object;
+    mObjects.removeItemsAt(idx, 1);
+    return value;
 }
 
 void BpBinder::ObjectManager::kill()
@@ -406,14 +409,11 @@
     recipient->binderDied(wp<BpBinder>::fromExisting(this));
 }
 
-
-void BpBinder::attachObject(
-    const void* objectID, void* object, void* cleanupCookie,
-    object_cleanup_func func)
-{
+void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie,
+                             object_cleanup_func func) {
     AutoMutex _l(mLock);
     ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
-    mObjects.attach(objectID, object, cleanupCookie, func);
+    return mObjects.attach(objectID, object, cleanupCookie, func);
 }
 
 void* BpBinder::findObject(const void* objectID) const
@@ -422,10 +422,14 @@
     return mObjects.find(objectID);
 }
 
-void BpBinder::detachObject(const void* objectID)
-{
+void* BpBinder::detachObject(const void* objectID) {
     AutoMutex _l(mLock);
-    mObjects.detach(objectID);
+    return mObjects.detach(objectID);
+}
+
+void BpBinder::withLock(const std::function<void()>& doWithLock) {
+    AutoMutex _l(mLock);
+    doWithLock();
 }
 
 BpBinder* BpBinder::remoteBinder()
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index d421060..2175fd4 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -35,6 +35,8 @@
 
 #ifdef __ANDROID__
 #include <cutils/properties.h>
+#else
+#include "ServiceManagerHost.h"
 #endif
 
 #include "Static.h"
@@ -84,8 +86,19 @@
     IBinder* onAsBinder() override {
         return IInterface::asBinder(mTheRealServiceManager).get();
     }
-private:
+
+protected:
     sp<AidlServiceManager> mTheRealServiceManager;
+
+    // Directly get the service in a way that, for lazy services, requests the service to be started
+    // if it is not currently started. This way, calls directly to ServiceManagerShim::getService
+    // will still have the 5s delay that is expected by a large amount of Android code.
+    //
+    // When implementing ServiceManagerShim, use realGetService instead of
+    // mTheRealServiceManager->getService so that it can be overridden in ServiceManagerHostShim.
+    virtual Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
+        return mTheRealServiceManager->getService(name, _aidl_return);
+    }
 };
 
 [[clang::no_destroy]] static std::once_flag gSmOnce;
@@ -319,7 +332,7 @@
     const std::string name = String8(name16).c_str();
 
     sp<IBinder> out;
-    if (Status status = mTheRealServiceManager->getService(name, &out); !status.isOk()) {
+    if (Status status = realGetService(name, &out); !status.isOk()) {
         ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(),
               status.toString8().c_str());
         return nullptr;
@@ -363,7 +376,7 @@
         // - init gets death signal, but doesn't know it needs to restart
         //   the service
         // - we need to request service again to get it to start
-        if (Status status = mTheRealServiceManager->getService(name, &out); !status.isOk()) {
+        if (Status status = realGetService(name, &out); !status.isOk()) {
             ALOGW("Failed to getService in waitForService on later try for %s: %s", name.c_str(),
                   status.toString8().c_str());
             return nullptr;
@@ -412,4 +425,38 @@
     return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
 }
 
+#ifndef __ANDROID__
+// ServiceManagerShim for host. Implements the old libbinder android::IServiceManager API.
+// The internal implementation of the AIDL interface android::os::IServiceManager calls into
+// on-device service manager.
+class ServiceManagerHostShim : public ServiceManagerShim {
+public:
+    using ServiceManagerShim::ServiceManagerShim;
+    // ServiceManagerShim::getService is based on checkService, so no need to override it.
+    sp<IBinder> checkService(const String16& name) const override {
+        return getDeviceService({String8(name).c_str()});
+    }
+
+protected:
+    // Override realGetService for ServiceManagerShim::waitForService.
+    Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
+        *_aidl_return = getDeviceService({"-g", name});
+        return Status::ok();
+    }
+};
+sp<IServiceManager> createRpcDelegateServiceManager() {
+    auto binder = getDeviceService({"manager"});
+    if (binder == nullptr) {
+        ALOGE("getDeviceService(\"manager\") returns null");
+        return nullptr;
+    }
+    auto interface = AidlServiceManager::asInterface(binder);
+    if (interface == nullptr) {
+        ALOGE("getDeviceService(\"manager\") returns non service manager");
+        return nullptr;
+    }
+    return sp<ServiceManagerHostShim>::make(interface);
+}
+#endif
+
 } // namespace android
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 6165911..f66993f 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -40,9 +40,9 @@
 
     void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
 
-    bool tryUnregister();
+    bool tryUnregisterLocked();
 
-    void reRegister();
+    void reRegisterLocked();
 
 protected:
     Status onClients(const sp<IBinder>& service, bool clients) override;
@@ -59,6 +59,9 @@
         bool registered = true;
     };
 
+    bool registerServiceLocked(const sp<IBinder>& service, const std::string& name,
+                               bool allowIsolated, int dumpFlags);
+
     /**
      * Looks up a service guaranteed to be registered (service from onClients).
      */
@@ -68,7 +71,7 @@
      * Unregisters all services that we can. If we can't unregister all, re-register other
      * services.
      */
-    void tryShutdown();
+    void tryShutdownLocked();
 
     /**
      * Try to shutdown the process, unless:
@@ -76,7 +79,10 @@
      * - The active services count callback returns 'true', or
      * - Some services have clients.
      */
-    void maybeTryShutdown();
+    void maybeTryShutdownLocked();
+
+    // for below
+    std::mutex mMutex;
 
     // count of services with clients
     size_t mNumConnectedServices;
@@ -117,6 +123,13 @@
 
 bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, const std::string& name,
                                             bool allowIsolated, int dumpFlags) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return registerServiceLocked(service, name, allowIsolated, dumpFlags);
+}
+
+bool ClientCounterCallbackImpl::registerServiceLocked(const sp<IBinder>& service,
+                                                      const std::string& name, bool allowIsolated,
+                                                      int dumpFlags) {
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     bool reRegister = mRegisteredServices.count(name) > 0;
@@ -164,14 +177,15 @@
 }
 
 void ClientCounterCallbackImpl::forcePersist(bool persist) {
+    std::lock_guard<std::mutex> lock(mMutex);
     mForcePersist = persist;
     if (!mForcePersist) {
         // Attempt a shutdown in case the number of clients hit 0 while the flag was on
-        maybeTryShutdown();
+        maybeTryShutdownLocked();
     }
 }
 
-bool ClientCounterCallbackImpl::tryUnregister() {
+bool ClientCounterCallbackImpl::tryUnregisterLocked() {
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     for (auto& [name, entry] : mRegisteredServices) {
@@ -187,15 +201,14 @@
     return true;
 }
 
-void ClientCounterCallbackImpl::reRegister() {
+void ClientCounterCallbackImpl::reRegisterLocked() {
     for (auto& [name, entry] : mRegisteredServices) {
         // re-register entry if not already registered
         if (entry.registered) {
             continue;
         }
 
-        if (!registerService(entry.service, name, entry.allowIsolated,
-                             entry.dumpFlags)) {
+        if (!registerServiceLocked(entry.service, name, entry.allowIsolated, entry.dumpFlags)) {
             // Must restart. Otherwise, clients will never be able to get a hold of this service.
             LOG_ALWAYS_FATAL("Bad state: could not re-register services");
         }
@@ -204,7 +217,7 @@
     }
 }
 
-void ClientCounterCallbackImpl::maybeTryShutdown() {
+void ClientCounterCallbackImpl::maybeTryShutdownLocked() {
     if (mForcePersist) {
         ALOGI("Shutdown prevented by forcePersist override flag.");
         return;
@@ -223,15 +236,12 @@
     // client count change event, try to shutdown the process if its services
     // have no clients.
     if (!handledInCallback && mNumConnectedServices == 0) {
-        tryShutdown();
+        tryShutdownLocked();
     }
 }
 
-/**
- * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
- * invocations could occur on different threads however.
- */
 Status ClientCounterCallbackImpl::onClients(const sp<IBinder>& service, bool clients) {
+    std::lock_guard<std::mutex> lock(mMutex);
     auto & [name, registered] = *assertRegisteredService(service);
     if (registered.clients == clients) {
         LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
@@ -252,23 +262,24 @@
     ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
           mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients);
 
-    maybeTryShutdown();
+    maybeTryShutdownLocked();
     return Status::ok();
 }
 
- void ClientCounterCallbackImpl::tryShutdown() {
-     ALOGI("Trying to shut down the service. No clients in use for any service in process.");
+void ClientCounterCallbackImpl::tryShutdownLocked() {
+    ALOGI("Trying to shut down the service. No clients in use for any service in process.");
 
-    if (tryUnregister()) {
-         ALOGI("Unregistered all clients and exiting");
-         exit(EXIT_SUCCESS);
-     }
+    if (tryUnregisterLocked()) {
+        ALOGI("Unregistered all clients and exiting");
+        exit(EXIT_SUCCESS);
+    }
 
-    reRegister();
+    reRegisterLocked();
 }
 
 void ClientCounterCallbackImpl::setActiveServicesCallback(const std::function<bool(bool)>&
                                                           activeServicesCallback) {
+    std::lock_guard<std::mutex> lock(mMutex);
     mActiveServicesCallback = activeServicesCallback;
 }
 
@@ -291,11 +302,15 @@
 }
 
 bool ClientCounterCallback::tryUnregister() {
-    return mImpl->tryUnregister();
+    // see comments in header, this should only be called from the active
+    // services callback, see also b/191781736
+    return mImpl->tryUnregisterLocked();
 }
 
 void ClientCounterCallback::reRegister() {
-    mImpl->reRegister();
+    // see comments in header, this should only be called from the active
+    // services callback, see also b/191781736
+    mImpl->reRegisterLocked();
 }
 
 }  // namespace internal
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index a8f3fa8..2a87ae4 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -278,7 +278,7 @@
                 return;
             }
 
-            RpcAddress sessionId = RpcAddress::zero();
+            sessionId = RpcAddress::zero();
             size_t tries = 0;
             do {
                 // don't block if there is some entropy issue
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 4f55eef..ee5e8bb 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -198,6 +198,8 @@
     uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
     uint8_t* end = buffer + size;
 
+    MAYBE_WAIT_IN_FLAKE_MODE;
+
     status_t status;
     while ((status = triggerablePollRead(fd)) == OK) {
         ssize_t readSize = TEMP_FAILURE_RETRY(recv(fd.get(), buffer, end - buffer, MSG_NOSIGNAL));
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index fd2eff6..b5eaaa3 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -26,12 +26,28 @@
 #include "Debug.h"
 #include "RpcWireFormat.h"
 
+#include <random>
+
 #include <inttypes.h>
 
 namespace android {
 
 using base::ScopeGuard;
 
+#ifdef RPC_FLAKE_PRONE
+void rpcMaybeWaitToFlake() {
+    static std::random_device r;
+    static std::mutex m;
+
+    unsigned num;
+    {
+        std::lock_guard<std::mutex> lock(m);
+        num = r();
+    }
+    if (num % 10 == 0) usleep(num % 1000);
+}
+#endif
+
 RpcState::RpcState() {}
 RpcState::~RpcState() {}
 
@@ -260,6 +276,8 @@
     LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, connection->fd.get(),
                    hexString(data, size).c_str());
 
+    MAYBE_WAIT_IN_FLAKE_MODE;
+
     if (size > std::numeric_limits<ssize_t>::max()) {
         ALOGE("Cannot send %s at size %zu (too big)", what, size);
         (void)session->shutdownAndWait(false);
@@ -483,7 +501,7 @@
     delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
     (void)dataSize;
     LOG_ALWAYS_FATAL_IF(objects != nullptr);
-    LOG_ALWAYS_FATAL_IF(objectsCount, 0);
+    LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount);
 }
 
 status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 529dee5..8201eba 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -42,6 +42,15 @@
 #define LOG_RPC_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
 #endif
 
+#define RPC_FLAKE_PRONE false
+
+#ifdef RPC_FLAKE_PRONE
+void rpcMaybeWaitToFlake();
+#define MAYBE_WAIT_IN_FLAKE_MODE rpcMaybeWaitToFlake()
+#else
+#define MAYBE_WAIT_IN_FLAKE_MODE do {} while (false)
+#endif
+
 /**
  * Abstracts away management of ref counts and the wire format from
  * RpcSession
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
new file mode 100644
index 0000000..1c2f9b4
--- /dev/null
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+#include "ServiceManagerHost.h"
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <binder/IServiceManager.h>
+#include <binder/RpcSession.h>
+
+#include "UtilsHost.h"
+
+namespace android {
+
+namespace {
+
+const void* kDeviceServiceExtraId = "DeviceServiceExtra";
+
+// Parse stdout of program execution to string. If any error, return 0.
+unsigned int parsePortNumber(const std::string& out, const std::string& what) {
+    auto trimmed = android::base::Trim(out);
+    unsigned int port = 0;
+    if (!android::base::ParseUint(trimmed, &port)) {
+        int savedErrno = errno;
+        ALOGE("%s is not a valid %s: %s", trimmed.c_str(), what.c_str(), strerror(savedErrno));
+        return 0;
+    }
+    if (port == 0) {
+        ALOGE("0 is not a valid %s", what.c_str());
+        return 0; // explicitly
+    }
+    return port;
+}
+
+// RAII object for adb forwarding
+class AdbForwarder {
+public:
+    AdbForwarder() = default;
+    static std::optional<AdbForwarder> forward(unsigned int devicePort);
+    AdbForwarder(AdbForwarder&& other) noexcept { (*this) = std::move(other); }
+    AdbForwarder& operator=(AdbForwarder&&) noexcept;
+    ~AdbForwarder();
+    [[nodiscard]] const std::optional<unsigned int>& hostPort() const { return mPort; }
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(AdbForwarder);
+    explicit AdbForwarder(unsigned int port) : mPort(port) {}
+    std::optional<unsigned int> mPort;
+};
+std::optional<AdbForwarder> AdbForwarder::forward(unsigned int devicePort) {
+    auto result =
+            execute({"adb", "forward", "tcp:0", "tcp:" + std::to_string(devicePort)}, nullptr);
+    if (!result.ok()) {
+        ALOGE("Unable to run `adb forward tcp:0 tcp:%d`: %s", devicePort,
+              result.error().message().c_str());
+        return std::nullopt;
+    }
+    // Must end with exit code 0 (`has_value() && value() == 0`)
+    if (result->exitCode.value_or(1) != 0) {
+        ALOGE("Unable to run `adb forward tcp:0 tcp:%d`, command exits with %s", devicePort,
+              result->toString().c_str());
+        return std::nullopt;
+    }
+    if (!result->stderr.empty()) {
+        LOG_HOST("`adb forward tcp:0 tcp:%d` writes to stderr: %s", devicePort,
+                 result->stderr.c_str());
+    }
+
+    unsigned int hostPort = parsePortNumber(result->stdout, "host port");
+    if (hostPort == 0) return std::nullopt;
+
+    return AdbForwarder(hostPort);
+}
+
+AdbForwarder& AdbForwarder::operator=(AdbForwarder&& other) noexcept {
+    std::swap(mPort, other.mPort);
+    return *this;
+}
+
+AdbForwarder::~AdbForwarder() {
+    if (!mPort.has_value()) return;
+
+    auto result = execute({"adb", "forward", "--remove", "tcp:" + std::to_string(*mPort)}, nullptr);
+    if (!result.ok()) {
+        ALOGE("Unable to run `adb forward --remove tcp:%d`: %s", *mPort,
+              result.error().message().c_str());
+        return;
+    }
+    // Must end with exit code 0 (`has_value() && value() == 0`)
+    if (result->exitCode.value_or(1) != 0) {
+        ALOGE("Unable to run `adb forward --remove tcp:%d`, command exits with %s", *mPort,
+              result->toString().c_str());
+        return;
+    }
+    if (!result->stderr.empty()) {
+        LOG_HOST("`adb forward --remove tcp:%d` writes to stderr: %s", *mPort,
+                 result->stderr.c_str());
+    }
+
+    LOG_HOST("Successfully run `adb forward --remove tcp:%d`", *mPort);
+}
+
+void cleanupCommandResult(const void* id, void* obj, void* /* cookie */) {
+    LOG_ALWAYS_FATAL_IF(id != kDeviceServiceExtraId,
+                        "cleanupCommandResult invoked with mismatched ID %p, "
+                        "expected %p",
+                        id, kDeviceServiceExtraId);
+    auto ptr = static_cast<CommandResult*>(obj);
+    delete ptr;
+}
+
+} // namespace
+
+sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs) {
+    std::vector<std::string> prefix{"adb", "shell", "servicedispatcher"};
+    serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end());
+
+    auto result = execute(std::move(serviceDispatcherArgs), &CommandResult::stdoutEndsWithNewLine);
+    if (!result.ok()) {
+        ALOGE("%s", result.error().message().c_str());
+        return nullptr;
+    }
+
+    // `servicedispatcher` process must be alive to keep the port open.
+    if (result->exitCode.has_value()) {
+        ALOGE("Command exits with: %s", result->toString().c_str());
+        return nullptr;
+    }
+    if (!result->stderr.empty()) {
+        LOG_HOST("servicedispatcher writes to stderr: %s", result->stderr.c_str());
+    }
+
+    if (!result->stdoutEndsWithNewLine()) {
+        ALOGE("Unexpected command result: %s", result->toString().c_str());
+        return nullptr;
+    }
+
+    unsigned int devicePort = parsePortNumber(result->stdout, "device port");
+    if (devicePort == 0) return nullptr;
+
+    auto forwardResult = AdbForwarder::forward(devicePort);
+    if (!forwardResult.has_value()) {
+        return nullptr;
+    }
+    LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
+
+    auto rpcSession = RpcSession::make();
+    if (!rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort())) {
+        ALOGE("Unable to set up inet client on host port %u", *forwardResult->hostPort());
+        return nullptr;
+    }
+    auto binder = rpcSession->getRootObject();
+    if (binder == nullptr) {
+        ALOGE("RpcSession::getRootObject returns nullptr");
+        return nullptr;
+    }
+
+    LOG_ALWAYS_FATAL_IF(
+            nullptr !=
+            binder->attachObject(kDeviceServiceExtraId,
+                                 static_cast<void*>(new CommandResult(std::move(*result))), nullptr,
+                                 &cleanupCommandResult));
+    return binder;
+}
+
+} // namespace android
diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h
new file mode 100644
index 0000000..e59724c
--- /dev/null
+++ b/libs/binder/ServiceManagerHost.h
@@ -0,0 +1,31 @@
+/*
+ * 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 <android-base/macros.h>
+#include <android/os/IServiceManager.h>
+
+namespace android {
+
+// Get a service on device by running servicedispatcher with the given args, e.g.
+//     getDeviceService({"foo"});
+// Return nullptr on any error.
+// When the returned binder object is destroyed, remove adb forwarding and kills
+// the long-running servicedispatcher process.
+sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs);
+
+} // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index e04e326..9c5ce67 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -16,10 +16,13 @@
       "name": "binderDriverInterfaceTest"
     },
     {
+      "name": "binderHostDeviceTest"
+    },
+    {
       "name": "binderTextOutputTest"
     },
     {
-      "name": "binderParcelTest"
+      "name": "binderUnitTest"
     },
     {
       "name": "binderLibTest"
@@ -55,6 +58,9 @@
       "name": "CtsOsTestCases",
       "options": [
         {
+          "exclude-annotation": "android.platform.test.annotations.LargeTest"
+        },
+        {
           "exclude-filter": "android.os.cts.BuildTest#testSdkInt"
         },
         {
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 472e546..46223bb 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -54,12 +54,11 @@
                                         uint32_t flags = 0,
                                         wp<DeathRecipient>* outRecipient = nullptr);
 
-    virtual void        attachObject(   const void* objectID,
-                                        void* object,
-                                        void* cleanupCookie,
-                                        object_cleanup_func func) final;
+    virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie,
+                               object_cleanup_func func) final;
     virtual void*       findObject(const void* objectID) const final;
-    virtual void        detachObject(const void* objectID) final;
+    virtual void* detachObject(const void* objectID) final;
+    void withLock(const std::function<void()>& doWithLock);
 
     virtual BBinder*    localBinder();
 
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 61bf018..c69bb9e 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -72,12 +72,11 @@
                                         uint32_t flags = 0,
                                         wp<DeathRecipient>* outRecipient = nullptr);
 
-    virtual void        attachObject(   const void* objectID,
-                                        void* object,
-                                        void* cleanupCookie,
-                                        object_cleanup_func func) final;
+    virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie,
+                               object_cleanup_func func) final;
     virtual void*       findObject(const void* objectID) const final;
-    virtual void        detachObject(const void* objectID) final;
+    virtual void* detachObject(const void* objectID) final;
+    void withLock(const std::function<void()>& doWithLock);
 
     virtual BpBinder*   remoteBinder();
 
@@ -91,27 +90,23 @@
     static void         setLimitCallback(binder_proxy_limit_callback cb);
     static void         setBinderProxyCountWatermarks(int high, int low);
 
-    class ObjectManager
-    {
+    class ObjectManager {
     public:
-                    ObjectManager();
-                    ~ObjectManager();
+        ObjectManager();
+        ~ObjectManager();
 
-        void        attach( const void* objectID,
-                            void* object,
-                            void* cleanupCookie,
-                            IBinder::object_cleanup_func func);
-        void*       find(const void* objectID) const;
-        void        detach(const void* objectID);
+        void* attach(const void* objectID, void* object, void* cleanupCookie,
+                     IBinder::object_cleanup_func func);
+        void* find(const void* objectID) const;
+        void* detach(const void* objectID);
 
-        void        kill();
+        void kill();
 
     private:
-                    ObjectManager(const ObjectManager&);
+        ObjectManager(const ObjectManager&);
         ObjectManager& operator=(const ObjectManager&);
 
-        struct entry_t
-        {
+        struct entry_t {
             void* object;
             void* cleanupCookie;
             IBinder::object_cleanup_func func;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index f9cdac7..43fc5ff 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -22,6 +22,8 @@
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
+#include <functional>
+
 // linux/binder.h defines this, but we don't want to include it here in order to
 // avoid exporting the kernel headers
 #ifndef B_PACK_CHARS
@@ -255,26 +257,30 @@
      * objects are invoked with their respective objectID, object, and
      * cleanupCookie. Access to these APIs can be made from multiple threads,
      * but calls from different threads are allowed to be interleaved.
+     *
+     * This returns the object which is already attached. If this returns a
+     * non-null value, it means that attachObject failed (a given objectID can
+     * only be used once).
      */
-    virtual void            attachObject(   const void* objectID,
-                                            void* object,
-                                            void* cleanupCookie,
-                                            object_cleanup_func func) = 0;
+    [[nodiscard]] virtual void* attachObject(const void* objectID, void* object,
+                                             void* cleanupCookie, object_cleanup_func func) = 0;
     /**
      * Returns object attached with attachObject.
      */
-    virtual void*           findObject(const void* objectID) const = 0;
+    [[nodiscard]] virtual void* findObject(const void* objectID) const = 0;
     /**
-     * WARNING: this API does not call the cleanup function for legacy reasons.
-     * It also does not return void* for legacy reasons. If you need to detach
-     * an object and destroy it, there are two options:
-     * - if you can, don't call detachObject and instead wait for the destructor
-     *   to clean it up.
-     * - manually retrieve and destruct the object (if multiple of your threads
-     *   are accessing these APIs, you must guarantee that attachObject isn't
-     *   called after findObject and before detachObject is called).
+     * Returns object attached with attachObject, and detaches it. This does not
+     * delete the object.
      */
-    virtual void            detachObject(const void* objectID) = 0;
+    [[nodiscard]] virtual void* detachObject(const void* objectID) = 0;
+
+    /**
+     * Use the lock that this binder contains internally. For instance, this can
+     * be used to modify an attached object without needing to add an additional
+     * lock (though, that attached object must be retrieved before calling this
+     * method). Calling (most) IBinder methods inside this will deadlock.
+     */
+    void withLock(const std::function<void()>& doWithLock);
 
     virtual BBinder*        localBinder();
     virtual BpBinder*       remoteBinder();
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 3dbe2c4..8e46147 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -169,4 +169,17 @@
                             int32_t* outPid, int32_t* outUid);
 bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
 
+#ifndef __ANDROID__
+// Create an IServiceManager that delegates the service manager on the device via adb.
+// This is can be set as the default service manager at program start, so that
+// defaultServiceManager() returns it:
+//    int main() {
+//        setDefaultServiceManager(createRpcDelegateServiceManager());
+//        auto sm = defaultServiceManager();
+//        // ...
+//    }
+// Resources are cleaned up when the object is destroyed.
+sp<IServiceManager> createRpcDelegateServiceManager();
+#endif
+
 } // namespace android
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index f3ba830..2e22b84 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -79,9 +79,10 @@
       */
      void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
 
-    /**
+     /**
       * Try to unregister all services previously registered with 'registerService'.
-      * Returns 'true' if successful.
+      * Returns 'true' if successful. This should only be called within the callback registered by
+      * setActiveServicesCallback.
       */
      bool tryUnregister();
 
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 9e4475c..42c85f9 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -42,6 +42,7 @@
         }
         mStability = other.mStability;
     }
+    ParcelableHolder(ParcelableHolder&& other) = default;
 
     status_t writeToParcel(Parcel* parcel) const override;
     status_t readFromParcel(const Parcel* parcel) override;
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 1dcb41b..785e032 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -47,7 +47,8 @@
 void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */};
 
 static void attach(const sp<IBinder>& binder) {
-    binder->attachObject(kId, kValue, nullptr /*cookie*/, clean);
+    // can only attach once
+    CHECK_EQ(nullptr, binder->attachObject(kId, kValue, nullptr /*cookie*/, clean));
 }
 static bool has(const sp<IBinder>& binder) {
     return binder != nullptr && binder->findObject(kId) == kValue;
@@ -57,7 +58,6 @@
 
 namespace ABpBinderTag {
 
-static std::mutex gLock;
 static const void* kId = "ABpBinder";
 struct Value {
     wp<ABpBinder> binder;
@@ -232,19 +232,16 @@
 ABpBinder::~ABpBinder() {}
 
 void ABpBinder::onLastStrongRef(const void* id) {
-    {
-        std::lock_guard<std::mutex> lock(ABpBinderTag::gLock);
-        // Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for
-        // the ABpBinder to be deleted. Since a strong reference to this ABpBinder object should no
-        // longer be able to exist at the time of this method call, there is no longer a need to
-        // recover it.
+    // Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for
+    // the ABpBinder to be deleted. Even though we have no more references on the ABpBinder
+    // (BpRefBase), the remote object may still exist (for instance, if we
+    // receive it from another process, before the ABpBinder is attached).
 
-        ABpBinderTag::Value* value =
-                static_cast<ABpBinderTag::Value*>(remote()->findObject(ABpBinderTag::kId));
-        if (value != nullptr) {
-            value->binder = nullptr;
-        }
-    }
+    ABpBinderTag::Value* value =
+            static_cast<ABpBinderTag::Value*>(remote()->findObject(ABpBinderTag::kId));
+    CHECK_NE(nullptr, value) << "ABpBinder must always be attached";
+
+    remote()->withLock([&]() { value->binder = nullptr; });
 
     BpRefBase::onLastStrongRef(id);
 }
@@ -259,21 +256,29 @@
 
     // The following code ensures that for a given binder object (remote or local), if it is not an
     // ABBinder then at most one ABpBinder object exists in a given process representing it.
-    std::lock_guard<std::mutex> lock(ABpBinderTag::gLock);
 
-    ABpBinderTag::Value* value =
-            static_cast<ABpBinderTag::Value*>(binder->findObject(ABpBinderTag::kId));
+    auto* value = static_cast<ABpBinderTag::Value*>(binder->findObject(ABpBinderTag::kId));
     if (value == nullptr) {
         value = new ABpBinderTag::Value;
-        binder->attachObject(ABpBinderTag::kId, static_cast<void*>(value), nullptr /*cookie*/,
-                             ABpBinderTag::clean);
+        auto oldValue = static_cast<ABpBinderTag::Value*>(
+                binder->attachObject(ABpBinderTag::kId, static_cast<void*>(value),
+                                     nullptr /*cookie*/, ABpBinderTag::clean));
+
+        // allocated by another thread
+        if (oldValue) {
+            delete value;
+            value = oldValue;
+        }
     }
 
-    sp<ABpBinder> ret = value->binder.promote();
-    if (ret == nullptr) {
-        ret = new ABpBinder(binder);
-        value->binder = ret;
-    }
+    sp<ABpBinder> ret;
+    binder->withLock([&]() {
+        ret = value->binder.promote();
+        if (ret == nullptr) {
+            ret = sp<ABpBinder>::make(binder);
+            value->binder = ret;
+        }
+    });
 
     return ret;
 }
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 6824306..3b515ab 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -105,6 +105,7 @@
     ABpBinder* asABpBinder() override { return this; }
 
    private:
+    friend android::sp<ABpBinder>;
     explicit ABpBinder(const ::android::sp<::android::IBinder>& binder);
 };
 
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 2277148..aa3b978 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -46,7 +46,7 @@
     AParcelableHolder() = delete;
     explicit AParcelableHolder(parcelable_stability_t stability)
         : mParcel(AParcel_create()), mStability(stability) {}
-
+    AParcelableHolder(AParcelableHolder&& other) = default;
     virtual ~AParcelableHolder() = default;
 
     binder_status_t writeToParcel(AParcel* parcel) const {
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 62db3cf..ce12ccf 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -39,6 +39,7 @@
 #include <condition_variable>
 #include <iostream>
 #include <mutex>
+#include <thread>
 #include "android/binder_ibinder.h"
 
 using namespace android;
@@ -270,6 +271,27 @@
     EXPECT_EQ(2, out);
 }
 
+TEST(NdkBinder, GetTestServiceStressTest) {
+    // libbinder has some complicated logic to make sure only one instance of
+    // ABpBinder is associated with each binder.
+
+    constexpr size_t kNumThreads = 10;
+    constexpr size_t kNumCalls = 1000;
+    std::vector<std::thread> threads;
+
+    for (size_t i = 0; i < kNumThreads; i++) {
+        threads.push_back(std::thread([&]() {
+            for (size_t j = 0; j < kNumCalls; j++) {
+                auto binder =
+                        ndk::SpAIBinder(AServiceManager_checkService(IFoo::kSomeInstanceName));
+                EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+            }
+        }));
+    }
+
+    for (auto& thread : threads) thread.join();
+}
+
 void defaultInstanceCounter(const char* instance, void* context) {
     if (strcmp(instance, "default") == 0) {
         ++*(size_t*)(context);
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 2a09afc..f79b1b7 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -50,7 +50,7 @@
 /// interfaces) must implement this trait.
 ///
 /// This is equivalent `IInterface` in C++.
-pub trait Interface: Send {
+pub trait Interface: Send + Sync {
     /// Convert this binder object into a generic [`SpIBinder`] reference.
     fn as_binder(&self) -> SpIBinder {
         panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 5e324b3..a0dfeec 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -51,6 +51,25 @@
 /// to how `Box<T>` is `Send` if `T` is `Send`.
 unsafe impl<T: Remotable> Send for Binder<T> {}
 
+/// # Safety
+///
+/// A `Binder<T>` is a pair of unique owning pointers to two values:
+///   * a C++ ABBinder which is thread-safe, i.e. `Send + Sync`
+///   * a Rust object which implements `Remotable`; this trait requires `Send + Sync`
+///
+/// `ABBinder` contains an immutable `mUserData` pointer, which is actually a
+/// pointer to a boxed `T: Remotable`, which is `Sync`. `ABBinder` also contains
+/// a mutable pointer to its class, but mutation of this field is controlled by
+/// a mutex and it is only allowed to be set once, therefore we can concurrently
+/// access this field safely. `ABBinder` inherits from `BBinder`, which is also
+/// thread-safe. Thus `ABBinder` is thread-safe.
+///
+/// Both pointers are unique (never escape the `Binder<T>` object and are not copied)
+/// so we can essentially treat `Binder<T>` as a box-like containing the two objects;
+/// the box-like object inherits `Sync` from the two inner values, similarly
+/// to how `Box<T>` is `Sync` if `T` is `Sync`.
+unsafe impl<T: Remotable> Sync for Binder<T> {}
+
 impl<T: Remotable> Binder<T> {
     /// Create a new Binder remotable object with default stability
     ///
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index e299963..cdd7c08 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -48,9 +48,14 @@
 
 /// # Safety
 ///
-/// An `SpIBinder` is a handle to a C++ IBinder, which is thread-safe
+/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe
 unsafe impl Send for SpIBinder {}
 
+/// # Safety
+///
+/// An `SpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe
+unsafe impl Sync for SpIBinder {}
+
 impl SpIBinder {
     /// Create an `SpIBinder` wrapper object from a raw `AIBinder` pointer.
     ///
@@ -448,9 +453,14 @@
 
 /// # Safety
 ///
-/// A `WpIBinder` is a handle to a C++ IBinder, which is thread-safe.
+/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe.
 unsafe impl Send for WpIBinder {}
 
+/// # Safety
+///
+/// A `WpIBinder` is an immutable handle to a C++ IBinder, which is thread-safe.
+unsafe impl Sync for WpIBinder {}
+
 impl WpIBinder {
     /// Create a new weak reference from an object that can be converted into a
     /// raw `AIBinder` pointer.
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index d5a2b61..24afcf6 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -77,14 +77,14 @@
 
 // unit test only, which can run on host and doesn't use /dev/binder
 cc_test {
-    name: "binderParcelTest",
+    name: "binderUnitTest",
     host_supported: true,
     target: {
         darwin: {
             enabled: false,
         },
     },
-    srcs: ["binderParcelTest.cpp"],
+    srcs: ["binderParcelUnitTest.cpp", "binderBinderUnitTest.cpp"],
     shared_libs: [
         "libbinder",
         "libutils",
@@ -348,3 +348,49 @@
         },
     },
 }
+
+cc_test_host {
+    name: "binderHostDeviceTest",
+    defaults: ["binder_test_defaults"],
+    srcs: ["binderHostDeviceTest.cpp"],
+    test_config: "binderHostDeviceTest.xml",
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+    target_required: [
+        "binderHostDeviceTestService",
+    ],
+    test_suites: ["general-tests"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    test_options: {
+        unit_test: false,
+    },
+}
+
+cc_test {
+    name: "binderHostDeviceTestService",
+    // The binary is named differently from the module so that PushFilePreparer pushes the binary
+    // directly, not the test module directory.
+    stem: "binderHostDeviceTest-service",
+    defaults: ["binder_test_defaults"],
+    gtest: false,
+    auto_gen_config: false,
+    srcs: ["binderHostDeviceTestService.cpp"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libutils",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/libs/binder/tests/binderBinderUnitTest.cpp b/libs/binder/tests/binderBinderUnitTest.cpp
new file mode 100644
index 0000000..1be0c59
--- /dev/null
+++ b/libs/binder/tests/binderBinderUnitTest.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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 <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <gtest/gtest.h>
+
+using android::BBinder;
+using android::OK;
+using android::sp;
+
+const void* kObjectId1 = reinterpret_cast<const void*>(1);
+const void* kObjectId2 = reinterpret_cast<const void*>(2);
+void* kObject1 = reinterpret_cast<void*>(101);
+void* kObject2 = reinterpret_cast<void*>(102);
+void* kObject3 = reinterpret_cast<void*>(103);
+
+TEST(Binder, AttachObject) {
+    auto binder = sp<BBinder>::make();
+    EXPECT_EQ(nullptr, binder->attachObject(kObjectId1, kObject1, nullptr, nullptr));
+    EXPECT_EQ(nullptr, binder->attachObject(kObjectId2, kObject2, nullptr, nullptr));
+    EXPECT_EQ(kObject1, binder->attachObject(kObjectId1, kObject3, nullptr, nullptr));
+}
+
+TEST(Binder, DetachObject) {
+    auto binder = sp<BBinder>::make();
+    EXPECT_EQ(nullptr, binder->attachObject(kObjectId1, kObject1, nullptr, nullptr));
+    EXPECT_EQ(kObject1, binder->detachObject(kObjectId1));
+    EXPECT_EQ(nullptr, binder->attachObject(kObjectId1, kObject2, nullptr, nullptr));
+}
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
new file mode 100644
index 0000000..5dd9212
--- /dev/null
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+// Integration test for servicedispatcher + adb forward. Requires ADB.
+
+#include <stdlib.h>
+
+#include <vector>
+
+#include <android-base/parsebool.h>
+#include <android-base/result-gmock.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/RpcSession.h>
+
+#include "../UtilsHost.h"
+
+using ::android::setDefaultServiceManager;
+using ::android::base::EndsWith;
+using ::android::base::Join;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
+using ::android::base::Split;
+using ::android::base::StartsWith;
+using ::android::base::StringReplace;
+using ::android::base::Trim;
+using ::android::base::testing::Ok;
+using ::std::chrono_literals::operator""ms;
+using ::std::string_literals::operator""s;
+using ::testing::AllOf;
+using ::testing::Contains;
+using ::testing::ContainsRegex;
+using ::testing::ExplainMatchResult;
+using ::testing::InitGoogleMock;
+
+namespace android {
+
+namespace {
+
+constexpr const char* kServiceBinary = "/data/local/tmp/binderHostDeviceTest-service";
+constexpr const char* kServiceName = "binderHostDeviceTestService";
+constexpr const char* kDescriptor = "android.binderHostDeviceTestService";
+
+// e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message";
+MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) {
+    *result_listener << statusToString(arg);
+    return expected == arg;
+}
+
+void initHostRpcServiceManagerOnce() {
+    static std::once_flag gSmOnce;
+    std::call_once(gSmOnce, [] { setDefaultServiceManager(createRpcDelegateServiceManager()); });
+}
+
+// Test for host service manager.
+class HostDeviceTest : public ::testing::Test {
+public:
+    void SetUp() override {
+        auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr);
+        ASSERT_THAT(debuggableResult, Ok());
+        ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult;
+        auto debuggableBool = ParseBool(Trim(debuggableResult->stdout));
+        ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdout);
+        if (debuggableBool == ParseBoolResult::kFalse) {
+            GTEST_SKIP() << "ro.debuggable=" << Trim(debuggableResult->stdout);
+        }
+
+        auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr);
+        ASSERT_THAT(lsResult, Ok());
+        if (lsResult->exitCode != 0) {
+            GTEST_SKIP() << "b/182914638: until feature is fully enabled, skip test on devices "
+                            "without servicedispatcher";
+        }
+
+        initHostRpcServiceManagerOnce();
+        ASSERT_NE(nullptr, defaultServiceManager()) << "No defaultServiceManager() over RPC";
+
+        auto service = execute({"adb", "shell", kServiceBinary, kServiceName, kDescriptor},
+                               &CommandResult::stdoutEndsWithNewLine);
+        ASSERT_THAT(service, Ok());
+        ASSERT_EQ(std::nullopt, service->exitCode) << *service;
+        mService = std::move(*service);
+    }
+    void TearDown() override { mService.reset(); }
+
+    [[nodiscard]] static sp<IBinder> get(unsigned int hostPort) {
+        auto rpcSession = RpcSession::make();
+        if (!rpcSession->setupInetClient("127.0.0.1", hostPort)) {
+            ADD_FAILURE() << "Failed to setupInetClient on " << hostPort;
+            return nullptr;
+        }
+        return rpcSession->getRootObject();
+    }
+
+private:
+    std::optional<CommandResult> mService;
+};
+
+TEST_F(HostDeviceTest, List) {
+    auto sm = defaultServiceManager();
+
+    auto services = sm->listServices();
+    ASSERT_THAT(services, Contains(String16(kServiceName)));
+}
+
+TEST_F(HostDeviceTest, CheckService) {
+    auto sm = defaultServiceManager();
+
+    auto rpcBinder = sm->checkService(String16(kServiceName));
+    ASSERT_NE(nullptr, rpcBinder);
+
+    EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
+    EXPECT_EQ(String16(kDescriptor), rpcBinder->getInterfaceDescriptor());
+}
+
+TEST_F(HostDeviceTest, GetService) {
+    auto sm = defaultServiceManager();
+
+    auto rpcBinder = sm->getService(String16(kServiceName));
+    ASSERT_NE(nullptr, rpcBinder);
+
+    EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
+    EXPECT_EQ(String16(kDescriptor), rpcBinder->getInterfaceDescriptor());
+}
+
+TEST_F(HostDeviceTest, WaitForService) {
+    auto sm = defaultServiceManager();
+
+    auto rpcBinder = sm->waitForService(String16(kServiceName));
+    ASSERT_NE(nullptr, rpcBinder);
+
+    EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
+    EXPECT_EQ(String16(kDescriptor), rpcBinder->getInterfaceDescriptor());
+}
+
+TEST_F(HostDeviceTest, TenClients) {
+    auto sm = defaultServiceManager();
+
+    auto threadFn = [&] {
+        auto rpcBinder = sm->checkService(String16(kServiceName));
+        ASSERT_NE(nullptr, rpcBinder);
+
+        EXPECT_THAT(rpcBinder->pingBinder(), StatusEq(OK));
+        EXPECT_EQ(String16(kDescriptor), rpcBinder->getInterfaceDescriptor());
+    };
+
+    std::vector<std::thread> threads;
+    for (size_t i = 0; i < 10; ++i) threads.emplace_back(threadFn);
+    for (auto& thread : threads) thread.join();
+}
+
+} // namespace
+
+} // namespace android
diff --git a/libs/binder/tests/binderHostDeviceTest.xml b/libs/binder/tests/binderHostDeviceTest.xml
new file mode 100644
index 0000000..250ed3a
--- /dev/null
+++ b/libs/binder/tests/binderHostDeviceTest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<configuration description="Runs binderHostDeviceTest.">
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push-file" key="binderHostDeviceTest-service"
+                value="/data/local/tmp/binderHostDeviceTest-service"/>
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.binary.ExecutableHostTest">
+        <option name="binary" value="binderHostDeviceTest"/>
+    </test>
+</configuration>
diff --git a/libs/binder/tests/binderHostDeviceTestService.cpp b/libs/binder/tests/binderHostDeviceTestService.cpp
new file mode 100644
index 0000000..6ddd2e7
--- /dev/null
+++ b/libs/binder/tests/binderHostDeviceTestService.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include <sysexits.h>
+
+#include <android-base/logging.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+
+namespace {
+class Service : public android::BBinder {
+public:
+    Service(std::string_view descriptor) : mDescriptor(descriptor.data(), descriptor.size()) {}
+    const android::String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+private:
+    android::String16 mDescriptor;
+};
+} // namespace
+
+int main(int argc, char** argv) {
+    if (argc != 3) {
+        std::cerr << "usage: " << argv[0] << " <service-name> <interface-descriptor>" << std::endl;
+        return EX_USAGE;
+    }
+    auto name = argv[1];
+    auto descriptor = argv[2];
+
+    auto sm = android::defaultServiceManager();
+    CHECK(sm != nullptr);
+    auto service = android::sp<Service>::make(descriptor);
+    auto status = sm->addService(android::String16(name), service);
+    CHECK_EQ(android::OK, status) << android::statusToString(status);
+    std::cout << "running..." << std::endl;
+    android::ProcessState::self()->startThreadPool();
+    android::IPCThreadState::self()->joinThreadPool();
+    LOG(ERROR) << "joinThreadPool exits";
+    return EX_SOFTWARE;
+}
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
similarity index 100%
rename from libs/binder/tests/binderParcelTest.cpp
rename to libs/binder/tests/binderParcelUnitTest.cpp
diff --git a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
index 626b758..4a0aeba 100644
--- a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
@@ -62,20 +62,22 @@
              object = fdp->ConsumeIntegral<uint32_t>();
              cleanup_cookie = fdp->ConsumeIntegral<uint32_t>();
              IBinder::object_cleanup_func func = IBinder::object_cleanup_func();
-             ibinder->attachObject(fdp->ConsumeBool() ? reinterpret_cast<void*>(&objectID)
-                                                      : nullptr,
-                                   fdp->ConsumeBool() ? reinterpret_cast<void*>(&object) : nullptr,
-                                   fdp->ConsumeBool() ? reinterpret_cast<void*>(&cleanup_cookie)
-                                                      : nullptr,
-                                   func);
+             (void)ibinder->attachObject(fdp->ConsumeBool() ? reinterpret_cast<void*>(&objectID)
+                                                            : nullptr,
+                                         fdp->ConsumeBool() ? reinterpret_cast<void*>(&object)
+                                                            : nullptr,
+                                         fdp->ConsumeBool()
+                                                 ? reinterpret_cast<void*>(&cleanup_cookie)
+                                                 : nullptr,
+                                         func);
          },
          [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
              uint32_t id = fdp->ConsumeIntegral<uint32_t>();
-             ibinder->findObject(reinterpret_cast<void*>(&id));
+             (void)ibinder->findObject(reinterpret_cast<void*>(&id));
          },
          [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
              uint32_t id = fdp->ConsumeIntegral<uint32_t>();
-             ibinder->detachObject(reinterpret_cast<void*>(&id));
+             (void)ibinder->detachObject(reinterpret_cast<void*>(&id));
          },
          [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
              uint32_t code = fdp->ConsumeIntegral<uint32_t>();
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 4366203..3026921 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -15,7 +15,9 @@
     },
     srcs: [
         "cast_test.cpp",
+        "Flags_test.cpp",
         "future_test.cpp",
+        "NamedEnum_test.cpp",
         "small_map_test.cpp",
         "small_vector_test.cpp",
         "static_vector_test.cpp",
@@ -27,4 +29,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/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index d54de49..7f0cac5 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -343,80 +343,6 @@
     return nullptr;
 }
 
-bool GraphicsEnv::checkAngleRules(void* so) {
-    auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
-    auto model = base::GetProperty("ro.product.model", "UNSET");
-
-    auto ANGLEGetFeatureSupportUtilAPIVersion =
-            (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
-                                                          "ANGLEGetFeatureSupportUtilAPIVersion");
-
-    if (!ANGLEGetFeatureSupportUtilAPIVersion) {
-        ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function");
-        return false;
-    }
-
-    // Negotiate the interface version by requesting most recent known to the platform
-    unsigned int versionToUse = CURRENT_ANGLE_API_VERSION;
-    if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) {
-        ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, "
-              "requested version %u",
-              versionToUse);
-        return false;
-    }
-
-    // Add and remove versions below as needed
-    bool useAngle = false;
-    switch (versionToUse) {
-        case 2: {
-            ALOGV("Using version %d of ANGLE feature-support library", versionToUse);
-            void* rulesHandle = nullptr;
-            int rulesVersion = 0;
-            void* systemInfoHandle = nullptr;
-
-            // Get the symbols for the feature-support-utility library:
-#define GET_SYMBOL(symbol)                                                 \
-    fp##symbol symbol = (fp##symbol)dlsym(so, #symbol);                    \
-    if (!symbol) {                                                         \
-        ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \
-        break;                                                             \
-    }
-            GET_SYMBOL(ANGLEAndroidParseRulesString);
-            GET_SYMBOL(ANGLEGetSystemInfo);
-            GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
-            GET_SYMBOL(ANGLEShouldBeUsedForApplication);
-            GET_SYMBOL(ANGLEFreeRulesHandle);
-            GET_SYMBOL(ANGLEFreeSystemInfoHandle);
-
-            // Parse the rules, obtain the SystemInfo, and evaluate the
-            // application against the rules:
-            if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) {
-                ALOGW("ANGLE feature-support library cannot parse rules file");
-                break;
-            }
-            if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) {
-                ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
-                break;
-            }
-            if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
-                                                  systemInfoHandle)) {
-                ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
-                break;
-            }
-            useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion,
-                                                         systemInfoHandle, mAngleAppName.c_str());
-            (ANGLEFreeRulesHandle)(rulesHandle);
-            (ANGLEFreeSystemInfoHandle)(systemInfoHandle);
-        } break;
-
-        default:
-            ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse);
-    }
-
-    ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
-    return useAngle;
-}
-
 bool GraphicsEnv::shouldUseAngle(std::string appName) {
     if (appName != mAngleAppName) {
         // Make sure we are checking the app we were init'ed for
@@ -444,31 +370,20 @@
     const char* ANGLE_PREFER_ANGLE = "angle";
     const char* ANGLE_PREFER_NATIVE = "native";
 
+    mUseAngle = NO;
     if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
         ALOGV("User set \"Developer Options\" to force the use of ANGLE");
         mUseAngle = YES;
     } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
         ALOGV("User set \"Developer Options\" to force the use of Native");
-        mUseAngle = NO;
     } else {
-        // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily
-        // load ANGLE and call the updatable opt-in/out logic:
-        void* featureSo = loadLibrary("feature_support");
-        if (featureSo) {
-            ALOGV("loaded ANGLE's opt-in/out logic from namespace");
-            mUseAngle = checkAngleRules(featureSo) ? YES : NO;
-            dlclose(featureSo);
-            featureSo = nullptr;
-        } else {
-            ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE.");
-        }
+        ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());
     }
 }
 
 void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
                                const std::string developerOptIn,
-                               const std::vector<std::string> eglFeatures, const int rulesFd,
-                               const long rulesOffset, const long rulesLength) {
+                               const std::vector<std::string> eglFeatures) {
     if (mUseAngle != UNKNOWN) {
         // We've already figured out an answer for this app, so just return.
         ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
@@ -485,22 +400,6 @@
     ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
     mAngleDeveloperOptIn = developerOptIn;
 
-    lseek(rulesFd, rulesOffset, SEEK_SET);
-    mRulesBuffer = std::vector<char>(rulesLength + 1);
-    ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength);
-    if (numBytesRead < 0) {
-        ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead);
-        numBytesRead = 0;
-    } else if (numBytesRead == 0) {
-        ALOGW("Empty rules file");
-    }
-    if (numBytesRead != rulesLength) {
-        ALOGW("Did not read all of the necessary bytes from the rules file."
-              "expected: %ld, got: %zd",
-              rulesLength, numBytesRead);
-    }
-    mRulesBuffer[numBytesRead] = '\0';
-
     // Update the current status of whether we should use ANGLE or not
     updateUseAngle();
 }
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 900fc49..56d1139 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,8 +97,7 @@
     // in the search path must have a '!' after the zip filename, e.g.
     //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
     void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
-                      const std::vector<std::string> eglFeatures, const int rulesFd,
-                      const long rulesOffset, const long rulesLength);
+                      const std::vector<std::string> eglFeatures);
     // Get the ANGLE driver namespace.
     android_namespace_t* getAngleNamespace();
     // Get the app name for ANGLE debug message.
@@ -129,8 +128,6 @@
 
     // Load requested ANGLE library.
     void* loadLibrary(std::string name);
-    // Check ANGLE support with the rules.
-    bool checkAngleRules(void* so);
     // Update whether ANGLE should be used.
     void updateUseAngle();
     // Link updatable driver namespace with llndk and vndk-sp libs.
@@ -159,8 +156,6 @@
     std::string mAngleDeveloperOptIn;
     // ANGLE EGL features;
     std::vector<std::string> mAngleEglFeatures;
-    // ANGLE rules.
-    std::vector<char> mRulesBuffer;
     // Use ANGLE flag.
     UseAngle mUseAngle = UNKNOWN;
     // Vulkan debug layers libs.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 64203f7..2d8fff9 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -51,6 +51,51 @@
     ],
 }
 
+// AIDL files that should be exposed to java
+filegroup {
+    name: "guiconstants_aidl",
+    srcs: [
+        "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/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",
+    ],
+}
+
 filegroup {
     name: "libgui_aidl",
     srcs: ["aidl/**/*.aidl"],
@@ -77,12 +122,15 @@
         "libbinder",
     ],
 
+    static_libs: [
+        "libui-types",
+    ],
+
     aidl: {
         export_aidl_headers: true
     }
 }
 
-
 cc_library_shared {
     name: "libgui",
     vendor_available: true,
@@ -96,9 +144,11 @@
 
     static_libs: [
         "libgui_aidl_static",
+        "libgui_window_info_static",
     ],
     export_static_lib_headers: [
         "libgui_aidl_static",
+        "libgui_window_info_static",
     ],
 
     srcs: [
@@ -150,13 +200,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: [
@@ -168,7 +216,6 @@
         vendor: {
             cflags: [
                 "-DNO_BUFFERHUB",
-                "-DNO_INPUT",
             ],
             exclude_srcs: [
                 "BufferHubConsumer.cpp",
@@ -178,7 +225,6 @@
                 "android.frameworks.bufferhub@1.0",
                 "libbufferhub",
                 "libbufferhubqueue",
-                "libinput",
                 "libpdx_default_transport",
             ],
         },
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 364c939..ebc436f 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
@@ -132,8 +132,7 @@
 
 BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
                                    int width, int height, int32_t format)
-      : mName(name),
-        mSurfaceControl(surface),
+      : mSurfaceControl(surface),
         mSize(width, height),
         mRequestedSize(mSize),
         mFormat(format),
@@ -150,8 +149,9 @@
                                                               GraphicBuffer::USAGE_HW_TEXTURE,
                                                       1, false);
     static int32_t id = 0;
+    mName = name + "#" + std::to_string(id);
     auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
-    mPendingBufferTrace = "PendingBuffer - " + mName + "BLAST#" + std::to_string(id);
+    mQueuedBufferTrace = "QueuedBuffer - " + mName + "BLAST#" + std::to_string(id);
     id++;
     mBufferItemConsumer->setName(String8(consumerName.c_str()));
     mBufferItemConsumer->setFrameAvailableListener(this);
@@ -197,15 +197,18 @@
     }
 
     SurfaceComposerClient::Transaction t;
+    const bool setBackpressureFlag = !SurfaceControl::isSameSurface(mSurfaceControl, surface);
     bool applyTransaction = false;
-    if (!SurfaceControl::isSameSurface(mSurfaceControl, surface)) {
-        mSurfaceControl = surface;
-        t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
-                   layer_state_t::eEnableBackpressure);
-        applyTransaction = true;
-    }
 
+    // Always update the native object even though they might have the same layer handle, so we can
+    // get the updated transform hint from WM.
+    mSurfaceControl = surface;
     if (mSurfaceControl != nullptr) {
+        if (setBackpressureFlag) {
+            t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,
+                       layer_state_t::eEnableBackpressure);
+            applyTransaction = true;
+        }
         mTransformHint = mSurfaceControl->getTransformHint();
         mBufferItemConsumer->setTransformHint(mTransformHint);
     }
@@ -221,7 +224,7 @@
             // We only need to update the scale if we've received at least one buffer. The reason
             // for this is the scale is calculated based on the requested size and buffer size.
             // If there's no buffer, the scale will always be 1.
-            if (mLastBufferInfo.hasBuffer) {
+            if (mSurfaceControl != nullptr && mLastBufferInfo.hasBuffer) {
                 t.setDestinationFrame(mSurfaceControl,
                                       Rect(0, 0, newSize.getWidth(), newSize.getHeight()));
             }
@@ -313,24 +316,24 @@
 // BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client.
 // So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
 // Otherwise, this is a no-op.
-static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, uint64_t graphicBufferId,
+static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id,
                                        const sp<Fence>& releaseFence, uint32_t transformHint,
                                        uint32_t currentMaxAcquiredBufferCount) {
     sp<BLASTBufferQueue> blastBufferQueue = context.promote();
-    ALOGV("releaseBufferCallbackThunk graphicBufferId=%" PRIu64 " blastBufferQueue=%s",
-          graphicBufferId, blastBufferQueue ? "alive" : "dead");
     if (blastBufferQueue) {
-        blastBufferQueue->releaseBufferCallback(graphicBufferId, releaseFence, transformHint,
+        blastBufferQueue->releaseBufferCallback(id, releaseFence, transformHint,
                                                 currentMaxAcquiredBufferCount);
+    } else {
+        ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
     }
 }
 
-void BLASTBufferQueue::releaseBufferCallback(uint64_t graphicBufferId,
+void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id,
                                              const sp<Fence>& releaseFence, uint32_t transformHint,
                                              uint32_t currentMaxAcquiredBufferCount) {
     ATRACE_CALL();
     std::unique_lock _lock{mMutex};
-    BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId);
+    BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
 
     if (mSurfaceControl != nullptr) {
         mTransformHint = transformHint;
@@ -343,34 +346,34 @@
     // on a lower refresh rate than the max supported. We only do that for EGL
     // clients as others don't care about latency
     const bool isEGL = [&] {
-        const auto it = mSubmitted.find(graphicBufferId);
+        const auto it = mSubmitted.find(id);
         return it != mSubmitted.end() && it->second.mApi == NATIVE_WINDOW_API_EGL;
     }();
 
     const auto numPendingBuffersToHold =
             isEGL ? std::max(0u, mMaxAcquiredBuffers - currentMaxAcquiredBufferCount) : 0;
-    mPendingRelease.emplace_back(ReleasedBuffer{graphicBufferId, releaseFence});
+    mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
 
     // Release all buffers that are beyond the ones that we need to hold
     while (mPendingRelease.size() > numPendingBuffersToHold) {
         const auto releaseBuffer = mPendingRelease.front();
         mPendingRelease.pop_front();
-        auto it = mSubmitted.find(releaseBuffer.bufferId);
+        auto it = mSubmitted.find(releaseBuffer.callbackId);
         if (it == mSubmitted.end()) {
-            BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64,
-                     graphicBufferId);
+            BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
+                     releaseBuffer.callbackId.to_string().c_str());
             return;
         }
-
+        mNumAcquired--;
+        BQA_LOGV("released %s", id.to_string().c_str());
         mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
         mSubmitted.erase(it);
+        processNextBufferLocked(false /* useNextTransaction */);
     }
 
     ATRACE_INT("PendingRelease", mPendingRelease.size());
-
-    mNumAcquired--;
-    ATRACE_INT(mPendingBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired);
-    processNextBufferLocked(false /* useNextTransaction */);
+    ATRACE_INT(mQueuedBufferTrace.c_str(),
+               mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
     mCallbackCV.notify_all();
 }
 
@@ -429,7 +432,9 @@
     }
 
     mNumAcquired++;
-    mSubmitted[buffer->getId()] = bufferItem;
+    mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
+    ReleaseCallbackId releaseCallbackId(buffer->getId(), mLastAcquiredFrameNumber);
+    mSubmitted[releaseCallbackId] = bufferItem;
 
     bool needsDisconnect = false;
     mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
@@ -443,7 +448,6 @@
     incStrong((void*)transactionCallbackThunk);
 
     Rect crop = computeCrop(bufferItem);
-    mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
     mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
                            bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
                            bufferItem.mScalingMode, crop);
@@ -452,7 +456,7 @@
             std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
                       std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
                       std::placeholders::_4);
-    t->setBuffer(mSurfaceControl, buffer, releaseBufferCallback);
+    t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback);
     t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
     t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
     t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
@@ -511,11 +515,11 @@
 
     BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
              " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
-             " graphicBufferId=%" PRIu64,
-             mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
+             " graphicBufferId=%" PRIu64 "%s",
+             mSize.width, mSize.height, bufferItem.mFrameNumber, boolToString(applyTransaction),
              bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
-             static_cast<uint32_t>(mPendingTransactions.size()),
-             bufferItem.mGraphicBuffer->getId());
+             static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(),
+             bufferItem.mAutoRefresh ? " mAutoRefresh" : "");
 }
 
 Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -538,10 +542,11 @@
     }
     // add to shadow queue
     mNumFrameAvailable++;
-    ATRACE_INT(mPendingBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired);
+    ATRACE_INT(mQueuedBufferTrace.c_str(),
+               mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
 
     BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
-             toString(nextTransactionSet));
+             boolToString(nextTransactionSet));
     processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
 }
 
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 17499ec..98e8b54 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -125,7 +125,7 @@
     for (const auto& data : jankData) {
         SAFE_PARCEL(output->writeParcelable, data);
     }
-    SAFE_PARCEL(output->writeUint64, previousBufferId);
+    SAFE_PARCEL(output->writeParcelable, previousReleaseCallbackId);
     return NO_ERROR;
 }
 
@@ -149,7 +149,7 @@
         SAFE_PARCEL(input->readParcelable, &data);
         jankData.push_back(data);
     }
-    SAFE_PARCEL(input->readUint64, &previousBufferId);
+    SAFE_PARCEL(input->readParcelable, &previousReleaseCallbackId);
     return NO_ERROR;
 }
 
@@ -253,11 +253,11 @@
                                                                   stats);
     }
 
-    void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence, uint32_t transformHint,
-                         uint32_t currentMaxAcquiredBufferCount) override {
+    void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
+                         uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override {
         callRemoteAsync<decltype(
                 &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
-                                                                  graphicBufferId, releaseFence,
+                                                                  callbackId, releaseFence,
                                                                   transformHint,
                                                                   currentMaxAcquiredBufferCount);
     }
@@ -308,4 +308,18 @@
     return NO_ERROR;
 }
 
+status_t ReleaseCallbackId::writeToParcel(Parcel* output) const {
+    SAFE_PARCEL(output->writeUint64, bufferId);
+    SAFE_PARCEL(output->writeUint64, framenumber);
+    return NO_ERROR;
+}
+
+status_t ReleaseCallbackId::readFromParcel(const Parcel* input) {
+    SAFE_PARCEL(input->readUint64, &bufferId);
+    SAFE_PARCEL(input->readUint64, &framenumber);
+    return NO_ERROR;
+}
+
+const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0);
+
 }; // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index d102e07..001570c 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),
@@ -94,9 +97,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);
@@ -205,9 +206,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);
@@ -491,14 +490,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;
@@ -589,11 +584,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;
@@ -601,31 +594,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 cf9afa1..ec03c21 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -39,14 +39,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 +51,9 @@
 
 namespace android {
 
+using gui::FocusRequest;
+using gui::WindowInfo;
+using gui::WindowInfoHandle;
 using ui::ColorMode;
 // ---------------------------------------------------------------------------
 
@@ -182,12 +182,12 @@
 
 void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
                                                    sp<SurfaceControl> surfaceControl) {
-    std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
     mJankListeners.insert({surfaceControl->getHandle(), listener});
 }
 
 void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
-    std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
     for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
         if (it->second == listener) {
             it = mJankListeners.erase(it);
@@ -197,15 +197,16 @@
     }
 }
 
-void TransactionCompletedListener::setReleaseBufferCallback(uint64_t graphicBufferId,
+void TransactionCompletedListener::setReleaseBufferCallback(const ReleaseCallbackId& callbackId,
                                                             ReleaseBufferCallback listener) {
     std::scoped_lock<std::mutex> lock(mMutex);
-    mReleaseBufferCallbacks[graphicBufferId] = listener;
+    mReleaseBufferCallbacks[callbackId] = listener;
 }
 
-void TransactionCompletedListener::removeReleaseBufferCallback(uint64_t graphicBufferId) {
+void TransactionCompletedListener::removeReleaseBufferCallback(
+        const ReleaseCallbackId& callbackId) {
     std::scoped_lock<std::mutex> lock(mMutex);
-    mReleaseBufferCallbacks.erase(graphicBufferId);
+    mReleaseBufferCallbacks.erase(callbackId);
 }
 
 void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
@@ -242,6 +243,7 @@
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
+    std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
     {
         std::lock_guard<std::mutex> lock(mMutex);
 
@@ -257,6 +259,7 @@
          * sp<SurfaceControl> that could possibly exist for the callbacks.
          */
         callbacksMap = mCallbacks;
+        jankListenersMap = mJankListeners;
         for (const auto& transactionStats : listenerStats.transactionStats) {
             for (auto& callbackId : transactionStats.callbackIds) {
                 mCallbacks.erase(callbackId);
@@ -317,14 +320,15 @@
                 // and call them. This is a performance optimization when we have a transaction
                 // callback and a release buffer callback happening at the same time to avoid an
                 // additional ipc call from the server.
-                if (surfaceStats.previousBufferId) {
+                if (surfaceStats.previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
                     ReleaseBufferCallback callback;
                     {
                         std::scoped_lock<std::mutex> lock(mMutex);
-                        callback = popReleaseBufferCallbackLocked(surfaceStats.previousBufferId);
+                        callback = popReleaseBufferCallbackLocked(
+                                surfaceStats.previousReleaseCallbackId);
                     }
                     if (callback) {
-                        callback(surfaceStats.previousBufferId,
+                        callback(surfaceStats.previousReleaseCallbackId,
                                  surfaceStats.previousReleaseFence
                                          ? surfaceStats.previousReleaseFence
                                          : Fence::NO_FENCE,
@@ -352,12 +356,7 @@
             }
 
             if (surfaceStats.jankData.empty()) continue;
-
-            // Acquire jank listener lock such that we guarantee that after calling unregister,
-            // there won't be any further callback.
-            std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
-            auto copy = mJankListeners;
-            auto jankRange = copy.equal_range(surfaceStats.surfaceControl);
+            auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl);
             for (auto it = jankRange.first; it != jankRange.second; it++) {
                 it->second->onJankDataAvailable(surfaceStats.jankData);
             }
@@ -365,25 +364,26 @@
     }
 }
 
-void TransactionCompletedListener::onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence,
-                                                   uint32_t transformHint,
+void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
+                                                   sp<Fence> releaseFence, uint32_t transformHint,
                                                    uint32_t currentMaxAcquiredBufferCount) {
     ReleaseBufferCallback callback;
     {
         std::scoped_lock<std::mutex> lock(mMutex);
-        callback = popReleaseBufferCallbackLocked(graphicBufferId);
+        callback = popReleaseBufferCallbackLocked(callbackId);
     }
     if (!callback) {
-        ALOGE("Could not call release buffer callback, buffer not found %" PRIu64, graphicBufferId);
+        ALOGE("Could not call release buffer callback, buffer not found %s",
+              callbackId.to_string().c_str());
         return;
     }
-    callback(graphicBufferId, releaseFence, transformHint, currentMaxAcquiredBufferCount);
+    callback(callbackId, releaseFence, transformHint, currentMaxAcquiredBufferCount);
 }
 
 ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
-        uint64_t graphicBufferId) {
+        const ReleaseCallbackId& callbackId) {
     ReleaseBufferCallback callback;
-    auto itr = mReleaseBufferCallbacks.find(graphicBufferId);
+    auto itr = mReleaseBufferCallbacks.find(callbackId);
     if (itr == mReleaseBufferCallbacks.end()) {
         return nullptr;
     }
@@ -1261,7 +1261,7 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
-        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+        const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, const ReleaseCallbackId& id,
         ReleaseBufferCallback callback) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
@@ -1274,7 +1274,7 @@
     if (mIsAutoTimestamp) {
         mDesiredPresentTime = systemTime();
     }
-    setReleaseBufferCallback(s, callback);
+    setReleaseBufferCallback(s, id, callback);
 
     registerSurfaceControlForCallback(sc);
 
@@ -1289,10 +1289,13 @@
 
     s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
     s->releaseBufferListener = nullptr;
-    TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(s->buffer->getId());
+    auto listener = TransactionCompletedListener::getInstance();
+    listener->removeReleaseBufferCallback(s->releaseCallbackId);
+    s->releaseCallbackId = ReleaseCallbackId::INVALID_ID;
 }
 
 void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
+                                                                  const ReleaseCallbackId& id,
                                                                   ReleaseBufferCallback callback) {
     if (!callback) {
         return;
@@ -1306,8 +1309,9 @@
 
     s->what |= layer_state_t::eReleaseBufferListenerChanged;
     s->releaseBufferListener = TransactionCompletedListener::getIInstance();
+    s->releaseCallbackId = id;
     auto listener = TransactionCompletedListener::getInstance();
-    listener->setReleaseBufferCallback(s->buffer->getId(), callback);
+    listener->setReleaseBufferCallback(id, callback);
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
@@ -1487,16 +1491,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;
 }
@@ -1512,8 +1514,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);
diff --git a/libs/input/InputWindow.cpp b/libs/gui/WindowInfo.cpp
similarity index 79%
rename from libs/input/InputWindow.cpp
rename to libs/gui/WindowInfo.cpp
index 9947720..ff0bb8a 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -15,43 +15,40 @@
  */
 
 #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 &&
@@ -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) ?:
@@ -122,7 +119,7 @@
     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;
@@ -176,11 +173,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;
@@ -192,33 +191,33 @@
     return OK;
 }
 
-// --- 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/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/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/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
index 3e20a37..55462c3 100644
--- a/libs/gui/bufferqueue/1.0/Conversion.cpp
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -1187,14 +1187,15 @@
  */
 constexpr size_t minFlattenedSize(
         HGraphicBufferProducer::QueueBufferInput const& /* t */) {
-    return sizeof(int64_t) + // timestamp
-            sizeof(int) + // isAutoTimestamp
+    return sizeof(int64_t) +            // timestamp
+            sizeof(int) +               // isAutoTimestamp
             sizeof(android_dataspace) + // dataSpace
-            sizeof(::android::Rect) + // crop
-            sizeof(int) + // scalingMode
-            sizeof(uint32_t) + // transform
-            sizeof(uint32_t) + // stickyTransform
-            sizeof(bool); // getFrameTimestamps
+            sizeof(::android::Rect) +   // crop
+            sizeof(int) +               // scalingMode
+            sizeof(uint32_t) +          // transform
+            sizeof(uint32_t) +          // stickyTransform
+            sizeof(bool) +              // getFrameTimestamps
+            sizeof(int);                // slot
 }
 
 /**
@@ -1267,6 +1268,7 @@
         return status;
     }
     FlattenableUtils::write(buffer, size, decltype(HdrMetadata::validTypes)(0));
+    FlattenableUtils::write(buffer, size, -1 /*slot*/);
     return NO_ERROR;
 }
 
@@ -1319,7 +1321,7 @@
     if (status != NO_ERROR) {
         return status;
     }
-    // HdrMetadata ignored
+    // HdrMetadata and slot ignored
     return unflatten(&(t->surfaceDamage), buffer, size);
 }
 
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index cb0e65e..0d6a673 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -89,7 +89,7 @@
 
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
             const std::vector<SurfaceControlStats>& stats);
-    void releaseBufferCallback(uint64_t graphicBufferId, const sp<Fence>& releaseFence,
+    void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
                                uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
     void setNextTransaction(SurfaceComposerClient::Transaction *t);
     void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
@@ -125,7 +125,11 @@
     static PixelFormat convertBufferFormat(PixelFormat& format);
 
     std::string mName;
-    std::string mPendingBufferTrace;
+    // Represents the queued buffer count from buffer queue,
+    // pre-BLAST. This is mNumFrameAvailable (buffers that queued to blast) +
+    // mNumAcquired (buffers that queued to SF)  mPendingRelease.size() (buffers that are held by
+    // blast). This counter is read by android studio profiler.
+    std::string mQueuedBufferTrace;
     sp<SurfaceControl> mSurfaceControl;
 
     std::mutex mMutex;
@@ -140,13 +144,14 @@
 
     // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
     // buffer or the buffer has been presented and a new buffer is ready to be presented.
-    std::unordered_map<uint64_t /* bufferId */, BufferItem> mSubmitted GUARDED_BY(mMutex);
+    std::unordered_map<ReleaseCallbackId, BufferItem, ReleaseBufferCallbackIdHash> mSubmitted
+            GUARDED_BY(mMutex);
 
     // Keep a queue of the released buffers instead of immediately releasing
     // the buffers back to the buffer queue. This would be controlled by SF
     // setting the max acquired buffer count.
     struct ReleasedBuffer {
-        uint64_t bufferId;
+        ReleaseCallbackId callbackId;
         sp<Fence> releaseFence;
     };
     std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex);
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 8ec7e6d..c0a2335 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -24,9 +24,9 @@
 #include <android/gui/ITunnelModeEnabledListener.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>
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index d286c34..937095c 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -53,6 +53,36 @@
     std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
 };
 
+class ReleaseCallbackId : public Parcelable {
+public:
+    static const ReleaseCallbackId INVALID_ID;
+
+    uint64_t bufferId;
+    uint64_t framenumber;
+    ReleaseCallbackId() {}
+    ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
+          : bufferId(bufferId), framenumber(framenumber) {}
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    bool operator==(const ReleaseCallbackId& rhs) const {
+        return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
+    }
+    bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
+    std::string to_string() const {
+        if (*this == INVALID_ID) return "INVALID_ID";
+
+        return "bufferId:" + std::to_string(bufferId) +
+                " framenumber:" + std::to_string(framenumber);
+    }
+};
+
+struct ReleaseBufferCallbackIdHash {
+    std::size_t operator()(const ReleaseCallbackId& key) const {
+        return std::hash<uint64_t>()(key.bufferId);
+    }
+};
+
 class FrameEventHistoryStats : public Parcelable {
 public:
     status_t writeToParcel(Parcel* output) const override;
@@ -103,7 +133,7 @@
     SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
                  uint32_t hint, uint32_t currentMaxAcquiredBuffersCount,
                  FrameEventHistoryStats frameEventStats, std::vector<JankData> jankData,
-                 uint64_t previousBufferId)
+                 ReleaseCallbackId previousReleaseCallbackId)
           : surfaceControl(sc),
             acquireTime(time),
             previousReleaseFence(prevReleaseFence),
@@ -111,7 +141,7 @@
             currentMaxAcquiredBufferCount(currentMaxAcquiredBuffersCount),
             eventStats(frameEventStats),
             jankData(std::move(jankData)),
-            previousBufferId(previousBufferId) {}
+            previousReleaseCallbackId(previousReleaseCallbackId) {}
 
     sp<IBinder> surfaceControl;
     nsecs_t acquireTime = -1;
@@ -120,7 +150,7 @@
     uint32_t currentMaxAcquiredBufferCount = 0;
     FrameEventHistoryStats eventStats;
     std::vector<JankData> jankData;
-    uint64_t previousBufferId;
+    ReleaseCallbackId previousReleaseCallbackId;
 };
 
 class TransactionStats : public Parcelable {
@@ -161,7 +191,7 @@
 
     virtual void onTransactionCompleted(ListenerStats stats) = 0;
 
-    virtual void onReleaseBuffer(uint64_t graphicBufferId, sp<Fence> releaseFence,
+    virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
                                  uint32_t transformHint,
                                  uint32_t currentMaxAcquiredBufferCount) = 0;
 };
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 869c082..92de74a 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -26,14 +26,12 @@
 #include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
 
-#ifndef NO_INPUT
-#include <android/FocusRequest.h>
-#include <input/InputWindow.h>
-#endif
+#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>
@@ -176,9 +174,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;
 
@@ -234,6 +230,12 @@
     // layers only. The callback includes a release fence as well as the graphic
     // buffer id to identify the buffer.
     sp<ITransactionCompletedListener> releaseBufferListener;
+
+    // Keeps track of the release callback id associated with the listener. This
+    // is not sent to the server since the id can be reconstructed there. This
+    // is used to remove the old callback from the client process map if it is
+    // overwritten by another setBuffer call.
+    ReleaseCallbackId releaseCallbackId;
 };
 
 struct ComposerState {
@@ -282,9 +284,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 35ea10c..1dfc83b 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -82,7 +82,7 @@
         std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
                            const std::vector<SurfaceControlStats>& /*stats*/)>;
 using ReleaseBufferCallback =
-        std::function<void(uint64_t /* graphicsBufferId */, const sp<Fence>& /*releaseFence*/,
+        std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
                            uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount)>;
 
 using SurfaceStatsCallback =
@@ -397,8 +397,9 @@
 
         void cacheBuffers();
         void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
-        void setReleaseBufferCallback(layer_state_t* state, ReleaseBufferCallback callback);
-        void removeReleaseBufferCallback(layer_state_t* state);
+        void setReleaseBufferCallback(layer_state_t*, const ReleaseCallbackId&,
+                                      ReleaseBufferCallback);
+        void removeReleaseBufferCallback(layer_state_t*);
 
     public:
         Transaction();
@@ -470,6 +471,7 @@
         Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
                                                   bool transformToDisplayInverse);
         Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+                               const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID,
                                ReleaseBufferCallback callback = nullptr);
         Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
         Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
@@ -500,11 +502,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,
@@ -652,9 +652,6 @@
     std::mutex mMutex;
 
     // This lock needs to be recursive so we can unregister a callback from within that callback.
-    std::recursive_mutex mJankListenerMutex;
-
-    // This lock needs to be recursive so we can unregister a callback from within that callback.
     std::recursive_mutex mSurfaceStatsListenerMutex;
 
     bool mListening GUARDED_BY(mMutex) = false;
@@ -679,11 +676,8 @@
 
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
             GUARDED_BY(mMutex);
-
-    // This is protected by mJankListenerMutex, but GUARDED_BY isn't supported for
-    // std::recursive_mutex
-    std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners;
-    std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
+    std::multimap<sp<IBinder>, 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
@@ -722,16 +716,16 @@
                 SurfaceStatsCallback listener);
     void removeSurfaceStatsListener(void* context, void* cookie);
 
-    void setReleaseBufferCallback(uint64_t /* graphicsBufferId */, ReleaseBufferCallback);
-    void removeReleaseBufferCallback(uint64_t /* graphicsBufferId */);
+    void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
+    void removeReleaseBufferCallback(const ReleaseCallbackId&);
 
     // BnTransactionCompletedListener overrides
     void onTransactionCompleted(ListenerStats stats) override;
-    void onReleaseBuffer(uint64_t /* graphicsBufferId */, sp<Fence> releaseFence,
-                         uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override;
+    void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint,
+                         uint32_t currentMaxAcquiredBufferCount) override;
 
 private:
-    ReleaseBufferCallback popReleaseBufferCallbackLocked(uint64_t /* graphicsBufferId */);
+    ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
 };
 
 } // namespace android
diff --git a/include/input/InputWindow.h b/libs/gui/include/gui/WindowInfo.h
similarity index 89%
rename from include/input/InputWindow.h
rename to libs/gui/include/gui/WindowInfo.h
index 121be6d..51ddc50 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 {
@@ -169,8 +165,8 @@
     ui::Transform transform;
 
     // 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 +202,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 +217,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;
 
@@ -257,7 +253,7 @@
     /**
      * 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 +266,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/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/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 49c44a7..fc84c1b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -37,9 +37,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 +49,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;
@@ -221,8 +226,8 @@
     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 +254,7 @@
     std::shared_ptr<InputChannel> mClientChannel;
     sp<IInputFlinger> mInputFlinger;
 
-    InputWindowInfo mInputInfo;
+    WindowInfo mInputInfo;
 
     PreallocatedInputEventFactory mInputEventFactory;
     InputConsumer* mInputConsumer;
@@ -686,7 +691,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 +711,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 +735,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 +756,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 +771,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);
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
similarity index 89%
rename from libs/input/tests/InputWindow_test.cpp
rename to libs/gui/tests/WindowInfo_test.cpp
index 493f2f4..09c02ca 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,14 @@
     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.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;
@@ -65,7 +69,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,7 +81,7 @@
     Parcel p;
     i.writeToParcel(&p);
     p.setDataPosition(0);
-    InputWindowInfo i2;
+    WindowInfo i2;
     i2.readFromParcel(&p);
     ASSERT_EQ(i.token, i2.token);
     ASSERT_EQ(i.id, i2.id);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index a63ec8f..1a142c9 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",
     ],
 }
 
@@ -69,26 +68,24 @@
 
     static_libs: [
         "libui-types",
+        "libgui_window_info_static",
     ],
 
     export_static_lib_headers: [
         "libui-types",
+        "libgui_window_info_static",
     ],
 
     target: {
         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"],
@@ -114,13 +111,9 @@
         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",
@@ -134,6 +127,9 @@
     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 eb7a060..9390467 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -24,6 +24,7 @@
 #include <string.h>
 
 #include <android-base/stringprintf.h>
+#include <gui/constants.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
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..d6c1161 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;
 
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/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 3038d9d..474a1e4 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -42,16 +42,15 @@
     const int INVALID_INPUT_EVENT_ID = 0;
 
     /**
-     * The injected event was originally sent from InputDispatcher. Most likely, the journey of the
-     * event looked as follows:
-     * InputDispatcherPolicyInterface::filterInputEvent -> InputFilter.java::onInputEvent ->
-     * InputFilter.java::sendInputEvent -> InputDispatcher::injectInputEvent, without being modified
-     * along the way.
-     */
-    const int POLICY_FLAG_INPUTFILTER_TRUSTED = 0x10000;
-
-    /**
-     * The input event was injected from accessibility
+     * The input event was injected from accessibility. Used in policyFlags for input event
+     * injection.
      */
     const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
+
+    /**
+     * The input event was generated or modified by accessibility service.
+     * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either
+     * set of flags, including in input/Input.h and in android/input.h.
+     */
+    const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
 }
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
index 1771d19..43b262f 100644
--- a/libs/input/android/os/IInputFlinger.aidl
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -16,9 +16,9 @@
 
 package android.os;
 
-import android.FocusRequest;
 import android.InputChannel;
-import android.InputWindowInfo;
+import android.gui.FocusRequest;
+import android.gui.WindowInfo;
 import android.os.ISetInputWindowsListener;
 
 /** @hide */
@@ -28,7 +28,7 @@
     // 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,
+    oneway void setInputWindows(in WindowInfo[] windowInfoHandles,
             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 cfb5986..f1a26ec 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -20,6 +20,7 @@
 #include <attestation/HmacKeyManager.h>
 #include <binder/Parcel.h>
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <input/Input.h>
 
 namespace android {
@@ -271,9 +272,8 @@
                       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,
-                      ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
-                      pointerCoords);
+                      INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_DOWN_TIME,
+                      ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords);
 
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -593,9 +593,8 @@
                      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,
-                     0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
-                     pointerCoords);
+                     INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
+                     pointerCount, pointerProperties, pointerCoords);
     float originalRawX = 0 + 3;
     float originalRawY = -RADIUS + 2;
 
@@ -795,9 +794,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, INVALID_DISPLAY_SIZE,
+                         INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
+                         pointerProperties, pointerCoords);
         ASSERT_EQ(classification, event.getClassification());
     }
 }
@@ -817,10 +816,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*/, 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..d0c337c 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>
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index aefc2ec..8ae7dcc 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,9 +184,9 @@
                          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*/,
-                         entry.eventTime.count(), pointerCount, properties, coords);
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, 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..a0c9c17 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*/, 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 e7bd977..6a14d4f 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.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index b5dd8ac..467f848 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1028,20 +1028,17 @@
     return mProtectedEGLContext != EGL_NO_CONTEXT;
 }
 
-bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
-    if (useProtectedContext == mInProtectedContext) {
-        return true;
+void GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
+    if (useProtectedContext == mInProtectedContext ||
+        (useProtectedContext && !supportsProtectedContent())) {
+        return;
     }
-    if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) {
-        return false;
-    }
+
     const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface;
     const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
-    const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
-    if (success) {
+    if (eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE) {
         mInProtectedContext = useProtectedContext;
     }
-    return success;
 }
 EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
                                                              bool isProtected,
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 915dba3..14627ce 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -62,7 +62,7 @@
     void deleteTextures(size_t count, uint32_t const* names) override;
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
-    bool useProtectedContext(bool useProtectedContext) override;
+    void useProtectedContext(bool useProtectedContext) override;
     status_t drawLayers(const DisplaySettings& display,
                         const std::vector<const LayerSettings*>& layers,
                         const std::shared_ptr<ExternalTexture>& buffer,
@@ -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 ac0affb..967cf5d 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -127,11 +127,11 @@
     virtual bool supportsProtectedContent() const = 0;
 
     // Attempt to switch RenderEngine into and out of protectedContext mode
-    virtual bool useProtectedContext(bool useProtectedContext) = 0;
+    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 0175af3..0be3ba6 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -44,7 +44,7 @@
     MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
     MOCK_CONST_METHOD0(isProtected, bool());
     MOCK_CONST_METHOD0(supportsProtectedContent, bool());
-    MOCK_METHOD1(useProtectedContext, bool(bool));
+    MOCK_METHOD1(useProtectedContext, void(bool));
     MOCK_METHOD0(cleanupPostRender, void());
     MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
     MOCK_METHOD6(drawLayers,
@@ -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/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index b3975b0..ae8f238 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -41,6 +41,10 @@
                                    0.f,  1.1f, 0.f, 0.f,
                                    0.f,  0.f,  1.f, 0.f,
                                    0.f,  0.f,  0.f, 1.f);
+const auto kFlip = mat4(1.1f, -0.1f,  0.f, 0.f,
+                        0.1f,  1.1f,  0.f, 0.f,
+                        0.f,    0.f,  1.f, 0.f,
+                        2.f,    2.f,  0.f, 1.f);
 // clang-format on
 // When setting layer.sourceDataspace, whether it matches the destination or not determines whether
 // a color correction effect is added to the shader.
@@ -52,40 +56,64 @@
                              const std::shared_ptr<ExternalTexture>& dstTexture) {
     // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
     // on actual use.
-    FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
+    const Rect& displayRect = display.physicalDisplay;
+    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+    FloatRect smallerRect(20, 20, displayRect.width()-20, displayRect.height()-20);
+
     LayerSettings layer{
             .geometry =
                     Geometry{
                             .boundaries = rect,
                             .roundedCornersCrop = rect,
+                            .roundedCornersRadius = 50.f,
                     },
             // drawShadow ignores alpha
             .shadow =
                     ShadowSettings{
+                            .boundaries = rect,
                             .ambientColor = vec4(0, 0, 0, 0.00935997f),
                             .spotColor = vec4(0, 0, 0, 0.0455841f),
-                            .lightPos = vec3(370.508f, -1527.03f, 1650.f),
-                            .lightRadius = 2200.0f,
-                            .length = 0.955342f,
+                            .lightPos = vec3(500.f, -1500.f, 1500.f),
+                            .lightRadius = 2500.0f,
+                            .length = 15.f,
                     },
-            // important that this matches dest so the general shadow fragment shader doesn't
-            // have color correction added, and important that it be srgb, so the *vertex* shader
-            // doesn't have color correction added.
-            .sourceDataspace = kDestDataSpace,
+            // setting this is mandatory for shadows and blurs
+            .skipContentDraw = true,
+            .alpha = 1,
+    };
+    LayerSettings caster{
+            .geometry =
+                    Geometry{
+                            .boundaries = smallerRect,
+                            .roundedCornersCrop = rect,
+                            .roundedCornersRadius = 50.f,
+                    },
+            .source =
+                    PixelSource{
+                            .solidColor = half3(0.f, 0.f, 0.f),
+                    },
+            .alpha = 1,
     };
 
-    auto layers = std::vector<const LayerSettings*>{&layer};
-    // The identity matrix will generate the fast shader
-    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd(),
-                             nullptr);
-    // This matrix, which has different scales for x and y, will
-    // generate the slower (more general case) version, which has variants for translucent
-    // casters and rounded rects.
-    layer.geometry.positionTransform = kScaleAsymmetric;
-    for (auto translucent : {false, true}) {
-        layer.shadow.casterIsTranslucent = translucent;
-        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                 base::unique_fd(), nullptr);
+    auto layers = std::vector<const LayerSettings*>{&layer, &caster};
+    // When sourceDataspace matches dest, the general shadow fragment shader doesn't
+    // have color correction added.
+    // independently, when it is not srgb, the *vertex* shader has color correction added.
+    // This may be a bug, but the shader still needs to be cached as it is triggered
+    // during youtube pip.
+    for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
+        layer.sourceDataspace = dataspace;
+        // The 2nd matrix, which has different scales for x and y, will
+        // generate the slower (more general case) shadow shader
+        for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) {
+            layer.geometry.positionTransform = transform;
+            caster.geometry.positionTransform = transform;
+            for (bool translucent : {false, true}){
+                layer.shadow.casterIsTranslucent = translucent;
+                renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                        base::unique_fd(), nullptr);
+            }
+        }
     }
 }
 
@@ -97,6 +125,9 @@
     LayerSettings layer{
             .geometry =
                     Geometry{
+                            // The position transform doesn't matter when the reduced shader mode
+                            // in in effect. A matrix transform stage is always included.
+                            .positionTransform = mat4(),
                             .boundaries = rect,
                             .roundedCornersCrop = rect,
                     },
@@ -107,29 +138,20 @@
                                           }},
     };
 
-    auto threeCornerRadii = {0.0f, 0.05f, 50.f};
-    auto oneCornerRadius = {50.f};
-
-    // Test both drawRect and drawRRect
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
         layer.sourceDataspace = dataspace;
-        for (bool identity : {true, false}) {
-            layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate;
-            // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage
-            // due to animating corner radius.
-            // For the non-idenity matrix, only the large corner radius will create a new shader.
-            for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) {
-                // roundedCornersCrop is always set, but it is this radius that triggers the
-                // behavior
-                layer.geometry.roundedCornersRadius = roundedCornersRadius;
-                for (bool isOpaque : {true, false}) {
-                    layer.source.buffer.isOpaque = isOpaque;
-                    for (auto alpha : {half(.23999f), half(1.0f)}) {
-                        layer.alpha = alpha;
-                        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
-                                                 base::unique_fd(), nullptr);
-                    }
+        // Cache shaders for both rects and round rects.
+        // In reduced shader mode, all non-zero round rect radii get the same code path.
+        for (float roundedCornersRadius : {0.0f, 50.0f}) {
+            // roundedCornersCrop is always set, but the radius triggers the behavior
+            layer.geometry.roundedCornersRadius = roundedCornersRadius;
+            for (bool isOpaque : {true, false}) {
+                layer.source.buffer.isOpaque = isOpaque;
+                for (auto alpha : {half(.2f), half(1.0f)}) {
+                    layer.alpha = alpha;
+                    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                                             base::unique_fd(), nullptr);
                 }
             }
         }
@@ -155,7 +177,7 @@
     auto layers = std::vector<const LayerSettings*>{&layer};
     for (auto transform : {mat4(), kScaleAndTranslate}) {
         layer.geometry.positionTransform = transform;
-        for (float roundedCornersRadius : {0.0f, 0.05f, 50.f}) {
+        for (float roundedCornersRadius : {0.0f, 50.f}) {
             layer.geometry.roundedCornersRadius = roundedCornersRadius;
             renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
                                      base::unique_fd(), nullptr);
@@ -173,9 +195,12 @@
                             .boundaries = rect,
                     },
             .alpha = 1,
+            // setting this is mandatory for shadows and blurs
+            .skipContentDraw = true,
     };
 
     auto layers = std::vector<const LayerSettings*>{&layer};
+    // Different blur code is invoked for radii less and greater than 30 pixels
     for (int radius : {9, 60}) {
         layer.backgroundBlurRadius = radius;
         renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
@@ -220,11 +245,9 @@
         layer.source = pixelSource;
         for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
             layer.sourceDataspace = dataspace;
-            // Produce a CircularRRect clip and an EllipticalRRect clip
+            // Produce a CircularRRect clip and an EllipticalRRect clip.
             for (auto transform : {kScaleAndTranslate, kScaleAsymmetric}) {
                 layer.geometry.positionTransform = transform;
-                // In real use, I saw alpha of 1.0 and 0.999, probably a mistake, but cache both
-                // shaders.
                 for (float alpha : {0.5f, 1.f}) {
                     layer.alpha = alpha,
                     renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
@@ -235,6 +258,69 @@
     }
 }
 
+static void drawPIPImageLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                            const std::shared_ptr<ExternalTexture>& dstTexture,
+                            const std::shared_ptr<ExternalTexture>& srcTexture) {
+    const Rect& displayRect = display.physicalDisplay;
+    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            // Note that this flip matrix only makes a difference when clipping,
+                            // which happens in this layer because the roundrect crop is just a bit
+                            // larger than the layer bounds.
+                            .positionTransform = kFlip,
+                            .boundaries = rect,
+                            .roundedCornersRadius = 94.2551,
+                            .roundedCornersCrop = FloatRect(
+                                -93.75, 0, displayRect.width() + 93.75, displayRect.height()),
+                    },
+            .source = PixelSource{.buffer =
+                                          Buffer{
+                                                  .buffer = srcTexture,
+                                                  .maxLuminanceNits = 1000.f,
+                                                  .isOpaque = 0,
+                                                  .usePremultipliedAlpha = 1,
+                                          }},
+            .sourceDataspace = kOtherDataSpace,
+            .alpha = 1,
+
+    };
+
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                             base::unique_fd(), nullptr);
+}
+
+static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                            const std::shared_ptr<ExternalTexture>& dstTexture) {
+    const Rect& displayRect = display.physicalDisplay;
+    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+    FloatRect small(0, 0, displayRect.width()-20, displayRect.height()+20);
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .positionTransform = kScaleAndTranslate,
+                            // the boundaries have to be smaller than the rounded crop so that
+                            // clipRRect is used instead of drawRRect
+                            .boundaries = small,
+                            .roundedCornersRadius = 50.f,
+                            .roundedCornersCrop = rect,
+                    },
+            .source = PixelSource{
+                            .solidColor = half3(0.f, 0.f, 0.f),
+                    },
+            .sourceDataspace = kDestDataSpace,
+            .alpha = 0,
+            .disableBlending = true,
+
+    };
+
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+                            base::unique_fd(), nullptr);
+}
+
 //
 // The collection of shaders cached here were found by using perfetto to record shader compiles
 // during actions that involve RenderEngine, logging the layer settings, and the shader code
@@ -246,8 +332,6 @@
 //    kFlushAfterEveryLayer = true
 // in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
 //    gPrintSKSL = true
-//
-// TODO(b/184631553) cache the shader involved in youtube pip return.
 void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
     const int previousCount = renderengine->reportShadersCompiled();
     if (previousCount) {
@@ -267,6 +351,12 @@
                 .maxLuminance = 500,
                 .outputDataspace = kDestDataSpace,
         };
+        DisplaySettings p3Display{
+                .physicalDisplay = displayRect,
+                .clip = displayRect,
+                .maxLuminance = 500,
+                .outputDataspace = kOtherDataSpace,
+        };
 
         const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
 
@@ -289,32 +379,47 @@
                 std::make_shared<ExternalTexture>(srcBuffer, *renderengine,
                                                   ExternalTexture::Usage::READABLE |
                                                           ExternalTexture::Usage::WRITEABLE);
-
+        drawHolePunchLayer(renderengine, display, dstTexture);
         drawSolidLayers(renderengine, display, dstTexture);
         drawShadowLayers(renderengine, display, srcTexture);
+        drawShadowLayers(renderengine, p3Display, srcTexture);
 
         if (renderengine->supportsBackgroundBlur()) {
             drawBlurLayers(renderengine, display, dstTexture);
         }
 
-        // The majority of shaders are related to sampling images.
-        drawImageLayers(renderengine, display, dstTexture, srcTexture);
-
         // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
         const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE;
-
         sp<GraphicBuffer> externalBuffer =
                 new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888,
                                   1, usageExternal, "primeShaderCache_external");
         const auto externalTexture =
                 std::make_shared<ExternalTexture>(externalBuffer, *renderengine,
                                                   ExternalTexture::Usage::READABLE);
-        // TODO(b/184665179) doubles number of image shader compilations, but only somewhere
-        // between 6 and 8 will occur in real uses.
-        drawImageLayers(renderengine, display, dstTexture, externalTexture);
 
-        // Draw layers for b/185569240.
-        drawClippedLayers(renderengine, display, dstTexture, externalTexture);
+        // Another external texture with a different pixel format triggers useIsOpaqueWorkaround
+        sp<GraphicBuffer> f16ExternalBuffer =
+                new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_FP16,
+                                  1, usageExternal, "primeShaderCache_external_f16");
+        const auto f16ExternalTexture =
+                std::make_shared<ExternalTexture>(f16ExternalBuffer, *renderengine,
+                                                  ExternalTexture::Usage::READABLE);
+
+        // The majority of shaders are related to sampling images.
+        // These need to be generated with various source textures
+        // The F16 texture may not be usable on all devices, so check first that it was created with
+        // the requested usage bit.
+        auto textures = {srcTexture, externalTexture};
+        auto texturesWithF16 = {srcTexture, externalTexture, f16ExternalTexture};
+        bool canUsef16 = f16ExternalBuffer->getUsage() & GRALLOC_USAGE_HW_TEXTURE;
+
+        for (auto texture : canUsef16 ? texturesWithF16 : textures) {
+            drawImageLayers(renderengine, display, dstTexture, texture);
+            // Draw layers for b/185569240.
+            drawClippedLayers(renderengine, display, dstTexture, texture);
+        }
+
+        drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
 
         const nsecs_t timeAfter = systemTime();
         const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index cb80ef4..f4b07d0 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -320,7 +320,8 @@
     options.fReducedShaderVariations = true;
     options.fPersistentCache = &mSkSLCacheMonitor;
     mGrContext = GrDirectContext::MakeGL(glInterface, options);
-    if (useProtectedContext(true)) {
+    if (supportsProtectedContent()) {
+        useProtectedContext(true);
         mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options);
         useProtectedContext(false);
     }
@@ -373,12 +374,10 @@
     return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
 }
 
-bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
-    if (useProtectedContext == mInProtectedContext) {
-        return true;
-    }
-    if (useProtectedContext && !supportsProtectedContent()) {
-        return false;
+void SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
+    if (useProtectedContext == mInProtectedContext ||
+        (useProtectedContext && !supportsProtectedContent())) {
+        return;
     }
 
     // release any scratch resources before switching into a new mode
@@ -389,9 +388,8 @@
     const EGLSurface surface =
             useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
     const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
-    const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
 
-    if (success) {
+    if (eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE) {
         mInProtectedContext = useProtectedContext;
         // given that we are sharing the same thread between two GrContexts we need to
         // make sure that the thread state is reset when switching between the two.
@@ -399,7 +397,6 @@
             getActiveGrContext()->resetContext();
         }
     }
-    return success;
 }
 
 base::unique_fd SkiaGLRenderEngine::flush() {
@@ -618,9 +615,9 @@
 
     if (requiresLinearEffect) {
         const ui::Dataspace inputDataspace =
-                mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN;
+                mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
         const ui::Dataspace outputDataspace =
-                mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
+                mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
 
         LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
                                            .outputDataspace = outputDataspace,
@@ -762,7 +759,7 @@
     }
 
     const ui::Dataspace dstDataspace =
-            mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
+            mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
     sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
 
     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
@@ -1401,7 +1398,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
@@ -1413,10 +1410,12 @@
     getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
 
     // if it is possible to switch contexts then we will resize the other context
-    if (useProtectedContext(!mInProtectedContext)) {
+    const bool originalProtectedState = mInProtectedContext;
+    useProtectedContext(!mInProtectedContext);
+    if (mInProtectedContext != originalProtectedState) {
         getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
         // reset back to the initial context that was active when this method was called
-        useProtectedContext(!mInProtectedContext);
+        useProtectedContext(originalProtectedState);
     }
 }
 
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index b30355b..cc91948 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -64,10 +64,10 @@
     int getContextPriority() override;
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
-    bool useProtectedContext(bool useProtectedContext) override;
+    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/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 31ad63e..7cd9eca 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -44,7 +44,6 @@
     virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
     virtual bool isProtected() const override { return false; } // mInProtectedContext; }
     virtual bool supportsProtectedContent() const override { return false; };
-    virtual bool useProtectedContext(bool /*useProtectedContext*/) override { return false; };
     virtual status_t drawLayers(const DisplaySettings& /*display*/,
                                 const std::vector<const LayerSettings*>& /*layers*/,
                                 const std::shared_ptr<ExternalTexture>& /*buffer*/,
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 9b044e1..fc45af9 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -167,13 +167,12 @@
 
                                 float nits = xyz.y;
 
-                                // clamp to max input luminance
-                                nits = clamp(nits, 0.0, maxInLumi);
-
-                                // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+                                // if the max input luminance is less than what we can output then
+                                // no tone mapping is needed as all color values will be in range.
                                 if (maxInLumi <= maxOutLumi) {
-                                    return xyz * (maxOutLumi / maxInLumi);
+                                    return xyz;
                                 } else {
+
                                     // three control points
                                     const float x0 = 10.0;
                                     const float y0 = 17.0;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index e258741..33e3773 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -81,6 +81,7 @@
                         .setSupportsBackgroundBlur(true)
                         .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
                         .setRenderEngineType(type())
+                        .setUseColorManagerment(useColorManagement())
                         .build();
         return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
     }
@@ -110,7 +111,7 @@
                         .setSupportsBackgroundBlur(true)
                         .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
                         .setRenderEngineType(type())
-                        .setUseColorManagerment(true)
+                        .setUseColorManagerment(useColorManagement())
                         .build();
         return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
     }
@@ -136,16 +137,12 @@
                         .setSupportsBackgroundBlur(true)
                         .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
                         .setRenderEngineType(type())
-                        // FIXME (b/189935602): This version is currently color managed.
-                        // We should change it and fix the tests that fail.
-                        //.setUseColorManagerment(false)
+                        .setUseColorManagerment(useColorManagement())
                         .build();
         return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
     }
 
-    // FIXME (b/189935602): This version is currently color managed.
-    // We should change it and fix the tests that fail.
-    bool useColorManagement() const override { return true; }
+    bool useColorManagement() const override { return false; }
 };
 
 class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
@@ -166,7 +163,7 @@
                         .setSupportsBackgroundBlur(true)
                         .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
                         .setRenderEngineType(type())
-                        .setUseColorManagerment(true)
+                        .setUseColorManagerment(useColorManagement())
                         .build();
         return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
     }
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index c65e731..830f463 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -118,16 +118,26 @@
     ASSERT_EQ(true, result);
 }
 
-TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) {
-    EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false));
-    status_t result = mThreadedRE->useProtectedContext(false);
-    ASSERT_EQ(false, result);
+TEST_F(RenderEngineThreadedTest, useProtectedContext) {
+    EXPECT_CALL(*mRenderEngine, useProtectedContext(true));
+    auto& ipExpect = EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderEngine, isProtected()).After(ipExpect).WillOnce(Return(true));
+
+    mThreadedRE->useProtectedContext(true);
+    ASSERT_EQ(true, mThreadedRE->isProtected());
+
+    // call ANY synchronous function to ensure that useProtectedContext has completed.
+    mThreadedRE->getContextPriority();
+    ASSERT_EQ(true, mThreadedRE->isProtected());
 }
 
-TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) {
-    EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
-    status_t result = mThreadedRE->useProtectedContext(false);
-    ASSERT_EQ(true, result);
+TEST_F(RenderEngineThreadedTest, useProtectedContext_quickReject) {
+    EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).Times(0);
+    EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+    mThreadedRE->useProtectedContext(false);
+    // call ANY synchronous function to ensure that useProtectedContext has completed.
+    mThreadedRE->getContextPriority();
 }
 
 TEST_F(RenderEngineThreadedTest, PostRenderCleanup_skipped) {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index ea3871f..8e666d5 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -90,6 +90,7 @@
     }
 
     mRenderEngine = factory();
+    mIsProtected = mRenderEngine->isProtected();
 
     pthread_setname_np(pthread_self(), mThreadName);
 
@@ -248,10 +249,8 @@
 
 bool RenderEngineThreaded::isProtected() const {
     waitUntilInitialized();
-    // ensure that useProtectedContext is not currently being changed by some
-    // other thread.
     std::lock_guard lock(mThreadMutex);
-    return mRenderEngine->isProtected();
+    return mIsProtected;
 }
 
 bool RenderEngineThreaded::supportsProtectedContent() const {
@@ -259,20 +258,28 @@
     return mRenderEngine->supportsProtectedContent();
 }
 
-bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
-    std::promise<bool> resultPromise;
-    std::future<bool> resultFuture = resultPromise.get_future();
+void RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
+    if (isProtected() == useProtectedContext ||
+        (useProtectedContext && !supportsProtectedContent())) {
+        return;
+    }
+
     {
         std::lock_guard lock(mThreadMutex);
-        mFunctionCalls.push(
-                [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) {
-                    ATRACE_NAME("REThreaded::useProtectedContext");
-                    bool returnValue = instance.useProtectedContext(useProtectedContext);
-                    resultPromise.set_value(returnValue);
-                });
+        mFunctionCalls.push([useProtectedContext, this](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::useProtectedContext");
+            instance.useProtectedContext(useProtectedContext);
+            if (instance.isProtected() != useProtectedContext) {
+                ALOGE("Failed to switch RenderEngine context.");
+                // reset the cached mIsProtected value to a good state, but this does not
+                // prevent other callers of this method and isProtected from reading the
+                // invalid cached value.
+                mIsProtected = instance.isProtected();
+            }
+        });
+        mIsProtected = useProtectedContext;
     }
     mCondition.notify_one();
-    return resultFuture.get();
 }
 
 void RenderEngineThreaded::cleanupPostRender() {
@@ -354,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 9b523b2..b197df7 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -53,7 +53,7 @@
 
     bool isProtected() const override;
     bool supportsProtectedContent() const override;
-    bool useProtectedContext(bool useProtectedContext) override;
+    void useProtectedContext(bool useProtectedContext) override;
     void cleanupPostRender() override;
 
     status_t drawLayers(const DisplaySettings& display,
@@ -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;
@@ -100,6 +100,7 @@
      * Render Engine
      */
     std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    std::atomic<bool> mIsProtected = false;
 };
 } // namespace threaded
 } // namespace renderengine
diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c
index c75213b..16e1e8a 100644
--- a/services/gpuservice/bpfprogs/gpu_mem.c
+++ b/services/gpuservice/bpfprogs/gpu_mem.c
@@ -72,4 +72,4 @@
     return 0;
 }
 
-char _license[] SEC("license") = "Apache 2.0";
+LICENSE("Apache 2.0");
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index a50e5c7..e81dcfe 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,9 +114,9 @@
     return mDispatcher;
 }
 
-class BinderWindowHandle : public InputWindowHandle {
+class BinderWindowHandle : public WindowInfoHandle {
 public:
-    BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; }
+    BinderWindowHandle(const WindowInfo& info) { mInfo = info; }
 
     bool updateInfo() override {
         return true;
@@ -120,13 +124,13 @@
 };
 
 binder::Status InputManager::setInputWindows(
-        const std::vector<InputWindowInfo>& infos,
+        const std::vector<WindowInfo>& infos,
         const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay;
+    std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
 
-    std::vector<sp<InputWindowHandle>> handles;
+    std::vector<sp<WindowInfoHandle>> handles;
     for (const auto& info : infos) {
-        handlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
+        handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
         handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info));
     }
     mDispatcher->setInputWindows(handlesPerDisplay);
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 49bea13..035a9a3 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -105,12 +105,12 @@
 
     status_t dump(int fd, const Vector<String16>& args) override;
     binder::Status setInputWindows(
-            const std::vector<InputWindowInfo>& handles,
+            const std::vector<gui::WindowInfo>& 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 9cc777d..f26a9a9 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/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index dc0e60c..3d85bef 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -27,7 +27,17 @@
       "name": "CtsViewTestCases",
       "options": [
         {
-          "include-filter": "android.view.cts.MotionEventTest"
+          "include-filter": "android.view.cts.MotionEventTest",
+          "include-filter": "android.view.cts.VerifyInputEventTest"
+        }
+      ]
+    },
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.view.VerifiedKeyEventTest",
+          "include-filter": "android.view.VerifiedMotionEventTest"
         }
       ]
     },
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index bc77b8a..55deec4 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;
@@ -188,7 +191,7 @@
     virtual bool updateInfo() override {
         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;
@@ -234,8 +237,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, INVALID_DISPLAY_SIZE,
+                     INVALID_DISPLAY_SIZE, currentTime, currentTime,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     return event;
 }
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.h b/services/inputflinger/dispatcher/Entry.h
index ebbd8e9..89d7659 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>
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 15c810d..d8af48e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -56,7 +56,6 @@
 #include <binder/IServiceManager.h>
 #include <com/android/internal/compat/IPlatformCompatNative.h>
 #include <input/InputDevice.h>
-#include <input/InputWindow.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
 #include <powermanager/PowerManager.h>
@@ -82,6 +81,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;
@@ -292,7 +295,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;
     }
@@ -304,7 +307,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;
     }
@@ -626,7 +629,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.
@@ -675,7 +678,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);
     }
@@ -887,7 +890,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() !=
@@ -900,8 +903,7 @@
         }
 
         // Alternatively, maybe there's a gesture monitor that could handle this event
-        std::vector<TouchedMonitor> gestureMonitors =
-                findTouchedGestureMonitorsLocked(displayId, {});
+        std::vector<TouchedMonitor> gestureMonitors = findTouchedGestureMonitorsLocked(displayId);
         for (TouchedMonitor& gestureMonitor : gestureMonitors) {
             sp<Connection> connection =
                     getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
@@ -992,46 +994,34 @@
     }
 }
 
-sp<InputWindowHandle> 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");
+sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
+                                                                int32_t y, TouchState* touchState,
+                                                                bool addOutsideTargets,
+                                                                bool ignoreDragWindow) {
+    if (addOutsideTargets && touchState == nullptr) {
+        LOG_ALWAYS_FATAL("Must provide a valid touch state if adding 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 &&
-                            portalToDisplayId != displayId) {
-                            if (addPortalWindows) {
-                                // For the monitoring channels of the display.
-                                touchState->addPortalWindow(windowHandle);
-                            }
-                            return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
-                                                             addOutsideTargets, addPortalWindows);
-                        }
                         // Found window.
                         return windowHandle;
                     }
                 }
 
-                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));
@@ -1043,17 +1033,11 @@
 }
 
 std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
-        int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const {
+        int32_t displayId) 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();
-        monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
-        addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft,
-                           -windowInfo->frameTop);
-    }
     return touchedMonitors;
 }
 
@@ -1589,23 +1573,6 @@
     // Add monitor channels from event's or focused display.
     addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
 
-    if (isPointerEvent) {
-        std::unordered_map<int32_t, TouchState>::iterator it =
-                mTouchStatesByDisplay.find(entry->displayId);
-        if (it != mTouchStatesByDisplay.end()) {
-            const TouchState& state = it->second;
-            if (!state.portalWindows.empty()) {
-                // 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();
-                    addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
-                                                     -windowInfo->frameLeft, -windowInfo->frameTop);
-                }
-            }
-        }
-    }
-
     // Dispatch the motion.
     if (conflictingPointerActions) {
         CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -1616,7 +1583,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.
@@ -1796,7 +1763,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);
 
@@ -1923,8 +1890,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.
@@ -1993,12 +1960,11 @@
             y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
         }
         bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        newTouchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
-                                          isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
+        newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+                                                           isDown /*addOutsideTargets*/);
 
         std::vector<TouchedMonitor> newGestureMonitors = isDown
-                ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)
+                ? findTouchedGestureMonitorsLocked(displayId)
                 : std::vector<TouchedMonitor>{};
 
         // Figure out whether splitting will be allowed for this window.
@@ -2117,7 +2083,7 @@
             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);
             if (oldTouchedWindowHandle != newTouchedWindowHandle &&
@@ -2216,15 +2182,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));
                     }
@@ -2240,15 +2206,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 |
@@ -2367,10 +2333,9 @@
 }
 
 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*/);
+                                      false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
     if (dropWindow) {
         vec2 local = dropWindow->getInfo()->transform.transform(x, y);
         notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
@@ -2402,10 +2367,9 @@
             return;
         }
 
-        const sp<InputWindowHandle> hoverWindowHandle =
+        const sp<WindowInfoHandle> hoverWindowHandle =
                 findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
-                                          false /*addOutsideTargets*/, false /*addPortalWindows*/,
-                                          true /*ignoreDragWindow*/);
+                                          false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
         // enqueue drag exit if needed.
         if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
             !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -2427,7 +2391,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 =
@@ -2437,7 +2401,7 @@
                                      windowHandle->getToken();
                          });
 
-    const InputWindowInfo* windowInfo = windowHandle->getInfo();
+    const WindowInfo* windowInfo = windowHandle->getInfo();
 
     if (it == inputTargets.end()) {
         InputTarget inputTarget;
@@ -2488,7 +2452,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 ||
@@ -2513,8 +2477,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;
@@ -2523,8 +2487,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
@@ -2562,20 +2525,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) {
@@ -2614,7 +2577,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, "
@@ -2648,15 +2611,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;
@@ -2665,15 +2628,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;
@@ -2683,8 +2646,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();
@@ -2707,10 +2669,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
@@ -3481,10 +3443,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;
     }
@@ -3548,10 +3510,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;
     }
@@ -3794,7 +3756,7 @@
         if (shouldSendKeyToInputFilterLocked(args)) {
             mLock.unlock();
 
-            policyFlags |= POLICY_FLAG_FILTERED | POLICY_FLAG_INPUTFILTER_TRUSTED;
+            policyFlags |= POLICY_FLAG_FILTERED;
             if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                 return; // event was consumed by the filter
             }
@@ -3877,9 +3839,9 @@
                              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->pointerCount, args->pointerProperties, args->pointerCoords);
+                             args->yCursorPosition, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
+                             args->downTime, args->eventTime, args->pointerCount,
+                             args->pointerProperties, args->pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
             if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -4017,16 +3979,14 @@
     }
 
     // For all injected events, set device id = VIRTUAL_KEYBOARD_ID. The only exception is events
-    // that have gone through the InputFilter. If the event passed through the InputFilter,
-    // but did not get modified, assign the provided device id. If the InputFilter modifies the
-    // events in any way, it is responsible for removing this flag.
-    // If the injected event originated from accessibility, assign the accessibility device id,
-    // so that it can be distinguished from regular injected events.
+    // that have gone through the InputFilter. If the event passed through the InputFilter, assign
+    // the provided device id. If the InputFilter is accessibility, and it modifies or synthesizes
+    // the injected event, it is responsible for setting POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY.
+    // For those events, we will set FLAG_IS_ACCESSIBILITY_EVENT to allow apps to distinguish them
+    // from events that originate from actual hardware.
     int32_t resolvedDeviceId = VIRTUAL_KEYBOARD_ID;
-    if (policyFlags & POLICY_FLAG_INPUTFILTER_TRUSTED) {
+    if (policyFlags & POLICY_FLAG_FILTERED) {
         resolvedDeviceId = event->getDeviceId();
-    } else if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
-        resolvedDeviceId = ACCESSIBILITY_DEVICE_ID;
     }
 
     std::queue<std::unique_ptr<EventEntry>> injectedEntries;
@@ -4039,6 +3999,9 @@
             }
 
             int32_t flags = incomingKey.getFlags();
+            if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
+                flags |= AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
+            }
             int32_t keyCode = incomingKey.getKeyCode();
             int32_t metaState = incomingKey.getMetaState();
             accelerateMetaShortcuts(resolvedDeviceId, action,
@@ -4080,6 +4043,7 @@
             size_t pointerCount = motionEvent.getPointerCount();
             const PointerProperties* pointerProperties = motionEvent.getPointerProperties();
             int32_t actionButton = motionEvent.getActionButton();
+            int32_t flags = motionEvent.getFlags();
             int32_t displayId = motionEvent.getDisplayId();
             if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
                 return InputEventInjectionResult::FAILED;
@@ -4095,6 +4059,10 @@
                 }
             }
 
+            if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
+                flags |= AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
+            }
+
             mLock.lock();
             const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
             const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords();
@@ -4102,8 +4070,7 @@
                     std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
                                                   resolvedDeviceId, motionEvent.getSource(),
                                                   motionEvent.getDisplayId(), policyFlags, action,
-                                                  actionButton, motionEvent.getFlags(),
-                                                  motionEvent.getMetaState(),
+                                                  actionButton, flags, motionEvent.getMetaState(),
                                                   motionEvent.getButtonState(),
                                                   motionEvent.getClassification(),
                                                   motionEvent.getEdgeFlags(),
@@ -4123,7 +4090,7 @@
                         std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
                                                       resolvedDeviceId, motionEvent.getSource(),
                                                       motionEvent.getDisplayId(), policyFlags,
-                                                      action, actionButton, motionEvent.getFlags(),
+                                                      action, actionButton, flags,
                                                       motionEvent.getMetaState(),
                                                       motionEvent.getButtonState(),
                                                       motionEvent.getClassification(),
@@ -4317,22 +4284,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;
             }
@@ -4341,13 +4308,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;
         }
@@ -4355,11 +4322,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) {
@@ -4375,15 +4342,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());
@@ -4413,8 +4380,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;
@@ -4422,26 +4389,25 @@
 
     // 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) {
+    std::vector<sp<WindowInfoHandle>> newHandles;
+    for (const sp<WindowInfoHandle>& handle : windowInfoHandles) {
         if (!handle->updateInfo()) {
             // handle no longer valid
             continue;
         }
 
-        const InputWindowInfo* info = handle->getInfo();
-        if ((getInputChannelLocked(handle->getToken()) == nullptr &&
-             info->portalToDisplayId == ADISPLAY_ID_NONE)) {
+        const WindowInfo* info = handle->getInfo();
+        if (getInputChannelLocked(handle->getToken()) == nullptr) {
             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());
@@ -4457,7 +4423,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 {
@@ -4470,7 +4436,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) {
@@ -4489,19 +4455,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());
@@ -4510,18 +4476,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()) {
@@ -4570,8 +4536,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()]) {
@@ -4590,7 +4556,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());
@@ -4600,7 +4566,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) {
@@ -4790,8 +4756,8 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
-        sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toToken);
+        sp<WindowInfoHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken);
         if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
             ALOGW("Cannot transfer focus because from or to window not found.");
             return false;
@@ -4869,7 +4835,7 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
         if (toWindowHandle == nullptr) {
             ALOGW("Could not find window associated with token=%p", destChannelToken.get());
             return false;
@@ -4936,7 +4902,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";
@@ -4989,14 +4955,6 @@
             } else {
                 dump += INDENT3 "Windows: <none>\n";
             }
-            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];
-                    dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i,
-                                         portalWindowHandle->getName().c_str());
-                }
-            }
         }
     } else {
         dump += INDENT "TouchStates: <no displays touched>\n";
@@ -5009,16 +4967,16 @@
 
     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, "
+                                                 "paused=%s, focusable=%s, "
                                                  "hasWallpaper=%s, visible=%s, alpha=%.2f, "
                                                  "flags=%s, type=%s, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
@@ -5026,8 +4984,7 @@
                                                  "applicationInfo.token=%s, "
                                                  "touchableRegion=",
                                          i, windowInfo->name.c_str(), windowInfo->id,
-                                         windowInfo->displayId, windowInfo->portalToDisplayId,
-                                         toString(windowInfo->paused),
+                                         windowInfo->displayId, toString(windowInfo->paused),
                                          toString(windowInfo->focusable),
                                          toString(windowInfo->hasWallpaper),
                                          toString(windowInfo->visible), windowInfo->alpha,
@@ -5396,7 +5353,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");
@@ -5567,7 +5524,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);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 9edf41c..32a467e 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>
@@ -109,8 +109,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 +128,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,
@@ -190,8 +191,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 +209,11 @@
     // 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 ignoreDragWindow = false)
+            REQUIRES(mLock);
 
     sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
             REQUIRES(mLock);
@@ -314,33 +315,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);
@@ -471,7 +475,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);
@@ -489,21 +493,20 @@
     android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
             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
+    std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId) 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
@@ -518,15 +521,18 @@
         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);
 
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
@@ -605,8 +611,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)
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 1c4980b..9c62b7e 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>
@@ -101,7 +102,7 @@
     float globalScaleFactor = 1.0f;
 
     // 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..b7ed658 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 {
 
@@ -36,7 +37,6 @@
     source = 0;
     displayId = ADISPLAY_ID_NONE;
     windows.clear();
-    portalWindows.clear();
     gestureMonitors.clear();
 }
 
@@ -47,11 +47,10 @@
     source = other.source;
     displayId = other.displayId;
     windows = other.windows;
-    portalWindows = other.portalWindows;
     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,16 +75,6 @@
     windows.push_back(touchedWindow);
 }
 
-void TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) {
-    size_t numWindows = portalWindows.size();
-    for (size_t i = 0; i < numWindows; i++) {
-        if (portalWindows[i] == windowHandle) {
-            return;
-        }
-    }
-    portalWindows.push_back(windowHandle);
-}
-
 void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) {
     const size_t newSize = gestureMonitors.size() + newMonitors.size();
     gestureMonitors.reserve(newSize);
@@ -118,10 +107,9 @@
 
 void TouchState::filterNonMonitors() {
     windows.clear();
-    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 +125,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..579b868 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 {
 
@@ -34,25 +36,20 @@
     int32_t displayId; // id to the display that currently has a touch, others are rejected
     std::vector<TouchedWindow> windows;
 
-    // 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<TouchedMonitor> gestureMonitors;
 
     TouchState();
     ~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..80db035 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -19,18 +19,17 @@
 
 #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 +90,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 +161,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 219f45a..ebfcbe1 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..c05ff39 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -30,6 +30,8 @@
 #include <utils/String8.h>
 #include <utils/StrongPointer.h>
 
+using android::gui::FocusRequest;
+using android::gui::WindowInfo;
 using android::os::BnInputFlinger;
 using android::os::ISetInputWindowsListener;
 
@@ -44,7 +46,7 @@
     InputFlinger() ANDROID_API;
 
     status_t dump(int fd, const Vector<String16>& args) override;
-    binder::Status setInputWindows(const std::vector<InputWindowInfo>&,
+    binder::Status setInputWindows(const std::vector<WindowInfo>&,
                                    const sp<ISetInputWindowsListener>&) override {
         return binder::Status::ok();
     }
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 410a706..1016a2c 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/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index ca43123..fab7f4c 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -104,36 +104,37 @@
 #endif
         } else {
             Slot* slot = &mSlots[mCurrentSlot];
+            // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
+            // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
+            // updating the slot.
+            if (!mUsingSlotsProtocol) {
+                slot->mInUse = true;
+            }
 
             switch (rawEvent->code) {
                 case ABS_MT_POSITION_X:
-                    slot->mInUse = true;
                     slot->mAbsMTPositionX = rawEvent->value;
+                    warnIfNotInUse(*rawEvent, *slot);
                     break;
                 case ABS_MT_POSITION_Y:
-                    slot->mInUse = true;
                     slot->mAbsMTPositionY = rawEvent->value;
+                    warnIfNotInUse(*rawEvent, *slot);
                     break;
                 case ABS_MT_TOUCH_MAJOR:
-                    slot->mInUse = true;
                     slot->mAbsMTTouchMajor = rawEvent->value;
                     break;
                 case ABS_MT_TOUCH_MINOR:
-                    slot->mInUse = true;
                     slot->mAbsMTTouchMinor = rawEvent->value;
                     slot->mHaveAbsMTTouchMinor = true;
                     break;
                 case ABS_MT_WIDTH_MAJOR:
-                    slot->mInUse = true;
                     slot->mAbsMTWidthMajor = rawEvent->value;
                     break;
                 case ABS_MT_WIDTH_MINOR:
-                    slot->mInUse = true;
                     slot->mAbsMTWidthMinor = rawEvent->value;
                     slot->mHaveAbsMTWidthMinor = true;
                     break;
                 case ABS_MT_ORIENTATION:
-                    slot->mInUse = true;
                     slot->mAbsMTOrientation = rawEvent->value;
                     break;
                 case ABS_MT_TRACKING_ID:
@@ -147,15 +148,12 @@
                     }
                     break;
                 case ABS_MT_PRESSURE:
-                    slot->mInUse = true;
                     slot->mAbsMTPressure = rawEvent->value;
                     break;
                 case ABS_MT_DISTANCE:
-                    slot->mInUse = true;
                     slot->mAbsMTDistance = rawEvent->value;
                     break;
                 case ABS_MT_TOOL_TYPE:
-                    slot->mInUse = true;
                     slot->mAbsMTToolType = rawEvent->value;
                     slot->mHaveAbsMTToolType = true;
                     break;
@@ -177,6 +175,13 @@
     return mHaveStylus;
 }
 
+void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
+    if (!slot.mInUse) {
+        ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
+              event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
+    }
+}
+
 // --- MultiTouchMotionAccumulator::Slot ---
 
 MultiTouchMotionAccumulator::Slot::Slot() {
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index ea6f207..225ad49 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -87,6 +87,7 @@
     bool mHaveStylus;
 
     void clearSlots(int32_t initialSlot);
+    void warnIfNotInUse(const RawEvent& event, const Slot& slot);
 };
 
 class MultiTouchInputMapper : public TouchInputMapper {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 6050238..af02844 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"
@@ -1471,6 +1471,13 @@
           next.rawPointerData.canceledIdBits.value);
 #endif
 
+    if (!next.rawPointerData.touchingIdBits.isEmpty() &&
+        !next.rawPointerData.hoveringIdBits.isEmpty() &&
+        last.rawPointerData.hoveringIdBits != next.rawPointerData.hoveringIdBits) {
+        ALOGI("Multi-touch contains some hovering ids 0x%08x",
+              next.rawPointerData.hoveringIdBits.value);
+    }
+
     processRawTouches(false /*timeout*/);
 }
 
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..8c0edca 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) {
@@ -47,7 +50,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 +85,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 +123,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 +145,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 +169,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 +210,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 +261,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..aff03bf 100644
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-import android.FocusRequest;
 import android.InputChannel;
-import android.InputWindowInfo;
+import android.gui.FocusRequest;
+import android.gui.WindowInfo;
 import android.os.ISetInputWindowsListener;
 
 /** @hide */
 interface IInputFlingerQuery
 {
     /* Test interfaces */
-    void getInputWindows(out InputWindowInfo[] inputHandles);
+    void getInputWindows(out WindowInfo[] 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 cb876c9..1a70e54 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 {
@@ -469,8 +472,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();
@@ -527,8 +530,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, 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,
@@ -541,9 +544,8 @@
                              (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);
+                     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))
@@ -554,9 +556,8 @@
                              (~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);
+                     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))
@@ -568,9 +569,8 @@
                              (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);
+                     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))
@@ -581,9 +581,8 @@
                              (~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);
+                     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))
@@ -593,8 +592,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, 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,
@@ -604,8 +603,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, 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,
@@ -617,8 +616,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, 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,
@@ -629,8 +628,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, 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,
@@ -643,8 +642,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, 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,
@@ -892,7 +891,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;
@@ -914,7 +913,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;
@@ -948,9 +947,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; }
 
@@ -964,11 +961,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);
@@ -1102,7 +1099,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)
@@ -1283,8 +1280,8 @@
     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};
+    int32_t mDisplayWidth{INVALID_DISPLAY_SIZE};
+    int32_t mDisplayHeight{INVALID_DISPLAY_SIZE};
 
     std::vector<PointerBuilder> mPointers;
 };
@@ -1410,7 +1407,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,
@@ -1433,7 +1430,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}}});
@@ -1469,11 +1466,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);
 
@@ -1580,7 +1577,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);
 
@@ -1662,11 +1659,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);
 
@@ -1860,15 +1857,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}}});
@@ -1934,15 +1929,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}}});
@@ -2060,15 +2053,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}}});
@@ -2736,8 +2727,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
@@ -3180,7 +3170,8 @@
         mWindow->consumeFocusEvent(true);
     }
 
-    void testInjectedKey(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId) {
+    void testInjectedKey(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId,
+                         int32_t flags) {
         KeyEvent event;
 
         const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -3197,6 +3188,45 @@
         InputEvent* received = mWindow->consume();
         ASSERT_NE(nullptr, received);
         ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
+        ASSERT_EQ(received->getType(), AINPUT_EVENT_TYPE_KEY);
+        KeyEvent& keyEvent = static_cast<KeyEvent&>(*received);
+        ASSERT_EQ(flags, keyEvent.getFlags());
+    }
+
+    void testInjectedMotion(int32_t policyFlags, int32_t injectedDeviceId, int32_t resolvedDeviceId,
+                            int32_t flags) {
+        MotionEvent event;
+        PointerProperties pointerProperties[1];
+        PointerCoords pointerCoords[1];
+        pointerProperties[0].clear();
+        pointerProperties[0].id = 0;
+        pointerCoords[0].clear();
+        pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 300);
+        pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 400);
+
+        ui::Transform identityTransform;
+        const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        event.initialize(InputEvent::nextId(), injectedDeviceId, AINPUT_SOURCE_TOUCHSCREEN,
+                         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,
+                         /*pointerCount*/ 1, pointerProperties, pointerCoords);
+
+        const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER;
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                  mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                                InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
+                                                policyFlags | additionalPolicyFlags));
+
+        InputEvent* received = mWindow->consume();
+        ASSERT_NE(nullptr, received);
+        ASSERT_EQ(resolvedDeviceId, received->getDeviceId());
+        ASSERT_EQ(received->getType(), AINPUT_EVENT_TYPE_MOTION);
+        MotionEvent& motionEvent = static_cast<MotionEvent&>(*received);
+        ASSERT_EQ(flags, motionEvent.getFlags());
     }
 
 private:
@@ -3204,20 +3234,29 @@
 };
 
 TEST_F(InputFilterInjectionPolicyTest, TrustedFilteredEvents_KeepOriginalDeviceId) {
-    // We don't need POLICY_FLAG_FILTERED here, but it will be set in practice, so keep it to make
-    // the test more closely resemble the real usage
-    testInjectedKey(POLICY_FLAG_FILTERED | POLICY_FLAG_INPUTFILTER_TRUSTED, 3 /*injectedDeviceId*/,
-                    3 /*resolvedDeviceId*/);
+    // Must have POLICY_FLAG_FILTERED here to indicate that the event has gone through the input
+    // filter. Without it, the event will no different from a regularly injected event, and the
+    // injected device id will be overwritten.
+    testInjectedKey(POLICY_FLAG_FILTERED, 3 /*injectedDeviceId*/, 3 /*resolvedDeviceId*/,
+                    0 /*flags*/);
 }
 
-TEST_F(InputFilterInjectionPolicyTest, EventsInjectedFromAccessibility_HaveAccessibilityDeviceId) {
+TEST_F(InputFilterInjectionPolicyTest, KeyEventsInjectedFromAccessibility_HaveAccessibilityFlag) {
     testInjectedKey(POLICY_FLAG_FILTERED | POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
-                    3 /*injectedDeviceId*/, ACCESSIBILITY_DEVICE_ID /*resolvedDeviceId*/);
+                    3 /*injectedDeviceId*/, 3 /*resolvedDeviceId*/,
+                    AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
+}
+
+TEST_F(InputFilterInjectionPolicyTest,
+       MotionEventsInjectedFromAccessibility_HaveAccessibilityFlag) {
+    testInjectedMotion(POLICY_FLAG_FILTERED | POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
+                       3 /*injectedDeviceId*/, 3 /*resolvedDeviceId*/,
+                       AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
 }
 
 TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDeviceId) {
     testInjectedKey(0 /*policyFlags*/, 3 /*injectedDeviceId*/,
-                    VIRTUAL_KEYBOARD_ID /*resolvedDeviceId*/);
+                    VIRTUAL_KEYBOARD_ID /*resolvedDeviceId*/, 0 /*flags*/);
 }
 
 class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
@@ -3231,12 +3270,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);
@@ -3345,14 +3384,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}}});
@@ -3363,7 +3400,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};
     }
@@ -3551,7 +3588,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);
@@ -3952,16 +3989,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);
@@ -4329,7 +4365,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
 
@@ -4371,7 +4407,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}}});
 
@@ -4671,10 +4707,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;
@@ -4788,7 +4824,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();
@@ -4799,7 +4835,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();
@@ -5053,11 +5089,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}}});
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index c368e79..d2a98df 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -28,9 +28,9 @@
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
 
+#include <gui/WindowInfo.h>
 #include <input/Input.h>
 #include <input/InputTransport.h>
-#include <input/InputWindow.h>
 
 #include <gtest/gtest.h>
 #include <inttypes.h>
@@ -44,6 +44,9 @@
 
 #define TAG "InputFlingerServiceTest"
 
+using android::gui::FocusRequest;
+using android::gui::WindowInfo;
+using android::gui::WindowInfoHandle;
 using android::os::BnInputFlinger;
 using android::os::BnSetInputWindowsListener;
 using android::os::IInputFlinger;
@@ -58,8 +61,8 @@
 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 Flags<WindowInfo::Flag> TestInfoFlags = WindowInfo::Flag::NOT_FOCUSABLE;
+static constexpr WindowInfo::Type TestInfoType = WindowInfo::Type::INPUT_METHOD;
 static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms;
 static constexpr int32_t TestInfoFrameLeft = 93;
 static constexpr int32_t TestInfoFrameTop = 34;
@@ -79,8 +82,7 @@
 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 WindowInfo::Feature TestInfoInputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
 static constexpr int32_t TestInfoDisplayId = 34;
 static constexpr int32_t TestInfoPortalToDisplayId = 2;
 static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true;
@@ -102,13 +104,13 @@
 
 protected:
     void InitializeInputFlinger();
-    void setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos);
+    void setInputWindowsByInfos(const std::vector<WindowInfo>& 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); }
+    void verifyInputWindowInfo(const WindowInfo& info) const;
+    WindowInfo& getInfo() const { return const_cast<WindowInfo&>(mInfo); }
 
     sp<IInputFlinger> mService;
     sp<IInputFlingerQuery> mQuery;
@@ -116,7 +118,7 @@
 private:
     sp<SetInputWindowsListener> mSetInputWindowsListener;
     std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
-    InputWindowInfo mInfo;
+    WindowInfo mInfo;
     std::mutex mLock;
     std::condition_variable mSetInputWindowsFinishedCondition;
 };
@@ -136,14 +138,14 @@
 public:
     TestInputManager(){};
 
-    binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
+    binder::Status getInputWindows(std::vector<WindowInfo>* 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 std::vector<WindowInfo>& handles,
             const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
 
     binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
@@ -154,7 +156,7 @@
 
 private:
     mutable Mutex mLock;
-    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
+    std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> mHandlesPerDisplay;
     std::vector<std::shared_ptr<InputChannel>> mInputChannels;
     FocusRequest mFocusRequest;
 };
@@ -162,7 +164,7 @@
 class TestInputQuery : public BnInputFlingerQuery {
 public:
     TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
-    binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
+    binder::Status getInputWindows(std::vector<WindowInfo>* inputHandles) override;
     binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
     binder::Status getLastFocusRequest(FocusRequest*) override;
     binder::Status resetInputManager() override;
@@ -171,8 +173,7 @@
     sp<android::TestInputManager> mManager;
 };
 
-binder::Status TestInputQuery::getInputWindows(
-        std::vector<::android::InputWindowInfo>* inputHandles) {
+binder::Status TestInputQuery::getInputWindows(std::vector<WindowInfo>* inputHandles) {
     return mManager->getInputWindows(inputHandles);
 }
 
@@ -197,13 +198,13 @@
 }
 
 binder::Status TestInputManager::setInputWindows(
-        const std::vector<InputWindowInfo>& infos,
+        const std::vector<WindowInfo>& 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));
+        mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
+        mHandlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info));
     }
     if (setInputWindowsListener) {
         setInputWindowsListener->onSetInputWindowsFinished();
@@ -248,8 +249,7 @@
     return NO_ERROR;
 }
 
-binder::Status TestInputManager::getInputWindows(
-        std::vector<::android::InputWindowInfo>* inputInfos) {
+binder::Status TestInputManager::getInputWindows(std::vector<WindowInfo>* inputInfos) {
     for (auto& [displayId, inputHandles] : mHandlesPerDisplay) {
         for (auto& inputHandle : inputHandles) {
             inputInfos->push_back(*inputHandle->getInfo());
@@ -331,7 +331,7 @@
     mQuery->resetInputManager();
 }
 
-void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
+void InputFlingerServiceTest::verifyInputWindowInfo(const WindowInfo& info) const {
     EXPECT_EQ(mInfo, info);
 }
 
@@ -345,7 +345,7 @@
     mQuery = interface_cast<IInputFlingerQuery>(input);
 }
 
-void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos) {
+void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<WindowInfo>& infos) {
     std::unique_lock<std::mutex> lock(mLock);
     mService->setInputWindows(infos, mSetInputWindowsListener);
     // Verify listener call
@@ -361,20 +361,20 @@
     request.timestamp = timestampNanos;
     mService->setFocusedWindow(request);
     // call set input windows and wait for the callback to drain the queue.
-    setInputWindowsByInfos(std::vector<InputWindowInfo>());
+    setInputWindowsByInfos(std::vector<WindowInfo>());
 }
 
 /**
  *  Test InputFlinger service interface SetInputWindows
  */
 TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) {
-    std::vector<InputWindowInfo> infos = {getInfo()};
+    std::vector<WindowInfo> infos = {getInfo()};
     setInputWindowsByInfos(infos);
 
     // Verify input windows from service
-    std::vector<::android::InputWindowInfo> windowInfos;
+    std::vector<WindowInfo> windowInfos;
     mQuery->getInputWindows(&windowInfos);
-    for (const ::android::InputWindowInfo& windowInfo : windowInfos) {
+    for (const WindowInfo& windowInfo : windowInfos) {
         verifyInputWindowInfo(windowInfo);
     }
 }
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 73198bc..778f6e6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -32,6 +32,7 @@
 #include <VibratorInputMapper.h>
 #include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <inttypes.h>
 #include <math.h>
 
@@ -2276,6 +2277,7 @@
     const Point centerPoint = mDevice->getCenterPoint();
 
     // ACTION_DOWN
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
@@ -2296,6 +2298,8 @@
     const Point centerPoint = mDevice->getCenterPoint();
 
     // ACTION_DOWN
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
     mDevice->sendDown(centerPoint);
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
@@ -8217,6 +8221,70 @@
     ASSERT_NE(AMOTION_EVENT_FLAG_CANCELED, motionArgs.flags);
 }
 
+/**
+ * Test multi-touch should sent ACTION_POINTER_UP/ACTION_UP when received the INVALID_TRACKING_ID,
+ * to prevent the driver side may send unexpected data after set tracking id as INVALID_TRACKING_ID
+ * cause slot be valid again.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | PRESSURE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 0, y2 = 0;
+    // First finger down.
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1, y1);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // First finger move.
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1 + 1, y1 + 1);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+
+    // Second finger down.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2, y2);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(uint32_t(2), motionArgs.pointerCount);
+
+    // second finger up with some unexpected data.
+    processSlot(mapper, SECOND_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(uint32_t(2), motionArgs.pointerCount);
+
+    // first finger up with some unexpected data.
+    processSlot(mapper, FIRST_SLOT);
+    processId(mapper, INVALID_TRACKING_ID);
+    processPosition(mapper, x2, y2);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
+}
+
 // --- MultiTouchInputMapperTest_ExternalDevice ---
 
 class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 2281721..726fe8e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2161,8 +2161,9 @@
 
 bool SensorService::isRateCappedBasedOnPermission(const String16& opPackageName) {
     int targetSdk = getTargetSdkVersion(opPackageName);
-    bool hasSamplingRatePermission = PermissionCache::checkCallingPermission(
-                    sAccessHighSensorSamplingRatePermission);
+    bool hasSamplingRatePermission = checkPermission(sAccessHighSensorSamplingRatePermission,
+            IPCThreadState::self()->getCallingPid(),
+            IPCThreadState::self()->getCallingUid());
     if (targetSdk < __ANDROID_API_S__ ||
             (targetSdk >= __ANDROID_API_S__ && hasSamplingRatePermission)) {
         return false;
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index e669e45..63f7efc 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -211,7 +211,6 @@
         "libcutils",
         "libdisplayservicehidl",
         "libhidlbase",
-        "libinput",
         "liblayers_proto",
         "liblog",
         "libprocessgroup",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 23779be..6a78b53 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,7 +421,7 @@
         mFrameTracker.setFrameReadyTime(desiredPresentTime);
     }
 
-    const Fps refreshRate = mFlinger->mRefreshRateConfigs->getCurrentRefreshRate().getFps();
+    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,
@@ -825,7 +827,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/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index cd3d80e..760c8b9 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -133,6 +133,7 @@
         bool mTransformToDisplayInverse{false};
 
         std::shared_ptr<renderengine::ExternalTexture> mBuffer;
+        uint64_t mFrameNumber;
         int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
 
         bool mFrameLatencyNeeded{false};
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index b236fd2..2fa8936 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -126,6 +126,11 @@
 
 bool BufferQueueLayer::fenceHasSignaled() const {
     Mutex::Autolock lock(mQueueItemLock);
+
+    if (SurfaceFlinger::enableLatchUnsignaled) {
+        return true;
+    }
+
     if (!hasFrameUpdate()) {
         return true;
     }
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 6b5cf04..645e883 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -44,25 +44,18 @@
 using PresentState = frametimeline::SurfaceFrame::PresentState;
 namespace {
 void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
-                               const sp<GraphicBuffer>& buffer, const sp<Fence>& releaseFence,
-                               uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) {
+                               const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+                               const sp<Fence>& releaseFence, uint32_t transformHint,
+                               uint32_t currentMaxAcquiredBufferCount) {
     if (!listener) {
         return;
     }
-    listener->onReleaseBuffer(buffer->getId(), releaseFence ? releaseFence : Fence::NO_FENCE,
-                              transformHint, currentMaxAcquiredBufferCount);
+    listener->onReleaseBuffer({buffer->getId(), framenumber},
+                              releaseFence ? releaseFence : Fence::NO_FENCE, transformHint,
+                              currentMaxAcquiredBufferCount);
 }
 } // namespace
 
-// clang-format off
-const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{
-        1, 0, 0, 0,
-        0, 1, 0, 0,
-        0, 0, 1, 0,
-        0, 0, 0, 1
-};
-// clang-format on
-
 BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
       : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
     mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
@@ -75,8 +68,8 @@
     // issue with the clone layer trying to use the texture.
     if (mBufferInfo.mBuffer != nullptr && !isClone()) {
         callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                  mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFence,
-                                  mTransformHint,
+                                  mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
+                                  mBufferInfo.mFence, mTransformHint,
                                   mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
                                           mOwnerUid));
     }
@@ -87,7 +80,7 @@
     if (ch == nullptr) {
         return OK;
     }
-    ch->previousBufferId = mPreviousBufferId;
+    ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
     if (!ch->previousReleaseFence.get()) {
         ch->previousReleaseFence = fence;
         return OK;
@@ -214,7 +207,7 @@
     // see BufferStateLayer::onLayerDisplayed.
     for (auto& handle : mDrawingState.callbackHandles) {
         if (handle->releasePreviousBuffer) {
-            handle->previousBufferId = mPreviousBufferId;
+            handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
             break;
         }
     }
@@ -438,8 +431,8 @@
             // dropped and we should decrement the pending buffer count and
             // call any release buffer callbacks if set.
             callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                      mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence,
-                                      mTransformHint,
+                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                      mDrawingState.acquireFence, mTransformHint,
                                       mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
                                               mOwnerUid));
             decrementPendingBufferCount();
@@ -642,6 +635,10 @@
 // Interface implementation for BufferLayer
 // -----------------------------------------------------------------------
 bool BufferStateLayer::fenceHasSignaled() const {
+    if (SurfaceFlinger::enableLatchUnsignaled) {
+        return true;
+    }
+
     const bool fenceSignaled =
             getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
     if (!fenceSignaled) {
@@ -684,7 +681,7 @@
  *     DeferTransactionUntil -> frameNumber = 2
  *     Random other stuff
  *  }
- * Now imagine getHeadFrameNumber returned mDrawingState.mFrameNumber (or mCurrentFrameNumber).
+ * Now imagine mFrameNumber returned mDrawingState.frameNumber (or mCurrentFrameNumber).
  * Prior to doTransaction SurfaceFlinger will call notifyAvailableFrames, but because we
  * haven't swapped mDrawingState to mDrawingState yet we will think the sync point
  * is not ready. So we will return false from applyPendingState and not swap
@@ -725,7 +722,7 @@
 
 bool BufferStateLayer::hasFrameUpdate() const {
     const State& c(getDrawingState());
-    return mDrawingStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
+    return (mDrawingStateModified || mDrawingState.modified) && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
@@ -792,9 +789,10 @@
         decrementPendingBufferCount();
     }
 
-    mPreviousBufferId = getCurrentBufferId();
+    mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
     mBufferInfo.mBuffer = s.buffer;
     mBufferInfo.mFence = s.acquireFence;
+    mBufferInfo.mFrameNumber = s.frameNumber;
 
     return NO_ERROR;
 }
@@ -969,8 +967,8 @@
         // then we will drop a buffer and should decrement the pending buffer count and
         // call any release buffer callbacks if set.
         callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                  mDrawingState.buffer->getBuffer(), mDrawingState.acquireFence,
-                                  mTransformHint,
+                                  mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                  mDrawingState.acquireFence, mTransformHint,
                                   mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
                                           mOwnerUid));
         decrementPendingBufferCount();
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 2747018..e567478 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -143,15 +143,8 @@
 
     bool bufferNeedsFiltering() const override;
 
-    static const std::array<float, 16> IDENTITY_MATRIX;
-
-    std::unique_ptr<renderengine::Image> mTextureImage;
-
-    mutable uint64_t mFrameNumber{0};
-    uint64_t mFrameCounter{0};
-
     sp<Fence> mPreviousReleaseFence;
-    uint64_t mPreviousBufferId = 0;
+    ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     uint64_t mPreviousReleasedFrameNumber = 0;
 
     bool mReleasePreviousBuffer = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 29937fb..554e2f4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -24,6 +24,7 @@
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/OutputColorSetting.h>
 #include <math/mat4.h>
+#include <ui/FenceTime.h>
 #include <ui/Transform.h>
 
 namespace android::compositionengine {
@@ -83,6 +84,10 @@
     // The earliest time to send the present command to the HAL
     std::chrono::steady_clock::time_point earliestPresentTime;
 
+    // The previous present fence. Used together with earliestPresentTime
+    // to prevent an early presentation of a frame.
+    std::shared_ptr<FenceTime> previousPresentFence;
+
     // The predicted next invalidation time
     std::optional<std::chrono::steady_clock::time_point> nextInvalidateTime;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index d41c2dd..f34cb94 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -19,6 +19,7 @@
 #include <cstdint>
 
 #include <math/mat4.h>
+#include <ui/FenceTime.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -118,6 +119,10 @@
     // The earliest time to send the present command to the HAL
     std::chrono::steady_clock::time_point earliestPresentTime;
 
+    // The previous present fence. Used together with earliestPresentTime
+    // to prevent an early presentation of a frame.
+    std::shared_ptr<FenceTime> previousPresentFence;
+
     // Current display brightness
     float displayBrightnessNits{-1.f};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 7cb0f6b..2bf931c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -141,7 +141,7 @@
 
     compositionengine::OutputLayer* getBlurLayer() const;
 
-    bool hasHdrLayers() const;
+    bool hasUnsupportedDataspace() const;
 
     bool hasProtectedLayers() const;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index a20d7b3..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>
 
@@ -242,13 +242,6 @@
 
     ui::Dataspace getDataspace() const { return mOutputDataspace.get(); }
 
-    bool isHdr() const {
-        const ui::Dataspace transfer =
-                static_cast<ui::Dataspace>(getDataspace() & ui::Dataspace::TRANSFER_MASK);
-        return (transfer == ui::Dataspace::TRANSFER_ST2084 ||
-                transfer == ui::Dataspace::TRANSFER_HLG);
-    }
-
     bool isProtected() const {
         return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index ae1336e..2f2c686 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -229,7 +229,8 @@
     auto& hwc = getCompositionEngine().getHwComposer();
     if (status_t result =
                 hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
-                                                getState().earliestPresentTime, &changes);
+                                                getState().earliestPresentTime,
+                                                getState().previousPresentFence, &changes);
         result != NO_ERROR) {
         ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
               strerror(-result));
@@ -330,7 +331,8 @@
     }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
+    hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime,
+                                   getState().previousPresentFence);
 
     fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 67bb149..cafcb40 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -729,6 +729,7 @@
     }
 
     editState().earliestPresentTime = refreshArgs.earliestPresentTime;
+    editState().previousPresentFence = refreshArgs.previousPresentFence;
 
     compositionengine::OutputLayer* peekThroughLayer = nullptr;
     sp<GraphicBuffer> previousOverride = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index e4e46a7..56e9d27 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -216,8 +216,18 @@
 
     // reduce uses a FloatRect to provide more accuracy during the
     // transformation. We then round upon constructing 'frame'.
-    Rect frame{
-            layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))};
+    FloatRect geomLayerBounds = layerState.geomLayerBounds;
+
+    // Some HWCs may clip client composited input to its displayFrame. Make sure
+    // that this does not cut off the shadow.
+    if (layerState.forceClientComposition && layerState.shadowRadius > 0.0f) {
+        const auto outset = layerState.shadowRadius;
+        geomLayerBounds.left -= outset;
+        geomLayerBounds.top -= outset;
+        geomLayerBounds.right += outset;
+        geomLayerBounds.bottom += outset;
+    }
+    Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))};
     if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
         frame.clear();
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index acc7ed2..b24274e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -295,9 +295,7 @@
         return false;
     }
 
-    // Do not use a hole punch with an HDR layer; this should be done in client
-    // composition to properly mix HDR with SDR.
-    if (hasHdrLayers()) {
+    if (hasUnsupportedDataspace()) {
         return false;
     }
 
@@ -352,9 +350,22 @@
     return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr;
 }
 
-bool CachedSet::hasHdrLayers() const {
-    return std::any_of(mLayers.cbegin(), mLayers.cend(),
-                       [](const Layer& layer) { return layer.getState()->isHdr(); });
+bool CachedSet::hasUnsupportedDataspace() const {
+    return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
+        auto dataspace = layer.getState()->getDataspace();
+        const auto transfer = static_cast<ui::Dataspace>(dataspace & ui::Dataspace::TRANSFER_MASK);
+        if (transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG) {
+            // Skip HDR.
+            return true;
+        }
+
+        if ((dataspace & HAL_DATASPACE_STANDARD_MASK) == HAL_DATASPACE_STANDARD_BT601_625) {
+            // RenderEngine does not match some DPUs, so skip
+            // to avoid flickering/color differences.
+            return true;
+        }
+        return false;
+    });
 }
 
 bool CachedSet::hasProtectedLayers() const {
@@ -378,10 +389,14 @@
     if (mLayers.size() == 1) {
         base::StringAppendF(&result, "    Layer [%s]\n", mLayers[0].getName().c_str());
         base::StringAppendF(&result, "    Buffer %p", mLayers[0].getBuffer().get());
+        base::StringAppendF(&result, "    Protected [%s]",
+                            mLayers[0].getState()->isProtected() ? "true" : "false");
     } else {
         result.append("    Cached set of:");
         for (const Layer& layer : mLayers) {
             base::StringAppendF(&result, "\n      Layer [%s]", layer.getName().c_str());
+            base::StringAppendF(&result, "\n      Protected [%s]",
+                                layer.getState()->isProtected() ? "true" : "false");
         }
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 2bcaf60..f033279 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -420,7 +420,7 @@
         const bool layerIsInactive = now - currentSet->getLastUpdate() > mActiveLayerTimeout;
         const bool layerHasBlur = currentSet->hasBlurBehind();
         if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
-            !currentSet->hasHdrLayers() && !currentSet->hasProtectedLayers()) {
+            !currentSet->hasUnsupportedDataspace()) {
             if (isPartOfRun) {
                 builder.append(currentSet->getLayerCount());
             } else {
@@ -491,6 +491,14 @@
         return;
     }
 
+    for (const CachedSet& layer : mLayers) {
+        // TODO (b/191997217): make it less aggressive, and sync with findCandidateRuns
+        if (layer.hasProtectedLayers()) {
+            ATRACE_NAME("layer->hasProtectedLayers()");
+            return;
+        }
+    }
+
     std::vector<Run> runs = findCandidateRuns(now);
 
     std::optional<Run> bestRun = findBestRun(runs);
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index f4a5835..72b16e0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -579,7 +579,7 @@
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
     EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _))
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _))
             .WillOnce(Return(INVALID_OPERATION));
 
     mDisplay->chooseCompositionStrategy();
@@ -602,7 +602,7 @@
             .WillOnce(Return(false));
 
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
@@ -633,8 +633,8 @@
             .WillOnce(Return(false));
 
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
-            .WillOnce(DoAll(SetArgPointee<3>(changes), Return(NO_ERROR)));
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(changes), Return(NO_ERROR)));
     EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
     EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
     EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
@@ -844,7 +844,7 @@
     sp<Fence> layer1Fence = new Fence();
     sp<Fence> layer2Fence = new Fence();
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _))
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _, _))
             .Times(1);
     EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
             .WillOnce(Return(presentFence));
@@ -1020,7 +1020,7 @@
 
     mDisplay->editState().isEnabled = true;
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _, _));
     EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
 
     mDisplay->postFramebuffer();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 72de0b1..65c97b7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -53,14 +53,16 @@
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
 
     MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
-    MOCK_METHOD4(getDeviceCompositionChanges,
+    MOCK_METHOD5(getDeviceCompositionChanges,
                  status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
+                          const std::shared_ptr<FenceTime>&,
                           std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
                  status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
                           ui::Dataspace));
-    MOCK_METHOD2(presentAndGetReleaseFences,
-                 status_t(HalDisplayId, std::chrono::steady_clock::time_point));
+    MOCK_METHOD3(presentAndGetReleaseFences,
+                 status_t(HalDisplayId, std::chrono::steady_clock::time_point,
+                          const std::shared_ptr<FenceTime>&));
     MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
     MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
     MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index b738096..fc8cb50 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -29,8 +29,10 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    MOCK_METHOD0(init, void());
     MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
+    MOCK_METHOD0(isUsingExpensiveRendering, bool());
     MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index e9ecf3e..c8c6012 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -324,6 +324,27 @@
     EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
+TEST_F(OutputLayerDisplayFrameTest, shadowExpandsDisplayFrame) {
+    const int kShadowRadius = 5;
+    mLayerFEState.shadowRadius = kShadowRadius;
+    mLayerFEState.forceClientComposition = true;
+
+    mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
+    Rect expected{mLayerFEState.geomLayerBounds};
+    expected.inset(-kShadowRadius, -kShadowRadius, -kShadowRadius, -kShadowRadius);
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
+}
+
+TEST_F(OutputLayerDisplayFrameTest, shadowExpandsDisplayFrame_onlyIfForcingClientComposition) {
+    const int kShadowRadius = 5;
+    mLayerFEState.shadowRadius = kShadowRadius;
+    mLayerFEState.forceClientComposition = false;
+
+    mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
+    Rect expected{mLayerFEState.geomLayerBounds};
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
+}
+
 /*
  * OutputLayer::calculateOutputRelativeBufferTransform()
  */
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 742b155..ee73cfc 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3487,7 +3487,7 @@
     mLayer2.mLayerFEState.hasProtectedContent = true;
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
     EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
-    EXPECT_CALL(mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(false));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 0acc317..7f0e186 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -457,6 +457,20 @@
     EXPECT_FALSE(cachedSet.requiresHolePunch());
 }
 
+TEST_F(CachedSetTest, holePunch_requiresNonBT601_625) {
+    mTestLayers[0]->outputLayerCompositionState.dataspace = ui::Dataspace::STANDARD_BT601_625;
+    mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer);
+
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+    sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+
+    CachedSet cachedSet(layer);
+    EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
 TEST_F(CachedSetTest, requiresHolePunch) {
     CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
     mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 334b855..f5cfd2f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -937,5 +937,152 @@
                                          (kCachedSetRenderDuration + 10ms));
 }
 
+TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    // The third layer uses a dataspace that will not be flattened due to
+    // possible mismatch with DPU rendering.
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+    mTestLayers[2]->outputLayerCompositionState.dataspace = ui::Dataspace::STANDARD_BT601_625;
+    mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    mTime += 200ms;
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    // This time we merge the CachedSet in, so we have a new hash, and we should
+    // only have two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_skipsHDR) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    // The third layer uses a dataspace that will not be flattened due to
+    // possible mismatch with DPU rendering.
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+    mTestLayers[2]->outputLayerCompositionState.dataspace = ui::Dataspace::BT2020_ITU_HLG;
+    mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    mTime += 200ms;
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    // This time we merge the CachedSet in, so we have a new hash, and we should
+    // only have two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
+TEST_F(FlattenerTest, flattenLayers_skipsHDR2) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    // The third layer uses a dataspace that will not be flattened due to
+    // possible mismatch with DPU rendering.
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+    mTestLayers[2]->outputLayerCompositionState.dataspace = ui::Dataspace::BT2020_PQ;
+    mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    mTime += 200ms;
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    // This time we merge the CachedSet in, so we have a new hash, and we should
+    // only have two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index c391901..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,7 +232,10 @@
 }
 
 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) {
@@ -226,7 +244,11 @@
 
 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,
@@ -266,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,
@@ -296,6 +318,10 @@
     }
     result.append("\n");
     getCompositionDisplay()->dump(result);
+
+    if (mRefreshRateConfigs) {
+        mRefreshRateConfigs->dump(result);
+    }
 }
 
 bool DisplayDevice::hasRenderIntent(ui::RenderIntent intent) const {
@@ -382,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 b068daf..afd12b5 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;
@@ -79,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.
@@ -175,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.
@@ -190,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;
@@ -214,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;
@@ -235,6 +281,15 @@
     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 {
@@ -280,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;
@@ -295,6 +351,7 @@
             hardware::graphics::composer::hal::PowerMode::ON};
     bool isPrimary{false};
     DisplayModes supportedModes;
+    DisplayModeId activeModeId;
 };
 
 } // namespace android
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 32f04e5..7e45dab 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -471,6 +471,7 @@
 status_t HWComposer::getDeviceCompositionChanges(
         HalDisplayId displayId, bool frameUsesClientComposition,
         std::chrono::steady_clock::time_point earliestPresentTime,
+        const std::shared_ptr<FenceTime>& previousPresentFence,
         std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
@@ -487,12 +488,16 @@
 
     hal::Error error = hal::Error::NONE;
 
-    // First try to skip validate altogether when we passed the earliest time
-    // to present and there is no client. Otherwise, we may present a frame too
-    // early or in case of client composition we first need to render the
+    // First try to skip validate altogether. We can do that when
+    // 1. The previous frame has not been presented yet or already passed the
+    // earliest time to present. Otherwise, we may present a frame too early.
+    // 2. There is no client composition. Otherwise, we first need to render the
     // client target buffer.
-    const bool canSkipValidate =
-            std::chrono::steady_clock::now() >= earliestPresentTime && !frameUsesClientComposition;
+    const bool prevFencePending =
+            previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
+    const bool canPresentEarly =
+            !prevFencePending && std::chrono::steady_clock::now() < earliestPresentTime;
+    const bool canSkipValidate = !canPresentEarly && !frameUsesClientComposition;
     displayData.validateWasSkipped = false;
     if (canSkipValidate) {
         sp<Fence> outPresentFence;
@@ -559,7 +564,8 @@
 }
 
 status_t HWComposer::presentAndGetReleaseFences(
-        HalDisplayId displayId, std::chrono::steady_clock::time_point earliestPresentTime) {
+        HalDisplayId displayId, std::chrono::steady_clock::time_point earliestPresentTime,
+        const std::shared_ptr<FenceTime>& previousPresentFence) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -575,7 +581,9 @@
         return NO_ERROR;
     }
 
-    {
+    const bool previousFramePending =
+            previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
+    if (!previousFramePending) {
         ATRACE_NAME("wait for earliest present time");
         std::this_thread::sleep_until(earliestPresentTime);
     }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index cd6f9f5..b1849e8 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -27,7 +27,7 @@
 #include <vector>
 
 #include <android-base/thread_annotations.h>
-#include <ui/Fence.h>
+#include <ui/FenceTime.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -134,6 +134,7 @@
     virtual status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
             std::chrono::steady_clock::time_point earliestPresentTime,
+            const std::shared_ptr<FenceTime>& previousPresentFence,
             std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
     virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
@@ -141,7 +142,8 @@
 
     // Present layers to the display and read releaseFences.
     virtual status_t presentAndGetReleaseFences(
-            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) = 0;
+            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime,
+            const std::shared_ptr<FenceTime>& previousPresentFence) = 0;
 
     // set power mode
     virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
@@ -275,6 +277,7 @@
     status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
             std::chrono::steady_clock::time_point earliestPresentTime,
+            const std::shared_ptr<FenceTime>& previousPresentFence,
             std::optional<DeviceRequestedChanges>* outChanges) override;
 
     status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
@@ -282,7 +285,8 @@
 
     // Present layers to the display and read releaseFences.
     status_t presentAndGetReleaseFences(
-            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) override;
+            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime,
+            const std::shared_ptr<FenceTime>& previousPresentFence) override;
 
     // set power mode
     status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 901e19a..1765caf 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -32,6 +32,7 @@
 #include "../SurfaceFlingerProperties.h"
 
 #include "PowerAdvisor.h"
+#include "SurfaceFlinger.h"
 
 namespace android {
 namespace Hwc2 {
@@ -61,14 +62,22 @@
 
 } // namespace
 
-PowerAdvisor::PowerAdvisor()
-      : mUseUpdateImminentTimer(getUpdateTimeout() > 0),
-        mUpdateImminentTimer(
+PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
+      : mFlinger(flinger),
+        mUseScreenUpdateTimer(getUpdateTimeout() > 0),
+        mScreenUpdateTimer(
                 "UpdateImminentTimer", OneShotTimer::Interval(getUpdateTimeout()),
                 /* resetCallback */ [this] { mSendUpdateImminent.store(false); },
-                /* timeoutCallback */ [this] { mSendUpdateImminent.store(true); }) {
-    if (mUseUpdateImminentTimer) {
-        mUpdateImminentTimer.start();
+                /* timeoutCallback */
+                [this] {
+                    mSendUpdateImminent.store(true);
+                    mFlinger.disableExpensiveRendering();
+                }) {}
+
+void PowerAdvisor::init() {
+    // Defer starting the screen update timer until SurfaceFlinger finishes construction.
+    if (mUseScreenUpdateTimer) {
+        mScreenUpdateTimer.start();
     }
 }
 
@@ -122,8 +131,8 @@
         }
     }
 
-    if (mUseUpdateImminentTimer) {
-        mUpdateImminentTimer.reset();
+    if (mUseScreenUpdateTimer) {
+        mScreenUpdateTimer.reset();
     }
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 95eb0e2..f2d0766 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -25,14 +25,20 @@
 #include "DisplayIdentification.h"
 
 namespace android {
+
+class SurfaceFlinger;
+
 namespace Hwc2 {
 
 class PowerAdvisor {
 public:
     virtual ~PowerAdvisor();
 
+    // Initializes resources that cannot be initialized on construction
+    virtual void init() = 0;
     virtual void onBootFinished() = 0;
     virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
+    virtual bool isUsingExpensiveRendering() = 0;
     virtual void notifyDisplayUpdateImminent() = 0;
 };
 
@@ -50,11 +56,13 @@
         virtual bool notifyDisplayUpdateImminent() = 0;
     };
 
-    PowerAdvisor();
+    PowerAdvisor(SurfaceFlinger& flinger);
     ~PowerAdvisor() override;
 
+    void init() override;
     void onBootFinished() override;
     void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
+    bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; }
     void notifyDisplayUpdateImminent() override;
 
 private:
@@ -67,9 +75,10 @@
     std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
 
-    const bool mUseUpdateImminentTimer;
+    SurfaceFlinger& mFlinger;
+    const bool mUseScreenUpdateTimer;
     std::atomic_bool mSendUpdateImminent = true;
-    scheduler::OneShotTimer mUpdateImminentTimer;
+    scheduler::OneShotTimer mScreenUpdateTimer;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 15ecf13..139f91f 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -96,7 +96,7 @@
     // The various thresholds for App and SF. If the actual timestamp falls within the threshold
     // compared to prediction, we treat it as on time.
     nsecs_t presentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
-    nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(0ms).count();
     nsecs_t startThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
 };
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ad31b3f..2976937 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;
@@ -177,6 +177,9 @@
     if (mDrawingState.sidebandStream != nullptr) {
         mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
     }
+    if (mHadClonedChild) {
+        mFlinger->mNumClones--;
+    }
 }
 
 LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
@@ -254,6 +257,7 @@
     if (mRemovedFromDrawingState) {
         mRemovedFromDrawingState = false;
         mFlinger->mScheduler->registerLayer(this);
+        mFlinger->removeFromOffscreenLayers(this);
     }
 
     for (const auto& child : mCurrentChildren) {
@@ -1986,7 +1990,7 @@
     return handle->owner;
 }
 
-void Layer::setInputInfo(const InputWindowInfo& info) {
+void Layer::setInputInfo(const WindowInfo& info) {
     mDrawingState.inputInfo = info;
     mDrawingState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
     mDrawingState.modified = true;
@@ -2136,7 +2140,7 @@
     }
 
     if (traceFlags & SurfaceTracing::TRACE_INPUT) {
-        InputWindowInfo info;
+        WindowInfo info;
         if (useDrawing) {
             info = fillInputInfo({nullptr});
         } else {
@@ -2167,7 +2171,7 @@
     return getCroppedBufferSize(getDrawingState());
 }
 
-void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay) {
+void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toPhysicalDisplay) {
     // 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
@@ -2262,7 +2266,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();
@@ -2272,17 +2276,17 @@
     }
 }
 
-InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
+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;
 
     if (info.displayId == ADISPLAY_ID_NONE) {
@@ -2456,7 +2460,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) {
@@ -2535,6 +2539,12 @@
     return parent == nullptr ? false : parent->getPrimaryDisplayOnly();
 }
 
+void Layer::setClonedChild(const sp<Layer>& clonedChild) {
+    mClonedChild = clonedChild;
+    mHadClonedChild = true;
+    mFlinger->mNumClones++;
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ec9bb7c..24789cf 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -21,7 +21,7 @@
 #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 +186,7 @@
         float cornerRadius;
         int backgroundBlurRadius;
 
-        InputWindowInfo inputInfo;
+        gui::WindowInfo inputInfo;
         wp<Layer> touchableRegionCrop;
 
         // dataspace is only used by BufferStateLayer and EffectLayer
@@ -686,7 +686,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;
 
@@ -838,9 +838,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.
      */
@@ -863,6 +863,8 @@
     // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
     // if the real layer is destroyed, then the clone layer will also be destroyed.
     sp<Layer> mClonedChild;
+    bool mHadClonedChild = false;
+    void setClonedChild(const sp<Layer>& mClonedChild);
 
     mutable bool contentDirty{false};
     Region surfaceDamageRegion;
@@ -1003,7 +1005,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.
@@ -1054,10 +1056,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& toPhysicalDisplay);
 
     bool updateFrameRateForLayerTree(bool treeHasFrameRateVote);
 
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index b1db6d3..79f7b1f 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,
@@ -125,7 +128,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 +136,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>);
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 502238d..187ce3d 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>
@@ -40,7 +40,7 @@
     static void writeToProto(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 5c85bf0..2a33ad8 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -179,10 +179,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() {
@@ -198,7 +202,6 @@
         return false;
     }
 
-    Mutex::Autolock _l(mFlinger.mStateLock);
     mLayer = mClient->getLayerUser(mIBinder);
     mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote));
 
@@ -261,8 +264,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());
@@ -274,6 +280,11 @@
     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];
@@ -299,13 +310,6 @@
     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 cb8b227..fd1e2df 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -39,12 +39,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/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 653aca6..aa2fec5 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -287,33 +287,12 @@
         descriptors.emplace_back(descriptor);
     }
 
-    auto dx = 0;
-    auto dy = 0;
-    switch (orientation) {
-        case ui::Transform::ROT_90:
-            dx = displaySize.getWidth();
-            break;
-        case ui::Transform::ROT_180:
-            dx = displaySize.getWidth();
-            dy = displaySize.getHeight();
-            break;
-        case ui::Transform::ROT_270:
-            dy = displaySize.getHeight();
-            break;
-        default:
-            break;
-    }
-
-    ui::Transform t(orientation);
-    auto screencapRegion = t.transform(sampleRegion);
-    screencapRegion = screencapRegion.translate(dx, dy);
-
     const Rect sampledBounds = sampleRegion.bounds();
+    constexpr bool kUseIdentityTransform = false;
 
     SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
-        return DisplayRenderArea::create(displayWeak, screencapRegion.bounds(),
-                                         sampledBounds.getSize(), ui::Dataspace::V0_SRGB,
-                                         orientation);
+        return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
+                                         ui::Dataspace::V0_SRGB, kUseIdentityTransform);
     });
 
     std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
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 0eb16e2..c38cd68 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -107,17 +107,47 @@
     return {quotient, remainder};
 }
 
+bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
+                                       const RefreshRate& refreshRate) const {
+    switch (layer.vote) {
+        case LayerVoteType::ExplicitExactOrMultiple:
+        case LayerVoteType::Heuristic:
+            if (mConfig.frameRateMultipleThreshold != 0 &&
+                refreshRate.getFps().greaterThanOrEqualWithMargin(
+                        Fps(mConfig.frameRateMultipleThreshold)) &&
+                layer.desiredRefreshRate.lessThanWithMargin(
+                        Fps(mConfig.frameRateMultipleThreshold / 2))) {
+                // Don't vote high refresh rates past the threshold for layers with a low desired
+                // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
+                // 120 Hz, but desired 60 fps should have a vote.
+                return false;
+            }
+            break;
+        case LayerVoteType::ExplicitDefault:
+        case LayerVoteType::ExplicitExact:
+        case LayerVoteType::Max:
+        case LayerVoteType::Min:
+        case LayerVoteType::NoVote:
+            break;
+    }
+    return true;
+}
+
 float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
                                                     const RefreshRate& refreshRate,
                                                     bool isSeamlessSwitch) const {
+    if (!isVoteAllowed(layer, refreshRate)) {
+        return 0;
+    }
+
     // Slightly prefer seamless switches.
     constexpr float kSeamedSwitchPenalty = 0.95f;
     const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
 
     // 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;
     }
@@ -331,8 +361,9 @@
     const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
 
     for (const auto& layer : layers) {
-        ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
-              layerVoteTypeString(layer.vote).c_str(), layer.weight);
+        ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
+              layerVoteTypeString(layer.vote).c_str(), layer.weight,
+              layer.desiredRefreshRate.getValue());
         if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
             continue;
         }
@@ -432,7 +463,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;
@@ -646,9 +677,8 @@
 }
 
 RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
-                                       bool enableFrameRateOverride)
-      : mKnownFrameRates(constructKnownFrameRates(modes)),
-        mEnableFrameRateOverride(enableFrameRateOverride) {
+                                       Config config)
+      : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
     updateDisplayModes(modes, currentModeId);
 }
 
@@ -669,8 +699,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();
         }
@@ -685,7 +714,7 @@
     mMaxSupportedRefreshRate = sortedModes.back();
 
     mSupportsFrameRateOverride = false;
-    if (mEnableFrameRateOverride) {
+    if (mConfig.enableFrameRateOverride) {
         for (const auto& mode1 : sortedModes) {
             for (const auto& mode2 : sortedModes) {
                 if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) {
@@ -763,7 +792,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;
         }
     }
@@ -778,7 +807,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());
         }
     }
@@ -859,6 +888,7 @@
     const auto& deviceMin = *mMinSupportedRefreshRate;
     const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
     const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+    const auto& currentPolicy = getCurrentPolicyLocked();
 
     // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
     // the min allowed refresh rate is higher than the device min, we do not want to enable the
@@ -867,6 +897,10 @@
         return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
     }
     if (minByPolicy == maxByPolicy) {
+        // when min primary range in display manager policy is below device min turn on the timer.
+        if (currentPolicy->primaryRange.min.lessThanWithMargin(deviceMin.getFps())) {
+            return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+        }
         return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
     }
     // Turn on the timer in all other cases.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 6cd0f42..4a9a1fd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -53,7 +53,7 @@
 public:
     // Margin used when matching refresh rates to the content desired ones.
     static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
-        std::chrono::nanoseconds(800us).count();
+            std::chrono::nanoseconds(800us).count();
 
     class RefreshRate {
     private:
@@ -64,25 +64,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 +97,7 @@
         friend RefreshRateConfigs;
         friend class RefreshRateConfigsTest;
 
-        const DisplayModeId modeId;
         DisplayModePtr mode;
-        // Refresh rate in frames per second
-        const Fps fps{0.0f};
     };
 
     using AllRefreshRatesMapType =
@@ -302,10 +297,19 @@
     // Returns a known frame rate that is the closest to frameRate
     Fps findClosestKnownFrameRate(Fps frameRate) const;
 
-    RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
-                       bool enableFrameRateOverride = false);
+    // Configuration flags.
+    struct Config {
+        bool enableFrameRateOverride = false;
 
-    void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
+        // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple
+        // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
+        // no threshold is set.
+        int frameRateMultipleThreshold = 0;
+    };
+
+    RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
+                       Config config = {.enableFrameRateOverride = false,
+                                        .frameRateMultipleThreshold = 0});
 
     // 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
@@ -315,8 +319,7 @@
         return mRefreshRates.size() > 1;
     }
 
-    // Class to enumerate options around toggling the kernel timer on and off. We have an option
-    // for no change to avoid extra calls to kernel.
+    // Class to enumerate options around toggling the kernel timer on and off.
     enum class KernelIdleTimerAction {
         TurnOff,  // Turn off the idle timer.
         TurnOn    // Turn on the idle timer.
@@ -344,6 +347,9 @@
 
     void dump(std::string& result) const EXCLUDES(mLock);
 
+    RefreshRateConfigs(const RefreshRateConfigs&) = delete;
+    void operator=(const RefreshRateConfigs&) = delete;
+
 private:
     friend class RefreshRateConfigsTest;
 
@@ -387,11 +393,16 @@
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
+    // Returns whether the layer is allowed to vote for the given refresh rate.
+    bool isVoteAllowed(const LayerRequirement&, const RefreshRate&) const;
+
     // calculates a score for a layer. Used to determine the display refresh rate
     // and the frame rate override for certains applications.
     float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
                                     bool isSeamlessSwitch) const REQUIRES(mLock);
 
+    void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
+
     // The list of refresh rates, indexed by display modes ID. This may change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
@@ -424,7 +435,7 @@
     // from based on the closest value.
     const std::vector<Fps> mKnownFrameRates;
 
-    const bool mEnableFrameRateOverride;
+    const Config mConfig;
     bool mSupportsFrameRateOverride;
 
     struct GetBestRefreshRateInvocation {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e0b3640..f808981 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,16 +117,17 @@
     }
 };
 
-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)}) {
 }
 
-Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
-                     Options options)
+Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
+                     ISchedulerCallback& callback, Options options)
       : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
-                  createLayerHistory(configs), options) {
+                  createLayerHistory(), options) {
     using namespace sysprop;
 
     const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
@@ -157,7 +160,8 @@
     }
 }
 
-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),
@@ -192,9 +196,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 +208,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 +243,8 @@
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
-    if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+    std::scoped_lock lock(mRefreshRateConfigsLock);
+    if (!mRefreshRateConfigs->supportsFrameRateOverride()) {
         return {};
     }
 
@@ -248,14 +255,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;
         }
@@ -341,7 +352,7 @@
 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 +371,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,32 +395,24 @@
         return;
     }
 
-    const auto modeId = *mFeatures.modeId;
-    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) {
@@ -530,7 +532,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);
     }
 }
 
@@ -579,10 +585,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 {
@@ -601,9 +606,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) {
@@ -611,25 +619,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) {
@@ -637,12 +648,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);
@@ -688,7 +699,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)) {
@@ -740,7 +755,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());
@@ -766,16 +781,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) {
@@ -790,33 +806,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
@@ -828,35 +844,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;
 
-    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) {
@@ -892,7 +909,7 @@
     }
 }
 
-void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
+void Scheduler::onActiveDisplayAreaChanged(uint32_t displayArea) {
     mLayerHistory->setDisplayArea(displayArea);
 }
 
@@ -901,7 +918,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..4b6905b 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,12 +125,13 @@
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
-    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
+    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();
@@ -147,7 +146,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 +155,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 +164,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 +175,21 @@
 
     // 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);
+    }
+
+    nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
+        std::scoped_lock lock(mRefreshRateConfigsLock);
+        return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+    }
 
 private:
     friend class TestableScheduler;
@@ -203,14 +214,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 +229,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 +243,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;
@@ -288,7 +306,7 @@
         TouchState touch = TouchState::Inactive;
         TimerState displayPowerTimer = TimerState::Expired;
 
-        std::optional<DisplayModeId> modeId;
+        DisplayModePtr mode;
         LayerHistory::Summary contentRequirements;
 
         bool isDisplayPowerStateNormal = true;
@@ -296,15 +314,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 +333,14 @@
 
     // 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);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 67390c7..07a67a9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -145,6 +145,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.\"")
@@ -159,6 +166,7 @@
 
 using android::hardware::power::Boost;
 using base::StringAppendF;
+using gui::WindowInfo;
 using ui::ColorMode;
 using ui::Dataspace;
 using ui::DisplayPrimaries;
@@ -310,6 +318,7 @@
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 bool SurfaceFlinger::useFrameRateApi;
 bool SurfaceFlinger::enableSdrDimming;
+bool SurfaceFlinger::enableLatchUnsignaled;
 
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
     switch(displayColorSetting) {
@@ -344,7 +353,8 @@
         mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
         mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
-        mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {
+        mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
+        mPowerAdvisor(*this) {
     ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
 
     mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); });
@@ -479,6 +489,8 @@
 
     // Debug property overrides ro. property
     enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
+
+    enableLatchUnsignaled = base::GetBoolProperty("debug.sf.latch_unsignaled"s, false);
 }
 
 SurfaceFlinger::~SurfaceFlinger() = default;
@@ -627,9 +639,7 @@
     mVirtualDisplayIdGenerators.gpu.releaseId(*id);
 }
 
-std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const {
-    Mutex::Autolock lock(mStateLock);
-
+std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
     const auto internalDisplayId = getInternalDisplayIdLocked();
     if (!internalDisplayId) {
         return {};
@@ -723,7 +733,7 @@
         mBootStage = BootStage::FINISHED;
 
         if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
-            enableRefreshRateOverlay(true);
+            ON_MAIN_THREAD(enableRefreshRateOverlay(true));
         }
     }));
 }
@@ -816,6 +826,8 @@
     // set initial conditions (e.g. unblank default device)
     initializeDisplays();
 
+    mPowerAdvisor.init();
+
     char primeShaderCache[PROPERTY_VALUE_MAX];
     property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
     if (atoi(primeShaderCache)) {
@@ -830,7 +842,7 @@
         }
     }
 
-    getRenderEngine().onPrimaryDisplaySizeChanged(display->getSize());
+    onActiveDisplaySizeChanged(display);
 
     // Inform native graphics APIs whether the present timestamp is supported:
 
@@ -1068,36 +1080,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);
     }
 }
@@ -1131,7 +1135,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}};
@@ -1151,59 +1155,43 @@
         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.
-        return;
-    }
-
-    if (display->getActiveMode()->getSize() != upcomingMode->getSize()) {
+    const auto upcomingModeInfo = MAIN_THREAD_GUARD(display->getUpcomingActiveMode());
+    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 (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);
 }
@@ -1211,66 +1199,88 @@
 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);
-    if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(desiredMode->getFps());
-    }
-
-    // Scheduler will submit an empty frame to HWC if needed.
-    mSetActiveModePending = true;
+void SurfaceFlinger::disableExpensiveRendering() {
+    schedule([=]() MAIN_THREAD {
+        ATRACE_CALL();
+        if (mPowerAdvisor.isUsingExpensiveRendering()) {
+            const auto& displays = ON_MAIN_THREAD(mDisplays);
+            for (const auto& [_, display] : displays) {
+                const static constexpr auto kDisable = false;
+                mPowerAdvisor.setExpensiveRenderingExpected(display->getId(), kDisable);
+            }
+        }
+    }).wait();
 }
 
 std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) {
@@ -1724,10 +1734,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();
@@ -1737,13 +1743,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,
@@ -1941,8 +1947,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__);
+            }
         }
     }
 
@@ -2070,6 +2081,7 @@
     const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
     const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
     refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
+    refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
     refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate();
 
     mGeometryInvalid = false;
@@ -2313,7 +2325,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);
     }
@@ -2450,7 +2462,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);
@@ -2502,6 +2513,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)
@@ -2563,11 +2575,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());
@@ -2622,6 +2629,14 @@
     if (const auto& physical = state.physical) {
         creationArgs.connectionType = physical->type;
         creationArgs.supportedModes = physical->supportedModes;
+        creationArgs.activeModeId = physical->activeMode->getId();
+        scheduler::RefreshRateConfigs::Config config =
+                {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
+                 .frameRateMultipleThreshold =
+                         base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0)};
+        creationArgs.refreshRateConfigs =
+                std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes,
+                                                                creationArgs.activeModeId, config);
     }
 
     if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
@@ -2678,7 +2693,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);
     }
 
@@ -2760,14 +2775,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) {
@@ -2832,16 +2845,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;
@@ -2867,16 +2871,18 @@
             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
@@ -3058,7 +3064,7 @@
 }
 
 void SurfaceFlinger::updateInputWindowInfo() {
-    std::vector<InputWindowInfo> inputInfos;
+    std::vector<WindowInfo> inputInfos;
 
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (!layer->needsInputInfo()) return;
@@ -3104,19 +3110,14 @@
     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");
         return;
     }
-    const auto displayId = displayState.physical->id;
-    mRefreshRateConfigs = std::make_unique<
-            scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
-                                           displayState.physical->activeMode->getId(),
-                                           android::sysprop::enable_frame_rate_override(false));
-    const auto currRefreshRate = displayState.physical->activeMode->getFps();
+    const auto currRefreshRate = display->getActiveMode()->getFps();
     mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
                                                                       hal::PowerMode::OFF);
 
@@ -3124,7 +3125,7 @@
     mVsyncModulator.emplace(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 =
@@ -3153,9 +3154,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(
@@ -3222,19 +3221,10 @@
         }
     }
 
-    // TODO(b/163019109): See if this traversal is needed at all...
-    if (!mOffscreenLayers.empty()) {
-        mDrawingState.traverse([&](Layer* layer) {
-            // If the layer can be reached when traversing mDrawingState, then the layer is no
-            // longer offscreen. Remove the layer from the offscreenLayer set.
-            if (mOffscreenLayers.count(layer)) {
-                mOffscreenLayers.erase(layer);
-            }
-        });
-    }
-
     commitOffscreenLayers();
-    mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
+    if (mNumClones > 0) {
+        mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
+    }
 }
 
 void SurfaceFlinger::commitOffscreenLayers() {
@@ -3340,7 +3330,9 @@
         mBootStage = BootStage::BOOTANIMATION;
     }
 
-    mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+    if (mNumClones > 0) {
+        mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+    }
 
     // Only continue with the refresh if there is actually new work to do
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
@@ -3560,7 +3552,7 @@
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
         const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
-        if (acquireFenceChanged && s.acquireFence &&
+        if (acquireFenceChanged && s.acquireFence && !enableLatchUnsignaled &&
             s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
             ATRACE_NAME("fence unsignaled");
             return false;
@@ -4080,10 +4072,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;
@@ -4231,7 +4223,7 @@
             return result;
         }
 
-        mirrorLayer->mClonedChild = mirrorFrom->createClone();
+        mirrorLayer->setClonedChild(mirrorFrom->createClone());
     }
 
     *outLayerId = mirrorLayer->sequence;
@@ -4473,7 +4465,8 @@
                           {}, 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();
     // Use phase of 0 since phase is not known.
@@ -4526,8 +4519,13 @@
     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) {
+        const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken);
+        if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
+            mActiveDisplayToken = display->getDisplayToken();
+            onActiveDisplayChangedLocked(getDisplayDeviceLocked(mActiveDisplayToken));
+        }
         // 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) {
@@ -4537,7 +4535,7 @@
             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) {
+        if (display->isInternal() && mode != hal::PowerMode::DOZE_SUSPEND) {
             getHwComposer().setVsyncEnabled(displayId, mHWCVsyncPendingState);
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
@@ -4554,7 +4552,7 @@
         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 (display->isInternal() && currentMode != hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4568,13 +4566,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 (display->isInternal() && 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 (display->isInternal()) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4584,7 +4582,7 @@
         getHwComposer().setPowerMode(displayId, mode);
     }
 
-    if (display->isPrimary()) {
+    if (display->isInternal()) {
         mTimeStats->setPowerMode(mode);
         mRefreshRateStats->setPowerMode(mode);
         mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
@@ -4753,8 +4751,6 @@
                   "      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");
 
@@ -5630,16 +5626,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: {
@@ -5648,7 +5645,7 @@
                 const auto display = [&]() -> sp<IBinder> {
                     uint64_t value;
                     if (data.readUint64(&value) != NO_ERROR) {
-                        return getInternalDisplayToken();
+                        return getDefaultDisplayDevice()->getDisplayToken().promote();
                     }
 
                     if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(value)) {
@@ -5769,22 +5766,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();
         }
     }));
@@ -5797,8 +5797,13 @@
     if (!mSupportKernelIdleTimer) {
         return;
     }
-    const KernelIdleTimerAction action = mRefreshRateConfigs->getIdleTimerAction();
+    const auto display = getDefaultDisplayDeviceLocked();
+    if (!display) {
+        ALOGW("%s: default display is null", __func__);
+        return;
+    }
 
+    const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction();
     switch (action) {
         case KernelIdleTimerAction::TurnOff:
             if (mKernelIdleTimerEnabled) {
@@ -6413,78 +6418,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;
@@ -6544,29 +6526,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) {
@@ -6598,7 +6570,7 @@
 
 void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
     mNumLayers--;
-    removeFromOffscreenLayers(layer);
+    removeHierarchyFromOffscreenLayers(layer);
     if (!layer->isRemovedFromCurrentState()) {
         mScheduler->deregisterLayer(layer);
     }
@@ -6611,13 +6583,17 @@
 // from dangling children layers such that they are not reachable from the
 // Drawing state nor the offscreen layer list
 // See b/141111965
-void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
+void SurfaceFlinger::removeHierarchyFromOffscreenLayers(Layer* layer) {
     for (auto& child : layer->getCurrentChildren()) {
         mOffscreenLayers.emplace(child.get());
     }
     mOffscreenLayers.erase(layer);
 }
 
+void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
+    mOffscreenLayers.erase(layer);
+}
+
 status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                                  float lightPosY, float lightPosZ,
                                                  float lightRadius) {
@@ -6697,7 +6673,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);
@@ -6765,25 +6742,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(
@@ -6811,7 +6774,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;
 }
@@ -6822,7 +6792,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);
 }
@@ -6938,6 +6915,24 @@
     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 (!activeDisplay) {
+        ALOGE("%s: activeDisplay is null", __func__);
+        return;
+    }
+    updateInternalDisplayVsyncLocked(activeDisplay);
+    mScheduler->setModeChangePending(false);
+    mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs());
+    onActiveDisplaySizeChanged(activeDisplay);
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1d6f016..80f3f9c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -270,6 +270,8 @@
     // being treated as native display brightness
     static bool enableSdrDimming;
 
+    static bool enableLatchUnsignaled;
+
     // must be called before clients can connect
     void init() ANDROID_API;
 
@@ -311,8 +313,12 @@
     void onLayerFirstRef(Layer*);
     void onLayerDestroyed(Layer*);
 
+    void removeHierarchyFromOffscreenLayers(Layer* layer);
     void removeFromOffscreenLayers(Layer* layer);
 
+    // TODO: Remove atomic if move dtor to main thread CL lands
+    std::atomic<uint32_t> mNumClones;
+
     TransactionCallbackInvoker& getTransactionCallbackInvoker() {
         return mTransactionCallbackInvoker;
     }
@@ -329,6 +335,10 @@
     bool mDisableClientCompositionCache = false;
     void setInputWindowsFinished();
 
+    // Disables expensive rendering for all displays
+    // This is scheduled on the main thread
+    void disableExpensiveRendering();
+
 protected:
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
@@ -446,14 +456,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,
@@ -584,7 +587,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);
         }
     }
@@ -603,7 +606,10 @@
     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();
+    }
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
     status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
                                  const Vector<ComposerState>& state,
@@ -739,7 +745,7 @@
     // 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;
@@ -768,9 +774,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);
@@ -803,7 +809,7 @@
     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);
 
@@ -940,18 +946,35 @@
         return it == mDisplays.end() ? nullptr : it->second;
     }
 
+    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);
+        }
+        return nullptr;
+    }
+
     sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
         return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
     }
 
     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();
     }
@@ -970,12 +993,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) REQUIRES(mStateLock) {
+        return display->getDisplayToken() == mActiveDisplayToken;
+    }
+
     /*
      * H/W composer
      */
@@ -1032,8 +1061,6 @@
     // 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;
@@ -1098,6 +1125,10 @@
             REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
+    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock);
+
+    void onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay);
+
     /*
      * Debugging & dumpsys
      */
@@ -1163,13 +1194,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);
 
@@ -1177,6 +1201,9 @@
                                                std::chrono::nanoseconds presentLatency);
     int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
 
+    void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
+            REQUIRES(mStateLock);
+
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
 
@@ -1373,24 +1400,13 @@
     // Optional to defer construction until PhaseConfiguration is created.
     std::optional<scheduler::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);
-
     // below flags are set by main thread only
-    TracedOrdinal<bool> mDesiredActiveModeChanged
-            GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
     bool mSetActiveModePending = false;
 
     bool mLumaSampling = true;
@@ -1413,8 +1429,7 @@
     // 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;
@@ -1465,6 +1480,15 @@
 
     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);
 };
 
 } // 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/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index fdf16a7..6af69f0 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -237,7 +237,8 @@
                                                     handle->previousReleaseFence,
                                                     handle->transformHint,
                                                     handle->currentMaxAcquiredBufferCount,
-                                                    eventStats, jankData, handle->previousBufferId);
+                                                    eventStats, jankData,
+                                                    handle->previousReleaseCallbackId);
     }
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 444bec6..6f4d812 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -51,7 +51,7 @@
     nsecs_t refreshStartTime = 0;
     nsecs_t dequeueReadyTime = 0;
     uint64_t frameNumber = 0;
-    uint64_t previousBufferId = 0;
+    ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
 };
 
 class TransactionCallbackInvoker {
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 5aa809d..579a26e 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -29,7 +29,7 @@
 // b/181132765 - disabled until cuttlefish failures are investigated
 class ReleaseBufferCallbackHelper {
 public:
-    static void function(void* callbackContext, uint64_t graphicsBufferId,
+    static void function(void* callbackContext, ReleaseCallbackId callbackId,
                          const sp<Fence>& releaseFence,
                          uint32_t /*currentMaxAcquiredBufferCount*/) {
         if (!callbackContext) {
@@ -38,11 +38,11 @@
         ReleaseBufferCallbackHelper* helper =
                 static_cast<ReleaseBufferCallbackHelper*>(callbackContext);
         std::lock_guard lock(helper->mMutex);
-        helper->mCallbackDataQueue.emplace(graphicsBufferId, releaseFence);
+        helper->mCallbackDataQueue.emplace(callbackId, releaseFence);
         helper->mConditionVariable.notify_all();
     }
 
-    void getCallbackData(uint64_t* bufferId) {
+    void getCallbackData(ReleaseCallbackId* callbackId) {
         std::unique_lock lock(mMutex);
         if (mCallbackDataQueue.empty()) {
             if (!mConditionVariable.wait_for(lock, std::chrono::seconds(3),
@@ -53,7 +53,7 @@
 
         auto callbackData = mCallbackDataQueue.front();
         mCallbackDataQueue.pop();
-        *bufferId = callbackData.first;
+        *callbackId = callbackData.first;
     }
 
     void verifyNoCallbacks() {
@@ -72,7 +72,7 @@
 
     std::mutex mMutex;
     std::condition_variable mConditionVariable;
-    std::queue<std::pair<uint64_t, sp<Fence>>> mCallbackDataQueue;
+    std::queue<std::pair<ReleaseCallbackId, sp<Fence>>> mCallbackDataQueue;
 };
 
 class ReleaseBufferCallbackTest : public LayerTransactionTest {
@@ -82,10 +82,11 @@
     }
 
     static void submitBuffer(const sp<SurfaceControl>& layer, sp<GraphicBuffer> buffer,
-                             sp<Fence> fence, CallbackHelper& callback,
+                             sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id,
                              ReleaseBufferCallbackHelper& releaseCallback) {
         Transaction t;
-        t.setBuffer(layer, buffer, releaseCallback.getCallback());
+        t.setFrameNumber(layer, id.framenumber);
+        t.setBuffer(layer, buffer, id, releaseCallback.getCallback());
         t.setAcquireFence(layer, fence);
         t.addTransactionCompletedCallback(callback.function, callback.getContext());
         t.apply();
@@ -98,10 +99,10 @@
     }
 
     static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback,
-                                             uint64_t expectedReleaseBufferId) {
-        uint64_t actualReleaseBufferId;
+                                             const ReleaseCallbackId& expectedCallbackId) {
+        ReleaseCallbackId actualReleaseBufferId;
         releaseCallback.getCallbackData(&actualReleaseBufferId);
-        EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId);
+        EXPECT_EQ(expectedCallbackId, actualReleaseBufferId);
         releaseCallback.verifyNoCallbacks();
     }
     static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() {
@@ -116,6 +117,10 @@
                                          BufferUsage::COMPOSER_OVERLAY,
                                  "test");
     }
+    static uint64_t generateFrameNumber() {
+        static uint64_t sFrameNumber = 0;
+        return ++sFrameNumber;
+    }
 };
 
 TEST_F(ReleaseBufferCallbackTest, DISABLED_PresentBuffer) {
@@ -125,7 +130,9 @@
 
     // If a buffer is being presented, we should not emit a release callback.
     sp<GraphicBuffer> firstBuffer = getBuffer();
-    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, firstBufferCallbackId,
+                 *releaseCallback);
     ExpectedResult expected;
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                         ExpectedResult::Buffer::NOT_ACQUIRED);
@@ -143,13 +150,15 @@
     // If a presented buffer is replaced, we should emit a release callback for the
     // previously presented buffer.
     sp<GraphicBuffer> secondBuffer = getBuffer();
-    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, secondBufferCallbackId,
+                 *releaseCallback);
     expected = ExpectedResult();
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                         ExpectedResult::Buffer::NOT_ACQUIRED,
                         ExpectedResult::PreviousBuffer::RELEASED);
     ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
-    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
 
 TEST_F(ReleaseBufferCallbackTest, DISABLED_OffScreenLayer) {
@@ -160,7 +169,9 @@
 
     // If a buffer is being presented, we should not emit a release callback.
     sp<GraphicBuffer> firstBuffer = getBuffer();
-    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, transactionCallback, firstBufferCallbackId,
+                 *releaseCallback);
     ExpectedResult expected;
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                         ExpectedResult::Buffer::NOT_ACQUIRED);
@@ -184,23 +195,27 @@
     // If a presented buffer is replaced, we should emit a release callback for the
     // previously presented buffer.
     sp<GraphicBuffer> secondBuffer = getBuffer();
-    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, transactionCallback, secondBufferCallbackId,
+                 *releaseCallback);
     expected = ExpectedResult();
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                         ExpectedResult::Buffer::NOT_ACQUIRED,
                         ExpectedResult::PreviousBuffer::NOT_RELEASED);
     ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
-    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 
     // If continue to submit buffer we continue to get release callbacks
     sp<GraphicBuffer> thirdBuffer = getBuffer();
-    submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, *releaseCallback);
+    ReleaseCallbackId thirdBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+    submitBuffer(layer, thirdBuffer, Fence::NO_FENCE, transactionCallback, thirdBufferCallbackId,
+                 *releaseCallback);
     expected = ExpectedResult();
     expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                         ExpectedResult::Buffer::NOT_ACQUIRED,
                         ExpectedResult::PreviousBuffer::NOT_RELEASED);
     ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
-    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBufferCallbackId));
 }
 
 TEST_F(ReleaseBufferCallbackTest, DISABLED_LayerLifecycle_layerdestroy) {
@@ -210,7 +225,9 @@
 
     // If a buffer is being presented, we should not emit a release callback.
     sp<GraphicBuffer> firstBuffer = getBuffer();
-    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, firstBufferCallbackId,
+                 *releaseCallback);
     {
         ExpectedResult expected;
         expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
@@ -225,7 +242,7 @@
     t.apply();
     layer = nullptr;
 
-    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
 
 // Destroying a never presented layer emits a callback.
@@ -242,7 +259,9 @@
 
     // Submitting a buffer does not emit a callback.
     sp<GraphicBuffer> firstBuffer = getBuffer();
-    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+    submitBuffer(layer, firstBuffer, Fence::NO_FENCE, *transactionCallback, firstBufferCallbackId,
+                 *releaseCallback);
     {
         ExpectedResult expected;
         expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
@@ -253,19 +272,21 @@
 
     // Submitting a second buffer will replace the drawing state buffer and emit a callback.
     sp<GraphicBuffer> secondBuffer = getBuffer();
-    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, *releaseCallback);
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+    submitBuffer(layer, secondBuffer, Fence::NO_FENCE, *transactionCallback, secondBufferCallbackId,
+                 *releaseCallback);
     {
         ExpectedResult expected;
         expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
                             ExpectedResult::Buffer::NOT_ACQUIRED);
         ASSERT_NO_FATAL_FAILURE(waitForCallback(*transactionCallback, expected));
         ASSERT_NO_FATAL_FAILURE(
-                waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+                waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
     }
 
     // Destroying the offscreen layer emits a callback.
     layer = nullptr;
-    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBuffer->getId()));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, secondBufferCallbackId));
 }
 
 TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) {
@@ -275,12 +296,13 @@
 
     // If a buffer is being presented, we should not emit a release callback.
     sp<GraphicBuffer> firstBuffer = getBuffer();
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
 
     // Try to present 100ms in the future
     nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
 
     Transaction t;
-    t.setBuffer(layer, firstBuffer, releaseCallback->getCallback());
+    t.setBuffer(layer, firstBuffer, firstBufferCallbackId, releaseCallback->getCallback());
     t.setAcquireFence(layer, Fence::NO_FENCE);
     t.addTransactionCompletedCallback(transactionCallback.function,
                                       transactionCallback.getContext());
@@ -295,7 +317,8 @@
 
     // Dropping frames in transaction queue emits a callback
     sp<GraphicBuffer> secondBuffer = getBuffer();
-    t.setBuffer(layer, secondBuffer, releaseCallback->getCallback());
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+    t.setBuffer(layer, secondBuffer, secondBufferCallbackId, releaseCallback->getCallback());
     t.setAcquireFence(layer, Fence::NO_FENCE);
     t.addTransactionCompletedCallback(transactionCallback.function,
                                       transactionCallback.getContext());
@@ -307,7 +330,7 @@
                         ExpectedResult::Buffer::NOT_ACQUIRED,
                         ExpectedResult::PreviousBuffer::RELEASED);
     ASSERT_NO_FATAL_FAILURE(waitForCallback(transactionCallback, expected));
-    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBuffer->getId()));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index bd6fe1d..8bd6e62 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -54,6 +54,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/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/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 62acc6b..28d0222 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -546,17 +546,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);
 }
 
@@ -565,7 +582,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/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 0a8c748..97b60e0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -160,7 +160,7 @@
     size_t maxTokens;
     static constexpr pid_t kSurfaceFlingerPid = 666;
     static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count();
-    static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(2ns).count();
+    static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(0ns).count();
     static constexpr nsecs_t kStartThreshold = std::chrono::nanoseconds(2ns).count();
     static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
                                                                   kDeadlineThreshold,
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index b67ebca..bb21ad6 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,26 @@
                 << "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::fromPort(0))
+                                                     .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
+                                                     .setGroup(0)
+                                                     .build(),
+                                             DisplayMode::Builder(1)
+                                                     .setId(DisplayModeId(1))
+                                                     .setPhysicalDisplayId(
+                                                             PhysicalDisplayId::fromPort(0))
+                                                     .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
+                                                     .setGroup(0)
+                                                     .build()},
+                                DisplayModeId(0));
 
-    mock::NoOpSchedulerCallback mSchedulerCallback;
+    mock::SchedulerCallback mSchedulerCallback;
 
-    TestableScheduler* const mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
+    TestableScheduler* mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
 
     TestableSurfaceFlinger mFlinger;
 };
@@ -142,22 +152,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 +183,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 +211,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 +234,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 +261,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 +289,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 +317,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 +349,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 +393,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 +405,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 +414,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 +424,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 +443,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 +455,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 +466,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 +482,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 +493,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 +503,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 +515,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 +536,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 +547,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 +561,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 +572,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 +600,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 +627,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 +637,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 +647,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 +737,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 +756,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 d4b229f..c04919f 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,18 @@
     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,
                                      ui::Size resolution = ui::Size());
@@ -188,6 +181,7 @@
                                                          int64_t vsyncPeriod, ui::Size resolution) {
     return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
             .setId(modeId)
+            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
             .setVsyncPeriod(int32_t(vsyncPeriod))
             .setGroup(group)
             .setHeight(resolution.height)
@@ -487,6 +481,52 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
+    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 90};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    lr.name = "Min";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    lr.name = "Max";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(90.0f);
+    lr.vote = LayerVoteType::Heuristic;
+    lr.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(60.0f);
+    lr.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(45.0f);
+    lr.name = "45Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(30.0f);
+    lr.name = "30Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(24.0f);
+    lr.name = "24Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_72_90Device,
@@ -649,6 +689,99 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
+    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected120Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "60Hz ExplicitDefault";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.name = "24Hz Heuristic";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.name = "90Hz ExplicitExactOrMultiple";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60Device,
@@ -819,6 +952,24 @@
     }
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
+    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = Fps(fps);
+        const auto& refreshRate =
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+    }
+}
+
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -1732,10 +1883,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
                                                 LayerRequirement{.weight = 0.5f}};
@@ -1846,10 +1997,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
                                                 LayerRequirement{.weight = 0.5f}};
@@ -1908,6 +2059,35 @@
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 }
 
+TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
+    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+    // Tests with 120Hz
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_120);
+    // SetPolicy(0, 60), current 60Hz => TurnOn.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(0), Fps(60)}}),
+              0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(60, 60), current 60Hz => TurnOff.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+              0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(60, 120), current 60Hz => TurnOn.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(120)}}),
+              0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(120, 120), current 120Hz => TurnOff.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_120, {Fps(120), Fps(120)}}),
+              0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+}
+
 TEST_F(RefreshRateConfigsTest, getFrameRateDivider) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
@@ -1950,10 +2130,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 HWC_CONFIG_ID_120, config);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     layers[0].name = "Test layer";
@@ -1995,10 +2175,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 HWC_CONFIG_ID_120, config);
 
     auto layers = std::vector<LayerRequirement>{
             LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
@@ -2035,10 +2215,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 HWC_CONFIG_ID_120, config);
 
     auto layers = std::vector<LayerRequirement>{
             LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
@@ -2085,26 +2265,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..0a8759d 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::fromPort(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 0023aa0..e2b3993 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::fromPort(0))
                                           .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
                                           .setGroup(0)
                                           .build();
     const DisplayModePtr mode120 = DisplayMode::Builder(1)
                                            .setId(DisplayModeId(1))
+                                           .setPhysicalDisplayId(PhysicalDisplayId::fromPort(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 1ed52ea..8e7554b 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/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 41fd6e3..a99dabe 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -32,15 +32,18 @@
 
 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),
+                      const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
+                      ISchedulerCallback& callback)
+          : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr},
+                      refreshRateConfigs, callback, createLayerHistory(),
                       {.supportKernelTimer = false, .useContentDetection = true}) {}
 
     // Used to inject mock event thread.
@@ -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 7f6e05e..ad284e3 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::fromPort(0))
                                    .setVsyncPeriod(16'666'667)
                                    .setGroup(0)
                                    .build()};
@@ -216,24 +217,23 @@
         if (hasMultipleModes) {
             modes.emplace_back(DisplayMode::Builder(1)
                                        .setId(DisplayModeId(1))
+                                       .setPhysicalDisplayId(PhysicalDisplayId::fromPort(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();
+        mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, currMode);
+        const auto currFps = mRefreshRateConfigs->getCurrentRefreshRate().getFps();
+        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
+        mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
                                                               /*powerMode=*/hal::PowerMode::OFF);
-        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
-        mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
 
         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));
@@ -643,6 +643,7 @@
             DisplayModePtr activeMode =
                     DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
                             .setId(mActiveModeId)
+                            .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
                             .setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
                             .setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
                             .setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
@@ -653,6 +654,7 @@
 
             DisplayModes modes{activeMode};
             mCreationArgs.supportedModes = modes;
+            mCreationArgs.refreshRateConfigs = flinger.mRefreshRateConfigs;
         }
 
         sp<IBinder> token() const { return mDisplayToken; }
@@ -722,7 +724,7 @@
             return *this;
         }
 
-        sp<DisplayDevice> inject() {
+        sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
             const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
 
             DisplayDeviceState state;
@@ -769,6 +771,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/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index 7450b5d..159bdf1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -27,8 +27,10 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    MOCK_METHOD0(init, void());
     MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
+    MOCK_METHOD0(isUsingExpensiveRendering, bool());
     MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
 
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 {
diff --git a/vulkan/README.md b/vulkan/README.md
index 185aa39..144805c 100644
--- a/vulkan/README.md
+++ b/vulkan/README.md
@@ -14,7 +14,7 @@
 
 ## Code Generation
 
-We generate several parts of the loader and tools driectly from the Vulkan Registry (external/vulkan-headers/registry/vk.xml). Code generation must be done manually because the generator is not part of the platform toolchain (yet?). Files named `foo_gen.*` are generated by the code generator.
+We generate several parts of the loader and tools directly from the Vulkan Registry (external/vulkan-headers/registry/vk.xml). Code generation must be done manually because the generator is not part of the platform toolchain (yet?). Files named `foo_gen.*` are generated by the code generator.
 
 ### Run The Code Generator