Merge "[SF] Change the flag to misc1 for the render rate updates" into main
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c40caf5..c86adef 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -353,7 +353,7 @@
     // Now go on and read dexopt lines from stdin and pass them on to otapreopt.
 
     int count = 1;
-    for (std::array<char, 1000> linebuf;
+    for (std::array<char, 10000> linebuf;
          std::cin.clear(), std::cin.getline(&linebuf[0], linebuf.size()); ++count) {
         // Subtract one from gcount() since getline() counts the newline.
         std::string line(&linebuf[0], std::cin.gcount() - 1);
diff --git a/data/etc/android.software.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml
index bd15eb6..62bb101 100644
--- a/data/etc/android.software.opengles.deqp.level-latest.xml
+++ b/data/etc/android.software.opengles.deqp.level-latest.xml
@@ -17,5 +17,5 @@
 <!-- This is the standard feature indicating that the device passes OpenGL ES
      dEQP tests associated with the most recent level for this Android version. -->
 <permissions>
-    <feature name="android.software.opengles.deqp.level" version="132580097" />
+    <feature name="android.software.opengles.deqp.level" version="132645633" />
 </permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml
index 87be070..0fc12b3 100644
--- a/data/etc/android.software.vulkan.deqp.level-latest.xml
+++ b/data/etc/android.software.vulkan.deqp.level-latest.xml
@@ -17,5 +17,5 @@
 <!-- This is the standard feature indicating that the device passes Vulkan
      dEQP tests associated with the most recent level for this Android version. -->
 <permissions>
-    <feature name="android.software.vulkan.deqp.level" version="132580097" />
+    <feature name="android.software.vulkan.deqp.level" version="132645633" />
 </permissions>
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 9c0c10e..2d23b97 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -160,4 +160,90 @@
     std::vector<PointerBuilder> mPointers;
 };
 
+class KeyEventBuilder {
+public:
+    KeyEventBuilder(int32_t action, int32_t source) {
+        mAction = action;
+        mSource = source;
+        mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        mDownTime = mEventTime;
+    }
+
+    KeyEventBuilder(const KeyEvent& event) {
+        mAction = event.getAction();
+        mDeviceId = event.getDeviceId();
+        mSource = event.getSource();
+        mDownTime = event.getDownTime();
+        mEventTime = event.getEventTime();
+        mDisplayId = event.getDisplayId();
+        mFlags = event.getFlags();
+        mKeyCode = event.getKeyCode();
+        mScanCode = event.getScanCode();
+        mMetaState = event.getMetaState();
+        mRepeatCount = event.getRepeatCount();
+    }
+
+    KeyEventBuilder& deviceId(int32_t deviceId) {
+        mDeviceId = deviceId;
+        return *this;
+    }
+
+    KeyEventBuilder& downTime(nsecs_t downTime) {
+        mDownTime = downTime;
+        return *this;
+    }
+
+    KeyEventBuilder& eventTime(nsecs_t eventTime) {
+        mEventTime = eventTime;
+        return *this;
+    }
+
+    KeyEventBuilder& displayId(int32_t displayId) {
+        mDisplayId = displayId;
+        return *this;
+    }
+
+    KeyEventBuilder& policyFlags(int32_t policyFlags) {
+        mPolicyFlags = policyFlags;
+        return *this;
+    }
+
+    KeyEventBuilder& addFlag(uint32_t flags) {
+        mFlags |= flags;
+        return *this;
+    }
+
+    KeyEventBuilder& keyCode(int32_t keyCode) {
+        mKeyCode = keyCode;
+        return *this;
+    }
+
+    KeyEventBuilder& repeatCount(int32_t repeatCount) {
+        mRepeatCount = repeatCount;
+        return *this;
+    }
+
+    KeyEvent build() const {
+        KeyEvent event{};
+        event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
+                         mAction, mFlags, mKeyCode, mScanCode, mMetaState, mRepeatCount, mDownTime,
+                         mEventTime);
+        return event;
+    }
+
+private:
+    int32_t mAction;
+    int32_t mDeviceId = DEFAULT_DEVICE_ID;
+    uint32_t mSource;
+    nsecs_t mDownTime;
+    nsecs_t mEventTime;
+    int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+    uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
+    int32_t mFlags{0};
+    int32_t mKeyCode{AKEYCODE_UNKNOWN};
+    int32_t mScanCode{0};
+    int32_t mMetaState{AMETA_NONE};
+    int32_t mRepeatCount{0};
+};
+
 } // namespace android
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 2e99495..ee74455 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -98,7 +98,7 @@
     void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position);
 
     // Adds movement information for all pointers in a MotionEvent, including historical samples.
-    void addMovement(const MotionEvent* event);
+    void addMovement(const MotionEvent& event);
 
     // Returns the velocity of the specified pointer id and axis in position units per second.
     // Returns empty optional if there is insufficient movement information for the pointer, or if
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index f22e90a..8243238 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -30,7 +30,6 @@
 #include <binder/RecordedTransaction.h>
 #include <binder/RpcServer.h>
 #include <pthread.h>
-#include <utils/misc.h>
 
 #include <inttypes.h>
 #include <stdio.h>
@@ -271,7 +270,7 @@
     bool mInheritRt = false;
 
     // for below objects
-    Mutex mLock;
+    RpcMutex mLock;
     std::set<sp<RpcServerLink>> mRpcServerLinks;
     BpBinder::ObjectManager mObjects;
 
@@ -307,7 +306,7 @@
         return PERMISSION_DENIED;
     }
     Extras* e = getOrCreateExtras();
-    AutoMutex lock(e->mLock);
+    RpcMutexUniqueLock lock(e->mLock);
     if (mRecordingOn) {
         LOG(INFO) << "Could not start Binder recording. Another is already in progress.";
         return INVALID_OPERATION;
@@ -337,7 +336,7 @@
         return PERMISSION_DENIED;
     }
     Extras* e = getOrCreateExtras();
-    AutoMutex lock(e->mLock);
+    RpcMutexUniqueLock lock(e->mLock);
     if (mRecordingOn) {
         e->mRecordingFd.reset();
         mRecordingOn = false;
@@ -405,7 +404,7 @@
 
     if (kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION) [[unlikely]] {
         Extras* e = mExtras.load(std::memory_order_acquire);
-        AutoMutex lock(e->mLock);
+        RpcMutexUniqueLock lock(e->mLock);
         if (mRecordingOn) {
             Parcel emptyReply;
             timespec ts;
@@ -452,7 +451,7 @@
     Extras* e = getOrCreateExtras();
     LOG_ALWAYS_FATAL_IF(!e, "no memory");
 
-    AutoMutex _l(e->mLock);
+    RpcMutexUniqueLock _l(e->mLock);
     return e->mObjects.attach(objectID, object, cleanupCookie, func);
 }
 
@@ -461,7 +460,7 @@
     Extras* e = mExtras.load(std::memory_order_acquire);
     if (!e) return nullptr;
 
-    AutoMutex _l(e->mLock);
+    RpcMutexUniqueLock _l(e->mLock);
     return e->mObjects.find(objectID);
 }
 
@@ -469,7 +468,7 @@
     Extras* e = mExtras.load(std::memory_order_acquire);
     if (!e) return nullptr;
 
-    AutoMutex _l(e->mLock);
+    RpcMutexUniqueLock _l(e->mLock);
     return e->mObjects.detach(objectID);
 }
 
@@ -477,7 +476,7 @@
     Extras* e = getOrCreateExtras();
     LOG_ALWAYS_FATAL_IF(!e, "no memory");
 
-    AutoMutex _l(e->mLock);
+    RpcMutexUniqueLock _l(e->mLock);
     doWithLock();
 }
 
@@ -485,7 +484,7 @@
                                         const void* makeArgs) {
     Extras* e = getOrCreateExtras();
     LOG_ALWAYS_FATAL_IF(!e, "no memory");
-    AutoMutex _l(e->mLock);
+    RpcMutexUniqueLock _l(e->mLock);
     return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
 }
 
@@ -692,7 +691,7 @@
     auto weakThis = wp<BBinder>::fromExisting(this);
 
     Extras* e = getOrCreateExtras();
-    AutoMutex _l(e->mLock);
+    RpcMutexUniqueLock _l(e->mLock);
     auto rpcServer = RpcServer::make();
     LOG_ALWAYS_FATAL_IF(rpcServer == nullptr, "RpcServer::make returns null");
     auto link = sp<RpcServerLink>::make(rpcServer, keepAliveBinder, weakThis);
@@ -716,7 +715,7 @@
 void BBinder::removeRpcServerLink(const sp<RpcServerLink>& link) {
     Extras* e = mExtras.load(std::memory_order_acquire);
     if (!e) return;
-    AutoMutex _l(e->mLock);
+    RpcMutexUniqueLock _l(e->mLock);
     (void)e->mRpcServerLinks.erase(link);
 }
 
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 3bc4f92..49038b1 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -23,7 +23,6 @@
 #include <binder/IResultReceiver.h>
 #include <binder/RpcSession.h>
 #include <binder/Stability.h>
-#include <utils/Log.h>
 
 #include <stdio.h>
 
@@ -38,7 +37,7 @@
 
 // ---------------------------------------------------------------------------
 
-Mutex BpBinder::sTrackingLock;
+RpcMutex BpBinder::sTrackingLock;
 std::unordered_map<int32_t, uint32_t> BpBinder::sTrackingMap;
 std::unordered_map<int32_t, uint32_t> BpBinder::sLastLimitCallbackMap;
 int BpBinder::sNumTrackedUids = 0;
@@ -163,7 +162,7 @@
     int32_t trackedUid = -1;
     if (sCountByUidEnabled) {
         trackedUid = IPCThreadState::self()->getCallingUid();
-        AutoMutex _l(sTrackingLock);
+        RpcMutexUniqueLock _l(sTrackingLock);
         uint32_t trackedValue = sTrackingMap[trackedUid];
         if (trackedValue & LIMIT_REACHED_MASK) [[unlikely]] {
             if (sBinderProxyThrottleCreate) {
@@ -276,7 +275,7 @@
 }
 
 bool BpBinder::isDescriptorCached() const {
-    Mutex::Autolock _l(mLock);
+    RpcMutexUniqueLock _l(mLock);
     return mDescriptorCache.c_str() != kDescriptorUninit.c_str();
 }
 
@@ -292,7 +291,7 @@
         status_t err = thiz->transact(INTERFACE_TRANSACTION, data, &reply);
         if (err == NO_ERROR) {
             String16 res(reply.readString16());
-            Mutex::Autolock _l(mLock);
+            RpcMutexUniqueLock _l(mLock);
             // mDescriptorCache could have been assigned while the lock was
             // released.
             if (mDescriptorCache.c_str() == kDescriptorUninit.c_str()) mDescriptorCache = res;
@@ -385,7 +384,7 @@
             status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
         }
         if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) {
-            Mutex::Autolock _l(mLock);
+            RpcMutexUniqueLock _l(mLock);
             ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d",
                   data.dataSize(), String8(mDescriptorCache).c_str(), code);
         }
@@ -431,7 +430,7 @@
                         "linkToDeath(): recipient must be non-NULL");
 
     {
-        AutoMutex _l(mLock);
+        RpcMutexUniqueLock _l(mLock);
 
         if (!mObitsSent) {
             if (!mObituaries) {
@@ -467,7 +466,7 @@
         return INVALID_OPERATION;
     }
 
-    AutoMutex _l(mLock);
+    RpcMutexUniqueLock _l(mLock);
 
     if (mObitsSent) {
         return DEAD_OBJECT;
@@ -555,30 +554,30 @@
 
 void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie,
                              object_cleanup_func func) {
-    AutoMutex _l(mLock);
+    RpcMutexUniqueLock _l(mLock);
     ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
     return mObjects.attach(objectID, object, cleanupCookie, func);
 }
 
 void* BpBinder::findObject(const void* objectID) const
 {
-    AutoMutex _l(mLock);
+    RpcMutexUniqueLock _l(mLock);
     return mObjects.find(objectID);
 }
 
 void* BpBinder::detachObject(const void* objectID) {
-    AutoMutex _l(mLock);
+    RpcMutexUniqueLock _l(mLock);
     return mObjects.detach(objectID);
 }
 
 void BpBinder::withLock(const std::function<void()>& doWithLock) {
-    AutoMutex _l(mLock);
+    RpcMutexUniqueLock _l(mLock);
     doWithLock();
 }
 
 sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make,
                                          const void* makeArgs) {
-    AutoMutex _l(mLock);
+    RpcMutexUniqueLock _l(mLock);
     return mObjects.lookupOrCreateWeak(objectID, make, makeArgs);
 }
 
@@ -602,7 +601,7 @@
     IPCThreadState* ipc = IPCThreadState::self();
 
     if (mTrackedUid >= 0) {
-        AutoMutex _l(sTrackingLock);
+        RpcMutexUniqueLock _l(sTrackingLock);
         uint32_t trackedValue = sTrackingMap[mTrackedUid];
         if ((trackedValue & COUNTING_VALUE_MASK) == 0) [[unlikely]] {
             ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this,
@@ -702,7 +701,7 @@
 
 uint32_t BpBinder::getBinderProxyCount(uint32_t uid)
 {
-    AutoMutex _l(sTrackingLock);
+    RpcMutexUniqueLock _l(sTrackingLock);
     auto it = sTrackingMap.find(uid);
     if (it != sTrackingMap.end()) {
         return it->second & COUNTING_VALUE_MASK;
@@ -717,7 +716,7 @@
 
 void BpBinder::getCountByUid(Vector<uint32_t>& uids, Vector<uint32_t>& counts)
 {
-    AutoMutex _l(sTrackingLock);
+    RpcMutexUniqueLock _l(sTrackingLock);
     uids.setCapacity(sTrackingMap.size());
     counts.setCapacity(sTrackingMap.size());
     for (const auto& it : sTrackingMap) {
@@ -731,12 +730,12 @@
 void BpBinder::setCountByUidEnabled(bool enable) { sCountByUidEnabled.store(enable); }
 
 void BpBinder::setLimitCallback(binder_proxy_limit_callback cb) {
-    AutoMutex _l(sTrackingLock);
+    RpcMutexUniqueLock _l(sTrackingLock);
     sLimitCallback = cb;
 }
 
 void BpBinder::setBinderProxyCountWatermarks(int high, int low) {
-    AutoMutex _l(sTrackingLock);
+    RpcMutexUniqueLock _l(sTrackingLock);
     sBinderProxyCountHighWatermark = high;
     sBinderProxyCountLowWatermark = low;
 }
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index c6e4fb3..7ae616e 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -19,8 +19,6 @@
 
 #include <binder/ProcessState.h>
 
-#include <utils/misc.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index 8ee6cb0..a1fbbf3 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -21,12 +21,15 @@
 
 #include <poll.h>
 
-#include <android-base/macros.h>
-#include <android-base/scopeguard.h>
+#include <binder/Functional.h>
 
 #include "RpcState.h"
+#include "Utils.h"
+
 namespace android {
 
+using namespace android::binder::impl;
+
 std::unique_ptr<FdTrigger> FdTrigger::make() {
     auto ret = std::make_unique<FdTrigger>();
 #ifndef BINDER_RPC_SINGLE_THREADED
@@ -74,10 +77,9 @@
                         "Only one thread should be polling on Fd!");
 
     transportFd.setPollingState(true);
-    auto pollingStateGuard =
-            android::base::make_scope_guard([&]() { transportFd.setPollingState(false); });
+    auto pollingStateGuard = make_scope_guard([&]() { transportFd.setPollingState(false); });
 
-    int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1));
+    int ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1));
     if (ret < 0) {
         return -errno;
     }
diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h
index 5fbf290..dba1dc9 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -17,7 +17,6 @@
 
 #include <memory>
 
-#include <android-base/result.h>
 #include <android-base/unique_fd.h>
 #include <utils/Errors.h>
 
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp
index 2780bd4..dea2603 100644
--- a/libs/binder/IInterface.cpp
+++ b/libs/binder/IInterface.cpp
@@ -15,7 +15,6 @@
  */
 
 #define LOG_TAG "IInterface"
-#include <utils/Log.h>
 #include <binder/IInterface.h>
 
 namespace android {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index da58251..9341eff 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -22,7 +22,6 @@
 #include <binder/BpBinder.h>
 #include <binder/TextOutput.h>
 
-#include <android-base/macros.h>
 #include <cutils/sched_policy.h>
 #include <utils/CallStack.h>
 #include <utils/Log.h>
@@ -395,7 +394,9 @@
 }
 
 void IPCThreadState::checkContextIsBinderForUse(const char* use) const {
-    if (LIKELY(mServingStackPointerGuard == nullptr)) return;
+    if (mServingStackPointerGuard == nullptr) [[likely]] {
+        return;
+    }
 
     if (!mServingStackPointer || mServingStackPointerGuard->address < mServingStackPointer) {
         LOG_ALWAYS_FATAL("In context %s, %s does not make sense (binder sp: %p, guard: %p).",
@@ -832,7 +833,7 @@
     }
 
     if ((flags & TF_ONE_WAY) == 0) {
-        if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) {
+        if (mCallRestriction != ProcessState::CallRestriction::NONE) [[unlikely]] {
             if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) {
                 ALOGE("Process making non-oneway call (code: %u) but is restricted.", code);
                 CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(),
@@ -842,13 +843,13 @@
             }
         }
 
-        #if 0
+#if 0
         if (code == 4) { // relayout
             ALOGI(">>>>>> CALLING transaction 4");
         } else {
             ALOGI(">>>>>> CALLING transaction %d", code);
         }
-        #endif
+#endif
         if (reply) {
             err = waitForResponse(reply);
         } else {
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index cd92217..60ece72 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -18,7 +18,6 @@
 
 #include <binder/IResultReceiver.h>
 
-#include <utils/Log.h>
 #include <binder/Parcel.h>
 #include <utils/String8.h>
 
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 6034f2b..fe566fc 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -200,7 +200,7 @@
 }
 
 bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logPermissionFailure) {
-    static Mutex gPermissionControllerLock;
+    static std::mutex gPermissionControllerLock;
     static sp<IPermissionController> gPermissionController;
 
     sp<IPermissionController> pc;
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index 5b1cb7e..95bdbb4 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -155,7 +155,7 @@
     void     dump_l(String8& res, const char* what) const;
 
     static const int    kMemoryAlign;
-    mutable Mutex       mLock;
+    mutable std::mutex mLock;
     LinkedList<chunk_t> mList;
     size_t              mHeapSize;
 };
@@ -305,14 +305,14 @@
 
 size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags)
 {
-    Mutex::Autolock _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
     ssize_t offset = alloc(size, flags);
     return offset;
 }
 
 status_t SimpleBestFitAllocator::deallocate(size_t offset)
 {
-    Mutex::Autolock _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
     chunk_t const * const freed = dealloc(offset);
     if (freed) {
         return NO_ERROR;
@@ -420,7 +420,7 @@
 
 void SimpleBestFitAllocator::dump(const char* what) const
 {
-    Mutex::Autolock _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
     dump_l(what);
 }
 
@@ -434,7 +434,7 @@
 void SimpleBestFitAllocator::dump(String8& result,
         const char* what) const
 {
-    Mutex::Autolock _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
     dump_l(result, what);
 }
 
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index 8dc1f6a..bb7caa9 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -18,14 +18,13 @@
 #include <stddef.h>
 #include <cstdint>
 
-#include <android-base/result.h>
 #include <android-base/unique_fd.h>
 #include <binder/RpcTransport.h>
 #include <utils/Errors.h>
 
 namespace android::binder::os {
 
-android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
+status_t setNonBlocking(android::base::borrowed_fd fd);
 
 status_t getRandomBytes(uint8_t* data, size_t size);
 
diff --git a/libs/binder/OS_unix_base.cpp b/libs/binder/OS_unix_base.cpp
index 81933d5..a3cf117 100644
--- a/libs/binder/OS_unix_base.cpp
+++ b/libs/binder/OS_unix_base.cpp
@@ -15,29 +15,29 @@
  */
 
 #include "OS.h"
+#include "Utils.h"
 
 #include <android-base/file.h>
 #include <binder/RpcTransportRaw.h>
 #include <log/log.h>
 #include <string.h>
 
-using android::base::ErrnoError;
-using android::base::Result;
-
 namespace android::binder::os {
 
 // Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
 constexpr size_t kMaxFdsPerMsg = 253;
 
-Result<void> setNonBlocking(android::base::borrowed_fd fd) {
+status_t setNonBlocking(android::base::borrowed_fd fd) {
     int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
     if (flags == -1) {
-        return ErrnoError() << "Could not get flags for fd";
+        PLOGE("Failed setNonBlocking: Could not get flags for fd");
+        return -errno;
     }
     if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) {
-        return ErrnoError() << "Could not set non-blocking flag for fd";
+        PLOGE("Failed setNonBlocking: Could not set non-blocking flag for fd");
+        return -errno;
     }
-    return {};
+    return OK;
 }
 
 status_t getRandomBytes(uint8_t* data, size_t size) {
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a3ff7d2..3349402 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "Parcel"
 //#define LOG_NDEBUG 0
 
+#include <endian.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -32,6 +33,7 @@
 
 #include <binder/Binder.h>
 #include <binder/BpBinder.h>
+#include <binder/Functional.h>
 #include <binder/IPCThreadState.h>
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
@@ -39,15 +41,11 @@
 #include <binder/Status.h>
 #include <binder/TextOutput.h>
 
-#include <android-base/scopeguard.h>
 #ifndef BINDER_DISABLE_BLOB
 #include <cutils/ashmem.h>
 #endif
-#include <utils/Flattenable.h>
-#include <utils/Log.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
-#include <utils/misc.h>
 
 #include "OS.h"
 #include "RpcState.h"
@@ -98,6 +96,8 @@
 
 namespace android {
 
+using namespace android::binder::impl;
+
 // many things compile this into prebuilts on the stack
 #ifdef __LP64__
 static_assert(sizeof(Parcel) == 120);
@@ -627,7 +627,7 @@
         }
 
         const size_t savedDataPos = mDataPos;
-        base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; };
+        auto scopeGuard = make_scope_guard([&]() { mDataPos = savedDataPos; });
 
         rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size());
         if (otherRpcFields->mFds != nullptr) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 8ec4af9..0344eb0 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -18,10 +18,9 @@
 
 #include <binder/ProcessState.h>
 
-#include <android-base/result.h>
-#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <binder/BpBinder.h>
+#include <binder/Functional.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/Stability.h>
@@ -32,6 +31,7 @@
 #include <utils/Thread.h>
 
 #include "Static.h"
+#include "Utils.h"
 #include "binder_module.h"
 
 #include <errno.h>
@@ -60,6 +60,8 @@
 
 namespace android {
 
+using namespace android::binder::impl;
+
 class PoolThread : public Thread
 {
 public:
@@ -189,7 +191,7 @@
 
 void ProcessState::startThreadPool()
 {
-    AutoMutex _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
     if (!mThreadPoolStarted) {
         if (mMaxThreads == 0) {
             // see also getThreadPoolMaxTotalThreadCount
@@ -203,7 +205,7 @@
 
 bool ProcessState::becomeContextManager()
 {
-    AutoMutex _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
 
     flat_binder_object obj {
         .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
@@ -310,7 +312,7 @@
 {
     sp<IBinder> result;
 
-    AutoMutex _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
 
     if (handle == 0 && the_context_object != nullptr) return the_context_object;
 
@@ -374,7 +376,7 @@
 
 void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
 {
-    AutoMutex _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
 
     handle_entry* e = lookupHandleLocked(handle);
 
@@ -430,7 +432,7 @@
 
 size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
     pthread_mutex_lock(&mThreadCountLock);
-    base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); };
+    auto detachGuard = make_scope_guard([&]() { pthread_mutex_unlock(&mThreadCountLock); });
 
     if (mThreadPoolStarted) {
         LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1,
@@ -512,31 +514,31 @@
     return mDriverName;
 }
 
-static base::Result<int> open_driver(const char* driver) {
-    int fd = open(driver, O_RDWR | O_CLOEXEC);
-    if (fd < 0) {
-        return base::ErrnoError() << "Opening '" << driver << "' failed";
+static base::unique_fd open_driver(const char* driver) {
+    auto fd = base::unique_fd(open(driver, O_RDWR | O_CLOEXEC));
+    if (!fd.ok()) {
+        PLOGE("Opening '%s' failed", driver);
+        return {};
     }
     int vers = 0;
-    status_t result = ioctl(fd, BINDER_VERSION, &vers);
+    int result = ioctl(fd.get(), BINDER_VERSION, &vers);
     if (result == -1) {
-        close(fd);
-        return base::ErrnoError() << "Binder ioctl to obtain version failed";
+        PLOGE("Binder ioctl to obtain version failed");
+        return {};
     }
     if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
-        close(fd);
-        return base::Error() << "Binder driver protocol(" << vers
-                             << ") does not match user space protocol("
-                             << BINDER_CURRENT_PROTOCOL_VERSION
-                             << ")! ioctl() return value: " << result;
+        ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! "
+              "ioctl() return value: %d",
+              vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
+        return {};
     }
     size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
-    result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
+    result = ioctl(fd.get(), BINDER_SET_MAX_THREADS, &maxThreads);
     if (result == -1) {
         ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
     }
     uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
-    result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
+    result = ioctl(fd.get(), BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
     if (result == -1) {
         ALOGE_IF(ProcessState::isDriverFeatureEnabled(
                      ProcessState::DriverFeature::ONEWAY_SPAM_DETECTION),
@@ -561,28 +563,27 @@
         mThreadPoolStarted(false),
         mThreadPoolSeq(1),
         mCallRestriction(CallRestriction::NONE) {
-    base::Result<int> opened = open_driver(driver);
+    base::unique_fd opened = open_driver(driver);
 
     if (opened.ok()) {
         // mmap the binder, providing a chunk of virtual address space to receive transactions.
         mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
-                        opened.value(), 0);
+                        opened.get(), 0);
         if (mVMStart == MAP_FAILED) {
-            close(opened.value());
             // *sigh*
-            opened = base::Error()
-                    << "Using " << driver << " failed: unable to mmap transaction memory.";
+            ALOGE("Using %s failed: unable to mmap transaction memory.", driver);
+            opened.reset();
             mDriverName.clear();
         }
     }
 
 #ifdef __ANDROID__
-    LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating: %s",
-                        driver, opened.error().message().c_str());
+    LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating.",
+                        driver);
 #endif
 
     if (opened.ok()) {
-        mDriverFD = opened.value();
+        mDriverFD = opened.release();
     }
 }
 
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 3246706..cedd3af 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -16,12 +16,13 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/scopeguard.h>
 #include <android-base/unique_fd.h>
+#include <binder/Functional.h>
 #include <binder/RecordedTransaction.h>
 #include <sys/mman.h>
 #include <algorithm>
 
+using namespace android::binder::impl;
 using android::Parcel;
 using android::base::borrowed_fd;
 using android::base::unique_fd;
@@ -218,7 +219,7 @@
         size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset;
         void* mappedMemory =
                 mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart);
-        auto mmap_guard = android::base::make_scope_guard(
+        auto mmap_guard = make_scope_guard(
                 [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); });
 
         transaction_checksum_t* payloadMap =
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 07ab093..1ba20b3 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -25,12 +25,11 @@
 #include <thread>
 #include <vector>
 
-#include <android-base/scopeguard.h>
+#include <binder/Functional.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcTransportRaw.h>
 #include <log/log.h>
-#include <utils/Compat.h>
 
 #include "BuildFlags.h"
 #include "FdTrigger.h"
@@ -45,7 +44,7 @@
 
 constexpr size_t kSessionIdBytes = 32;
 
-using base::ScopeGuard;
+using namespace android::binder::impl;
 using base::unique_fd;
 
 RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {}
@@ -231,10 +230,7 @@
     }
 
     unique_fd fd(std::move(std::get<unique_fd>(fds.back())));
-    if (auto res = binder::os::setNonBlocking(fd); !res.ok()) {
-        ALOGE("Failed setNonBlocking: %s", res.error().message().c_str());
-        return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
-    }
+    if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res;
 
     *out = RpcTransportFd(std::move(fd));
     return OK;
@@ -458,11 +454,12 @@
         LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(),
                             "Must establish connection on owned thread");
         thisThread = std::move(threadId->second);
-        ScopeGuard detachGuard = [&]() {
+        auto detachGuardLambda = [&]() {
             thisThread.detach();
             _l.unlock();
             server->mShutdownCv.notify_all();
         };
+        auto detachGuard = make_scope_guard(std::ref(detachGuardLambda));
         server->mConnectingThreads.erase(threadId);
 
         if (status != OK || server->mShutdownTrigger->isTriggered()) {
@@ -548,7 +545,7 @@
             return;
         }
 
-        detachGuard.Disable();
+        detachGuard.release();
         session->preJoinThreadOwnership(std::move(thisThread));
     }
 
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index fa8f2b5..c895b21 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -26,14 +26,12 @@
 
 #include <string_view>
 
-#include <android-base/macros.h>
-#include <android-base/scopeguard.h>
 #include <binder/BpBinder.h>
+#include <binder/Functional.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcTransportRaw.h>
 #include <binder/Stability.h>
-#include <utils/Compat.h>
 #include <utils/String8.h>
 
 #include "BuildFlags.h"
@@ -52,6 +50,7 @@
 
 namespace android {
 
+using namespace android::binder::impl;
 using base::unique_fd;
 
 RpcSession::RpcSession(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {
@@ -194,10 +193,7 @@
             fd = request();
             if (!fd.ok()) return BAD_VALUE;
         }
-        if (auto res = binder::os::setNonBlocking(fd); !res.ok()) {
-            ALOGE("setupPreconnectedClient: %s", res.error().message().c_str());
-            return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
-        }
+        if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res;
 
         RpcTransportFd transportFd(std::move(fd));
         status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming);
@@ -412,7 +408,9 @@
     }
 
 private:
-    DISALLOW_COPY_AND_ASSIGN(JavaThreadAttacher);
+    JavaThreadAttacher(const JavaThreadAttacher&) = delete;
+    void operator=(const JavaThreadAttacher&) = delete;
+
     bool mAttached = false;
 
     static JavaVM* getJavaVM() {
@@ -497,7 +495,7 @@
     if (auto status = initShutdownTrigger(); status != OK) return status;
 
     auto oldProtocolVersion = mProtocolVersion;
-    auto cleanup = base::ScopeGuard([&] {
+    auto cleanup = make_scope_guard([&] {
         // if any threads are started, shut them down
         (void)shutdownAndWait(true);
 
@@ -577,7 +575,7 @@
         if (status_t status = connectAndInit(mId, true /*incoming*/); status != OK) return status;
     }
 
-    cleanup.Disable();
+    cleanup.release();
 
     return OK;
 }
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 26a2f4f..008e5d2 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,9 +18,8 @@
 
 #include "RpcState.h"
 
-#include <android-base/macros.h>
-#include <android-base/scopeguard.h>
 #include <binder/BpBinder.h>
+#include <binder/Functional.h>
 #include <binder/IPCThreadState.h>
 #include <binder/RpcServer.h>
 
@@ -39,6 +38,8 @@
 
 namespace android {
 
+using namespace android::binder::impl;
+
 #if RPC_FLAKE_PRONE
 void rpcMaybeWaitToFlake() {
     [[clang::no_destroy]] static std::random_device r;
@@ -357,7 +358,7 @@
 status_t RpcState::rpcSend(
         const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
         const char* what, iovec* iovs, int niovs,
-        const std::optional<android::base::function_ref<status_t()>>& altPoll,
+        const std::optional<SmallFunction<status_t()>>& altPoll,
         const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
     for (int i = 0; i < niovs; i++) {
         LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s",
@@ -412,10 +413,8 @@
             return false;
         }
 #else
-        // TODO(b/305983144)
-        // don't restrict on other platforms, though experimental should
-        // only really be used for testing, we don't have a good way to see
-        // what is shipping outside of Android
+        ALOGE("Cannot use experimental RPC binder protocol outside of Android.");
+        return false;
 #endif
     } else if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT) {
         ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol "
@@ -602,25 +601,24 @@
             {const_cast<uint8_t*>(data.data()), data.dataSize()},
             objectTableSpan.toIovec(),
     };
-    if (status_t status = rpcSend(
-                connection, session, "transaction", iovs, arraysize(iovs),
-                [&] {
-                    if (waitUs > kWaitLogUs) {
-                        ALOGE("Cannot send command, trying to process pending refcounts. Waiting "
-                              "%zuus. Too many oneway calls?",
-                              waitUs);
-                    }
+    auto altPoll = [&] {
+        if (waitUs > kWaitLogUs) {
+            ALOGE("Cannot send command, trying to process pending refcounts. Waiting "
+                  "%zuus. Too many oneway calls?",
+                  waitUs);
+        }
 
-                    if (waitUs > 0) {
-                        usleep(waitUs);
-                        waitUs = std::min(kWaitMaxUs, waitUs * 2);
-                    } else {
-                        waitUs = 1;
-                    }
+        if (waitUs > 0) {
+            usleep(waitUs);
+            waitUs = std::min(kWaitMaxUs, waitUs * 2);
+        } else {
+            waitUs = 1;
+        }
 
-                    return drainCommands(connection, session, CommandType::CONTROL_ONLY);
-                },
-                rpcFields->mFds.get());
+        return drainCommands(connection, session, CommandType::CONTROL_ONLY);
+    };
+    if (status_t status = rpcSend(connection, session, "transaction", iovs, countof(iovs),
+                                  std::ref(altPoll), rpcFields->mFds.get());
         status != OK) {
         // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate
         // errors here, then we may need to undo the binder-sent counts for the transaction as
@@ -692,7 +690,7 @@
             {&rpcReply, rpcReplyWireSize},
             {data.data(), data.size()},
     };
-    if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr);
+    if (status_t status = rpcRec(connection, session, "reply body", iovs, countof(iovs), nullptr);
         status != OK)
         return status;
 
@@ -762,7 +760,7 @@
             .bodySize = sizeof(RpcDecStrong),
     };
     iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}};
-    return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt);
+    return rpcSend(connection, session, "dec ref", iovs, countof(iovs), std::nullopt);
 }
 
 status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection,
@@ -811,11 +809,11 @@
         origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
     }
 
-    base::ScopeGuard guardUnguard = [&]() {
+    auto guardUnguard = make_scope_guard([&]() {
         if (kernelBinderState != nullptr) {
             kernelBinderState->restoreGetCallingSpGuard(origGuard);
         }
-    };
+    });
 #endif // BINDER_WITH_KERNEL_IPC
 
     switch (command.command) {
@@ -1145,7 +1143,7 @@
             {const_cast<uint8_t*>(reply.data()), reply.dataSize()},
             objectTableSpan.toIovec(),
     };
-    return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt,
+    return rpcSend(connection, session, "reply", iovs, countof(iovs), std::nullopt,
                    rpcFields->mFds.get());
 }
 
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 1fe71a5..2a954e6 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -16,6 +16,7 @@
 #pragma once
 
 #include <android-base/unique_fd.h>
+#include <binder/Functional.h>
 #include <binder/IBinder.h>
 #include <binder/Parcel.h>
 #include <binder/RpcSession.h>
@@ -190,7 +191,7 @@
     [[nodiscard]] status_t rpcSend(
             const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
             const char* what, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll,
             const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds =
                     nullptr);
     [[nodiscard]] status_t rpcRec(
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index c089811..ffa3151 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -29,6 +29,8 @@
 
 namespace android {
 
+using namespace android::binder::impl;
+
 // RpcTransport with TLS disabled.
 class RpcTransportRaw : public RpcTransport {
 public:
@@ -54,7 +56,7 @@
 
     status_t interruptableWriteFully(
             FdTrigger* fdTrigger, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::optional<SmallFunction<status_t()>>& altPoll,
             const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
             override {
         bool sentFds = false;
@@ -70,7 +72,7 @@
 
     status_t interruptableReadFully(
             FdTrigger* fdTrigger, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::optional<SmallFunction<status_t()>>& altPoll,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
         auto recv = [&](iovec* iovs, int niovs) -> ssize_t {
             return binder::os::receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds);
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 0c81d83..188ba3b 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -26,8 +26,7 @@
 #include "RpcState.h"
 #include "RpcTransportUtils.h"
 
-using android::base::Error;
-using android::base::Result;
+using namespace android::binder::impl;
 
 namespace android {
 
@@ -75,7 +74,7 @@
 
     status_t interruptableWriteFully(
             FdTrigger* fdTrigger, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::optional<SmallFunction<status_t()>>& altPoll,
             const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
             override {
         auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
@@ -93,7 +92,7 @@
 
     status_t interruptableReadFully(
             FdTrigger* fdTrigger, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::optional<SmallFunction<status_t()>>& altPoll,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/)
             override {
         auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index efb09e9..fef4be4 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -29,6 +29,8 @@
 #include "RpcState.h"
 #include "Utils.h"
 
+#include <sstream>
+
 #define SHOULD_LOG_TLS_DETAIL false
 
 #if SHOULD_LOG_TLS_DETAIL
@@ -38,6 +40,9 @@
 #endif
 
 namespace android {
+
+using namespace android::binder::impl;
+
 namespace {
 
 // Implement BIO for socket that ignores SIGPIPE.
@@ -181,10 +186,9 @@
     // |sslError| should be from Ssl::getError().
     // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise
     // return error. Also return error if |fdTrigger| is triggered before or during poll().
-    status_t pollForSslError(
-            const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger,
-            const char* fnString, int additionalEvent,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+    status_t pollForSslError(const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger,
+                             const char* fnString, int additionalEvent,
+                             const std::optional<SmallFunction<status_t()>>& altPoll) {
         switch (sslError) {
             case SSL_ERROR_WANT_READ:
                 return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll);
@@ -200,7 +204,7 @@
 
     status_t handlePoll(int event, const android::RpcTransportFd& fd, FdTrigger* fdTrigger,
                         const char* fnString,
-                        const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+                        const std::optional<SmallFunction<status_t()>>& altPoll) {
         status_t ret;
         if (altPoll) {
             ret = (*altPoll)();
@@ -284,12 +288,12 @@
     status_t pollRead(void) override;
     status_t interruptableWriteFully(
             FdTrigger* fdTrigger, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::optional<SmallFunction<status_t()>>& altPoll,
             const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
             override;
     status_t interruptableReadFully(
             FdTrigger* fdTrigger, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::optional<SmallFunction<status_t()>>& altPoll,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override;
 
     bool isWaiting() override { return mSocket.isInPollingState(); };
@@ -320,7 +324,7 @@
 
 status_t RpcTransportTls::interruptableWriteFully(
         FdTrigger* fdTrigger, iovec* iovs, int niovs,
-        const std::optional<android::base::function_ref<status_t()>>& altPoll,
+        const std::optional<SmallFunction<status_t()>>& altPoll,
         const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
     (void)ancillaryFds;
 
@@ -366,7 +370,7 @@
 
 status_t RpcTransportTls::interruptableReadFully(
         FdTrigger* fdTrigger, iovec* iovs, int niovs,
-        const std::optional<android::base::function_ref<status_t()>>& altPoll,
+        const std::optional<SmallFunction<status_t()>>& altPoll,
         std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
     (void)ancillaryFds;
 
diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h
index 32f0db8..a0e502e 100644
--- a/libs/binder/RpcTransportUtils.h
+++ b/libs/binder/RpcTransportUtils.h
@@ -27,7 +27,7 @@
 status_t interruptableReadOrWrite(
         const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs,
         SendOrReceive sendOrReceiveFun, const char* funName, int16_t event,
-        const std::optional<android::base::function_ref<status_t()>>& altPoll) {
+        const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll) {
     MAYBE_WAIT_IN_FLAKE_MODE;
 
     if (niovs < 0) {
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 2b67f03..9482e3e 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -56,16 +56,16 @@
     [[nodiscard]] const std::optional<unsigned int>& hostPort() const { return mPort; }
 
 private:
-    DISALLOW_COPY_AND_ASSIGN(AdbForwarder);
+    AdbForwarder(const AdbForwarder&) = delete;
+    void operator=(const AdbForwarder&) = delete;
     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());
+    if (!result.has_value()) {
+        ALOGE("Unable to run `adb forward tcp:0 tcp:%d`", devicePort);
         return std::nullopt;
     }
     // Must end with exit code 0 (`has_value() && value() == 0`)
@@ -94,9 +94,8 @@
     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());
+    if (!result.has_value()) {
+        ALOGE("Unable to run `adb forward --remove tcp:%d`", *mPort);
         return;
     }
     // Must end with exit code 0 (`has_value() && value() == 0`)
@@ -130,8 +129,7 @@
     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());
+    if (!result.has_value()) {
         return nullptr;
     }
 
diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h
index c5310da..941ba3a 100644
--- a/libs/binder/ServiceManagerHost.h
+++ b/libs/binder/ServiceManagerHost.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <android-base/macros.h>
 #include <android/os/IServiceManager.h>
 
 namespace android {
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index dd632c0..c8431aa 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -22,6 +22,22 @@
 #include <log/log.h>
 #include <utils/Errors.h>
 
+#define PLOGE_VA_ARGS(...) , ##__VA_ARGS__
+#define PLOGE(fmt, ...) ALOGE(fmt ": %s" PLOGE_VA_ARGS(__VA_ARGS__), strerror(errno))
+
+/* TEMP_FAILURE_RETRY is not available on macOS and Trusty. */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp)                \
+    ({                                         \
+        __typeof__(exp) _rc;                   \
+        do {                                   \
+            _rc = (exp);                       \
+        } while (_rc == -1 && errno == EINTR); \
+        _rc;                                   \
+    })
+#endif
+
 #define TEST_AND_RETURN(value, expr)            \
     do {                                        \
         if (!(expr)) {                          \
@@ -32,6 +48,17 @@
 
 namespace android {
 
+/**
+ * Get the size of a statically initialized array.
+ *
+ * \param N the array to get the size of.
+ * \return the size of the array.
+ */
+template <typename T, size_t N>
+constexpr size_t countof(T (&)[N]) {
+    return N;
+}
+
 // avoid optimizations
 void zeroMemory(uint8_t* data, size_t size);
 
diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp
index 52b8f69..3db038f 100644
--- a/libs/binder/UtilsHost.cpp
+++ b/libs/binder/UtilsHost.cpp
@@ -25,6 +25,8 @@
 
 #include <log/log.h>
 
+#include "Utils.h"
+
 namespace android {
 
 CommandResult::~CommandResult() {
@@ -72,8 +74,8 @@
     return ss.str();
 }
 
-android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec,
-                                             const std::function<bool(const CommandResult&)>& end) {
+std::optional<CommandResult> execute(std::vector<std::string> argStringVec,
+                                     const std::function<bool(const CommandResult&)>& end) {
     // turn vector<string> into null-terminated char* vector.
     std::vector<char*> argv;
     argv.reserve(argStringVec.size() + 1);
@@ -82,14 +84,21 @@
 
     CommandResult ret;
     android::base::unique_fd outWrite;
-    if (!android::base::Pipe(&ret.outPipe, &outWrite))
-        return android::base::ErrnoError() << "pipe() for outPipe";
+    if (!android::base::Pipe(&ret.outPipe, &outWrite)) {
+        PLOGE("pipe() for outPipe");
+        return {};
+    }
     android::base::unique_fd errWrite;
-    if (!android::base::Pipe(&ret.errPipe, &errWrite))
-        return android::base::ErrnoError() << "pipe() for errPipe";
+    if (!android::base::Pipe(&ret.errPipe, &errWrite)) {
+        PLOGE("pipe() for errPipe");
+        return {};
+    }
 
     int pid = fork();
-    if (pid == -1) return android::base::ErrnoError() << "fork()";
+    if (pid == -1) {
+        PLOGE("fork()");
+        return {};
+    }
     if (pid == 0) {
         // child
         ret.outPipe.reset();
@@ -140,12 +149,19 @@
             *errPollFd = {.fd = ret.errPipe.get(), .events = POLLIN};
         }
         int pollRet = poll(fds, nfds, 1000 /* ms timeout */);
-        if (pollRet == -1) return android::base::ErrnoError() << "poll()";
+        if (pollRet == -1) {
+            PLOGE("poll()");
+            return {};
+        }
 
-        if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr))
-            return android::base::ErrnoError() << "read(stdout)";
-        if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr))
-            return android::base::ErrnoError() << "read(stderr)";
+        if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) {
+            PLOGE("read(stdout)");
+            return {};
+        }
+        if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) {
+            PLOGE("read(stderr)");
+            return {};
+        }
 
         if (end && end(ret)) return ret;
     }
@@ -154,7 +170,10 @@
     while (ret.pid.has_value()) {
         int status;
         auto exitPid = waitpid(pid, &status, 0);
-        if (exitPid == -1) return android::base::ErrnoError() << "waitpid(" << pid << ")";
+        if (exitPid == -1) {
+            PLOGE("waitpid(%d)", pid);
+            return {};
+        }
         if (exitPid == pid) {
             if (WIFEXITED(status)) {
                 ret.pid = std::nullopt;
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index 98ac4e0..5de0980 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -23,8 +23,8 @@
 #include <vector>
 
 #include <android-base/macros.h>
-#include <android-base/result.h>
 #include <android-base/unique_fd.h>
+#include <utils/Errors.h>
 
 /**
  * Log a lot more information about host-device binder communication, when debugging issues.
@@ -67,7 +67,8 @@
     }
 
 private:
-    DISALLOW_COPY_AND_ASSIGN(CommandResult);
+    CommandResult(const CommandResult&) = delete;
+    void operator=(const CommandResult&) = delete;
 };
 
 std::ostream& operator<<(std::ostream& os, const CommandResult& res);
@@ -94,6 +95,6 @@
 //
 // If the parent process has encountered any errors for system calls, return ExecuteError with
 // the proper errno set.
-android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec,
-                                             const std::function<bool(const CommandResult&)>& end);
+std::optional<CommandResult> execute(std::vector<std::string> argStringVec,
+                                     const std::function<bool(const CommandResult&)>& end);
 } // namespace android
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 28fb9f1..d78ea0d 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -18,7 +18,7 @@
 
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
-#include <utils/Mutex.h>
+#include <binder/RpcThreads.h>
 
 #include <map>
 #include <optional>
@@ -193,7 +193,7 @@
             void                reportOneDeath(const Obituary& obit);
             bool                isDescriptorCached() const;
 
-    mutable Mutex               mLock;
+    mutable RpcMutex            mLock;
             volatile int32_t    mAlive;
             volatile int32_t    mObitsSent;
             Vector<Obituary>*   mObituaries;
@@ -201,7 +201,7 @@
     mutable String16            mDescriptorCache;
             int32_t             mTrackedUid;
 
-    static Mutex                                sTrackingLock;
+    static RpcMutex                             sTrackingLock;
     static std::unordered_map<int32_t,uint32_t> sTrackingMap;
     static int                                  sNumTrackedUids;
     static std::atomic_bool                     sCountByUidEnabled;
diff --git a/libs/binder/include/binder/Functional.h b/libs/binder/include/binder/Functional.h
new file mode 100644
index 0000000..08e3b21
--- /dev/null
+++ b/libs/binder/include/binder/Functional.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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 <functional>
+#include <memory>
+
+namespace android::binder::impl {
+
+template <typename F>
+constexpr void assert_small_callable() {
+    // While this buffer (std::function::__func::__buf_) is an implementation detail generally not
+    // accessible to users, it's a good bet to assume its size to be around 3 pointers.
+    constexpr size_t kFunctionBufferSize = 3 * sizeof(void*);
+
+    static_assert(sizeof(F) <= kFunctionBufferSize,
+                  "Supplied callable is larger than std::function optimization buffer. "
+                  "Try using std::ref, but make sure lambda lives long enough to be called.");
+}
+
+template <typename F>
+std::unique_ptr<void, std::function<void(void*)>> make_scope_guard(F&& f) {
+    assert_small_callable<decltype(std::bind(f))>();
+    return {reinterpret_cast<void*>(true), std::bind(f)};
+}
+
+template <typename T>
+class SmallFunction : public std::function<T> {
+public:
+    template <typename F>
+    SmallFunction(F&& f) : std::function<T>(f) {
+        assert_small_callable<F>();
+    }
+};
+
+} // namespace android::binder::impl
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 98d12bb..6961abc 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -33,7 +33,6 @@
 #include <utils/RefBase.h>
 #include <utils/String16.h>
 #include <utils/Vector.h>
-#include <utils/Flattenable.h>
 
 #include <binder/IInterface.h>
 #include <binder/Parcelable.h>
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9dc370b..3672702 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -17,13 +17,13 @@
 #pragma once
 
 #include <binder/IBinder.h>
-#include <utils/KeyedVector.h>
-#include <utils/Mutex.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
 
 #include <pthread.h>
 
+#include <mutex>
+
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -178,7 +178,7 @@
     // Time when thread pool was emptied
     int64_t mStarvationStartTimeMs;
 
-    mutable Mutex mLock; // protects everything below.
+    mutable std::mutex mLock; // protects everything below.
 
     Vector<handle_entry> mHandleToObject;
 
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index cb64603..e3805ac 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -15,7 +15,6 @@
  */
 #pragma once
 
-#include <android-base/threads.h>
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
 #include <binder/RpcThreads.h>
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 6db9ad9..115a173 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -25,10 +25,10 @@
 #include <variant>
 #include <vector>
 
-#include <android-base/function_ref.h>
 #include <android-base/unique_fd.h>
 #include <utils/Errors.h>
 
+#include <binder/Functional.h>
 #include <binder/RpcCertificateFormat.h>
 #include <binder/RpcThreads.h>
 
@@ -85,13 +85,13 @@
      *   error - interrupted (failure or trigger)
      */
     [[nodiscard]] virtual status_t interruptableWriteFully(
-            FdTrigger *fdTrigger, iovec *iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>> &altPoll,
-            const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
+            FdTrigger* fdTrigger, iovec* iovs, int niovs,
+            const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll,
+            const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) = 0;
     [[nodiscard]] virtual status_t interruptableReadFully(
-            FdTrigger *fdTrigger, iovec *iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>> &altPoll,
-            std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0;
+            FdTrigger* fdTrigger, iovec* iovs, int niovs,
+            const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll,
+            std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) = 0;
 
     /**
      *  Check whether any threads are blocked while polling the transport
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index a08cb7a..78f8877 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -21,6 +21,7 @@
 use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
 use crate::sys;
 
+use downcast_rs::{impl_downcast, DowncastSync};
 use std::borrow::Borrow;
 use std::cmp::Ordering;
 use std::convert::TryFrom;
@@ -51,7 +52,7 @@
 /// interfaces) must implement this trait.
 ///
 /// This is equivalent `IInterface` in C++.
-pub trait Interface: Send + Sync {
+pub trait Interface: Send + Sync + DowncastSync {
     /// 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.")
@@ -66,6 +67,8 @@
     }
 }
 
+impl_downcast!(sync Interface);
+
 /// Implemented by sync interfaces to specify what the associated async interface is.
 /// Generic to handle the fact that async interfaces are generic over a thread pool.
 ///
@@ -143,7 +146,7 @@
 /// When using the AIDL backend, users need only implement the high-level AIDL-defined
 /// interface. The AIDL compiler then generates a container struct that wraps
 /// the user-defined service and implements `Remotable`.
-pub trait Remotable: Send + Sync {
+pub trait Remotable: Send + Sync + 'static {
     /// The Binder interface descriptor string.
     ///
     /// This string is a unique identifier for a Binder interface, and should be
@@ -893,6 +896,23 @@
                 $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
                 $crate::Strong::new(Box::new(binder))
             }
+
+            /// Tries to downcast the interface to another type.
+            /// When receiving this object from a binder call, make sure that the object received is
+            /// a binder native object and that is of the right type for the Downcast:
+            ///
+            /// let binder = received_object.as_binder();
+            /// if !binder.is_remote() {
+            ///     let binder_native: Binder<BnFoo> = binder.try_into()?;
+            ///     let original_object = binder_native.downcast_binder::<MyFoo>();
+            ///     // Check that returned type is not None before using it
+            /// }
+            ///
+            /// Handle the error cases instead of just calling `unwrap` or `expect` to prevent a
+            /// malicious caller to mount a Denial of Service attack.
+            pub fn downcast_binder<T: $interface>(&self) -> Option<&T> {
+                self.0.as_any().downcast_ref::<T>()
+            }
         }
 
         impl $crate::binder_impl::Remotable for $native {
@@ -1004,7 +1024,7 @@
 
         $(
         // Async interface trait implementations.
-        impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
+        impl<P: $crate::BinderAsyncPool + 'static> $crate::FromIBinder for dyn $async_interface<P> {
             fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> {
                 use $crate::binder_impl::AssociateClass;
 
@@ -1030,27 +1050,27 @@
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
+        impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ {
             fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 let binder = $crate::Interface::as_binder(self);
                 parcel.write(&binder)
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
+        impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ {
             fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
                 parcel.write(&this.map($crate::Interface::as_binder))
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> std::fmt::Debug for dyn $async_interface<P> + '_ {
+        impl<P: $crate::BinderAsyncPool + 'static> std::fmt::Debug for dyn $async_interface<P> + '_ {
             fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                 f.pad(stringify!($async_interface))
             }
         }
 
         /// Convert a &dyn $async_interface to Strong<dyn $async_interface>
-        impl<P: $crate::BinderAsyncPool> std::borrow::ToOwned for dyn $async_interface<P> {
+        impl<P: $crate::BinderAsyncPool + 'static> std::borrow::ToOwned for dyn $async_interface<P> {
             type Owned = $crate::Strong<dyn $async_interface<P>>;
             fn to_owned(&self) -> Self::Owned {
                 self.as_binder().into_interface()
@@ -1058,11 +1078,11 @@
             }
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
+        impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface {
             type Target = dyn $async_interface<P>;
         }
 
-        impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
+        impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> {
             type Target = dyn $interface;
         }
         )?
diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp
index 6712c9c..7e0b594 100644
--- a/libs/binder/tests/binderAllocationLimits.cpp
+++ b/libs/binder/tests/binderAllocationLimits.cpp
@@ -16,6 +16,7 @@
 
 #include <android-base/logging.h>
 #include <binder/Binder.h>
+#include <binder/Functional.h>
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
@@ -28,6 +29,8 @@
 #include <functional>
 #include <vector>
 
+using namespace android::binder::impl;
+
 static android::String8 gEmpty(""); // make sure first allocation from optimization runs
 
 struct DestructionAction {
@@ -172,6 +175,18 @@
     a_binder->pingBinder();
 }
 
+TEST(BinderAllocation, MakeScopeGuard) {
+    const auto m = ScopeDisallowMalloc();
+    {
+        auto guard1 = make_scope_guard([] {});
+        guard1.release();
+
+        auto guard2 = make_scope_guard([&guard1, ptr = imaginary_use] {
+            if (ptr == nullptr) guard1.release();
+        });
+    }
+}
+
 TEST(BinderAllocation, InterfaceDescriptorTransaction) {
     sp<IBinder> a_binder = GetRemoteBinder();
 
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 0075688..0ae536c 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -75,7 +75,7 @@
 public:
     void SetUp() override {
         auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr);
-        ASSERT_THAT(debuggableResult, Ok());
+        ASSERT_TRUE(debuggableResult.has_value());
         ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult;
         auto debuggableBool = ParseBool(Trim(debuggableResult->stdoutStr));
         ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdoutStr);
@@ -84,7 +84,7 @@
         }
 
         auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr);
-        ASSERT_THAT(lsResult, Ok());
+        ASSERT_TRUE(lsResult.has_value());
         if (lsResult->exitCode != 0) {
             GTEST_SKIP() << "b/182914638: until feature is fully enabled, skip test on devices "
                             "without servicedispatcher";
@@ -95,7 +95,7 @@
 
         auto service = execute({"adb", "shell", kServiceBinary, kServiceName, kDescriptor},
                                &CommandResult::stdoutEndsWithNewLine);
-        ASSERT_THAT(service, Ok());
+        ASSERT_TRUE(service.has_value());
         ASSERT_EQ(std::nullopt, service->exitCode) << *service;
         mService = std::move(*service);
     }
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 341e9ce..f3969f1 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -29,17 +29,17 @@
 
 #include <android-base/properties.h>
 #include <android-base/result-gmock.h>
-#include <android-base/result.h>
-#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <binder/Binder.h>
 #include <binder/BpBinder.h>
+#include <binder/Functional.h>
 #include <binder/IBinder.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
+#include <utils/Flattenable.h>
 
 #include <linux/sched.h>
 #include <sys/epoll.h>
@@ -52,6 +52,7 @@
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
 using namespace android;
+using namespace android::binder::impl;
 using namespace std::string_literals;
 using namespace std::chrono_literals;
 using android::base::testing::HasValue;
@@ -1325,7 +1326,7 @@
     ASSERT_EQ(0, ret);
 
     // Restore the original file limits when the test finishes
-    base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); });
+    auto guardUnguard = make_scope_guard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); });
 
     rlimit testNofile = {1024, 1024};
     ret = setrlimit(RLIMIT_NOFILE, &testNofile);
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 786fab8..c3070dd 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -74,8 +74,7 @@
 #ifdef __ANDROID__
     return base::GetProperty("ro.build.version.codename", "") != "REL";
 #else
-    // TODO(b/305983144): restrict on other platforms
-    return true;
+    return false;
 #endif
 }
 
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index 7ec7c99..e59dc82 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <android-base/logging.h>
-#include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <binder/Parcel.h>
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 1c13866..cbbbe74 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -28,6 +28,7 @@
 #include <gtest/gtest.h>
 #pragma clang diagnostic pop
 
+#include <utils/Flattenable.h>
 #include <utils/LightRefBase.h>
 #include <utils/NativeHandle.h>
 
diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp
index 25e286c..6301c74 100644
--- a/libs/binder/tests/binderUtilsHostTest.cpp
+++ b/libs/binder/tests/binderUtilsHostTest.cpp
@@ -32,7 +32,7 @@
 
 TEST(UtilsHost, ExecuteImmediately) {
     auto result = execute({"echo", "foo"}, nullptr);
-    ASSERT_THAT(result, Ok());
+    ASSERT_TRUE(result.has_value());
     EXPECT_THAT(result->exitCode, Optional(EX_OK));
     EXPECT_EQ(result->stdoutStr, "foo\n");
 }
@@ -58,7 +58,7 @@
         EXPECT_GE(elapsedMs, 1000);
         EXPECT_LT(elapsedMs, 2000);
 
-        ASSERT_THAT(result, Ok());
+        ASSERT_TRUE(result.has_value());
         EXPECT_EQ(std::nullopt, result->exitCode);
         EXPECT_EQ(result->stdoutStr, "foo\n");
     }
@@ -83,7 +83,7 @@
         EXPECT_GE(elapsedMs, 4000);
         EXPECT_LT(elapsedMs, 6000);
 
-        ASSERT_THAT(result, Ok());
+        ASSERT_TRUE(result.has_value());
         EXPECT_EQ(std::nullopt, result->exitCode);
         EXPECT_EQ(result->stdoutStr, "foo\n");
     }
@@ -104,7 +104,7 @@
         return false;
     });
 
-    ASSERT_THAT(result, Ok());
+    ASSERT_TRUE(result.has_value());
     EXPECT_EQ(std::nullopt, result->exitCode);
     EXPECT_THAT(result->signal, Optional(SIGKILL));
 }
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 383795e..fe79f8e 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -32,7 +32,11 @@
     host_supported: true,
 
     fuzz_config: {
-        cc: ["smoreland@google.com"],
+        cc: [
+            "smoreland@google.com",
+            "waghpawan@google.com",
+        ],
+        use_for_presubmit: true,
     },
 
     srcs: [
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 416ffad..ffeca2d 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -25,6 +25,7 @@
 #include <binder/ParcelableHolder.h>
 #include <binder/PersistableBundle.h>
 #include <binder/Status.h>
+#include <utils/Flattenable.h>
 
 #include "../../Utils.h"
 
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
index e494366..0706182 100644
--- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <android-base/macros.h>
 #include <binder/RecordedTransaction.h>
 #include <filesystem>
 
@@ -35,8 +34,8 @@
     if (transaction.has_value()) {
         intermediateFile = std::tmpfile();
 
-        android::base::unique_fd fdForWriting(fileno(intermediateFile));
-        auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+        android::base::unique_fd fdForWriting(dup(fileno(intermediateFile)));
+        auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting);
 
         std::fclose(intermediateFile);
     }
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
index 33a653e..9289f6a 100644
--- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <android-base/macros.h>
 #include <binder/RecordedTransaction.h>
 #include <fuzzbinder/random_parcel.h>
 #include <filesystem>
@@ -55,7 +54,7 @@
     if (transaction.has_value()) {
         std::FILE* intermediateFile = std::tmpfile();
         android::base::unique_fd fdForWriting(dup(fileno(intermediateFile)));
-        auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+        auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting);
 
         std::fclose(intermediateFile);
     }
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 43e06e0..0d18b0b 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -26,13 +26,11 @@
 #include "../OS.h"
 #include "TrustyStatus.h"
 
-using android::base::Result;
-
 namespace android::binder::os {
 
-Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) {
+status_t setNonBlocking(android::base::borrowed_fd /*fd*/) {
     // Trusty IPC syscalls are all non-blocking by default.
-    return {};
+    return OK;
 }
 
 status_t getRandomBytes(uint8_t* data, size_t size) {
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index 692f82d..6bb45e2 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -29,6 +29,8 @@
 
 namespace android {
 
+using namespace android::binder::impl;
+
 // RpcTransport for Trusty.
 class RpcTransportTipcTrusty : public RpcTransport {
 public:
@@ -45,7 +47,7 @@
 
     status_t interruptableWriteFully(
             FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/,
+            const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
             const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
             override {
         if (niovs < 0) {
@@ -115,7 +117,7 @@
 
     status_t interruptableReadFully(
             FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
-            const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/,
+            const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
         if (niovs < 0) {
             return BAD_VALUE;
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 8924b36..aa476f9 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <android-base/expected.h>
-#include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
 #include <binder/RpcServer.h>
@@ -83,7 +82,8 @@
     // Both this class and RpcServer have multiple non-copyable fields,
     // including mPortAcl below which can't be copied because mUuidPtrs
     // holds pointers into it
-    DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty);
+    RpcServerTrusty(const RpcServerTrusty&) = delete;
+    void operator=(const RpcServerTrusty&) = delete;
 
     friend sp<RpcServerTrusty>;
     explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp
index b4243af..88a1075 100644
--- a/libs/binder/trusty/logging.cpp
+++ b/libs/binder/trusty/logging.cpp
@@ -22,7 +22,6 @@
 #include <iostream>
 #include <string>
 
-#include <android-base/macros.h>
 #include <android-base/strings.h>
 
 namespace android {
diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
index d4605ea..3fe71ce 100644
--- a/libs/bufferqueueconverter/Android.bp
+++ b/libs/bufferqueueconverter/Android.bp
@@ -34,5 +34,7 @@
         "libbase",
         "liblog",
     ],
+    static_libs: ["libguiflags"],
     export_include_dirs: ["include"],
+    export_static_lib_headers: ["libguiflags"],
 }
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index ed5d5c1..394a000 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -575,17 +575,23 @@
         return mAngleNamespace;
     }
 
-    if (mAnglePath.empty() && !mShouldUseSystemAngle) {
-        ALOGV("mAnglePath is empty and not using system ANGLE, abort creating ANGLE namespace");
+    // If ANGLE path is not set, it means ANGLE should not be used for this process;
+    // or if ANGLE path is set and set to use system ANGLE, then a namespace is not needed
+    // because:
+    //     1) if the default OpenGL ES driver is already ANGLE, then the loader will skip;
+    //     2) if the default OpenGL ES driver is native, then there's no symbol conflict;
+    //     3) if there's no OpenGL ES driver is preloaded, then there's no symbol conflict.
+    if (mAnglePath.empty() || mShouldUseSystemAngle) {
+        ALOGV("mAnglePath is empty or use system ANGLE, abort creating ANGLE namespace");
         return nullptr;
     }
 
     // Construct the search paths for system ANGLE.
     const char* const defaultLibraryPaths =
 #if defined(__LP64__)
-            "/vendor/lib64/egl:/system/lib64/egl";
+            "/vendor/lib64/egl:/system/lib64";
 #else
-            "/vendor/lib/egl:/system/lib/egl";
+            "/vendor/lib/egl:/system/lib";
 #endif
 
     // If the application process will run on top of system ANGLE, construct the namespace
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 47607a0..9ebaf16 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -104,7 +104,7 @@
         GL_UPDATED = 2,
         VULKAN = 3,
         VULKAN_UPDATED = 4,
-        ANGLE = 5,
+        ANGLE = 5, // cover both system ANGLE and ANGLE APK
     };
 
     enum Stats {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index f17a654..c84ee1f 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -20,6 +20,25 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
+aconfig_declarations {
+    name: "libgui_flags",
+    package: "com.android.graphics.libgui.flags",
+    srcs: ["libgui_flags.aconfig"],
+}
+
+cc_aconfig_library {
+    name: "libguiflags",
+    host_supported: true,
+    vendor_available: true,
+    min_sdk_version: "29",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+        "test_com.android.media.swcodec",
+    ],
+    aconfig_declarations: "libgui_flags",
+}
+
 cc_library_headers {
     name: "libgui_headers",
     vendor_available: true,
@@ -36,6 +55,8 @@
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hardware.graphics.bufferqueue@2.0",
     ],
+    static_libs: ["libguiflags"],
+    export_static_lib_headers: ["libguiflags"],
     min_sdk_version: "29",
     // TODO(b/218719284) can media use be constrained to libgui_bufferqueue_static?
     apex_available: [
@@ -192,19 +213,6 @@
     },
 }
 
-aconfig_declarations {
-    name: "libgui_flags",
-    package: "com.android.graphics.libgui.flags",
-    srcs: ["libgui_flags.aconfig"],
-}
-
-cc_aconfig_library {
-    name: "libguiflags",
-    host_supported: true,
-    vendor_available: true,
-    aconfig_declarations: "libgui_flags",
-}
-
 filegroup {
     name: "libgui-sources",
     srcs: [
@@ -265,6 +273,9 @@
         "libbinder",
         "libGLESv2",
     ],
+    export_static_lib_headers: [
+        "libguiflags",
+    ],
 }
 
 cc_library_shared {
@@ -460,6 +471,7 @@
     static_libs: [
         "libgtest",
         "libgmock",
+        "libguiflags",
     ],
 
     srcs: [
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index dd0a028..8d0331e 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -26,7 +26,7 @@
 #include <gui/BufferQueueConsumer.h>
 #include <gui/BufferQueueCore.h>
 #include <gui/BufferQueueProducer.h>
-#include <gui/Flags.h>
+
 #include <gui/FrameRateUtils.h>
 #include <gui/GLConsumer.h>
 #include <gui/IProducerListener.h>
@@ -41,8 +41,6 @@
 #include <android-base/thread_annotations.h>
 #include <chrono>
 
-#include <com_android_graphics_libgui_flags.h>
-
 using namespace com::android::graphics::libgui;
 using namespace std::chrono_literals;
 
@@ -144,7 +142,7 @@
     }
 }
 
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
 void BLASTBufferItemConsumer::onSetFrameRate(float frameRate, int8_t compatibility,
                                              int8_t changeFrameRateStrategy) {
     sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index ab0f6d2..b0f6e69 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -22,7 +22,6 @@
 #include <gui/BufferQueueConsumer.h>
 #include <gui/BufferQueueCore.h>
 #include <gui/BufferQueueProducer.h>
-#include <gui/Flags.h>
 
 namespace android {
 
@@ -99,7 +98,7 @@
     }
 }
 
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
 void BufferQueue::ProxyConsumerListener::onSetFrameRate(float frameRate, int8_t compatibility,
                                                         int8_t changeFrameRateStrategy) {
     sp<ConsumerListener> listener(mConsumerListener.promote());
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 67dff6d..19693e3 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -32,7 +32,7 @@
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueCore.h>
 #include <gui/BufferQueueProducer.h>
-#include <gui/Flags.h>
+
 #include <gui/FrameRateUtils.h>
 #include <gui/GLConsumer.h>
 #include <gui/IConsumerListener.h>
@@ -1753,7 +1753,7 @@
     return NO_ERROR;
 }
 
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
 status_t BufferQueueProducer::setFrameRate(float frameRate, int8_t compatibility,
                                            int8_t changeFrameRateStrategy) {
     ATRACE_CALL();
diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp
index 6993bfa..11524e2 100644
--- a/libs/gui/FrameRateUtils.cpp
+++ b/libs/gui/FrameRateUtils.cpp
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include <gui/Flags.h>
 #include <gui/FrameRateUtils.h>
 #include <system/window.h>
 #include <utils/Log.h>
 
 #include <cmath>
 
+#include <com_android_graphics_libgui_flags.h>
+
 namespace android {
+using namespace com::android::graphics::libgui;
 // Returns true if the frameRate is valid.
 //
 // @param frameRate the frame rate in Hz
@@ -53,7 +55,7 @@
             changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) {
             ALOGE("%s failed - invalid change frame rate strategy value %d", functionName,
                   changeFrameRateStrategy);
-            if (FLAG_BQ_SET_FRAME_RATE) {
+            if (flags::bq_setframerate()) {
                 return false;
             }
         }
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index d0c09e4..e81c098 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -28,7 +28,7 @@
 #include <binder/IInterface.h>
 
 #include <gui/BufferQueueDefs.h>
-#include <gui/Flags.h>
+
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
 #include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
@@ -763,7 +763,7 @@
         }
         return result;
     }
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
     virtual status_t setFrameRate(float frameRate, int8_t compatibility,
                                   int8_t changeFrameRateStrategy) override {
         Parcel data, reply;
@@ -973,7 +973,7 @@
     return INVALID_OPERATION;
 }
 
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
 status_t IGraphicBufferProducer::setFrameRate(float /*frameRate*/, int8_t /*compatibility*/,
                                               int8_t /*changeFrameRateStrategy*/) {
     // No-op for IGBP other than BufferQueue.
@@ -1522,7 +1522,7 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
         case SET_FRAME_RATE: {
             CHECK_INTERFACE(IGraphicBuffer, data, reply);
             float frameRate = data.readFloat();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index a87f053..07a0cfe 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -43,7 +43,7 @@
 
 #include <gui/AidlStatusUtil.h>
 #include <gui/BufferItem.h>
-#include <gui/Flags.h>
+
 #include <gui/IProducerListener.h>
 
 #include <gui/ISurfaceComposer.h>
@@ -2571,7 +2571,7 @@
 
 status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
                                int8_t changeFrameRateStrategy) {
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
     if (flags::bq_setframerate()) {
         status_t err = mGraphicBufferProducer->setFrameRate(frameRate, compatibility,
                                                             changeFrameRateStrategy);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 02d7c4d..892215e 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -19,7 +19,7 @@
 
 #include <gui/BufferItem.h>
 #include <gui/BufferItemConsumer.h>
-#include <gui/Flags.h>
+
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/SurfaceComposerClient.h>
 
@@ -31,6 +31,8 @@
 #include <thread>
 #include <queue>
 
+#include <com_android_graphics_libgui_flags.h>
+
 namespace android {
 
 class BLASTBufferQueue;
@@ -59,7 +61,7 @@
 
 protected:
     void onSidebandStreamChanged() override EXCLUDES(mMutex);
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
     void onSetFrameRate(float frameRate, int8_t compatibility,
                         int8_t changeFrameRateStrategy) override;
 #endif
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 2756277..0948c4d0 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -19,11 +19,13 @@
 
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueDefs.h>
-#include <gui/Flags.h>
+
 #include <gui/IConsumerListener.h>
 #include <gui/IGraphicBufferConsumer.h>
 #include <gui/IGraphicBufferProducer.h>
 
+#include <com_android_graphics_libgui_flags.h>
+
 namespace android {
 
 class BufferQueue {
@@ -70,7 +72,7 @@
         void addAndGetFrameTimestamps(
                 const NewFrameEventsEntry* newTimestamps,
                 FrameEventHistoryDelta* outDelta) override;
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
         void onSetFrameRate(float frameRate, int8_t compatibility,
                             int8_t changeFrameRateStrategy) override;
 #endif
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 38805d0..de47483 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -18,7 +18,7 @@
 #define ANDROID_GUI_BUFFERQUEUEPRODUCER_H
 
 #include <gui/BufferQueueDefs.h>
-#include <gui/Flags.h>
+
 #include <gui/IGraphicBufferProducer.h>
 
 namespace android {
@@ -202,7 +202,7 @@
 
     // See IGraphicBufferProducer::setAutoPrerotation
     virtual status_t setAutoPrerotation(bool autoPrerotation);
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
     // See IGraphicBufferProducer::setFrameRate
     status_t setFrameRate(float frameRate, int8_t compatibility,
                           int8_t changeFrameRateStrategy) override;
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
deleted file mode 100644
index a2cff56..0000000
--- a/libs/gui/include/gui/Flags.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2023 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
-
-// TODO(281695725): replace this with build time flags, whenever they are available
-#ifndef FLAG_BQ_SET_FRAME_RATE
-#define FLAG_BQ_SET_FRAME_RATE false
-#endif
\ No newline at end of file
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index e183bf2..51d3959 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -19,13 +19,13 @@
 #include <binder/IInterface.h>
 #include <binder/SafeInterface.h>
 
-#include <gui/Flags.h>
-
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
 #include <cstdint>
 
+#include <com_android_graphics_libgui_flags.h>
+
 namespace android {
 
 class BufferItem;
@@ -93,7 +93,7 @@
     virtual void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/,
                                           FrameEventHistoryDelta* /*outDelta*/) {}
 
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
     // Notifies the consumer of a setFrameRate call from the producer side.
     virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/,
                                 int8_t /*changeFrameRateStrategy*/) {}
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 3562906..7639e70 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -31,7 +31,6 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
-#include <gui/Flags.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/HdrMetadata.h>
 
@@ -42,6 +41,8 @@
 #include <optional>
 #include <vector>
 
+#include <com_android_graphics_libgui_flags.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
@@ -677,7 +678,7 @@
     // the width and height used for dequeueBuffer will be additionally swapped.
     virtual status_t setAutoPrerotation(bool autoPrerotation);
 
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
     // Sets the apps intended frame rate.
     virtual status_t setFrameRate(float frameRate, int8_t compatibility,
                                   int8_t changeFrameRateStrategy);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 38c0eed..6dcd501 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -21,7 +21,7 @@
         "-Wall",
         "-Werror",
         "-Wno-extra",
-        "-DFLAG_BQ_SET_FRAME_RATE=true",
+        "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
     ],
 
     srcs: [
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 17aa5f1..1410c7d 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -1266,9 +1266,7 @@
 }
 
 TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) {
-    if (flags::bq_setframerate()) {
-        ASSERT_EQ(true, FLAG_BQ_SET_FRAME_RATE);
-    }
+    ASSERT_EQ(flags::bq_setframerate(), COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE));
 }
 
 struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e7b1232..86ced2c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -1341,7 +1341,7 @@
                 newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime :
                 FenceTime::NO_FENCE;
         // HWC2 releases the previous buffer after a new latch just before
-        // calling postComposition.
+        // calling onCompositionPresented.
         if (oldFrame != nullptr) {
             mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime,
                     std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime));
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 69a4f0a..c37db16 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -37,14 +37,14 @@
 // flags
 /////////////////////////////////////////////////
 aconfig_declarations {
-    name: "aconfig_input_flags",
+    name: "com.android.input.flags-aconfig",
     package: "com.android.input.flags",
     srcs: ["input_flags.aconfig"],
 }
 
 cc_aconfig_library {
-    name: "aconfig_input_flags_c_lib",
-    aconfig_declarations: "aconfig_input_flags",
+    name: "com.android.input.flags-aconfig-cc",
+    aconfig_declarations: "com.android.input.flags-aconfig",
     host_supported: true,
     // Use the test version of the aconfig flag library by default to allow tests to set local
     // overrides for flags, without having to link against a separate version of libinput or of this
@@ -242,7 +242,7 @@
     ],
 
     whole_static_libs: [
-        "aconfig_input_flags_c_lib",
+        "com.android.input.flags-aconfig-cc",
         "libinput_rust_ffi",
     ],
 
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 116b778..613a0df 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -275,10 +275,10 @@
     }
 }
 
-void VelocityTracker::addMovement(const MotionEvent* event) {
+void VelocityTracker::addMovement(const MotionEvent& event) {
     // Stores data about which axes to process based on the incoming motion event.
     std::set<int32_t> axesToProcess;
-    int32_t actionMasked = event->getActionMasked();
+    int32_t actionMasked = event.getActionMasked();
 
     switch (actionMasked) {
         case AMOTION_EVENT_ACTION_DOWN:
@@ -291,7 +291,7 @@
             // Start a new movement trace for a pointer that just went down.
             // We do this on down instead of on up because the client may want to query the
             // final velocity for a pointer that just went up.
-            clearPointer(event->getPointerId(event->getActionIndex()));
+            clearPointer(event.getPointerId(event.getActionIndex()));
             axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
             break;
         }
@@ -300,8 +300,14 @@
             axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end());
             break;
         case AMOTION_EVENT_ACTION_POINTER_UP:
+            if (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) {
+                clearPointer(event.getPointerId(event.getActionIndex()));
+                return;
+            }
+            // Continue to ACTION_UP to ensure that the POINTER_STOPPED logic is triggered.
+            [[fallthrough]];
         case AMOTION_EVENT_ACTION_UP: {
-            std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime);
+            std::chrono::nanoseconds delaySinceLastEvent(event.getEventTime() - mLastEventTime);
             if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) {
                 ALOGD_IF(DEBUG_VELOCITY,
                          "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.",
@@ -325,21 +331,26 @@
         case AMOTION_EVENT_ACTION_SCROLL:
             axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL);
             break;
+        case AMOTION_EVENT_ACTION_CANCEL: {
+            clear();
+            return;
+        }
+
         default:
             // Ignore all other actions.
             return;
     }
 
-    const size_t historySize = event->getHistorySize();
+    const size_t historySize = event.getHistorySize();
     for (size_t h = 0; h <= historySize; h++) {
-        const nsecs_t eventTime = event->getHistoricalEventTime(h);
-        for (size_t i = 0; i < event->getPointerCount(); i++) {
-            if (event->isResampled(i, h)) {
+        const nsecs_t eventTime = event.getHistoricalEventTime(h);
+        for (size_t i = 0; i < event.getPointerCount(); i++) {
+            if (event.isResampled(i, h)) {
                 continue; // skip resampled samples
             }
-            const int32_t pointerId = event->getPointerId(i);
+            const int32_t pointerId = event.getPointerId(i);
             for (int32_t axis : axesToProcess) {
-                const float position = event->getHistoricalAxisValue(axis, i, h);
+                const float position = event.getHistoricalAxisValue(axis, i, h);
                 addMovement(eventTime, pointerId, axis, position);
             }
         }
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 978a80f..3672387 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -48,3 +48,17 @@
   description: "Enable additional palm rejection on touchpad while typing"
   bug: "301055381"
 }
+
+flag {
+  name: "remove_app_switch_drops"
+  namespace: "input"
+  description: "Remove the logic of dropping events due to pending app switch"
+  bug: "284808102"
+}
+
+flag {
+  name: "disable_reject_touch_on_stylus_hover"
+  namespace: "input"
+  description: "Disable touch rejection when the stylus hovers the screen"
+  bug: "301216095"
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index bbc6d98..5f05a0f 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -118,18 +118,17 @@
 
         match action.into() {
             MotionAction::Down => {
+                if self.touching_pointer_ids_by_device.contains_key(&device_id) {
+                    return Err(format!(
+                        "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
+                        self.name, device_id, self.touching_pointer_ids_by_device
+                    ));
+                }
                 let it = self
                     .touching_pointer_ids_by_device
                     .entry(device_id)
                     .or_insert_with(HashSet::new);
-                let pointer_id = pointer_properties[0].id;
-                if it.contains(&pointer_id) {
-                    return Err(format!(
-                        "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
-                        self.name, device_id, it
-                    ));
-                }
-                it.insert(pointer_id);
+                it.insert(pointer_properties[0].id);
             }
             MotionAction::PointerDown { action_index } => {
                 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
@@ -353,6 +352,56 @@
     }
 
     #[test]
+    fn two_pointer_stream() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                Source::Touchscreen,
+                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        // POINTER 1 DOWN
+        let two_pointer_properties =
+            Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                Source::Touchscreen,
+                input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
+                    | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                &two_pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        // POINTER 0 UP
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                Source::Touchscreen,
+                input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP
+                    | (0 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                &two_pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+        // ACTION_UP for pointer id=1
+        let pointer_1_properties = Vec::from([RustPointerProperties { id: 1 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                Source::Touchscreen,
+                input_bindgen::AMOTION_EVENT_ACTION_UP,
+                &pointer_1_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+    }
+
+    #[test]
     fn multi_device_stream() {
         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 1c8ec90..f9ca280 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -229,41 +229,23 @@
     return events;
 }
 
-static std::optional<float> computePlanarVelocity(
-        const VelocityTracker::Strategy strategy,
-        const std::vector<PlanarMotionEventEntry>& motions, int32_t axis,
-        uint32_t pointerId = DEFAULT_POINTER_ID) {
+static std::optional<float> computeVelocity(const VelocityTracker::Strategy strategy,
+                                            const std::vector<MotionEvent>& events, int32_t axis,
+                                            uint32_t pointerId = DEFAULT_POINTER_ID) {
     VelocityTracker vt(strategy);
 
-    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
-    for (MotionEvent event : events) {
-        vt.addMovement(&event);
+    for (const MotionEvent& event : events) {
+        vt.addMovement(event);
     }
 
     return vt.getVelocity(axis, pointerId);
 }
 
-static std::vector<MotionEvent> createMotionEventStream(
-        int32_t axis, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motion) {
-    switch (axis) {
-        case AMOTION_EVENT_AXIS_SCROLL:
-            return createAxisScrollMotionEventStream(motion);
-        default:
-            ADD_FAILURE() << "Axis " << axis << " is not supported";
-            return {};
-    }
-}
-
-static std::optional<float> computeVelocity(
+static std::optional<float> computePlanarVelocity(
         const VelocityTracker::Strategy strategy,
-        const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, int32_t axis) {
-    VelocityTracker vt(strategy);
-
-    for (const MotionEvent& event : createMotionEventStream(axis, motions)) {
-        vt.addMovement(&event);
-    }
-
-    return vt.getVelocity(axis, DEFAULT_POINTER_ID);
+        const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, uint32_t pointerId) {
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
+    return computeVelocity(strategy, events, axis, pointerId);
 }
 
 static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
@@ -277,23 +259,23 @@
         const VelocityTracker::Strategy strategy,
         const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions,
         std::optional<float> targetVelocity) {
-    checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
+    std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions);
+    checkVelocity(computeVelocity(strategy, events, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
     // The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall
     // back to a strategy that supports differential axes.
-    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions,
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events,
                                   AMOTION_EVENT_AXIS_SCROLL),
                   targetVelocity);
 }
 
 static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions,
                                              float velocity) {
-    VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
-    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
-    for (MotionEvent event : events) {
-        vt.addMovement(&event);
-    }
-    std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0);
-    std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
+    std::optional<float> velocityX =
+            computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                                  DEFAULT_POINTER_ID);
+    std::optional<float> velocityY =
+            computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                                  DEFAULT_POINTER_ID);
     ASSERT_TRUE(velocityX);
     ASSERT_TRUE(velocityY);
 
@@ -330,12 +312,14 @@
                                                    {30ms, {{6, 20}}},
                                                    {40ms, {{10, 30}}}};
 
-    EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X),
+    EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                                    DEFAULT_POINTER_ID),
               computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions,
-                                    AMOTION_EVENT_AXIS_X));
-    EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y),
+                                    AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID));
+    EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                                    DEFAULT_POINTER_ID),
               computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions,
-                                    AMOTION_EVENT_AXIS_Y));
+                                    AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID));
 }
 
 TEST_F(VelocityTrackerTest, TestComputedVelocity) {
@@ -431,7 +415,7 @@
     VelocityTracker vt(VelocityTracker::Strategy::IMPULSE);
     std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
     for (const MotionEvent& event : events) {
-        vt.addMovement(&event);
+        vt.addMovement(event);
     }
 
     float maxFloat = std::numeric_limits<float>::max();
@@ -509,6 +493,89 @@
     computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
 }
 
+/**
+ * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0.
+ */
+TEST_F(VelocityTrackerTest, ActionCancelResultsInZeroVelocity) {
+    std::vector<PlanarMotionEventEntry> motions = {
+            {0ms, {{0, 0}}},    // DOWN
+            {10ms, {{5, 10}}},  // MOVE
+            {20ms, {{10, 20}}}, // MOVE
+            {20ms, {{10, 20}}}, // ACTION_UP
+    };
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
+    // By default, `createTouchMotionEventStream` produces an event stream that terminates with
+    // ACTION_UP. We need to manually change it to ACTION_CANCEL.
+    MotionEvent& lastEvent = events.back();
+    lastEvent.setAction(AMOTION_EVENT_ACTION_CANCEL);
+    lastEvent.setFlags(lastEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED);
+    const int32_t pointerId = lastEvent.getPointerId(0);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X,
+                                  pointerId),
+                  /*targetVelocity*/ std::nullopt);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y,
+                                  pointerId),
+                  /*targetVelocity*/ std::nullopt);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X,
+                                  pointerId),
+                  /*targetVelocity*/ std::nullopt);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y,
+                                  pointerId),
+                  /*targetVelocity*/ std::nullopt);
+}
+
+/**
+ * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0.
+ */
+TEST_F(VelocityTrackerTest, ActionPointerCancelResultsInZeroVelocityForThatPointer) {
+    std::vector<PlanarMotionEventEntry> motions = {
+            {0ms, {{0, 5}, {NAN, NAN}}},    // DOWN
+            {0ms, {{0, 5}, {10, 15}}},      // POINTER_DOWN
+            {10ms, {{5, 10}, {15, 20}}},    // MOVE
+            {20ms, {{10, 15}, {20, 25}}},   // MOVE
+            {30ms, {{10, 15}, {20, 25}}},   // POINTER_UP
+            {30ms, {{10, 15}, {NAN, NAN}}}, // UP
+    };
+    std::vector<MotionEvent> events = createTouchMotionEventStream(motions);
+    // Cancel the lifting pointer of the ACTION_POINTER_UP event
+    MotionEvent& pointerUpEvent = events.rbegin()[1];
+    pointerUpEvent.setFlags(pointerUpEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED);
+    const int32_t pointerId = pointerUpEvent.getPointerId(pointerUpEvent.getActionIndex());
+    // Double check the stream
+    ASSERT_EQ(1, pointerId);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP, pointerUpEvent.getActionMasked());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, events.back().getActionMasked());
+
+    // Ensure the velocity of the lifting pointer is zero
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X,
+                                  pointerId),
+                  /*targetVelocity*/ std::nullopt);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y,
+                                  pointerId),
+                  /*targetVelocity*/ std::nullopt);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X,
+                                  pointerId),
+                  /*targetVelocity*/ std::nullopt);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y,
+                                  pointerId),
+                  /*targetVelocity*/ std::nullopt);
+
+    // The remaining pointer should have the correct velocity.
+    const int32_t remainingPointerId = events.back().getPointerId(0);
+    ASSERT_EQ(0, remainingPointerId);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X,
+                                  remainingPointerId),
+                  /*targetVelocity*/ 500);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y,
+                                  remainingPointerId),
+                  /*targetVelocity*/ 500);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X,
+                                  remainingPointerId),
+                  /*targetVelocity*/ 500);
+    checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y,
+                                  remainingPointerId),
+                  /*targetVelocity*/ 500);
+}
 
 /**
  * ================== VelocityTracker tests generated by recording real events =====================
@@ -1336,9 +1403,10 @@
             {40ms, 100},
     };
 
-    EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, motions,
+    std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions);
+    EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, events,
                               AMOTION_EVENT_AXIS_SCROLL),
-              computeVelocity(VelocityTracker::Strategy::DEFAULT, motions,
+              computeVelocity(VelocityTracker::Strategy::DEFAULT, events,
                               AMOTION_EVENT_AXIS_SCROLL));
 }
 
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 32fb350..099f47d 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -19,7 +19,7 @@
 #include <android/hardware_buffer.h>
 #include <gui/BufferQueueDefs.h>
 #include <gui/ConsumerBase.h>
-#include <gui/Flags.h>
+
 #include <gui/IGraphicBufferProducer.h>
 #include <sys/cdefs.h>
 #include <system/graphics.h>
@@ -352,7 +352,7 @@
     /**
      * onSetFrameRate Notifies the consumer of a setFrameRate call from the producer side.
      */
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
     void onSetFrameRate(float frameRate, int8_t compatibility,
                         int8_t changeFrameRateStrategy) override;
 #endif
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index c2535e0..3a09204 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -515,7 +515,7 @@
     }
 }
 
-#if FLAG_BQ_SET_FRAME_RATE
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)
 void SurfaceTexture::onSetFrameRate(float frameRate, int8_t compatibility,
                                     int8_t changeFrameRateStrategy) {
     SFT_LOGV("onSetFrameRate: %.2f", frameRate);
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
index 0eeca54..86dcaef 100644
--- a/libs/permission/Android.bp
+++ b/libs/permission/Android.bp
@@ -20,6 +20,12 @@
     ],
 }
 
+filegroup {
+    name: "framework-permission-aidl-filegroup",
+    srcs: ["aidl/android/**/*.aidl"],
+    path: "aidl",
+}
+
 cc_library {
     name: "libpermission",
     host_supported: true,
@@ -35,6 +41,7 @@
         "-Werror",
     ],
     srcs: [
+        ":framework-permission-aidl-filegroup",
         "AppOpsManager.cpp",
         "IAppOpsCallback.cpp",
         "IAppOpsService.cpp",
diff --git a/libs/permission/AppOpsManager.cpp b/libs/permission/AppOpsManager.cpp
index 6959274..b407d02 100644
--- a/libs/permission/AppOpsManager.cpp
+++ b/libs/permission/AppOpsManager.cpp
@@ -31,6 +31,9 @@
 
 namespace android {
 
+using ::android::String16;
+using ::android::String8;
+
 static const sp<IBinder>& getClientId() {
     static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
     static sp<IBinder> gClientId;
@@ -43,6 +46,11 @@
     return gClientId;
 }
 
+
+static std::string getString(const String16& stringToConvert) {
+  return std::string(String8(stringToConvert).c_str());
+}
+
 AppOpsManager::AppOpsManager()
 {
 }
@@ -78,9 +86,14 @@
 int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
 {
     sp<IAppOpsService> service = getService();
-    return service != nullptr
-            ? service->checkOperation(op, uid, callingPackage)
-            : AppOpsManager::MODE_IGNORED;
+    if (service == nullptr) {
+        return AppOpsManager::MODE_IGNORED;
+    }
+    AttributionSourceState attributionSourceState;
+    attributionSourceState.uid = uid;
+    attributionSourceState.packageName = getString(callingPackage);
+
+    return service->checkOperationWithState(op, attributionSourceState);
 }
 
 int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
@@ -99,12 +112,18 @@
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
         const std::optional<String16>& attributionTag, const String16& message) {
     sp<IAppOpsService> service = getService();
-    int32_t mode = service != nullptr
-            ? service->noteOperation(op, uid, callingPackage, attributionTag,
-                    shouldCollectNotes(op), message, uid == AID_SYSTEM)
-            : AppOpsManager::MODE_IGNORED;
+    if (service == nullptr) {
+        return AppOpsManager::MODE_IGNORED;
+    }
+    AttributionSourceState attributionSourceState;
+    attributionSourceState.uid = uid;
+    attributionSourceState.packageName = getString(callingPackage);
+    if (attributionTag.has_value()) {
+        attributionSourceState.attributionTag = getString(attributionTag.value());
+    }
 
-    return mode;
+    return service->noteOperationWithState(op, attributionSourceState,
+            shouldCollectNotes(op), message, uid == AID_SYSTEM);
 }
 
 int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
@@ -117,13 +136,18 @@
         bool startIfModeDefault, const std::optional<String16>& attributionTag,
         const String16& message) {
     sp<IAppOpsService> service = getService();
-    int32_t mode = service != nullptr
-            ? service->startOperation(getClientId(), op, uid, callingPackage,
-                    attributionTag, startIfModeDefault, shouldCollectNotes(op), message,
-                    uid == AID_SYSTEM)
-            : AppOpsManager::MODE_IGNORED;
+    if (service == nullptr) {
+        return AppOpsManager::MODE_IGNORED;
+    }
+    AttributionSourceState attributionSourceState;
+    attributionSourceState.uid = uid;
+    attributionSourceState.packageName = getString(callingPackage);
+    if (attributionTag.has_value()) {
+        attributionSourceState.attributionTag = getString(attributionTag.value());
+    }
 
-    return mode;
+    return service->startOperationWithState(getClientId(), op, attributionSourceState,
+            startIfModeDefault,shouldCollectNotes(op), message, uid == AID_SYSTEM);
 }
 
 void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
@@ -133,9 +157,16 @@
 void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
         const std::optional<String16>& attributionTag) {
     sp<IAppOpsService> service = getService();
-    if (service != nullptr) {
-        service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
+    if (service == nullptr) {
+        return;
     }
+    AttributionSourceState attributionSourceState;
+    attributionSourceState.uid = uid;
+    attributionSourceState.packageName = getString(callingPackage);
+    if (attributionTag.has_value()) {
+        attributionSourceState.attributionTag = getString(attributionTag.value());
+    }
+    service->finishOperationWithState(getClientId(), op, attributionSourceState);
 }
 
 void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName,
diff --git a/libs/permission/IAppOpsService.cpp b/libs/permission/IAppOpsService.cpp
index 7f235a4..33dd24d 100644
--- a/libs/permission/IAppOpsService.cpp
+++ b/libs/permission/IAppOpsService.cpp
@@ -26,6 +26,8 @@
 
 namespace android {
 
+using android::content::AttributionSourceState;
+
 // ----------------------------------------------------------------------
 
 class BpAppOpsService : public BpInterface<IAppOpsService>
@@ -36,31 +38,30 @@
     {
     }
 
-    virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) {
+    virtual int32_t checkOperationWithState(int32_t code,
+                const AttributionSourceState &attributionSourceState) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeInt32(code);
-        data.writeInt32(uid);
-        data.writeString16(packageName);
-        remote()->transact(CHECK_OPERATION_TRANSACTION, data, &reply);
+        data.writeParcelable(attributionSourceState);
+        remote()->transact(CHECK_OPERATION_WITH_STATE_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
         return reply.readInt32();
     }
 
-    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
-                const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
-                const String16& message, bool shouldCollectMessage) {
+    virtual int32_t noteOperationWithState(int32_t code,
+                const AttributionSourceState& attributionSourceState,
+                bool shouldCollectAsyncNotedOp, const String16& message,
+                bool shouldCollectMessage) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeInt32(code);
-        data.writeInt32(uid);
-        data.writeString16(packageName);
-        data.writeString16(attributionTag);
+        data.writeParcelable(attributionSourceState);
         data.writeBool(shouldCollectAsyncNotedOp);
         data.writeString16(message);
         data.writeBool(shouldCollectMessage);
-        remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
+        remote()->transact(NOTE_OPERATION_WITH_STATE_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
         // TODO b/184855056: extract to class
@@ -69,22 +70,20 @@
         return reply.readInt32();
     }
 
-    virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-                const String16& packageName, const std::optional<String16>& attributionTag,
-                bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
+    virtual int32_t startOperationWithState(const sp<IBinder>& token, int32_t code,
+                const AttributionSourceState& attributionSourceState, bool startIfModeDefault,
+                bool shouldCollectAsyncNotedOp, const String16& message,
                 bool shouldCollectMessage) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
         data.writeInt32(code);
-        data.writeInt32(uid);
-        data.writeString16(packageName);
-        data.writeString16(attributionTag);
+        data.writeParcelable(attributionSourceState);
         data.writeBool(startIfModeDefault);
         data.writeBool(shouldCollectAsyncNotedOp);
         data.writeString16(message);
         data.writeBool(shouldCollectMessage);
-        remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
+        remote()->transact(START_OPERATION_WITH_STATE_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
         // TODO b/184855056: extract to class
@@ -93,16 +92,14 @@
         return reply.readInt32();
     }
 
-    virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, const std::optional<String16>& attributionTag) {
+    virtual void finishOperationWithState(const sp<IBinder>& token, int32_t code,
+                const AttributionSourceState& attributionSourceState) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
         data.writeInt32(code);
-        data.writeInt32(uid);
-        data.writeString16(packageName);
-        data.writeString16(attributionTag);
-        remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
+        data.writeParcelable(attributionSourceState);
+        remote()->transact(FINISH_OPERATION_WITH_STATE_TRANSACTION, data, &reply);
     }
 
     virtual void startWatchingMode(int32_t op, const String16& packageName,
@@ -189,59 +186,65 @@
 {
     //printf("AppOpsService received: "); data.print();
     switch(code) {
-        case CHECK_OPERATION_TRANSACTION: {
+        case CHECK_OPERATION_WITH_STATE_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             int32_t code = data.readInt32();
-            int32_t uid = data.readInt32();
-            String16 packageName = data.readString16();
-            int32_t res = checkOperation(code, uid, packageName);
+            AttributionSourceState attributionSourceState;
+            status_t status = data.readParcelable(&attributionSourceState);
+            if (status != NO_ERROR) {
+                return status;
+            }
+            int32_t res = checkOperationWithState(code, attributionSourceState);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
         } break;
-        case NOTE_OPERATION_TRANSACTION: {
+        case NOTE_OPERATION_WITH_STATE_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             int32_t code = data.readInt32();
-            int32_t uid = data.readInt32();
-            String16 packageName = data.readString16();
-            std::optional<String16> attributionTag;
-            data.readString16(&attributionTag);
+            AttributionSourceState attributionSourceState;
+            status_t status = data.readParcelable(&attributionSourceState);
+            if (status != NO_ERROR) {
+                return status;
+            }
             bool shouldCollectAsyncNotedOp = data.readBool();
             String16 message = data.readString16();
             bool shouldCollectMessage = data.readBool();
-            int32_t res = noteOperation(code, uid, packageName, attributionTag,
+            int32_t res = noteOperationWithState(code, attributionSourceState,
                     shouldCollectAsyncNotedOp, message, shouldCollectMessage);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
         } break;
-        case START_OPERATION_TRANSACTION: {
+        case START_OPERATION_WITH_STATE_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             sp<IBinder> token = data.readStrongBinder();
             int32_t code = data.readInt32();
-            int32_t uid = data.readInt32();
-            String16 packageName = data.readString16();
-            std::optional<String16> attributionTag;
-            data.readString16(&attributionTag);
+            AttributionSourceState attributionSourceState;
+            status_t status = data.readParcelable(&attributionSourceState);
+            if (status != NO_ERROR) {
+                return status;
+            }
             bool startIfModeDefault = data.readBool();
             bool shouldCollectAsyncNotedOp = data.readBool();
             String16 message = data.readString16();
             bool shouldCollectMessage = data.readBool();
-            int32_t res = startOperation(token, code, uid, packageName, attributionTag,
+            int32_t res = startOperationWithState(token, code, attributionSourceState,
                     startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
         } break;
-        case FINISH_OPERATION_TRANSACTION: {
+        case FINISH_OPERATION_WITH_STATE_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             sp<IBinder> token = data.readStrongBinder();
             int32_t code = data.readInt32();
-            int32_t uid = data.readInt32();
-            String16 packageName = data.readString16();
-            std::optional<String16> attributionTag;
-            data.readString16(&attributionTag);
-            finishOperation(token, code, uid, packageName, attributionTag);
+            AttributionSourceState attributionSourceState;
+            status_t status = data.readParcelable(&attributionSourceState);
+            if (status != NO_ERROR) {
+                return status;
+            }
+            finishOperationWithState(token, code, attributionSourceState);
             reply->writeNoException();
             return NO_ERROR;
         } break;
diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h
index 918fcdb..a5fdc54 100644
--- a/libs/permission/include/binder/IAppOpsService.h
+++ b/libs/permission/include/binder/IAppOpsService.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <android/content/AttributionSourceState.h>
 #include <binder/IAppOpsCallback.h>
 #include <binder/IInterface.h>
 
@@ -27,23 +28,24 @@
 
 namespace android {
 
+using android::content::AttributionSourceState;
+
 // ----------------------------------------------------------------------
 
 class IAppOpsService : public IInterface
 {
 public:
     DECLARE_META_INTERFACE(AppOpsService)
-
-    virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
-    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
-            const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
+    virtual int32_t checkOperationWithState(int32_t code,
+            const AttributionSourceState& attributionSourceState) = 0;
+    virtual int32_t noteOperationWithState(int32_t code,
+            const AttributionSourceState& attributionSourceState, bool shouldCollectAsyncNotedOp,
             const String16& message, bool shouldCollectMessage) = 0;
-    virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, const std::optional<String16>& attributionTag,
-            bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message,
-            bool shouldCollectMessage) = 0;
-    virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, const std::optional<String16>& attributionTag) = 0;
+    virtual int32_t startOperationWithState(const sp<IBinder>& token, int32_t code,
+            const AttributionSourceState& attributionSourceState, bool startIfModeDefault,
+            bool shouldCollectAsyncNotedOp, const String16& message, bool shouldCollectMessage) = 0;
+    virtual void finishOperationWithState(const sp<IBinder>& token, int32_t code,
+            const AttributionSourceState& attributionSourceState) = 0;
     virtual void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback) = 0;
     virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
@@ -56,10 +58,10 @@
             int32_t flags, const sp<IAppOpsCallback>& callback) = 0;
 
     enum {
-        CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
-        NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1,
-        START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2,
-        FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
+        CHECK_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+50,
+        NOTE_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+52,
+        START_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+53,
+        FINISH_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+54,
         START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
         STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
         PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 04e2fff..e487cbc 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -66,6 +66,8 @@
 static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl";
 static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
 static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform";
+static const char* ANGLE_SUFFIX_VALUE = "angle";
+static const char* VENDOR_ANGLE_BUILD = "ro.gfx.angle.supported";
 
 static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = {
         PERSIST_DRIVER_SUFFIX_PROPERTY,
@@ -80,6 +82,13 @@
         "/vendor/lib/egl";
 #endif
 
+static const char* const SYSTEM_LIB_DIR =
+#if defined(__LP64__)
+        "/system/lib64";
+#else
+        "/system/lib";
+#endif
+
 static void* do_dlopen(const char* path, int mode) {
     ATRACE_CALL();
     return dlopen(path, mode);
@@ -434,98 +443,110 @@
     }
 }
 
+static std::string findLibrary(const std::string libraryName, const std::string searchPath,
+                               const bool exact) {
+    if (exact) {
+        std::string absolutePath = searchPath + "/" + libraryName + ".so";
+        if (!access(absolutePath.c_str(), R_OK)) {
+            return absolutePath;
+        }
+        return std::string();
+    }
+
+    DIR* d = opendir(searchPath.c_str());
+    if (d != nullptr) {
+        struct dirent* e;
+        while ((e = readdir(d)) != nullptr) {
+            if (e->d_type == DT_DIR) {
+                continue;
+            }
+            if (!strcmp(e->d_name, "libGLES_android.so")) {
+                // always skip the software renderer
+                continue;
+            }
+            if (strstr(e->d_name, libraryName.c_str()) == e->d_name) {
+                if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {
+                    std::string result = searchPath + "/" + e->d_name;
+                    closedir(d);
+                    return result;
+                }
+            }
+        }
+        closedir(d);
+    }
+    // Driver not found. gah.
+    return std::string();
+}
+
 static void* load_system_driver(const char* kind, const char* suffix, const bool exact) {
     ATRACE_CALL();
-    class MatchFile {
-    public:
-        static std::string find(const char* libraryName, const bool exact) {
-            std::string absolutePath;
-            if (findLibPath(absolutePath, libraryName, exact)) {
-                return absolutePath;
-            }
-
-            // Driver not found. gah.
-            return std::string();
-        }
-    private:
-        static bool findLibPath(std::string& result, const std::string& pattern, bool exact) {
-            const std::string vendorLibEglDirString = std::string(VENDOR_LIB_EGL_DIR);
-            if (exact) {
-                std::string absolutePath = vendorLibEglDirString + "/" + pattern + ".so";
-                if (!access(absolutePath.c_str(), R_OK)) {
-                    result = absolutePath;
-                    return true;
-                }
-                return false;
-            }
-
-            DIR* d = opendir(VENDOR_LIB_EGL_DIR);
-            if (d != nullptr) {
-                struct dirent* e;
-                while ((e = readdir(d)) != nullptr) {
-                    if (e->d_type == DT_DIR) {
-                        continue;
-                    }
-                    if (!strcmp(e->d_name, "libGLES_android.so")) {
-                        // always skip the software renderer
-                        continue;
-                    }
-                    if (strstr(e->d_name, pattern.c_str()) == e->d_name) {
-                        if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) {
-                            result = vendorLibEglDirString + "/" + e->d_name;
-                            closedir(d);
-                            return true;
-                        }
-                    }
-                }
-                closedir(d);
-            }
-            return false;
-        }
-    };
 
     std::string libraryName = std::string("lib") + kind;
     if (suffix) {
         libraryName += std::string("_") + suffix;
     } else if (!exact) {
-        // Deprecated: we look for files that match
-        //      libGLES_*.so, or:
+        // Deprecated for devices launching in Android 14
+        // Look for files that match
+        //      libGLES_*.so, or,
         //      libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so
         libraryName += std::string("_");
     }
-    std::string absolutePath = MatchFile::find(libraryName.c_str(), exact);
+
+    void* dso = nullptr;
+
+    const bool AngleInVendor = property_get_bool(VENDOR_ANGLE_BUILD, false);
+    const bool isSuffixAngle = suffix != nullptr && strcmp(suffix, ANGLE_SUFFIX_VALUE) == 0;
+    // Only use sphal namespace when system ANGLE binaries are not the default drivers.
+    const bool useSphalNamespace =  !isSuffixAngle || AngleInVendor;
+
+    const std::string absolutePath =
+            findLibrary(libraryName, useSphalNamespace ? VENDOR_LIB_EGL_DIR : SYSTEM_LIB_PATH,
+                        exact);
     if (absolutePath.empty()) {
         // this happens often, we don't want to log an error
         return nullptr;
     }
-    const char* const driver_absolute_path = absolutePath.c_str();
+    const char* const driverAbsolutePath = absolutePath.c_str();
 
-    // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to
-    // the original routine when the namespace does not exist.
-    // See /system/core/rootdir/etc/ld.config.txt for the configuration of the
-    // sphal namespace.
-    void* dso = do_android_load_sphal_library(driver_absolute_path,
-                                              RTLD_NOW | RTLD_LOCAL);
+    // Currently the default driver is unlikely to be ANGLE on most devices,
+    // hence put this first.
+    if (useSphalNamespace) {
+        // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to
+        // the original routine when the namespace does not exist.
+        // See /system/linkerconfig/contents/namespace for the configuration of the
+        // sphal namespace.
+        dso = do_android_load_sphal_library(driverAbsolutePath, RTLD_NOW | RTLD_LOCAL);
+    } else {
+        // Try to load drivers from the default namespace.
+        // See /system/linkerconfig/contents/namespace for the configuration of the
+        // default namespace.
+        dso = do_dlopen(driverAbsolutePath, RTLD_NOW | RTLD_LOCAL);
+    }
+
     if (dso == nullptr) {
         const char* err = dlerror();
-        ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown");
+        ALOGE("load_driver(%s): %s", driverAbsolutePath, err ? err : "unknown");
         return nullptr;
     }
 
-    ALOGD("loaded %s", driver_absolute_path);
+    ALOGV("loaded %s", driverAbsolutePath);
 
     return dso;
 }
 
 static void* load_angle(const char* kind, android_namespace_t* ns) {
-    const android_dlextinfo dlextinfo = {
-            .flags = ANDROID_DLEXT_USE_NAMESPACE,
-            .library_namespace = ns,
-    };
-
     std::string name = std::string("lib") + kind + "_angle.so";
+    void* so = nullptr;
 
-    void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+    if (android::GraphicsEnv::getInstance().shouldUseSystemAngle()) {
+        so = do_dlopen(name.c_str(), RTLD_NOW | RTLD_LOCAL);
+    } else {
+        const android_dlextinfo dlextinfo = {
+                .flags = ANDROID_DLEXT_USE_NAMESPACE,
+                .library_namespace = ns,
+        };
+        so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+    }
 
     if (so) {
         return so;
@@ -563,10 +584,14 @@
     ATRACE_CALL();
 
     android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
-    if (!ns) {
+    // ANGLE namespace is used for loading ANGLE from apk, and hence if namespace is not
+    // constructed, it means ANGLE apk is not set to be the OpenGL ES driver.
+    // Hence skip if ANGLE apk and system ANGLE are not set to be the OpenGL ES driver.
+    if (!ns && !android::GraphicsEnv::getInstance().shouldUseSystemAngle()) {
         return nullptr;
     }
 
+    // use ANGLE APK driver
     android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE);
     driver_t* hnd = nullptr;
 
@@ -588,10 +613,13 @@
 }
 
 void Loader::attempt_to_init_angle_backend(void* dso, egl_connection_t* cnx) {
-    void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
-    if (pANGLEGetDisplayPlatform) {
+    cnx->angleGetDisplayPlatformFunc = dlsym(dso, "ANGLEGetDisplayPlatform");
+    cnx->angleResetDisplayPlatformFunc = dlsym(dso, "ANGLEResetDisplayPlatform");
+
+    if (cnx->angleGetDisplayPlatformFunc) {
         ALOGV("ANGLE GLES library loaded");
         cnx->angleLoaded = true;
+        android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE);
     } else {
         ALOGV("Native GLES library loaded");
         cnx->angleLoaded = false;
@@ -635,7 +663,13 @@
 Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
                                                         const bool exact) {
     ATRACE_CALL();
-    android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL);
+    if (suffix && strcmp(suffix, "angle") == 0) {
+        // use system ANGLE driver
+        android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE);
+    } else {
+        android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL);
+    }
+
     driver_t* hnd = nullptr;
     void* dso = load_system_driver("GLES", suffix, exact);
     if (dso) {
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index ee605c2..f0054a7 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -35,12 +35,6 @@
 
 namespace angle {
 
-constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so";
-constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW;
-
-static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr;
-static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr;
-
 static time_t startTime = time(nullptr);
 
 static const unsigned char* getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/,
@@ -111,50 +105,19 @@
 }
 
 // Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace
-bool initializeAnglePlatform(EGLDisplay dpy) {
-    // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform
-    android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
-    void* so = nullptr;
-    if (ns) {
-        const android_dlextinfo dlextinfo = {
-                .flags = ANDROID_DLEXT_USE_NAMESPACE,
-                .library_namespace = ns,
-        };
-        so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo);
-        if (so) {
-            ALOGD("dlopen_ext from APK (%s) success at %p", kAngleEs2Lib, so);
-        } else {
-            ALOGE("dlopen_ext(\"%s\") failed: %s", kAngleEs2Lib, dlerror());
-            return false;
-        }
-    } else {
-        // If we are here, ANGLE is loaded as built-in gl driver in the sphal.
-        so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags);
-        if (so) {
-            ALOGD("dlopen (%s) success at %p", kAngleEs2Lib, so);
-        } else {
-            ALOGE("%s failed to dlopen %s: %s!", __FUNCTION__, kAngleEs2Lib, dlerror());
-            return false;
-        }
-    }
-
-    angleGetDisplayPlatform =
-            reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform"));
-
-    if (!angleGetDisplayPlatform) {
-        ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!");
-        dlclose(so);
+bool initializeAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx) {
+    if (cnx->angleGetDisplayPlatformFunc == nullptr) {
+        ALOGE("ANGLEGetDisplayPlatform is not initialized!");
         return false;
     }
 
-    angleResetDisplayPlatform =
-            reinterpret_cast<ResetDisplayPlatformFunc>(dlsym(so, "ANGLEResetDisplayPlatform"));
+    GetDisplayPlatformFunc angleGetDisplayPlatform =
+            reinterpret_cast<GetDisplayPlatformFunc>(cnx->angleGetDisplayPlatformFunc);
 
     PlatformMethods* platformMethods = nullptr;
     if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
                                     &platformMethods))) {
         ALOGE("ANGLEGetDisplayPlatform call failed!");
-        dlclose(so);
         return false;
     }
     if (platformMethods) {
@@ -166,8 +129,10 @@
     return true;
 }
 
-void resetAnglePlatform(EGLDisplay dpy) {
-    if (angleResetDisplayPlatform) {
+void resetAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx) {
+    if (cnx->angleResetDisplayPlatformFunc) {
+        ResetDisplayPlatformFunc angleResetDisplayPlatform =
+                reinterpret_cast<ResetDisplayPlatformFunc>(cnx->angleResetDisplayPlatformFunc);
         angleResetDisplayPlatform(dpy);
     }
 }
diff --git a/opengl/libs/EGL/egl_angle_platform.h b/opengl/libs/EGL/egl_angle_platform.h
index 6c24aa5..63806c2 100644
--- a/opengl/libs/EGL/egl_angle_platform.h
+++ b/opengl/libs/EGL/egl_angle_platform.h
@@ -25,8 +25,8 @@
 
 namespace angle {
 
-bool initializeAnglePlatform(EGLDisplay dpy);
-void resetAnglePlatform(EGLDisplay dpy);
+bool initializeAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx);
+void resetAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx);
 
 }; // namespace angle
 
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3317347..55a2682 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -168,7 +168,7 @@
         if (dpy == EGL_NO_DISPLAY) {
             ALOGE("eglGetPlatformDisplay failed!");
         } else {
-            if (!angle::initializeAnglePlatform(dpy)) {
+            if (!angle::initializeAnglePlatform(dpy, cnx)) {
                 ALOGE("initializeAnglePlatform failed!");
             }
         }
@@ -433,7 +433,7 @@
         if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
             // If we're using ANGLE reset any custom DisplayPlatform
             if (cnx->angleLoaded) {
-                angle::resetAnglePlatform(disp.dpy);
+                angle::resetAnglePlatform(disp.dpy, cnx);
             }
             if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
                 ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 3bd37cb..90a3c19 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -42,7 +42,9 @@
             libGles1(nullptr),
             libGles2(nullptr),
             systemDriverUnloaded(false),
-            angleLoaded(false) {
+            angleLoaded(false),
+            angleGetDisplayPlatformFunc(nullptr),
+            angleResetDisplayPlatformFunc(nullptr) {
         const char* const* entries = platform_names;
         EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
         while (*entries) {
@@ -75,6 +77,9 @@
 
     bool systemDriverUnloaded;
     bool angleLoaded; // Was ANGLE successfully loaded
+
+    void* angleGetDisplayPlatformFunc;
+    void* angleResetDisplayPlatformFunc;
 };
 
 extern gl_hooks_t gHooks[2];
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index f06a045..11b636d 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -163,11 +163,13 @@
         addLoadingTime(driver, driverLoadingTime, &appInfo);
         appInfo.appPackageName = appPackageName;
         appInfo.driverVersionCode = driverVersionCode;
-        appInfo.angleInUse = driverPackageName == "angle";
+        appInfo.angleInUse =
+                driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle";
         appInfo.lastAccessTime = std::chrono::system_clock::now();
         mAppStats.insert({appStatsKey, appInfo});
     } else {
-        mAppStats[appStatsKey].angleInUse = driverPackageName == "angle";
+        mAppStats[appStatsKey].angleInUse =
+                driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle";
         addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
         mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now();
     }
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index e411abb..1092bdb 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -16,17 +16,37 @@
 
 #define LOG_TAG "PointerChoreographer"
 
+#include <android-base/logging.h>
+#include <input/PrintTools.h>
+
 #include "PointerChoreographer.h"
 
+#define INDENT "  "
+
 namespace android {
 
+namespace {
+bool isFromMouse(const NotifyMotionArgs& args) {
+    return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
+            args.pointerProperties[0].toolType == ToolType::MOUSE;
+}
+
+} // namespace
+
 // --- PointerChoreographer ---
 
 PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
                                            PointerChoreographerPolicyInterface& policy)
-      : mNextListener(listener) {}
+      : mNextListener(listener),
+        mPolicy(policy),
+        mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
+        mNotifiedPointerDisplayId(ADISPLAY_ID_NONE) {}
 
 void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+    std::scoped_lock _l(mLock);
+
+    mInputDeviceInfos = args.inputDeviceInfos;
+    updatePointerControllersLocked();
     mNextListener.notify(args);
 }
 
@@ -39,7 +59,67 @@
 }
 
 void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
-    mNextListener.notify(args);
+    NotifyMotionArgs newArgs = processMotion(args);
+
+    mNextListener.notify(newArgs);
+}
+
+NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
+    std::scoped_lock _l(mLock);
+
+    if (isFromMouse(args)) {
+        return processMouseEventLocked(args);
+    } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
+        return processTouchscreenEventLocked(args);
+    }
+    return args;
+}
+
+NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
+    if (args.getPointerCount() != 1) {
+        LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
+                   << args.dump();
+    }
+
+    const int32_t displayId = getTargetMouseDisplayLocked(args.displayId);
+
+    // Get the mouse pointer controller for the display, or create one if it doesn't exist.
+    auto [it, emplaced] =
+            mMousePointersByDisplay.try_emplace(displayId,
+                                                getMouseControllerConstructor(displayId));
+    if (emplaced) {
+        notifyPointerDisplayIdChangedLocked();
+    }
+
+    PointerControllerInterface& pc = *it->second;
+
+    const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+    const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+    pc.move(deltaX, deltaY);
+    pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+    const auto [x, y] = pc.getPosition();
+    NotifyMotionArgs newArgs(args);
+    newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+    newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+    newArgs.xCursorPosition = x;
+    newArgs.yCursorPosition = y;
+    newArgs.displayId = displayId;
+    return newArgs;
+}
+
+/**
+ * When screen is touched, fade the mouse pointer on that display. We only call fade for
+ * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
+ * mouse device keeps moving and unfades the cursor.
+ * For touch events, we do not need to populate the cursor position.
+ */
+NotifyMotionArgs PointerChoreographer::processTouchscreenEventLocked(const NotifyMotionArgs& args) {
+    if (const auto it = mMousePointersByDisplay.find(args.displayId);
+        it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
+        it->second->fade(PointerControllerInterface::Transition::GRADUAL);
+    }
+    return args;
 }
 
 void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
@@ -60,11 +140,141 @@
 
 void PointerChoreographer::notifyPointerCaptureChanged(
         const NotifyPointerCaptureChangedArgs& args) {
+    if (args.request.enable) {
+        std::scoped_lock _l(mLock);
+        for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
+            mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+        }
+    }
     mNextListener.notify(args);
 }
 
 void PointerChoreographer::dump(std::string& dump) {
+    std::scoped_lock _l(mLock);
+
     dump += "PointerChoreographer:\n";
+
+    dump += INDENT "MousePointerControllers:\n";
+    for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
+        std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
+        dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
+    }
+    dump += "\n";
+}
+
+const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const {
+    for (auto& viewport : mViewports) {
+        if (viewport.displayId == displayId) {
+            return &viewport;
+        }
+    }
+    return nullptr;
+}
+
+int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const {
+    return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
+}
+
+void PointerChoreographer::updatePointerControllersLocked() {
+    std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
+
+    // Mark the displayIds or deviceIds of PointerControllers currently needed.
+    for (const auto& info : mInputDeviceInfos) {
+        const uint32_t sources = info.getSources();
+        if (isFromSource(sources, AINPUT_SOURCE_MOUSE) ||
+            isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) {
+            const int32_t resolvedDisplayId =
+                    getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
+            mouseDisplaysToKeep.insert(resolvedDisplayId);
+        }
+    }
+
+    // Remove PointerControllers no longer needed.
+    // This has the side-effect of fading pointers or clearing spots before removal.
+    std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
+        auto& [displayId, controller] = pair;
+        if (mouseDisplaysToKeep.find(displayId) == mouseDisplaysToKeep.end()) {
+            controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
+            return true;
+        }
+        return false;
+    });
+
+    // Notify the policy if there's a change on the pointer display ID.
+    notifyPointerDisplayIdChangedLocked();
+}
+
+void PointerChoreographer::notifyPointerDisplayIdChangedLocked() {
+    int32_t displayIdToNotify = ADISPLAY_ID_NONE;
+    FloatPoint cursorPosition = {0, 0};
+    if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
+        it != mMousePointersByDisplay.end()) {
+        const auto& pointerController = it->second;
+        // Use the displayId from the pointerController, because it accurately reflects whether
+        // the viewport has been added for that display. Otherwise, we would have to check if
+        // the viewport exists separately.
+        displayIdToNotify = pointerController->getDisplayId();
+        cursorPosition = pointerController->getPosition();
+    }
+
+    if (mNotifiedPointerDisplayId == displayIdToNotify) {
+        return;
+    }
+    mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition);
+    mNotifiedPointerDisplayId = displayIdToNotify;
+}
+
+void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
+    std::scoped_lock _l(mLock);
+
+    mDefaultMouseDisplayId = displayId;
+    updatePointerControllersLocked();
+}
+
+void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
+    std::scoped_lock _l(mLock);
+    for (const auto& viewport : viewports) {
+        if (const auto it = mMousePointersByDisplay.find(viewport.displayId);
+            it != mMousePointersByDisplay.end()) {
+            it->second->setDisplayViewport(viewport);
+        }
+    }
+    mViewports = viewports;
+    notifyPointerDisplayIdChangedLocked();
+}
+
+std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
+        int32_t associatedDisplayId) {
+    std::scoped_lock _l(mLock);
+    const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
+    if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
+        return *viewport;
+    }
+    return std::nullopt;
+}
+
+FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) {
+    std::scoped_lock _l(mLock);
+    const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
+    if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
+        it != mMousePointersByDisplay.end()) {
+        return it->second->getPosition();
+    }
+    return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
+}
+
+PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
+        int32_t displayId) {
+    std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
+            [this, displayId]() REQUIRES(mLock) {
+                auto pc = mPolicy.createPointerController(
+                        PointerControllerInterface::ControllerType::MOUSE);
+                if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
+                    pc->setDisplayViewport(*viewport);
+                }
+                return pc;
+            };
+    return ConstructorDelegate(std::move(ctor));
 }
 
 } // namespace android
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 5e5f782..c1b900f 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -20,9 +20,26 @@
 #include "NotifyArgs.h"
 #include "PointerChoreographerPolicyInterface.h"
 
+#include <android-base/thread_annotations.h>
+#include <type_traits>
+
 namespace android {
 
 /**
+ * A helper class that wraps a factory method that acts as a constructor for the type returned
+ * by the factory method.
+ */
+template <typename Factory>
+struct ConstructorDelegate {
+    constexpr ConstructorDelegate(Factory&& factory) : mFactory(std::move(factory)) {}
+
+    using ConstructedType = std::invoke_result_t<const Factory&>;
+    constexpr operator ConstructedType() const { return mFactory(); }
+
+    Factory mFactory;
+};
+
+/**
  * PointerChoreographer manages the icons shown by the system for input interactions.
  * This includes showing the mouse cursor, stylus hover icons, and touch spots.
  * It is responsible for accumulating the location of the mouse cursor, and populating
@@ -31,6 +48,15 @@
 class PointerChoreographerInterface : public InputListenerInterface {
 public:
     /**
+     * Set the display that pointers, like the mouse cursor and drawing tablets,
+     * should be drawn on.
+     */
+    virtual void setDefaultMouseDisplayId(int32_t displayId) = 0;
+    virtual void setDisplayViewports(const std::vector<DisplayViewport>& viewports) = 0;
+    virtual std::optional<DisplayViewport> getViewportForPointerDevice(
+            int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
+    virtual FloatPoint getMouseCursorPosition(int32_t displayId) = 0;
+    /**
      * This method may be called on any thread (usually by the input manager on a binder thread).
      */
     virtual void dump(std::string& dump) = 0;
@@ -42,6 +68,12 @@
                                   PointerChoreographerPolicyInterface&);
     ~PointerChoreographer() override = default;
 
+    void setDefaultMouseDisplayId(int32_t displayId) override;
+    void setDisplayViewports(const std::vector<DisplayViewport>& viewports) override;
+    std::optional<DisplayViewport> getViewportForPointerDevice(
+            int32_t associatedDisplayId) override;
+    FloatPoint getMouseCursorPosition(int32_t displayId) override;
+
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
     void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
@@ -55,7 +87,31 @@
     void dump(std::string& dump) override;
 
 private:
+    void updatePointerControllersLocked() REQUIRES(mLock);
+    void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock);
+    const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
+    int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);
+
+    NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
+    NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+    NotifyMotionArgs processTouchscreenEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+
+    using ControllerConstructor =
+            ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
+    ControllerConstructor getMouseControllerConstructor(int32_t displayId) REQUIRES(mLock);
+
+    std::mutex mLock;
+
     InputListenerInterface& mNextListener;
+    PointerChoreographerPolicyInterface& mPolicy;
+
+    std::map<int32_t, std::shared_ptr<PointerControllerInterface>> mMousePointersByDisplay
+            GUARDED_BY(mLock);
+
+    int32_t mDefaultMouseDisplayId GUARDED_BY(mLock);
+    int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock);
+    std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
+    std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
 };
 
 } // namespace android
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
index ee0ab33..d9d0450 100644
--- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -15,10 +15,15 @@
  */
 
 #include "PreferStylusOverTouchBlocker.h"
+#include <com_android_input_flags.h>
 #include <input/PrintTools.h>
 
+namespace input_flags = com::android::input::flags;
+
 namespace android {
 
+const bool BLOCK_TOUCH_WHEN_STYLUS_HOVER = !input_flags::disable_reject_touch_on_stylus_hover();
+
 static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) {
     bool hasStylus = false;
     bool hasTouch = false;
@@ -96,8 +101,11 @@
 std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion(
         const NotifyMotionArgs& args) {
     const auto [hasTouch, hasStylus] = checkToolType(args);
-    const bool isUpOrCancel =
-            args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL;
+    const bool isDisengageOrCancel = BLOCK_TOUCH_WHEN_STYLUS_HOVER
+            ? (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT ||
+               args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL)
+            : (args.action == AMOTION_EVENT_ACTION_UP ||
+               args.action == AMOTION_EVENT_ACTION_CANCEL);
 
     if (hasTouch && hasStylus) {
         mDevicesWithMixedToolType.insert(args.deviceId);
@@ -109,7 +117,7 @@
         if (mCanceledDevices.find(args.deviceId) != mCanceledDevices.end()) {
             // If we started to cancel events from this device, continue to do so to keep
             // the stream consistent. It should happen at most once per "mixed" device.
-            if (isUpOrCancel) {
+            if (isDisengageOrCancel) {
                 mCanceledDevices.erase(args.deviceId);
                 mLastTouchEvents.erase(args.deviceId);
             }
@@ -119,10 +127,13 @@
     }
 
     const bool isStylusEvent = hasStylus;
-    const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
+    const bool isEngage = BLOCK_TOUCH_WHEN_STYLUS_HOVER
+            ? (args.action == AMOTION_EVENT_ACTION_DOWN ||
+               args.action == AMOTION_EVENT_ACTION_HOVER_ENTER)
+            : (args.action == AMOTION_EVENT_ACTION_DOWN);
 
     if (isStylusEvent) {
-        if (isDown) {
+        if (isEngage) {
             // Reject all touch while stylus is down
             mActiveStyli.insert(args.deviceId);
 
@@ -143,7 +154,7 @@
             result.push_back(args);
             return result;
         }
-        if (isUpOrCancel) {
+        if (isDisengageOrCancel) {
             mActiveStyli.erase(args.deviceId);
         }
         // Never drop stylus events
@@ -158,7 +169,7 @@
         }
 
         const bool shouldDrop = mCanceledDevices.find(args.deviceId) != mCanceledDevices.end();
-        if (isUpOrCancel) {
+        if (isDisengageOrCancel) {
             mCanceledDevices.erase(args.deviceId);
             mLastTouchEvents.erase(args.deviceId);
         }
@@ -169,7 +180,7 @@
             return {};
         }
 
-        if (!isUpOrCancel) {
+        if (!isDisengageOrCancel) {
             mLastTouchEvents[args.deviceId] = args;
         }
         return {args};
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 6f092a6..513cbfa 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -148,7 +148,7 @@
       ]
     }
   ],
-  "hwasan-postsubmit": [
+  "postsubmit": [
     {
       "name": "CtsWindowManagerDeviceWindow",
       "options": [
@@ -281,6 +281,9 @@
           "include-filter": "android.security.cts.Poc19_03#testPocBug_115739809"
         }
       ]
+    },
+    {
+      "name": "CtsInputHostTestCases"
     }
   ]
 }
diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h
index c7d98ab..c889b9b 100644
--- a/services/inputflinger/dispatcher/DebugConfig.h
+++ b/services/inputflinger/dispatcher/DebugConfig.h
@@ -69,6 +69,16 @@
         android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Injection");
 
 /**
+ * Generally, we always log whenever events are dropped. However, to reduce logspam, some messages
+ * are suppressed.
+ * Log additional debug messages about dropped input events with this flag.
+ * Enable this via "adb shell setprop log.tag.InputDispatcherDroppedEventsVerbose DEBUG".
+ * Requires system_server restart via `adb shell stop && adb shell start`.
+ */
+const bool DEBUG_DROPPED_EVENTS_VERBOSE =
+        android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "DroppedEventsVerbose");
+
+/**
  * Log debug messages about input focus tracking.
  * Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart)
  */
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 30e6802..2d1a22b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -67,24 +67,11 @@
         injectionState(nullptr),
         dispatchInProgress(false) {}
 
-EventEntry::~EventEntry() {
-    releaseInjectionState();
-}
-
-void EventEntry::releaseInjectionState() {
-    if (injectionState) {
-        injectionState->release();
-        injectionState = nullptr;
-    }
-}
-
 // --- ConfigurationChangedEntry ---
 
 ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime)
       : EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
 
-ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
-
 std::string ConfigurationChangedEntry::getDescription() const {
     return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
 }
@@ -94,8 +81,6 @@
 DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId)
       : EventEntry(id, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
 
-DeviceResetEntry::~DeviceResetEntry() {}
-
 std::string DeviceResetEntry::getDescription() const {
     return StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
 }
@@ -110,8 +95,6 @@
         hasFocus(hasFocus),
         reason(reason) {}
 
-FocusEntry::~FocusEntry() {}
-
 std::string FocusEntry::getDescription() const {
     return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
 }
@@ -125,8 +108,6 @@
       : EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
         pointerCaptureRequest(request) {}
 
-PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
-
 std::string PointerCaptureChangedEntry::getDescription() const {
     return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
                         pointerCaptureRequest.enable ? "true" : "false");
@@ -143,18 +124,16 @@
         x(x),
         y(y) {}
 
-DragEntry::~DragEntry() {}
-
 std::string DragEntry::getDescription() const {
     return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
 }
 
 // --- KeyEntry ---
 
-KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                   int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
-                   int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
-                   nsecs_t downTime)
+KeyEntry::KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+                   int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+                   int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+                   int32_t metaState, int32_t repeatCount, nsecs_t downTime)
       : EventEntry(id, Type::KEY, eventTime, policyFlags),
         deviceId(deviceId),
         source(source),
@@ -168,9 +147,9 @@
         downTime(downTime),
         syntheticRepeat(false),
         interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN),
-        interceptKeyWakeupTime(0) {}
-
-KeyEntry::~KeyEntry() {}
+        interceptKeyWakeupTime(0) {
+    EventEntry::injectionState = std::move(injectionState);
+}
 
 std::string KeyEntry::getDescription() const {
     if (!IS_DEBUGGABLE_BUILD) {
@@ -185,15 +164,6 @@
                         keyCode, scanCode, metaState, repeatCount, policyFlags);
 }
 
-void KeyEntry::recycle() {
-    releaseInjectionState();
-
-    dispatchInProgress = false;
-    syntheticRepeat = false;
-    interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
-    interceptKeyWakeupTime = 0;
-}
-
 // --- TouchModeEntry ---
 
 TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId)
@@ -201,21 +171,19 @@
         inTouchMode(inTouchMode),
         displayId(displayId) {}
 
-TouchModeEntry::~TouchModeEntry() {}
-
 std::string TouchModeEntry::getDescription() const {
     return StringPrintf("TouchModeEvent(inTouchMode=%s)", inTouchMode ? "true" : "false");
 }
 
 // --- MotionEntry ---
 
-MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                         int32_t displayId, uint32_t policyFlags, int32_t action,
-                         int32_t actionButton, int32_t flags, int32_t metaState,
-                         int32_t buttonState, MotionClassification classification,
-                         int32_t edgeFlags, float xPrecision, float yPrecision,
-                         float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-                         const std::vector<PointerProperties>& pointerProperties,
+MotionEntry::MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState,
+                         nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+                         uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
+                         int32_t metaState, int32_t buttonState,
+                         MotionClassification classification, int32_t edgeFlags, float xPrecision,
+                         float yPrecision, float xCursorPosition, float yCursorPosition,
+                         nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties,
                          const std::vector<PointerCoords>& pointerCoords)
       : EventEntry(id, Type::MOTION, eventTime, policyFlags),
         deviceId(deviceId),
@@ -234,9 +202,9 @@
         yCursorPosition(yCursorPosition),
         downTime(downTime),
         pointerProperties(pointerProperties),
-        pointerCoords(pointerCoords) {}
-
-MotionEntry::~MotionEntry() {}
+        pointerCoords(pointerCoords) {
+    EventEntry::injectionState = std::move(injectionState);
+}
 
 std::string MotionEntry::getDescription() const {
     if (!IS_DEBUGGABLE_BUILD) {
@@ -285,8 +253,6 @@
         hwTimestamp(hwTimestamp),
         values(std::move(values)) {}
 
-SensorEntry::~SensorEntry() {}
-
 std::string SensorEntry::getDescription() const {
     std::string msg;
     msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, "
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index b341784..d44a211 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -48,7 +48,7 @@
     Type type;
     nsecs_t eventTime;
     uint32_t policyFlags;
-    InjectionState* injectionState;
+    std::shared_ptr<InjectionState> injectionState;
 
     bool dispatchInProgress; // initially false, set to true while dispatching
 
@@ -72,17 +72,12 @@
     virtual std::string getDescription() const = 0;
 
     EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
-    virtual ~EventEntry();
-
-protected:
-    void releaseInjectionState();
+    virtual ~EventEntry() = default;
 };
 
 struct ConfigurationChangedEntry : EventEntry {
     explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
     std::string getDescription() const override;
-
-    ~ConfigurationChangedEntry() override;
 };
 
 struct DeviceResetEntry : EventEntry {
@@ -90,8 +85,6 @@
 
     DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
     std::string getDescription() const override;
-
-    ~DeviceResetEntry() override;
 };
 
 struct FocusEntry : EventEntry {
@@ -102,8 +95,6 @@
     FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
                const std::string& reason);
     std::string getDescription() const override;
-
-    ~FocusEntry() override;
 };
 
 struct PointerCaptureChangedEntry : EventEntry {
@@ -111,8 +102,6 @@
 
     PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
     std::string getDescription() const override;
-
-    ~PointerCaptureChangedEntry() override;
 };
 
 struct DragEntry : EventEntry {
@@ -123,8 +112,6 @@
     DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
               float y);
     std::string getDescription() const override;
-
-    ~DragEntry() override;
 };
 
 struct KeyEntry : EventEntry {
@@ -150,13 +137,11 @@
     InterceptKeyResult interceptKeyResult; // set based on the interception result
     nsecs_t interceptKeyWakeupTime;        // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
 
-    KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
-             int32_t metaState, int32_t repeatCount, nsecs_t downTime);
+    KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+             int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+             int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+             int32_t repeatCount, nsecs_t downTime);
     std::string getDescription() const override;
-    void recycle();
-
-    ~KeyEntry() override;
 };
 
 struct MotionEntry : EventEntry {
@@ -180,16 +165,14 @@
 
     size_t getPointerCount() const { return pointerProperties.size(); }
 
-    MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-                uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
-                int32_t metaState, int32_t buttonState, MotionClassification classification,
-                int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition,
-                float yCursorPosition, nsecs_t downTime,
-                const std::vector<PointerProperties>& pointerProperties,
+    MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
+                int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+                int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
+                int32_t buttonState, MotionClassification classification, int32_t edgeFlags,
+                float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition,
+                nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties,
                 const std::vector<PointerCoords>& pointerCoords);
     std::string getDescription() const override;
-
-    ~MotionEntry() override;
 };
 
 std::ostream& operator<<(std::ostream& out, const MotionEntry& motionEntry);
@@ -209,8 +192,6 @@
                 InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
                 std::vector<float> values);
     std::string getDescription() const override;
-
-    ~SensorEntry() override;
 };
 
 struct TouchModeEntry : EventEntry {
@@ -219,8 +200,6 @@
 
     TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int32_t displayId);
     std::string getDescription() const override;
-
-    ~TouchModeEntry() override;
 };
 
 // Tracks the progress of dispatching a particular event to a particular connection.
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index 053594b..f693dcf 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -20,22 +20,10 @@
 
 namespace android::inputdispatcher {
 
-InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid)
-      : refCount(1),
-        targetUid(targetUid),
+InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid, bool isAsync)
+      : targetUid(targetUid),
+        injectionIsAsync(isAsync),
         injectionResult(android::os::InputEventInjectionResult::PENDING),
-        injectionIsAsync(false),
         pendingForegroundDispatches(0) {}
 
-InjectionState::~InjectionState() {}
-
-void InjectionState::release() {
-    refCount -= 1;
-    if (refCount == 0) {
-        delete this;
-    } else {
-        ALOG_ASSERT(refCount > 0);
-    }
-}
-
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 3a3f5ae..8225dec 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -24,18 +24,12 @@
 namespace inputdispatcher {
 
 struct InjectionState {
-    mutable int32_t refCount;
-
-    std::optional<gui::Uid> targetUid;
+    const std::optional<gui::Uid> targetUid;
+    const bool injectionIsAsync; // set to true if injection is not waiting for the result
     android::os::InputEventInjectionResult injectionResult; // initially PENDING
-    bool injectionIsAsync;               // set to true if injection is not waiting for the result
     int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
 
-    explicit InjectionState(const std::optional<gui::Uid>& targetUid);
-    void release();
-
-private:
-    ~InjectionState();
+    explicit InjectionState(const std::optional<gui::Uid>& targetUid, bool isAsync);
 };
 
 } // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 53e0855..c8528e1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -72,6 +72,8 @@
 using android::os::InputEventInjectionSync;
 namespace input_flags = com::android::input::flags;
 
+static const bool REMOVE_APP_SWITCH_DROPS = input_flags::remove_app_switch_drops();
+
 namespace android::inputdispatcher {
 
 namespace {
@@ -390,21 +392,17 @@
     }
 
     std::unique_ptr<MotionEntry> combinedMotionEntry =
-            std::make_unique<MotionEntry>(motionEntry.id, motionEntry.eventTime,
-                                          motionEntry.deviceId, motionEntry.source,
-                                          motionEntry.displayId, motionEntry.policyFlags,
-                                          motionEntry.action, motionEntry.actionButton,
-                                          motionEntry.flags, motionEntry.metaState,
-                                          motionEntry.buttonState, motionEntry.classification,
-                                          motionEntry.edgeFlags, motionEntry.xPrecision,
-                                          motionEntry.yPrecision, motionEntry.xCursorPosition,
-                                          motionEntry.yCursorPosition, motionEntry.downTime,
-                                          motionEntry.pointerProperties, pointerCoords);
-
-    if (motionEntry.injectionState) {
-        combinedMotionEntry->injectionState = motionEntry.injectionState;
-        combinedMotionEntry->injectionState->refCount += 1;
-    }
+            std::make_unique<MotionEntry>(motionEntry.id, motionEntry.injectionState,
+                                          motionEntry.eventTime, motionEntry.deviceId,
+                                          motionEntry.source, motionEntry.displayId,
+                                          motionEntry.policyFlags, motionEntry.action,
+                                          motionEntry.actionButton, motionEntry.flags,
+                                          motionEntry.metaState, motionEntry.buttonState,
+                                          motionEntry.classification, motionEntry.edgeFlags,
+                                          motionEntry.xPrecision, motionEntry.yPrecision,
+                                          motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+                                          motionEntry.downTime, motionEntry.pointerProperties,
+                                          pointerCoords);
 
     std::unique_ptr<DispatchEntry> dispatchEntry =
             std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
@@ -756,10 +754,6 @@
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
-      : InputDispatcher(policy, STALE_EVENT_TIMEOUT) {}
-
-InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
-                                 std::chrono::nanoseconds staleEventTimeout)
       : mPolicy(policy),
         mPendingEvent(nullptr),
         mLastDropReason(DropReason::NOT_DROPPED),
@@ -774,7 +768,6 @@
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
-        mStaleEventTimeout(staleEventTimeout),
         mLatencyAggregator(),
         mLatencyTracker(&mLatencyAggregator) {
     mLooper = sp<Looper>::make(false);
@@ -956,20 +949,25 @@
     // Optimize latency of app switches.
     // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
     // been pressed.  When it expires, we preempt dispatch and drop all other pending events.
-    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
-    if (mAppSwitchDueTime < *nextWakeupTime) {
-        *nextWakeupTime = mAppSwitchDueTime;
+    bool isAppSwitchDue;
+    if (!REMOVE_APP_SWITCH_DROPS) {
+        isAppSwitchDue = mAppSwitchDueTime <= currentTime;
+        if (mAppSwitchDueTime < *nextWakeupTime) {
+            *nextWakeupTime = mAppSwitchDueTime;
+        }
     }
 
     // Ready to start a new event.
     // If we don't already have a pending event, go grab one.
     if (!mPendingEvent) {
         if (mInboundQueue.empty()) {
-            if (isAppSwitchDue) {
-                // The inbound queue is empty so the app switch key we were waiting
-                // for will never arrive.  Stop waiting for it.
-                resetPendingAppSwitchLocked(false);
-                isAppSwitchDue = false;
+            if (!REMOVE_APP_SWITCH_DROPS) {
+                if (isAppSwitchDue) {
+                    // The inbound queue is empty so the app switch key we were waiting
+                    // for will never arrive.  Stop waiting for it.
+                    resetPendingAppSwitchLocked(false);
+                    isAppSwitchDue = false;
+                }
             }
 
             // Synthesize a key repeat if appropriate.
@@ -1067,12 +1065,14 @@
 
         case EventEntry::Type::KEY: {
             std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
-            if (isAppSwitchDue) {
-                if (isAppSwitchKeyEvent(*keyEntry)) {
-                    resetPendingAppSwitchLocked(true);
-                    isAppSwitchDue = false;
-                } else if (dropReason == DropReason::NOT_DROPPED) {
-                    dropReason = DropReason::APP_SWITCH;
+            if (!REMOVE_APP_SWITCH_DROPS) {
+                if (isAppSwitchDue) {
+                    if (isAppSwitchKeyEvent(*keyEntry)) {
+                        resetPendingAppSwitchLocked(true);
+                        isAppSwitchDue = false;
+                    } else if (dropReason == DropReason::NOT_DROPPED) {
+                        dropReason = DropReason::APP_SWITCH;
+                    }
                 }
             }
             if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
@@ -1088,8 +1088,10 @@
         case EventEntry::Type::MOTION: {
             std::shared_ptr<MotionEntry> motionEntry =
                     std::static_pointer_cast<MotionEntry>(mPendingEvent);
-            if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
-                dropReason = DropReason::APP_SWITCH;
+            if (!REMOVE_APP_SWITCH_DROPS) {
+                if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
+                    dropReason = DropReason::APP_SWITCH;
+                }
             }
             if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
                 dropReason = DropReason::STALE;
@@ -1104,8 +1106,10 @@
         case EventEntry::Type::SENSOR: {
             std::shared_ptr<SensorEntry> sensorEntry =
                     std::static_pointer_cast<SensorEntry>(mPendingEvent);
-            if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
-                dropReason = DropReason::APP_SWITCH;
+            if (!REMOVE_APP_SWITCH_DROPS) {
+                if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
+                    dropReason = DropReason::APP_SWITCH;
+                }
             }
             //  Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use
             // 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead.
@@ -1131,7 +1135,7 @@
 }
 
 bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
-    return std::chrono::nanoseconds(currentTime - entry.eventTime) >= mStaleEventTimeout;
+    return mPolicy.isStaleEvent(currentTime, entry.eventTime);
 }
 
 /**
@@ -1207,21 +1211,23 @@
             // If the application takes too long to catch up then we drop all events preceding
             // the app switch key.
             const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
-            if (isAppSwitchKeyEvent(keyEntry)) {
-                if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
-                    mAppSwitchSawKeyDown = true;
-                } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
-                    if (mAppSwitchSawKeyDown) {
-                        if (DEBUG_APP_SWITCH) {
-                            ALOGD("App switch is pending!");
+
+            if (!REMOVE_APP_SWITCH_DROPS) {
+                if (isAppSwitchKeyEvent(keyEntry)) {
+                    if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
+                        mAppSwitchSawKeyDown = true;
+                    } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
+                        if (mAppSwitchSawKeyDown) {
+                            if (DEBUG_APP_SWITCH) {
+                                ALOGD("App switch is pending!");
+                            }
+                            mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
+                            mAppSwitchSawKeyDown = false;
+                            needWake = true;
                         }
-                        mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
-                        mAppSwitchSawKeyDown = false;
-                        needWake = true;
                     }
                 }
             }
-
             // If a new up event comes in, and the pending event with same key code has been asked
             // to try again later because of the policy. We have to reset the intercept key wake up
             // time for it may have been handled in the policy and could be dropped.
@@ -1482,7 +1488,7 @@
 }
 
 void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
-    InjectionState* injectionState = entry->injectionState;
+    const std::shared_ptr<InjectionState>& injectionState = entry->injectionState;
     if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
         if (DEBUG_DISPATCH_CYCLE) {
             ALOGD("Injected inbound event was dropped.");
@@ -1508,10 +1514,11 @@
             (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
 
     std::shared_ptr<KeyEntry> newEntry =
-            std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, entry->deviceId,
-                                       entry->source, entry->displayId, policyFlags, entry->action,
-                                       entry->flags, entry->keyCode, entry->scanCode,
-                                       entry->metaState, entry->repeatCount + 1, entry->downTime);
+            std::make_unique<KeyEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+                                       currentTime, entry->deviceId, entry->source,
+                                       entry->displayId, policyFlags, entry->action, entry->flags,
+                                       entry->keyCode, entry->scanCode, entry->metaState,
+                                       entry->repeatCount + 1, entry->downTime);
 
     newEntry->syntheticRepeat = true;
     mKeyRepeatState.lastKeyEntry = newEntry;
@@ -2032,10 +2039,10 @@
         if (connection != nullptr) {
             prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
         } else {
-            if (DEBUG_FOCUS) {
-                ALOGD("Dropping event delivery to target with channel '%s' because it "
-                      "is no longer registered with the input dispatcher.",
-                      inputTarget.inputChannel->getName().c_str());
+            if (DEBUG_DROPPED_EVENTS_VERBOSE) {
+                LOG(INFO) << "Dropping event delivery to target with channel "
+                          << inputTarget.inputChannel->getName()
+                          << " because it is no longer registered with the input dispatcher.";
             }
         }
     }
@@ -2453,10 +2460,11 @@
         // If the pointer is not currently down, then ignore the event.
         if (!tempTouchState.isDown(entry.deviceId) &&
             maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
-            LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId
-                      << " is not down or we previously "
-                         "dropped the pointer down event in display "
-                      << displayId << ": " << entry.getDescription();
+            if (DEBUG_DROPPED_EVENTS_VERBOSE) {
+                LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId
+                          << " is not down or we previously dropped the pointer down event in "
+                          << "display " << displayId << ": " << entry.getDescription();
+            }
             outInjectionResult = InputEventInjectionResult::FAILED;
             return {};
         }
@@ -2864,8 +2872,13 @@
         it = inputTargets.end() - 1;
     }
 
-    LOG_ALWAYS_FATAL_IF(it->flags != targetFlags);
-    LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
+    if (it->flags != targetFlags) {
+        LOG(ERROR) << "Flags don't match! targetFlags=" << targetFlags.string() << ", it=" << *it;
+    }
+    if (it->globalScaleFactor != windowInfo->globalScaleFactor) {
+        LOG(ERROR) << "Mismatch! it->globalScaleFactor=" << it->globalScaleFactor
+                   << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor;
+    }
 }
 
 void InputDispatcher::addPointerWindowTargetLocked(
@@ -2911,10 +2924,12 @@
     }
 
     if (it->flags != targetFlags) {
-        logDispatchStateLocked();
-        LOG(FATAL) << "Flags don't match! targetFlags=" << targetFlags.string() << ", it=" << *it;
+        LOG(ERROR) << "Flags don't match! targetFlags=" << targetFlags.string() << ", it=" << *it;
     }
-    LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
+    if (it->globalScaleFactor != windowInfo->globalScaleFactor) {
+        LOG(ERROR) << "Mismatch! it->globalScaleFactor=" << it->globalScaleFactor
+                   << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor;
+    }
 
     it->addPointers(pointerIds, windowInfo->transform);
 }
@@ -4229,7 +4244,8 @@
                                 ").",
                                 originalMotionEntry.id, newId));
     std::unique_ptr<MotionEntry> splitMotionEntry =
-            std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime,
+            std::make_unique<MotionEntry>(newId, originalMotionEntry.injectionState,
+                                          originalMotionEntry.eventTime,
                                           originalMotionEntry.deviceId, originalMotionEntry.source,
                                           originalMotionEntry.displayId,
                                           originalMotionEntry.policyFlags, action,
@@ -4244,11 +4260,6 @@
                                           originalMotionEntry.yCursorPosition, splitDownTime,
                                           splitPointerProperties, splitPointerCoords);
 
-    if (originalMotionEntry.injectionState) {
-        splitMotionEntry->injectionState = originalMotionEntry.injectionState;
-        splitMotionEntry->injectionState->refCount += 1;
-    }
-
     return splitMotionEntry;
 }
 
@@ -4336,9 +4347,10 @@
         }
 
         std::unique_ptr<KeyEntry> newEntry =
-                std::make_unique<KeyEntry>(args.id, args.eventTime, args.deviceId, args.source,
-                                           args.displayId, policyFlags, args.action, flags, keyCode,
-                                           args.scanCode, metaState, repeatCount, args.downTime);
+                std::make_unique<KeyEntry>(args.id, /*injectionState=*/nullptr, args.eventTime,
+                                           args.deviceId, args.source, args.displayId, policyFlags,
+                                           args.action, flags, keyCode, args.scanCode, metaState,
+                                           repeatCount, args.downTime);
 
         needWake = enqueueInboundEventLocked(std::move(newEntry));
         mLock.unlock();
@@ -4422,7 +4434,8 @@
             const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);
             if (touchStateIt != mTouchStatesByDisplay.end()) {
                 const TouchState& touchState = touchStateIt->second;
-                if (touchState.hasTouchingPointers(args.deviceId)) {
+                if (touchState.hasTouchingPointers(args.deviceId) ||
+                    touchState.hasHoveringPointers(args.deviceId)) {
                     policyFlags |= POLICY_FLAG_PASS_TO_USER;
                 }
             }
@@ -4455,14 +4468,14 @@
 
         // Just enqueue a new motion event.
         std::unique_ptr<MotionEntry> newEntry =
-                std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source,
-                                              args.displayId, policyFlags, args.action,
-                                              args.actionButton, args.flags, args.metaState,
-                                              args.buttonState, args.classification, args.edgeFlags,
-                                              args.xPrecision, args.yPrecision,
-                                              args.xCursorPosition, args.yCursorPosition,
-                                              args.downTime, args.pointerProperties,
-                                              args.pointerCoords);
+                std::make_unique<MotionEntry>(args.id, /*injectionState=*/nullptr, args.eventTime,
+                                              args.deviceId, args.source, args.displayId,
+                                              policyFlags, args.action, args.actionButton,
+                                              args.flags, args.metaState, args.buttonState,
+                                              args.classification, args.edgeFlags, args.xPrecision,
+                                              args.yPrecision, args.xCursorPosition,
+                                              args.yCursorPosition, args.downTime,
+                                              args.pointerProperties, args.pointerCoords);
 
         if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
             IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
@@ -4534,6 +4547,7 @@
 }
 
 void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+    // TODO(b/308677868) Remove device reset from the InputListener interface
     if (debugInboundEventDetails()) {
         ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args.eventTime,
               args.deviceId);
@@ -4608,6 +4622,9 @@
         resolvedDeviceId = event->getDeviceId();
     }
 
+    const bool isAsync = syncMode == InputEventInjectionSync::NONE;
+    auto injectionState = std::make_shared<InjectionState>(targetUid, isAsync);
+
     std::queue<std::unique_ptr<EventEntry>> injectedEntries;
     switch (event->getType()) {
         case InputEventType::KEY: {
@@ -4640,10 +4657,11 @@
 
             mLock.lock();
             std::unique_ptr<KeyEntry> injectedEntry =
-                    std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
-                                               resolvedDeviceId, incomingKey.getSource(),
-                                               incomingKey.getDisplayId(), policyFlags, action,
-                                               flags, keyCode, incomingKey.getScanCode(), metaState,
+                    std::make_unique<KeyEntry>(incomingKey.getId(), injectionState,
+                                               incomingKey.getEventTime(), resolvedDeviceId,
+                                               incomingKey.getSource(), incomingKey.getDisplayId(),
+                                               policyFlags, action, flags, keyCode,
+                                               incomingKey.getScanCode(), metaState,
                                                incomingKey.getRepeatCount(),
                                                incomingKey.getDownTime());
             injectedEntries.push(std::move(injectedEntry));
@@ -4675,6 +4693,30 @@
             }
 
             mLock.lock();
+
+            if (policyFlags & POLICY_FLAG_FILTERED) {
+                // The events from InputFilter impersonate real hardware devices. Check these
+                // events for consistency and print an error. An inconsistent event sent from
+                // InputFilter could cause a crash in the later stages of dispatching pipeline.
+                auto [it, _] =
+                        mInputFilterVerifiersByDisplay
+                                .try_emplace(displayId,
+                                             StringPrintf("Injection on %" PRId32, displayId));
+                InputVerifier& verifier = it->second;
+
+                Result<void> result =
+                        verifier.processMovement(resolvedDeviceId, motionEvent.getSource(),
+                                                 motionEvent.getAction(),
+                                                 motionEvent.getPointerCount(),
+                                                 motionEvent.getPointerProperties(),
+                                                 motionEvent.getSamplePointerCoords(), flags);
+                if (!result.ok()) {
+                    logDispatchStateLocked();
+                    LOG(ERROR) << "Inconsistent event: " << motionEvent
+                               << ", reason: " << result.error();
+                }
+            }
+
             const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
             const size_t pointerCount = motionEvent.getPointerCount();
             const std::vector<PointerProperties>
@@ -4683,9 +4725,10 @@
 
             const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords();
             std::unique_ptr<MotionEntry> injectedEntry =
-                    std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
-                                                  resolvedDeviceId, motionEvent.getSource(),
-                                                  displayId, policyFlags, motionEvent.getAction(),
+                    std::make_unique<MotionEntry>(motionEvent.getId(), injectionState,
+                                                  *sampleEventTimes, resolvedDeviceId,
+                                                  motionEvent.getSource(), displayId, policyFlags,
+                                                  motionEvent.getAction(),
                                                   motionEvent.getActionButton(), flags,
                                                   motionEvent.getMetaState(),
                                                   motionEvent.getButtonState(),
@@ -4705,9 +4748,10 @@
                 sampleEventTimes += 1;
                 samplePointerCoords += motionEvent.getPointerCount();
                 std::unique_ptr<MotionEntry> nextInjectedEntry = std::make_unique<
-                        MotionEntry>(motionEvent.getId(), *sampleEventTimes, resolvedDeviceId,
-                                     motionEvent.getSource(), displayId, policyFlags,
-                                     motionEvent.getAction(), motionEvent.getActionButton(), flags,
+                        MotionEntry>(motionEvent.getId(), injectionState, *sampleEventTimes,
+                                     resolvedDeviceId, motionEvent.getSource(), displayId,
+                                     policyFlags, motionEvent.getAction(),
+                                     motionEvent.getActionButton(), flags,
                                      motionEvent.getMetaState(), motionEvent.getButtonState(),
                                      motionEvent.getClassification(), motionEvent.getEdgeFlags(),
                                      motionEvent.getXPrecision(), motionEvent.getYPrecision(),
@@ -4729,14 +4773,6 @@
             return InputEventInjectionResult::FAILED;
     }
 
-    InjectionState* injectionState = new InjectionState(targetUid);
-    if (syncMode == InputEventInjectionSync::NONE) {
-        injectionState->injectionIsAsync = true;
-    }
-
-    injectionState->refCount += 1;
-    injectedEntries.back()->injectionState = injectionState;
-
     bool needWake = false;
     while (!injectedEntries.empty()) {
         if (DEBUG_INJECTION) {
@@ -4799,8 +4835,6 @@
                 }
             }
         }
-
-        injectionState->release();
     } // release lock
 
     if (DEBUG_INJECTION) {
@@ -4846,37 +4880,40 @@
 
 void InputDispatcher::setInjectionResult(EventEntry& entry,
                                          InputEventInjectionResult injectionResult) {
-    InjectionState* injectionState = entry.injectionState;
-    if (injectionState) {
-        if (DEBUG_INJECTION) {
-            LOG(INFO) << "Setting input event injection result to "
-                      << ftl::enum_string(injectionResult);
-        }
-
-        if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
-            // Log the outcome since the injector did not wait for the injection result.
-            switch (injectionResult) {
-                case InputEventInjectionResult::SUCCEEDED:
-                    ALOGV("Asynchronous input event injection succeeded.");
-                    break;
-                case InputEventInjectionResult::TARGET_MISMATCH:
-                    ALOGV("Asynchronous input event injection target mismatch.");
-                    break;
-                case InputEventInjectionResult::FAILED:
-                    ALOGW("Asynchronous input event injection failed.");
-                    break;
-                case InputEventInjectionResult::TIMED_OUT:
-                    ALOGW("Asynchronous input event injection timed out.");
-                    break;
-                case InputEventInjectionResult::PENDING:
-                    ALOGE("Setting result to 'PENDING' for asynchronous injection");
-                    break;
-            }
-        }
-
-        injectionState->injectionResult = injectionResult;
-        mInjectionResultAvailable.notify_all();
+    if (!entry.injectionState) {
+        // Not an injected event.
+        return;
     }
+
+    InjectionState& injectionState = *entry.injectionState;
+    if (DEBUG_INJECTION) {
+        LOG(INFO) << "Setting input event injection result to "
+                  << ftl::enum_string(injectionResult);
+    }
+
+    if (injectionState.injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
+        // Log the outcome since the injector did not wait for the injection result.
+        switch (injectionResult) {
+            case InputEventInjectionResult::SUCCEEDED:
+                ALOGV("Asynchronous input event injection succeeded.");
+                break;
+            case InputEventInjectionResult::TARGET_MISMATCH:
+                ALOGV("Asynchronous input event injection target mismatch.");
+                break;
+            case InputEventInjectionResult::FAILED:
+                ALOGW("Asynchronous input event injection failed.");
+                break;
+            case InputEventInjectionResult::TIMED_OUT:
+                ALOGW("Asynchronous input event injection timed out.");
+                break;
+            case InputEventInjectionResult::PENDING:
+                ALOGE("Setting result to 'PENDING' for asynchronous injection");
+                break;
+        }
+    }
+
+    injectionState.injectionResult = injectionResult;
+    mInjectionResultAvailable.notify_all();
 }
 
 void InputDispatcher::transformMotionEntryForInjectionLocked(
@@ -4903,18 +4940,16 @@
 }
 
 void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
-    InjectionState* injectionState = entry.injectionState;
-    if (injectionState) {
-        injectionState->pendingForegroundDispatches += 1;
+    if (entry.injectionState) {
+        entry.injectionState->pendingForegroundDispatches += 1;
     }
 }
 
 void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) {
-    InjectionState* injectionState = entry.injectionState;
-    if (injectionState) {
-        injectionState->pendingForegroundDispatches -= 1;
+    if (entry.injectionState) {
+        entry.injectionState->pendingForegroundDispatches -= 1;
 
-        if (injectionState->pendingForegroundDispatches == 0) {
+        if (entry.injectionState->pendingForegroundDispatches == 0) {
             mInjectionSyncFinished.notify_all();
         }
     }
@@ -5800,6 +5835,9 @@
         dump += INDENT "Connections: <none>\n";
     }
 
+    dump += "input_flags::remove_app_switch_drops() = ";
+    dump += toString(REMOVE_APP_SWITCH_DROPS);
+    dump += "\n";
     if (isAppSwitchPendingLocked()) {
         dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
                              ns2ms(mAppSwitchDueTime - now()));
@@ -6131,7 +6169,7 @@
                                                      uint32_t seq, bool handled,
                                                      nsecs_t consumeTime) {
     // Handle post-event policy actions.
-    bool restartEvent;
+    std::unique_ptr<KeyEntry> fallbackKeyEntry;
 
     { // Start critical section
         auto dispatchEntryIt =
@@ -6155,15 +6193,9 @@
         }
 
         if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) {
-            KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry.eventEntry));
-            restartEvent =
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(dispatchEntry.eventEntry));
+            fallbackKeyEntry =
                     afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
-        } else if (dispatchEntry.eventEntry->type == EventEntry::Type::MOTION) {
-            MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry.eventEntry));
-            restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry,
-                                                               motionEntry, handled);
-        } else {
-            restartEvent = false;
         }
     } // End critical section: The -LockedInterruptable methods may have released the lock.
 
@@ -6187,12 +6219,13 @@
             }
         }
         traceWaitQueueLength(*connection);
-        if (restartEvent && connection->status == Connection::Status::NORMAL) {
-            connection->outboundQueue.emplace_front(std::move(dispatchEntry));
-            traceOutboundQueueLength(*connection);
-        } else {
-            releaseDispatchEntry(std::move(dispatchEntry));
+        if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {
+            const InputTarget target{.inputChannel = connection->inputChannel,
+                                     .flags = dispatchEntry->targetFlags};
+            enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target,
+                                       InputTarget::Flags::DISPATCH_AS_IS);
         }
+        releaseDispatchEntry(std::move(dispatchEntry));
     }
 
     // Start the next dispatch cycle for this connection.
@@ -6377,15 +6410,15 @@
     sendWindowResponsiveCommandLocked(connectionToken, pid);
 }
 
-bool InputDispatcher::afterKeyEventLockedInterruptable(
+std::unique_ptr<KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable(
         const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
-        KeyEntry& keyEntry, bool handled) {
+        const KeyEntry& keyEntry, bool handled) {
     if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
         if (!handled) {
             // Report the key as unhandled, since the fallback was not handled.
             mReporter->reportUnhandledKey(keyEntry.id);
         }
-        return false;
+        return {};
     }
 
     // Get the fallback key state.
@@ -6445,7 +6478,7 @@
                       "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
                       originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
             }
-            return false;
+            return {};
         }
 
         // Dispatch the unhandled key to the policy.
@@ -6470,7 +6503,7 @@
 
         if (connection->status != Connection::Status::NORMAL) {
             connection->inputState.removeFallbackKey(originalKeyCode);
-            return false;
+            return {};
         }
 
         // Latch the fallback keycode for this key on an initial down.
@@ -6531,25 +6564,22 @@
         }
 
         if (fallback) {
-            // Restart the dispatch cycle using the fallback key.
-            keyEntry.eventTime = event.getEventTime();
-            keyEntry.deviceId = event.getDeviceId();
-            keyEntry.source = event.getSource();
-            keyEntry.displayId = event.getDisplayId();
-            keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
-            keyEntry.keyCode = *fallbackKeyCode;
-            keyEntry.scanCode = event.getScanCode();
-            keyEntry.metaState = event.getMetaState();
-            keyEntry.repeatCount = event.getRepeatCount();
-            keyEntry.downTime = event.getDownTime();
-            keyEntry.syntheticRepeat = false;
-
+            // Return the fallback key that we want dispatched to the channel.
+            std::unique_ptr<KeyEntry> newEntry =
+                    std::make_unique<KeyEntry>(mIdGenerator.nextId(), keyEntry.injectionState,
+                                               event.getEventTime(), event.getDeviceId(),
+                                               event.getSource(), event.getDisplayId(),
+                                               keyEntry.policyFlags, keyEntry.action,
+                                               event.getFlags() | AKEY_EVENT_FLAG_FALLBACK,
+                                               *fallbackKeyCode, event.getScanCode(),
+                                               event.getMetaState(), event.getRepeatCount(),
+                                               event.getDownTime());
             if (DEBUG_OUTBOUND_EVENT_DETAILS) {
                 ALOGD("Unhandled key event: Dispatching fallback key.  "
                       "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
                       originalKeyCode, *fallbackKeyCode, keyEntry.metaState);
             }
-            return true; // restart the event
+            return newEntry;
         } else {
             if (DEBUG_OUTBOUND_EVENT_DETAILS) {
                 ALOGD("Unhandled key event: No fallback key.");
@@ -6559,13 +6589,7 @@
             mReporter->reportUnhandledKey(keyEntry.id);
         }
     }
-    return false;
-}
-
-bool InputDispatcher::afterMotionEventLockedInterruptable(
-        const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
-        MotionEntry& motionEntry, bool handled) {
-    return false;
+    return {};
 }
 
 void InputDispatcher::traceInboundQueueLengthLocked() {
@@ -6736,6 +6760,7 @@
         // Remove the associated touch mode state.
         mTouchModePerDisplay.erase(displayId);
         mVerifiersByDisplay.erase(displayId);
+        mInputFilterVerifiersByDisplay.erase(displayId);
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index a1127a0..f0f6772 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -82,8 +82,6 @@
     static constexpr bool kDefaultInTouchMode = true;
 
     explicit InputDispatcher(InputDispatcherPolicyInterface& policy);
-    explicit InputDispatcher(InputDispatcherPolicyInterface& policy,
-                             std::chrono::nanoseconds staleEventTimeout);
     ~InputDispatcher() override;
 
     void dump(std::string& dump) const override;
@@ -288,7 +286,8 @@
     void transformMotionEntryForInjectionLocked(MotionEntry&,
                                                 const ui::Transform& injectedTransform) const
             REQUIRES(mLock);
-
+    // Per-display correction of injected events
+    std::map</*displayId*/ int32_t, InputVerifier> mInputFilterVerifiersByDisplay GUARDED_BY(mLock);
     std::condition_variable mInjectionSyncFinished;
     void incrementPendingForegroundDispatches(EventEntry& entry);
     void decrementPendingForegroundDispatches(EventEntry& entry);
@@ -461,9 +460,6 @@
      */
     std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
 
-    // Amount of time to allow for an event to be dispatched (measured since its eventTime)
-    // before considering it stale and dropping it.
-    const std::chrono::nanoseconds mStaleEventTimeout;
     bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry);
 
     bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
@@ -664,12 +660,10 @@
     void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
             REQUIRES(mLock);
     std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay;
-    bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
-                                          DispatchEntry& dispatchEntry, KeyEntry& keyEntry,
-                                          bool handled) REQUIRES(mLock);
-    bool afterMotionEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
-                                             DispatchEntry& dispatchEntry, MotionEntry& motionEntry,
-                                             bool handled) REQUIRES(mLock);
+    // Returns a fallback KeyEntry that should be sent to the connection, if required.
+    std::unique_ptr<KeyEntry> afterKeyEventLockedInterruptable(
+            const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
+            const KeyEntry& keyEntry, bool handled) REQUIRES(mLock);
 
     // Find touched state and touched window by token.
     std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/>
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 09b5186..16cc266 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -421,9 +421,10 @@
     if (action == AMOTION_EVENT_ACTION_CANCEL) {
         flags |= AMOTION_EVENT_FLAG_CANCELED;
     }
-    return std::make_unique<MotionEntry>(mIdGenerator.nextId(), eventTime, memento.deviceId,
-                                         memento.source, memento.displayId, memento.policyFlags,
-                                         action, /*actionButton=*/0, flags, AMETA_NONE,
+    return std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+                                         eventTime, memento.deviceId, memento.source,
+                                         memento.displayId, memento.policyFlags, action,
+                                         /*actionButton=*/0, flags, AMETA_NONE,
                                          /*buttonState=*/0, MotionClassification::NONE,
                                          AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
                                          memento.yPrecision, memento.xCursorPosition,
@@ -437,9 +438,10 @@
     for (KeyMemento& memento : mKeyMementos) {
         if (shouldCancelKey(memento, options)) {
             events.push_back(
-                    std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
-                                               memento.source, memento.displayId,
-                                               memento.policyFlags, AKEY_EVENT_ACTION_UP,
+                    std::make_unique<KeyEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+                                               currentTime, memento.deviceId, memento.source,
+                                               memento.displayId, memento.policyFlags,
+                                               AKEY_EVENT_ACTION_UP,
                                                memento.flags | AKEY_EVENT_FLAG_CANCELED,
                                                memento.keyCode, memento.scanCode, memento.metaState,
                                                /*repeatCount=*/0, memento.downTime));
@@ -498,8 +500,8 @@
                             | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
             events.push_back(
-                    std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
-                                                  memento.deviceId, memento.source,
+                    std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+                                                  currentTime, memento.deviceId, memento.source,
                                                   memento.displayId, memento.policyFlags, action,
                                                   /*actionButton=*/0, memento.flags, AMETA_NONE,
                                                   /*buttonState=*/0, MotionClassification::NONE,
@@ -539,11 +541,11 @@
             flags |= AMOTION_EVENT_FLAG_CANCELED;
         }
         events.push_back(
-                std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
-                                              memento.source, memento.displayId,
-                                              memento.policyFlags, action, /*actionButton=*/0,
-                                              flags, AMETA_NONE, /*buttonState=*/0,
-                                              MotionClassification::NONE,
+                std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+                                              currentTime, memento.deviceId, memento.source,
+                                              memento.displayId, memento.policyFlags, action,
+                                              /*actionButton=*/0, flags, AMETA_NONE,
+                                              /*buttonState=*/0, MotionClassification::NONE,
                                               AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
                                               memento.yPrecision, memento.xCursorPosition,
                                               memento.yCursorPosition, memento.downTime,
@@ -564,8 +566,8 @@
                                                      : AMOTION_EVENT_ACTION_POINTER_UP |
                             (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
             events.push_back(
-                    std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
-                                                  memento.deviceId, memento.source,
+                    std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
+                                                  currentTime, memento.deviceId, memento.source,
                                                   memento.displayId, memento.policyFlags, action,
                                                   /*actionButton=*/0,
                                                   memento.flags | AMOTION_EVENT_FLAG_CANCELED,
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index af28e48..bf48804 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -18,6 +18,7 @@
 
 #include "InputDispatcherConfiguration.h"
 
+#include <android-base/properties.h>
 #include <binder/IBinder.h>
 #include <gui/InputApplication.h>
 #include <input/Input.h>
@@ -118,6 +119,16 @@
     /* Poke user activity for an event dispatched to a window. */
     virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
 
+    /*
+     * Return true if the provided event is stale, and false otherwise. Used for determining
+     * whether the dispatcher should drop the event.
+     */
+    virtual bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) {
+        static const std::chrono::duration STALE_EVENT_TIMEOUT =
+                std::chrono::seconds(10) * android::base::HwTimeoutMultiplier();
+        return std::chrono::nanoseconds(currentTime - eventTime) >= STALE_EVENT_TIMEOUT;
+    }
+
     /* Notifies the policy that a pointer down event has occurred outside the current focused
      * window.
      *
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 25e1d21..efc8b26 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -451,6 +451,15 @@
 
     /* Returns true if any InputConnection is currently active. */
     virtual bool isInputMethodConnectionActive() = 0;
+
+    /* Gets the viewport of a particular display that the pointer device is associated with. If
+     * the pointer device is not associated with any display, it should ADISPLAY_IS_NONE to get
+     * the viewport that should be used. The device should get a new viewport using this method
+     * every time there is a display configuration change. The logical bounds of the viewport should
+     * be used as the range of possible values for pointing devices, like mice and touchpads.
+     */
+    virtual std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
+            int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 9e020c7..8b47b55 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -38,7 +38,16 @@
      * library, libinputservice, that has the additional dependencies. The PointerController
      * will be mocked when testing PointerChoreographer.
      */
-    virtual std::shared_ptr<PointerControllerInterface> createPointerController() = 0;
+    virtual std::shared_ptr<PointerControllerInterface> createPointerController(
+            PointerControllerInterface::ControllerType type) = 0;
+
+    /**
+     * Notifies the policy that the default pointer displayId has changed. PointerChoreographer is
+     * the single source of truth for all pointers on screen.
+     * @param displayId The updated display on which the mouse cursor is shown
+     * @param position The new position of the mouse cursor on the logical display
+     */
+    virtual void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 95f819a..8837b25 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -52,6 +52,22 @@
     virtual ~PointerControllerInterface() { }
 
 public:
+    /**
+     * Enum used to differentiate various types of PointerControllers for the transition to
+     * using PointerChoreographer.
+     *
+     * TODO(b/293587049): Refactor the PointerController class into different controller types.
+     */
+    enum class ControllerType {
+        // The PointerController that is responsible for drawing all icons.
+        LEGACY,
+        // Represents a single mouse pointer.
+        MOUSE,
+    };
+
+    /* Dumps the state of the pointer controller. */
+    virtual std::string dump() = 0;
+
     /* Gets the bounds of the region that the pointer can traverse.
      * Returns true if the bounds are available. */
     virtual std::optional<FloatRect> getBounds() const = 0;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 5766b14..0582649 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -1046,6 +1046,14 @@
     return mReader->mPreventingTouchpadTaps;
 }
 
+void InputReader::ContextImpl::setLastKeyDownTimestamp(nsecs_t when) {
+    mReader->mLastKeyDownTimestamp = when;
+}
+
+nsecs_t InputReader::ContextImpl::getLastKeyDownTimestamp() {
+    return mReader->mLastKeyDownTimestamp;
+}
+
 void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
     // lock is already held by the input loop
     mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9a297c9..4c78db3 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -158,6 +158,9 @@
         void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock)
                 REQUIRES(mLock) override;
         bool isPreventingTouchpadTaps() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
+        void setLastKeyDownTimestamp(nsecs_t when) REQUIRES(mReader->mLock)
+                REQUIRES(mLock) override;
+        nsecs_t getLastKeyDownTimestamp() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
     } mContext;
 
     friend class ContextImpl;
@@ -198,6 +201,9 @@
     // true if tap-to-click on touchpad currently disabled
     bool mPreventingTouchpadTaps GUARDED_BY(mLock){false};
 
+    // records timestamp of the last key press on the physical keyboard
+    nsecs_t mLastKeyDownTimestamp GUARDED_BY(mLock){0};
+
     // low-level input event decoding and device management
     [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
             REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index aed7563..69b2315 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -65,6 +65,9 @@
 
     virtual void setPreventingTouchpadTaps(bool prevent) = 0;
     virtual bool isPreventingTouchpadTaps() = 0;
+
+    virtual void setLastKeyDownTimestamp(nsecs_t when) = 0;
+    virtual nsecs_t getLastKeyDownTimestamp() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 79f07a5..7aeb215 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -20,6 +20,7 @@
 
 #include "CursorInputMapper.h"
 
+#include <com_android_input_flags.h>
 #include <optional>
 
 #include "CursorButtonAccumulator.h"
@@ -29,6 +30,8 @@
 
 #include "input/PrintTools.h"
 
+namespace input_flags = com::android::input::flags;
+
 namespace android {
 
 // The default velocity control parameters that has no effect.
@@ -71,7 +74,8 @@
 CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
                                      const InputReaderConfiguration& readerConfig)
       : InputMapper(deviceContext, readerConfig),
-        mLastEventTime(std::numeric_limits<nsecs_t>::min()) {}
+        mLastEventTime(std::numeric_limits<nsecs_t>::min()),
+        mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {}
 
 CursorInputMapper::~CursorInputMapper() {
     if (mPointerController != nullptr) {
@@ -87,11 +91,11 @@
     InputMapper::populateDeviceInfo(info);
 
     if (mParameters.mode == Parameters::Mode::POINTER) {
-        if (const auto bounds = mPointerController->getBounds(); bounds) {
-            info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, bounds->left, bounds->right, 0.0f,
-                                0.0f, 0.0f);
-            info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, bounds->top, bounds->bottom, 0.0f,
-                                0.0f, 0.0f);
+        if (!mBoundsInLogicalDisplay.isEmpty()) {
+            info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, mBoundsInLogicalDisplay.left,
+                                mBoundsInLogicalDisplay.right, 0.0f, 0.0f, 0.0f);
+            info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, mBoundsInLogicalDisplay.top,
+                                mBoundsInLogicalDisplay.bottom, 0.0f, 0.0f, 0.0f);
         }
     } else {
         info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
@@ -283,19 +287,22 @@
     float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mSource == AINPUT_SOURCE_MOUSE) {
-        if (moved || scrolled || buttonsChanged) {
-            mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+        if (!mEnablePointerChoreographer) {
+            if (moved || scrolled || buttonsChanged) {
+                mPointerController->setPresentation(
+                        PointerControllerInterface::Presentation::POINTER);
 
-            if (moved) {
-                mPointerController->move(deltaX, deltaY);
+                if (moved) {
+                    mPointerController->move(deltaX, deltaY);
+                }
+                mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
             }
-            mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+            std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
+
+            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         }
-
-        std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
-
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
     } else {
@@ -499,31 +506,55 @@
     const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
 
     mDisplayId = ADISPLAY_ID_NONE;
-    if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+    std::optional<DisplayViewport> resolvedViewport;
+    bool isBoundsSet = false;
+    if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) {
         // This InputDevice is associated with a viewport.
         // Only generate events for the associated display.
-        const bool mismatchedPointerDisplay =
-                isPointer && (viewport->displayId != mPointerController->getDisplayId());
-        mDisplayId =
-                mismatchedPointerDisplay ? std::nullopt : std::make_optional(viewport->displayId);
+        mDisplayId = assocViewport->displayId;
+        resolvedViewport = *assocViewport;
+        if (!mEnablePointerChoreographer) {
+            const bool mismatchedPointerDisplay =
+                    isPointer && (assocViewport->displayId != mPointerController->getDisplayId());
+            if (mismatchedPointerDisplay) {
+                // This device's associated display doesn't match PointerController's current
+                // display. Do not associate it with any display.
+                mDisplayId.reset();
+            }
+        }
     } else if (isPointer) {
         // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
-        mDisplayId = mPointerController->getDisplayId();
+        if (mEnablePointerChoreographer) {
+            // Always use DISPLAY_ID_NONE for mouse events.
+            // PointerChoreographer will make it target the correct the displayId later.
+            const auto pointerViewport =
+                    getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
+            mDisplayId = pointerViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
+            resolvedViewport = pointerViewport;
+        } else {
+            mDisplayId = mPointerController->getDisplayId();
+            if (auto v = config.getDisplayViewportById(*mDisplayId); v) {
+                resolvedViewport = *v;
+            }
+            if (auto bounds = mPointerController->getBounds(); bounds) {
+                mBoundsInLogicalDisplay = *bounds;
+                isBoundsSet = true;
+            }
+        }
     }
 
-    mOrientation = ui::ROTATION_0;
-    const bool isOrientedDevice =
-            (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
-    // InputReader works in the un-rotated display coordinate space, so we don't need to do
-    // anything if the device is already orientation-aware. If the device is not
-    // orientation-aware, then we need to apply the inverse rotation of the display so that
-    // when the display rotation is applied later as a part of the per-window transform, we
-    // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
-    // rotations and report values directly from the input device.
-    if (!isOrientedDevice && mDisplayId && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
-        if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
-            mOrientation = getInverseRotation(viewport->orientation);
-        }
+    mOrientation = (mParameters.orientationAware && mParameters.hasAssociatedDisplay) ||
+                    mParameters.mode == Parameters::Mode::POINTER_RELATIVE || !resolvedViewport
+            ? ui::ROTATION_0
+            : getInverseRotation(resolvedViewport->orientation);
+
+    if (!isBoundsSet) {
+        mBoundsInLogicalDisplay = resolvedViewport
+                ? FloatRect{static_cast<float>(resolvedViewport->logicalLeft),
+                            static_cast<float>(resolvedViewport->logicalTop),
+                            static_cast<float>(resolvedViewport->logicalRight - 1),
+                            static_cast<float>(resolvedViewport->logicalBottom - 1)}
+                : FloatRect{0, 0, 0, 0};
     }
 
     bumpGeneration();
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index b879bfd..308adaa 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -119,7 +119,8 @@
     // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
     // std::nullopt), all events will be ignored.
     std::optional<int32_t> mDisplayId;
-    ui::Rotation mOrientation;
+    ui::Rotation mOrientation{ui::ROTATION_0};
+    FloatRect mBoundsInLogicalDisplay{};
 
     std::shared_ptr<PointerControllerInterface> mPointerController;
 
@@ -127,6 +128,8 @@
     nsecs_t mDownTime;
     nsecs_t mLastEventTime;
 
+    const bool mEnablePointerChoreographer;
+
     explicit CursorInputMapper(InputDeviceContext& deviceContext,
                                const InputReaderConfiguration& readerConfig);
     void dumpParameters(std::string& dump);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 531fc67..f068cc8 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -270,7 +270,7 @@
             keyDown.flags = flags;
             mKeyDowns.push_back(keyDown);
         }
-        onKeyDownProcessed();
+        onKeyDownProcessed(downTime);
     } else {
         // Remove key down.
         if (keyDownIndex) {
@@ -448,8 +448,9 @@
     return out;
 }
 
-void KeyboardInputMapper::onKeyDownProcessed() {
+void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) {
     InputReaderContext& context = *getContext();
+    context.setLastKeyDownTimestamp(downTime);
     if (context.isPreventingTouchpadTaps()) {
         // avoid pinging java service unnecessarily, just fade pointer again if it became visible
         context.fadePointer();
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 09808df..500256b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -107,7 +107,7 @@
     void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
     std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig);
     [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
-    void onKeyDownProcessed();
+    void onKeyDownProcessed(nsecs_t downTime);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index f70be72..b0fc903 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -152,6 +152,14 @@
     }
 }
 
+size_t MultiTouchMotionAccumulator::getActiveSlotsCount() const {
+    if (!mUsingSlotsProtocol) {
+        return mCurrentSlot < 0 ? 0 : mCurrentSlot;
+    }
+    return std::count_if(mSlots.begin(), mSlots.end(),
+                         [](const Slot& slot) { return slot.mInUse; });
+}
+
 // --- MultiTouchMotionAccumulator::Slot ---
 
 ToolType MultiTouchMotionAccumulator::Slot::getToolType() const {
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 943dde5..0e3e2bb 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -77,6 +77,7 @@
     void process(const RawEvent* rawEvent);
     void finishSync();
 
+    size_t getActiveSlotsCount() const;
     inline size_t getSlotCount() const { return mSlots.size(); }
     inline const Slot& getSlot(size_t index) const {
         LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index);
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 64e8825..db31ded 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -58,6 +58,7 @@
         "InputReader_test.cpp",
         "InstrumentedInputReader.cpp",
         "LatencyTracker_test.cpp",
+        "MultiTouchMotionAccumulator_test.cpp",
         "NotifyArgs_test.cpp",
         "PointerChoreographer_test.cpp",
         "PreferStylusOverTouch_test.cpp",
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 41c98ef..88f514f 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -261,4 +261,17 @@
     mStylusGestureNotified = deviceId;
 }
 
+std::optional<DisplayViewport> FakeInputReaderPolicy::getPointerViewportForAssociatedDisplay(
+        int32_t associatedDisplayId) {
+    if (associatedDisplayId == ADISPLAY_ID_NONE) {
+        associatedDisplayId = mConfig.defaultPointerDisplayId;
+    }
+    for (auto& viewport : mViewports) {
+        if (viewport.displayId == associatedDisplayId) {
+            return std::make_optional(viewport);
+        }
+    }
+    return std::nullopt;
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 48912a6..4ef9c3e 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -79,6 +79,8 @@
     void setStylusPointerIconEnabled(bool enabled);
     void setIsInputMethodConnectionActive(bool active);
     bool isInputMethodConnectionActive() override;
+    std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
+            int32_t associatedDisplayId) override;
 
 private:
     void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index c374267..c75f6ed 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -40,6 +40,7 @@
     bool isPointerShown();
 
 private:
+    std::string dump() override { return ""; }
     std::optional<FloatRect> getBounds() const override;
     void move(float deltaX, float deltaY) override;
     void fade(Transition) override;
@@ -52,7 +53,7 @@
     bool mHaveBounds{false};
     float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
     float mX{0}, mY{0};
-    int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+    int32_t mDisplayId{ADISPLAY_ID_NONE};
     bool mIsPointerShown{false};
 
     std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3c87f71..3c2b31d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -112,8 +112,6 @@
 // An arbitrary pid of the gesture monitor window
 static constexpr gui::Pid MONITOR_PID{2001};
 
-static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
-
 /**
  * If we expect to receive the event, the timeout can be made very long. When the test are running
  * correctly, we will actually never wait until the end of the timeout because the wait will end
@@ -337,7 +335,7 @@
         std::optional<sp<IBinder>> receivedToken =
                 getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock,
                                                       mNotifyInputChannelBroken);
-        ASSERT_TRUE(receivedToken.has_value());
+        ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token";
         ASSERT_EQ(token, *receivedToken);
     }
 
@@ -348,6 +346,8 @@
         mInterceptKeyTimeout = timeout;
     }
 
+    void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; }
+
     void assertUserActivityPoked() {
         std::scoped_lock lock(mLock);
         ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked";
@@ -366,6 +366,30 @@
         ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms));
     }
 
+    void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler) {
+        std::scoped_lock lock(mLock);
+        mUnhandledKeyHandler = handler;
+    }
+
+    void assertUnhandledKeyReported(int32_t keycode) {
+        std::unique_lock lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        std::optional<int32_t> unhandledKeycode =
+                getItemFromStorageLockedInterruptible(100ms, mReportedUnhandledKeycodes, lock,
+                                                      mNotifyUnhandledKey);
+        ASSERT_TRUE(unhandledKeycode) << "Expected unhandled key to be reported";
+        ASSERT_EQ(unhandledKeycode, keycode);
+    }
+
+    void assertUnhandledKeyNotReported() {
+        std::unique_lock lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        std::optional<int32_t> unhandledKeycode =
+                getItemFromStorageLockedInterruptible(10ms, mReportedUnhandledKeycodes, lock,
+                                                      mNotifyUnhandledKey);
+        ASSERT_FALSE(unhandledKeycode) << "Expected unhandled key NOT to be reported";
+    }
+
 private:
     std::mutex mLock;
     std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -391,8 +415,14 @@
 
     std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
 
+    std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
+
     BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
 
+    std::condition_variable mNotifyUnhandledKey;
+    std::queue<int32_t> mReportedUnhandledKeycodes GUARDED_BY(mLock);
+    std::function<std::optional<KeyEvent>(const KeyEvent&)> mUnhandledKeyHandler GUARDED_BY(mLock);
+
     // All three ANR-related callbacks behave the same way, so we use this generic function to wait
     // for a specific container to become non-empty. When the container is non-empty, return the
     // first entry from the container and erase it.
@@ -435,7 +465,6 @@
         condition.wait_for(lock, timeout,
                            [&storage]() REQUIRES(mLock) { return !storage.empty(); });
         if (storage.empty()) {
-            ADD_FAILURE() << "Did not receive the expected callback";
             return std::nullopt;
         }
         T item = storage.front();
@@ -526,9 +555,12 @@
         return delay;
     }
 
-    std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+    std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event,
                                                  uint32_t) override {
-        return {};
+        std::scoped_lock lock(mLock);
+        mReportedUnhandledKeycodes.emplace(event.getKeyCode());
+        mNotifyUnhandledKey.notify_all();
+        return mUnhandledKeyHandler != nullptr ? mUnhandledKeyHandler(event) : std::nullopt;
     }
 
     void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
@@ -545,6 +577,10 @@
         mPokedUserActivity = true;
     }
 
+    bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override {
+        return std::chrono::nanoseconds(currentTime - eventTime) >= mStaleEventTimeout;
+    }
+
     void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
         std::scoped_lock lock(mLock);
         mOnPointerDownToken = newToken;
@@ -586,7 +622,8 @@
 
     void SetUp() override {
         mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
-        mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, STALE_EVENT_TIMEOUT);
+        mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
+
         mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
         // Start InputDispatcher thread
         ASSERT_EQ(OK, mDispatcher->start());
@@ -838,13 +875,13 @@
         mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
     }
 
-    InputEvent* consume(std::chrono::milliseconds timeout) {
+    InputEvent* consume(std::chrono::milliseconds timeout, bool handled = false) {
         InputEvent* event;
         std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event);
         if (!consumeSeq) {
             return nullptr;
         }
-        finishEvent(*consumeSeq);
+        finishEvent(*consumeSeq, handled);
         return event;
     }
 
@@ -890,8 +927,8 @@
     /**
      * To be used together with "receiveEvent" to complete the consumption of an event.
      */
-    void finishEvent(uint32_t consumeSeq) {
-        const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true);
+    void finishEvent(uint32_t consumeSeq, bool handled = true) {
+        const status_t status = mConsumer->sendFinishedSignal(consumeSeq, handled);
         ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
     }
 
@@ -1202,8 +1239,8 @@
 
     void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
 
-    KeyEvent* consumeKey() {
-        InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+    KeyEvent* consumeKey(bool handled = true) {
+        InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
         if (event == nullptr) {
             ADD_FAILURE() << "Consume failed : no event";
             return nullptr;
@@ -1342,11 +1379,11 @@
         mInputReceiver->sendTimeline(inputEventId, timeline);
     }
 
-    InputEvent* consume(std::chrono::milliseconds timeout) {
+    InputEvent* consume(std::chrono::milliseconds timeout, bool handled = true) {
         if (mInputReceiver == nullptr) {
             return nullptr;
         }
-        return mInputReceiver->consume(timeout);
+        return mInputReceiver->consume(timeout, handled);
     }
 
     MotionEvent* consumeMotion() {
@@ -2160,6 +2197,69 @@
 }
 
 /**
+ * Same as the above 'TwoPointerCancelInconsistentPolicy' test, but for hovers.
+ * The policy typically sets POLICY_FLAG_PASS_TO_USER to the events. But when the display is not
+ * interactive, it might stop sending this flag.
+ * We've already ensured the consistency of the touch event in this case, and we should also ensure
+ * the consistency of the hover event in this case.
+ *
+ * Test procedure:
+ * HOVER_ENTER -> HOVER_MOVE -> (stop sending POLICY_FLAG_PASS_TO_USER) -> HOVER_EXIT
+ * HOVER_ENTER -> HOVER_MOVE -> HOVER_EXIT
+ *
+ * We expect to receive two full streams of hover events.
+ */
+TEST_F(InputDispatcherTest, HoverEventInconsistentPolicy) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    sp<FakeWindowHandle> window =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+    window->setFrame(Rect(0, 0, 300, 300));
+
+    mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                                      .policyFlags(DEFAULT_POLICY_FLAGS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(101))
+                                      .build());
+    window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+                                      .policyFlags(DEFAULT_POLICY_FLAGS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102))
+                                      .build());
+    window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+
+    // Send hover exit without the default policy flags.
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+                                      .policyFlags(0)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102))
+                                      .build());
+
+    window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+    // Send a simple hover event stream, ensure dispatcher not crashed and window can receive
+    // right event.
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                                      .policyFlags(DEFAULT_POLICY_FLAGS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(200).y(201))
+                                      .build());
+    window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+                                      .policyFlags(DEFAULT_POLICY_FLAGS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(201).y(202))
+                                      .build());
+    window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+                                      .policyFlags(DEFAULT_POLICY_FLAGS)
+                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(201).y(202))
+                                      .build());
+    window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+}
+
+/**
  * Two windows: a window on the left and a window on the right.
  * Mouse is hovered from the right window into the left window.
  * Next, we tap on the left window, where the cursor was last seen.
@@ -6464,6 +6564,213 @@
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
 }
 
+class InputDispatcherFallbackKeyTest : public InputDispatcherTest {
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApp;
+    sp<FakeWindowHandle> mWindow;
+
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        mApp = std::make_shared<FakeApplicationHandle>();
+
+        mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+        mWindow->setFrame(Rect(0, 0, 100, 100));
+
+        mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
+        setFocusedWindow(mWindow);
+        ASSERT_NO_FATAL_FAILURE(mWindow->consumeFocusEvent(/*hasFocus=*/true));
+    }
+
+    void setFallback(int32_t keycode) {
+        mFakePolicy->setUnhandledKeyHandler([keycode](const KeyEvent& event) {
+            return KeyEventBuilder(event).keyCode(keycode).build();
+        });
+    }
+
+    void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) {
+        KeyEvent* event = mWindow->consumeKey(handled);
+        ASSERT_NE(event, nullptr) << "Did not receive key event";
+        ASSERT_THAT(*event, matcher);
+    }
+};
+
+TEST_F(InputDispatcherFallbackKeyTest, PolicyNotNotifiedForHandledKey) {
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+    consumeKey(/*handled=*/true, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, PolicyNotifiedForUnhandledKey) {
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+    consumeKey(/*handled=*/false, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, NoFallbackRequestedByPolicy) {
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+    // Do not handle this key event.
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+
+    // Since the policy did not request any fallback to be generated, ensure there are no events.
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, FallbackDispatchForUnhandledKey) {
+    setFallback(AKEYCODE_B);
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+    // Do not handle this key event.
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+
+    // Since the key was not handled, ensure the fallback event was dispatched instead.
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+    // Release the original key, and ensure the fallback key is also released.
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+    mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, AppHandlesPreviouslyUnhandledKey) {
+    setFallback(AKEYCODE_B);
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+    // Do not handle this key event, but handle the fallback.
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+    // Release the original key, and ensure the fallback key is also released.
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+    // But this time, the app handles the original key.
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    // Ensure the fallback key is canceled.
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+    mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, AppDoesNotHandleFallback) {
+    setFallback(AKEYCODE_B);
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+    // Do not handle this key event.
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    // App does not handle the fallback either, so ensure another fallback is not generated.
+    setFallback(AKEYCODE_C);
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+    // Release the original key, and ensure the fallback key is also released.
+    setFallback(AKEYCODE_B);
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+    mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, InconsistentPolicyCancelsFallback) {
+    setFallback(AKEYCODE_B);
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+    // Do not handle this key event, so fallback is generated.
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+    // Release the original key, but assume the policy is misbehaving and it
+    // generates an inconsistent fallback to the one from the DOWN event.
+    setFallback(AKEYCODE_C);
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    // Ensure the fallback key reported before as DOWN is canceled due to the inconsistency.
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+    mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) {
+    setFallback(AKEYCODE_B);
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+    // Do not handle this key event, so fallback is generated.
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+    // The original key is canceled.
+    mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD)
+                                   .keyCode(AKEYCODE_A)
+                                   .addFlag(AKEY_EVENT_FLAG_CANCELED)
+                                   .build());
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A),
+                     WithFlags(AKEY_EVENT_FLAG_CANCELED)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    // Ensure the fallback key is also canceled due to the original key being canceled.
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+    mWindow->assertNoEvents();
+}
+
 class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
 protected:
     static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
@@ -7529,6 +7836,8 @@
     mWindow->consumeFocusEvent(false);
 
     KeyEvent event;
+    static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms;
+    mFakePolicy->setStaleEventTimeout(STALE_EVENT_TIMEOUT);
     const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC) -
             std::chrono::nanoseconds(STALE_EVENT_TIMEOUT).count();
 
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index dac4ea0..787444c 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -80,9 +80,15 @@
 }
 
 std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, int32_t value) {
+    nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+    return process(when, type, code, value);
+}
+
+std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, int32_t type, int32_t code,
+                                                   int32_t value) {
     RawEvent event;
-    event.when = systemTime(SYSTEM_TIME_MONOTONIC);
-    event.readTime = event.when;
+    event.when = when;
+    event.readTime = when;
     event.deviceId = mMapper->getDeviceContext().getEventHubId();
     event.type = type;
     event.code = code;
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index c2ac258..3f9061f 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -52,6 +52,7 @@
     void setKeyCodeState(KeyState state, std::set<int> keyCodes);
 
     std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value);
+    std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value);
 
     MockEventHubInterface mMockEventHub;
     std::shared_ptr<FakePointerController> mFakePointerController;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 64ae9e8..5044b3e 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -37,6 +37,7 @@
 #include <UinputDevice.h>
 #include <VibratorInputMapper.h>
 #include <android-base/thread_annotations.h>
+#include <com_android_input_flags.h>
 #include <ftl/enum.h>
 #include <gtest/gtest.h>
 #include <gui/constants.h>
@@ -99,6 +100,8 @@
 // Maximum smoothing time delta so that we don't generate events too far into the future.
 constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
 
+namespace input_flags = com::android::input::flags;
+
 template<typename T>
 static inline T min(T a, T b) {
     return a < b ? a : b;
@@ -4097,9 +4100,9 @@
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 }
 
-// --- CursorInputMapperTest ---
+// --- CursorInputMapperTestBase ---
 
-class CursorInputMapperTest : public InputMapperTest {
+class CursorInputMapperTestBase : public InputMapperTest {
 protected:
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD;
 
@@ -4133,11 +4136,11 @@
     }
 };
 
-const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
+const int32_t CursorInputMapperTestBase::TRACKBALL_MOVEMENT_THRESHOLD = 6;
 
-void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_t originalX,
-                                               int32_t originalY, int32_t rotatedX,
-                                               int32_t rotatedY) {
+void CursorInputMapperTestBase::testMotionRotation(CursorInputMapper& mapper, int32_t originalX,
+                                                   int32_t originalY, int32_t rotatedX,
+                                                   int32_t rotatedY) {
     NotifyMotionArgs args;
 
     process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, originalX);
@@ -4151,6 +4154,16 @@
                                       float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f));
 }
 
+// --- CursorInputMapperTest ---
+
+class CursorInputMapperTest : public CursorInputMapperTestBase {
+protected:
+    void SetUp() override {
+        input_flags::enable_pointer_choreographer(false);
+        CursorInputMapperTestBase::SetUp();
+    }
+};
+
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
     addConfigurationProperty("cursor.mode", "pointer");
     CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
@@ -4180,6 +4193,7 @@
 
     // When the bounds are set, then there should be a valid motion range.
     mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
 
     InputDeviceInfo info2;
     mapper.populateDeviceInfo(info2);
@@ -4906,11 +4920,459 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
-// --- BluetoothCursorInputMapperTest ---
+// --- CursorInputMapperTestWithChoreographer ---
 
-class BluetoothCursorInputMapperTest : public CursorInputMapperTest {
+class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase {
 protected:
     void SetUp() override {
+        input_flags::enable_pointer_choreographer(true);
+        CursorInputMapperTestBase::SetUp();
+    }
+};
+
+TEST_F(CursorInputMapperTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) {
+    addConfigurationProperty("cursor.mode", "pointer");
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    InputDeviceInfo info;
+    mapper.populateDeviceInfo(info);
+
+    // Initially there may not be a valid motion range.
+    ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
+    ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+                                              AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
+
+    // When the viewport and the default pointer display ID is set, then there should be a valid
+    // motion range.
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+    mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+                                    /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    InputDeviceInfo info2;
+    mapper.populateDeviceInfo(info2);
+
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0,
+                                              DISPLAY_WIDTH - 1, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0,
+                                              DISPLAY_HEIGHT - 1, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
+                                              AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) {
+    addConfigurationProperty("cursor.mode", "pointer");
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+    prepareDisplay(ui::ROTATION_0);
+
+    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setPosition(100, 200);
+
+    NotifyMotionArgs motionArgs;
+    NotifyKeyArgs keyArgs;
+
+    // press BTN_LEFT, release BTN_LEFT
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+              motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+              motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    // press BTN_BACK, release BTN_BACK
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    // press BTN_SIDE, release BTN_SIDE
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    // press BTN_FORWARD, release BTN_FORWARD
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+    // press BTN_EXTRA, release BTN_EXTRA
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_NO_FATAL_FAILURE(
+            assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) {
+    addConfigurationProperty("cursor.mode", "pointer");
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+    prepareDisplay(ui::ROTATION_0);
+
+    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setPosition(100, 200);
+
+    NotifyMotionArgs args;
+
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+                                                0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) {
+    addConfigurationProperty("cursor.mode", "pointer");
+    const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
+                                               /*highThreshold=*/100.f, /*acceleration=*/10.f);
+    mFakePolicy->setVelocityControlParams(testParams);
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+    prepareDisplay(ui::ROTATION_0);
+
+    NotifyDeviceResetArgs resetArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
+
+    NotifyMotionArgs args;
+
+    // Move and verify scale is applied.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+    const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+    const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+    ASSERT_GT(relX, 10);
+    ASSERT_GT(relY, 20);
+
+    // Enable Pointer Capture
+    mFakePolicy->setPointerCapture(true);
+    configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
+    NotifyPointerCaptureChangedArgs captureArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
+    ASSERT_TRUE(captureArgs.request.enable);
+
+    // Move and verify scale is not applied.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+    const float relX2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+    const float relY2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+    ASSERT_EQ(10, relX2);
+    ASSERT_EQ(20, relY2);
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) {
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(ui::ROTATION_90);
+
+    // Set up the secondary display as the display on which the pointer should be shown.
+    // The InputDevice is not associated with any display.
+    prepareSecondaryDisplay();
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+    mFakePointerController->setPosition(100, 200);
+
+    // Ensure input events are generated without display ID and coords,
+    // because they will be decided later by PointerChoreographer.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE),
+                  WithCoords(0.0f, 0.0f))));
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) {
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(ui::ROTATION_90);
+
+    // Set up the secondary display as the display on which the pointer should be shown,
+    // and associate the InputDevice with the secondary display.
+    prepareSecondaryDisplay();
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+    mFakePointerController->setPosition(100, 200);
+
+    // Ensure input events are generated with associated display ID but not with coords,
+    // because the coords will be decided later by PointerChoreographer.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+                  WithCoords(0.0f, 0.0f))));
+}
+
+TEST_F(CursorInputMapperTestWithChoreographer,
+       ConfigureDisplayIdShouldGenerateEventWithMismatchedPointerDisplay) {
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    // Set up the default display as the display on which the pointer should be shown.
+    prepareDisplay(ui::ROTATION_90);
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+
+    // Associate the InputDevice with the secondary display.
+    prepareSecondaryDisplay();
+    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    // With PointerChoreographer enabled, there could be a PointerController for the associated
+    // display even if it is different from the pointer display. So the mapper should generate an
+    // event.
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+                  WithCoords(0.0f, 0.0f))));
+}
+
+// --- BluetoothCursorInputMapperTest ---
+
+class BluetoothCursorInputMapperTest : public CursorInputMapperTestBase {
+protected:
+    void SetUp() override {
+        input_flags::enable_pointer_choreographer(false);
         InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
 
         mFakePointerController = std::make_shared<FakePointerController>();
@@ -5006,6 +5468,122 @@
                   WithEventTime(expectedEventTime))));
 }
 
+// --- BluetoothCursorInputMapperTestWithChoreographer ---
+
+class BluetoothCursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase {
+protected:
+    void SetUp() override {
+        input_flags::enable_pointer_choreographer(true);
+        InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
+
+        mFakePointerController = std::make_shared<FakePointerController>();
+        mFakePolicy->setPointerController(mFakePointerController);
+    }
+};
+
+TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmoothening) {
+    addConfigurationProperty("cursor.mode", "pointer");
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(ui::ROTATION_0);
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    nsecs_t kernelEventTime = ARBITRARY_TIME;
+    nsecs_t expectedEventTime = ARBITRARY_TIME;
+    process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+    process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithEventTime(expectedEventTime))));
+
+    // Process several events that come in quick succession, according to their timestamps.
+    for (int i = 0; i < 3; i++) {
+        constexpr static nsecs_t delta = ms2ns(1);
+        static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA);
+        kernelEventTime += delta;
+        expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+        process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+        process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                      WithEventTime(expectedEventTime))));
+    }
+}
+
+TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningIsCapped) {
+    addConfigurationProperty("cursor.mode", "pointer");
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(ui::ROTATION_0);
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    nsecs_t expectedEventTime = ARBITRARY_TIME;
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithEventTime(expectedEventTime))));
+
+    // Process several events with the same timestamp from the kernel.
+    // Ensure that we do not generate events too far into the future.
+    constexpr static int32_t numEvents =
+            MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA;
+    for (int i = 0; i < numEvents; i++) {
+        expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+        process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+        process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                      WithEventTime(expectedEventTime))));
+    }
+
+    // By processing more events with the same timestamp, we should not generate events with a
+    // timestamp that is more than the specified max time delta from the timestamp at its injection.
+    const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA;
+    for (int i = 0; i < 3; i++) {
+        process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+        process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                      WithEventTime(cappedEventTime))));
+    }
+}
+
+TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningNotUsed) {
+    addConfigurationProperty("cursor.mode", "pointer");
+    CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+
+    // Set up the default display.
+    prepareDisplay(ui::ROTATION_0);
+    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+    configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
+
+    nsecs_t kernelEventTime = ARBITRARY_TIME;
+    nsecs_t expectedEventTime = ARBITRARY_TIME;
+    process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+    process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithEventTime(expectedEventTime))));
+
+    // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
+    // smoothening is not needed, its timestamp is not affected.
+    kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1);
+    expectedEventTime = kernelEventTime;
+
+    process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+    process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                  WithEventTime(expectedEventTime))));
+}
+
 // --- TouchInputMapperTest ---
 
 class TouchInputMapperTest : public InputMapperTest {
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index ca85558..e9c7bb4 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -106,6 +106,9 @@
         void setPreventingTouchpadTaps(bool prevent) override { mPreventingTouchpadTaps = prevent; }
         bool isPreventingTouchpadTaps() override { return mPreventingTouchpadTaps; }
 
+        void setLastKeyDownTimestamp(nsecs_t when) override { mLastKeyDownTimestamp = when; };
+        nsecs_t getLastKeyDownTimestamp() override { return mLastKeyDownTimestamp; };
+
     private:
         int32_t mGlobalMetaState;
         bool mUpdateGlobalMetaStateWasCalled;
@@ -113,6 +116,7 @@
         std::optional<nsecs_t> mRequestedTimeout;
         std::vector<InputDeviceInfo> mExternalStylusDevices;
         bool mPreventingTouchpadTaps{false};
+        nsecs_t mLastKeyDownTimestamp;
     } mFakeContext;
 
     friend class InputReaderTest;
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 05823cd..7394913 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -77,6 +77,9 @@
     MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override));
     MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override));
 
+    MOCK_METHOD(void, setLastKeyDownTimestamp, (nsecs_t when));
+    MOCK_METHOD(nsecs_t, getLastKeyDownTimestamp, ());
+
 private:
     int32_t mGeneration = 0;
 };
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 48f5673..2ef7999 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -26,6 +26,7 @@
 namespace android {
 
 using testing::_;
+using testing::Args;
 using testing::DoAll;
 using testing::Return;
 using testing::SetArgPointee;
@@ -158,4 +159,18 @@
     testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false);
 }
 
+TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) {
+    nsecs_t when = ARBITRARY_TIME;
+    std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT};
+    EXPECT_CALL(mMockInputReaderContext, setLastKeyDownTimestamp)
+            .With(Args<0>(when))
+            .Times(keyCodes.size());
+    for (int32_t keyCode : keyCodes) {
+        process(when, EV_KEY, keyCode, 1);
+        process(when, EV_SYN, SYN_REPORT, 0);
+        process(when, EV_KEY, keyCode, 0);
+        process(when, EV_SYN, SYN_REPORT, 0);
+    }
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
new file mode 100644
index 0000000..9fa6cdd
--- /dev/null
+++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 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 "MultiTouchMotionAccumulator.h"
+#include "InputMapperTest.h"
+
+namespace android {
+
+class MultiTouchMotionAccumulatorTest : public InputMapperUnitTest {
+protected:
+    static constexpr size_t SLOT_COUNT = 8;
+
+    MultiTouchMotionAccumulator mMotionAccumulator;
+
+    void processMotionEvent(int32_t type, int32_t code, int32_t value) {
+        RawEvent event;
+        event.when = ARBITRARY_TIME;
+        event.readTime = READ_TIME;
+        event.deviceId = EVENTHUB_ID;
+        event.type = type;
+        event.code = code;
+        event.value = value;
+        mMotionAccumulator.process(&event);
+    }
+};
+
+TEST_F(MultiTouchMotionAccumulatorTest, ActiveSlotCountUsingSlotsProtocol) {
+    mMotionAccumulator.configure(*mDeviceContext, SLOT_COUNT, /*usingSlotsProtocol=*/true);
+    // We expect active slot count to match the touches being tracked
+    // first touch
+    processMotionEvent(EV_ABS, ABS_MT_SLOT, 0);
+    processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, 123);
+    processMotionEvent(EV_SYN, SYN_REPORT, 0);
+    ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount());
+
+    // second touch
+    processMotionEvent(EV_ABS, ABS_MT_SLOT, 1);
+    processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, 456);
+    processMotionEvent(EV_SYN, SYN_REPORT, 0);
+    ASSERT_EQ(2u, mMotionAccumulator.getActiveSlotsCount());
+
+    // second lifted
+    processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+    processMotionEvent(EV_SYN, SYN_REPORT, 0);
+    ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount());
+
+    // first lifted
+    processMotionEvent(EV_ABS, ABS_MT_SLOT, 0);
+    processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
+    processMotionEvent(EV_SYN, SYN_REPORT, 0);
+    ASSERT_EQ(0u, mMotionAccumulator.getActiveSlotsCount());
+}
+
+TEST_F(MultiTouchMotionAccumulatorTest, ActiveSlotCountNotUsingSlotsProtocol) {
+    mMotionAccumulator.configure(*mDeviceContext, SLOT_COUNT, /*usingSlotsProtocol=*/false);
+
+    // first touch
+    processMotionEvent(EV_ABS, ABS_MT_POSITION_X, 0);
+    processMotionEvent(EV_ABS, ABS_MT_POSITION_Y, 0);
+    processMotionEvent(EV_SYN, SYN_MT_REPORT, 0);
+    ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount());
+
+    // second touch
+    processMotionEvent(EV_ABS, ABS_MT_POSITION_X, 50);
+    processMotionEvent(EV_ABS, ABS_MT_POSITION_Y, 50);
+    processMotionEvent(EV_SYN, SYN_MT_REPORT, 0);
+    ASSERT_EQ(2u, mMotionAccumulator.getActiveSlotsCount());
+
+    // reset
+    mMotionAccumulator.finishSync();
+    ASSERT_EQ(0u, mMotionAccumulator.getActiveSlotsCount());
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 7237424..da2e205 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -19,16 +19,54 @@
 #include <gtest/gtest.h>
 #include <vector>
 
+#include "FakePointerController.h"
+#include "NotifyArgsBuilders.h"
+#include "TestEventMatchers.h"
 #include "TestInputListener.h"
 
 namespace android {
 
+using ControllerType = PointerControllerInterface::ControllerType;
+
+namespace {
+
 // Helpers to std::visit with lambdas.
 template <typename... V>
 struct Visitor : V... {};
 template <typename... V>
 Visitor(V...) -> Visitor<V...>;
 
+constexpr int32_t DEVICE_ID = 3;
+constexpr int32_t DISPLAY_ID = 5;
+constexpr int32_t ANOTHER_DISPLAY_ID = 10;
+
+const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+                                   .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
+                                   .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20);
+
+static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source,
+                                              int32_t associatedDisplayId) {
+    InputDeviceIdentifier identifier;
+
+    auto info = InputDeviceInfo();
+    info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias",
+                    /*isExternal=*/false, /*hasMic=*/false, associatedDisplayId);
+    info.addSource(source);
+    return info;
+}
+
+static std::vector<DisplayViewport> createViewports(std::vector<int32_t> displayIds) {
+    std::vector<DisplayViewport> viewports;
+    for (auto displayId : displayIds) {
+        DisplayViewport viewport;
+        viewport.displayId = displayId;
+        viewports.push_back(viewport);
+    }
+    return viewports;
+}
+
+} // namespace
+
 // --- PointerChoreographerTest ---
 
 class PointerChoreographerTest : public testing::Test, public PointerChoreographerPolicyInterface {
@@ -36,7 +74,54 @@
     TestInputListener mTestListener;
     PointerChoreographer mChoreographer{mTestListener, *this};
 
-    std::shared_ptr<PointerControllerInterface> createPointerController() { return {}; }
+    std::shared_ptr<FakePointerController> assertPointerControllerCreated(
+            ControllerType expectedType) {
+        EXPECT_TRUE(mLastCreatedController) << "No PointerController was created";
+        auto [type, controller] = std::move(*mLastCreatedController);
+        EXPECT_EQ(expectedType, type);
+        mLastCreatedController.reset();
+        return controller;
+    }
+
+    void assertPointerControllerNotCreated() { ASSERT_EQ(std::nullopt, mLastCreatedController); }
+
+    void assertPointerControllerRemoved(const std::shared_ptr<FakePointerController>& pc) {
+        // Ensure that the code under test is not holding onto this PointerController.
+        // While the policy initially creates the PointerControllers, the PointerChoreographer is
+        // expected to manage their lifecycles. Although we may not want to strictly enforce how
+        // the object is managed, in this case, we need to have a way of ensuring that the
+        // corresponding graphical resources have been released by the PointerController, and the
+        // simplest way of checking for that is to just make sure that the PointerControllers
+        // themselves are released by Choreographer when no longer in use. This check is ensuring
+        // that the reference retained by the test is the last one.
+        ASSERT_EQ(1, pc.use_count()) << "Expected PointerChoreographer to release all references "
+                                        "to this PointerController";
+    }
+
+    void assertPointerDisplayIdNotified(int32_t displayId) {
+        ASSERT_EQ(displayId, mPointerDisplayIdNotified);
+        mPointerDisplayIdNotified.reset();
+    }
+
+    void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); }
+
+private:
+    std::optional<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
+            mLastCreatedController;
+    std::optional<int32_t> mPointerDisplayIdNotified;
+
+    std::shared_ptr<PointerControllerInterface> createPointerController(
+            ControllerType type) override {
+        EXPECT_FALSE(mLastCreatedController.has_value())
+                << "More than one PointerController created at a time";
+        std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
+        mLastCreatedController = {type, pc};
+        return pc;
+    }
+
+    void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override {
+        mPointerDisplayIdNotified = displayId;
+    }
 };
 
 TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
@@ -86,4 +171,218 @@
     }
 }
 
+TEST_F(PointerChoreographerTest, WhenMouseIsJustAddedDoesNotCreatePointerController) {
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenMouseEventOccursCreatesPointerController) {
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    assertPointerControllerCreated(ControllerType::MOUSE);
+}
+
+TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) {
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+
+    // Remove the mouse.
+    mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+    assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenKeyboardIsAddedDoesNotCreatePointerController) {
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE)}});
+    assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) {
+    // Just adding a viewport or device should not create a PointerController.
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+    assertPointerControllerNotCreated();
+
+    // After the mouse emits event, PointerController will be created and viewport will be set.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) {
+    // Without viewport information, PointerController will be created by a mouse event
+    // but viewport won't be set.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+
+    // After Choreographer gets viewport, PointerController should also have viewport.
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) {
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+    // For a mouse event without a target display, default viewport should be set for
+    // the PointerController.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest,
+       WhenDefaultMouseDisplayChangesSetsDefaultMouseViewportForPointerController) {
+    // Set one display as a default mouse display and emit mouse event to create PointerController.
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, firstDisplayPc->getDisplayId());
+
+    // Change default mouse display. Existing PointerController should be removed.
+    mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+    assertPointerControllerRemoved(firstDisplayPc);
+    assertPointerControllerNotCreated();
+
+    // New PointerController for the new default display will be created by the motion event.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(ANOTHER_DISPLAY_ID, secondDisplayPc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) {
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    assertPointerControllerCreated(ControllerType::MOUSE);
+
+    assertPointerDisplayIdNotified(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) {
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    assertPointerControllerCreated(ControllerType::MOUSE);
+    assertPointerDisplayIdNotNotified();
+
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    assertPointerDisplayIdNotified(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdChanged) {
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    assertPointerDisplayIdNotified(DISPLAY_ID);
+
+    mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+    assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+    assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointerDisplayIdChanged) {
+    // Add two viewports.
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+
+    // Set one viewport as a default mouse display ID.
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+    assertPointerDisplayIdNotified(DISPLAY_ID);
+
+    // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified
+    // before a mouse event.
+    mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+    assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+    assertPointerControllerRemoved(firstDisplayPc);
+
+    // After a mouse event, pointer display ID will be notified with new default mouse display.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    assertPointerControllerCreated(ControllerType::MOUSE);
+    assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID);
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index ee6ff53..a0a0f5a 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -464,9 +464,32 @@
     return WithPointersMatcher(pointers);
 }
 
-MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
-    *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
-    return arg.keyCode == keyCode;
+/// Key code
+class WithKeyCodeMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithKeyCodeMatcher(int32_t keyCode) : mKeyCode(keyCode) {}
+
+    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+        return mKeyCode == args.keyCode;
+    }
+
+    bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+        return mKeyCode == event.getKeyCode();
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with key code " << KeyEvent::getLabel(mKeyCode);
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong key code"; }
+
+private:
+    const int32_t mKeyCode;
+};
+
+inline WithKeyCodeMatcher WithKeyCode(int32_t keyCode) {
+    return WithKeyCodeMatcher(keyCode);
 }
 
 MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index bdedfdf..2f84497 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -275,6 +275,7 @@
     void clearSpots() override {}
     int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); }
     void setDisplayViewport(const DisplayViewport& displayViewport) override {}
+    std::string dump() override { return ""; }
 };
 
 class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
@@ -309,6 +310,10 @@
     void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
     void notifyStylusGestureStarted(int32_t, nsecs_t) {}
     bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); }
+    std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
+            int32_t associatedDisplayId) override {
+        return {};
+    }
 };
 
 class FuzzInputListener : public virtual InputListenerInterface {
@@ -360,6 +365,12 @@
 
     void setPreventingTouchpadTaps(bool prevent) {}
     bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); };
+
+    void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; };
+    nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; };
+
+private:
+    nsecs_t mLastKeyDownTimestamp;
 };
 
 template <class Fdp>
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 6807c8e..a44e4be 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -148,7 +148,7 @@
                 getOverlaySupport, (), (const, override));
     MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
     MOCK_METHOD(status_t, notifyExpectedPresentIfRequired,
-                (PhysicalDisplayId, nsecs_t, int32_t, int32_t));
+                (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index fb6089d..1d9f9ce 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -78,6 +78,59 @@
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 namespace hal = android::hardware::graphics::composer::hal;
 
+namespace {
+bool isFrameIntervalOnCadence(android::TimePoint expectedPresentTime,
+                              android::TimePoint lastExpectedPresentTimestamp,
+                              android::Fps lastFrameInterval, android::Period timeout,
+                              android::Duration threshold) {
+    if (lastFrameInterval.getPeriodNsecs() == 0) {
+        return false;
+    }
+
+    const auto expectedPresentTimeDeltaNs =
+            expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns();
+
+    if (expectedPresentTimeDeltaNs > timeout.ns()) {
+        return false;
+    }
+
+    const auto expectedPresentPeriods = static_cast<nsecs_t>(
+            std::round(static_cast<float>(expectedPresentTimeDeltaNs) /
+                       static_cast<float>(lastFrameInterval.getPeriodNsecs())));
+    const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods;
+    const auto calculatedExpectedPresentTimeNs =
+            lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs;
+    const auto presentTimeDelta =
+            std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs);
+    return presentTimeDelta < threshold.ns();
+}
+
+bool isExpectedPresentWithinTimeout(android::TimePoint expectedPresentTime,
+                                    android::TimePoint lastExpectedPresentTimestamp,
+                                    std::optional<android::Period> timeoutOpt,
+                                    android::Duration threshold) {
+    if (!timeoutOpt) {
+        // Always within timeout if timeoutOpt is absent and don't send hint
+        // for the timeout
+        return true;
+    }
+
+    if (timeoutOpt->ns() == 0) {
+        // Always outside timeout if timeoutOpt is 0 and always send
+        // the hint for the timeout.
+        return false;
+    }
+
+    if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) {
+        return true;
+    }
+
+    // Check if within the threshold as it can be just outside the timeout
+    return std::abs(expectedPresentTime.ns() -
+                    (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns();
+}
+} // namespace
+
 namespace android {
 
 HWComposer::~HWComposer() = default;
@@ -485,7 +538,12 @@
     }();
 
     displayData.validateWasSkipped = false;
-    displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+    {
+        std::scoped_lock lock{displayData.expectedPresentLock};
+        displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime);
+        // TODO(b/296636176) Update displayData.lastFrameInterval for present display commands
+    }
+
     if (canSkipValidate) {
         sp<Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
@@ -879,21 +937,44 @@
 }
 
 status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
-                                                     nsecs_t expectedPresentTime,
-                                                     int32_t frameIntervalNs, int32_t timeoutNs) {
+                                                     Period vsyncPeriod,
+                                                     TimePoint expectedPresentTime,
+                                                     Fps frameInterval,
+                                                     std::optional<Period> timeoutOpt) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
-
     auto& displayData = mDisplayData[displayId];
-    if (expectedPresentTime >= displayData.lastExpectedPresentTimestamp &&
-        expectedPresentTime < displayData.lastExpectedPresentTimestamp + timeoutNs) {
-        return NO_ERROR;
-    }
+    {
+        std::scoped_lock lock{displayData.expectedPresentLock};
+        const auto lastExpectedPresentTimestamp = displayData.lastExpectedPresentTimestamp;
+        const auto lastFrameInterval = displayData.lastFrameInterval;
+        displayData.lastFrameInterval = frameInterval;
+        const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2);
 
-    displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+        const constexpr nsecs_t kOneSecondNs =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+        const bool frameIntervalIsOnCadence =
+                isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp,
+                                         lastFrameInterval,
+                                         Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0
+                                                                ? timeoutOpt->ns()
+                                                                : kOneSecondNs),
+                                         threshold);
+
+        const bool expectedPresentWithinTimeout =
+                isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
+                                               timeoutOpt, threshold);
+
+        if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+            return NO_ERROR;
+        }
+
+        displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+    }
     ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__,
-                  expectedPresentTime, frameIntervalNs);
+                  expectedPresentTime, frameInterval.getPeriodNsecs());
     const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(),
-                                                        expectedPresentTime, frameIntervalNs);
+                                                        expectedPresentTime.ns(),
+                                                        frameInterval.getPeriodNsecs());
 
     if (error != hal::Error::NONE) {
         ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str());
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 726a8ea..51e9319 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -301,9 +301,10 @@
             aidl::android::hardware::graphics::common::HdrConversionStrategy,
             aidl::android::hardware::graphics::common::Hdr*) = 0;
     virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
-    virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime,
-                                                     int32_t frameIntervalNs,
-                                                     int32_t timeoutNs) = 0;
+    virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
+                                                     TimePoint expectedPresentTime,
+                                                     Fps frameInterval,
+                                                     std::optional<Period> timeoutOpt) = 0;
 };
 
 static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -462,8 +463,9 @@
             aidl::android::hardware::graphics::common::HdrConversionStrategy,
             aidl::android::hardware::graphics::common::Hdr*) override;
     status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
-    status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime,
-                                             int32_t frameIntervalNs, int32_t timeoutNs) override;
+    status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
+                                             TimePoint expectedPresentTime, Fps frameInterval,
+                                             std::optional<Period> timeoutOpt) override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
@@ -497,7 +499,10 @@
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
         nsecs_t lastPresentTimestamp = 0;
 
-        nsecs_t lastExpectedPresentTimestamp = 0;
+        std::mutex expectedPresentLock;
+        TimePoint lastExpectedPresentTimestamp GUARDED_BY(expectedPresentLock) =
+                TimePoint::fromNs(0);
+        Fps lastFrameInterval GUARDED_BY(expectedPresentLock) = Fps::fromValue(0);
 
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
 
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
index a8f05bb..6d0dbc7 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -60,10 +60,6 @@
     return getter();
 }
 
-void dumpFlag(std::string& result, const char* name, std::function<bool()> getter) {
-    base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false");
-}
-
 } // namespace
 
 const FlagManager& FlagManager::getInstance() {
@@ -90,20 +86,43 @@
     mBootCompleted = true;
 }
 
+void FlagManager::dumpFlag(std::string& result, bool readonly, const char* name,
+                           std::function<bool()> getter) const {
+    if (readonly || mBootCompleted) {
+        base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false");
+    } else {
+        base::StringAppendF(&result, "%s: in progress (still booting)\n", name);
+    }
+}
+
 void FlagManager::dump(std::string& result) const {
-#define DUMP_FLAG(name) dumpFlag(result, #name, std::bind(&FlagManager::name, this))
+#define DUMP_FLAG_INTERVAL(name, readonly) \
+    dumpFlag(result, (readonly), #name, std::bind(&FlagManager::name, this))
+#define DUMP_SERVER_FLAG(name) DUMP_FLAG_INTERVAL(name, false)
+#define DUMP_READ_ONLY_FLAG(name) DUMP_FLAG_INTERVAL(name, true)
 
     base::StringAppendF(&result, "FlagManager values: \n");
-    DUMP_FLAG(use_adpf_cpu_hint);
-    DUMP_FLAG(use_skia_tracing);
-    DUMP_FLAG(connected_display);
-    DUMP_FLAG(dont_skip_on_early);
-    DUMP_FLAG(enable_small_area_detection);
-    DUMP_FLAG(misc1);
-    DUMP_FLAG(late_boot_misc2);
-    DUMP_FLAG(vrr_config);
 
-#undef DUMP_FLAG
+    /// Legacy server flags ///
+    DUMP_SERVER_FLAG(use_adpf_cpu_hint);
+    DUMP_SERVER_FLAG(use_skia_tracing);
+
+    /// Trunk stable server flags ///
+    DUMP_SERVER_FLAG(late_boot_misc2);
+    DUMP_SERVER_FLAG(dont_skip_on_early);
+
+    /// Trunk stable readonly flags ///
+    DUMP_READ_ONLY_FLAG(connected_display);
+    DUMP_READ_ONLY_FLAG(enable_small_area_detection);
+    DUMP_READ_ONLY_FLAG(misc1);
+    DUMP_READ_ONLY_FLAG(vrr_config);
+    DUMP_READ_ONLY_FLAG(hotplug2);
+    DUMP_READ_ONLY_FLAG(hdcp_level_hal);
+    DUMP_READ_ONLY_FLAG(multithreaded_present);
+
+#undef DUMP_READ_ONLY_FLAG
+#undef DUMP_SERVER_FLAG
+#undef DUMP_FLAG_INTERVAL
 }
 
 std::optional<bool> FlagManager::getBoolProperty(const char* property) const {
@@ -165,6 +184,9 @@
 FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
 FLAG_MANAGER_READ_ONLY_FLAG(misc1, "")
 FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config")
+FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
+FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "")
+FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
 
 /// Trunk stable server flags ///
 FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
index 5a353bb..cefce9b 100644
--- a/services/surfaceflinger/FlagManager.h
+++ b/services/surfaceflinger/FlagManager.h
@@ -46,15 +46,18 @@
     bool use_adpf_cpu_hint() const;
     bool use_skia_tracing() const;
 
+    /// Trunk stable server flags ///
+    bool late_boot_misc2() const;
+    bool dont_skip_on_early() const;
+
     /// Trunk stable readonly flags ///
     bool connected_display() const;
     bool enable_small_area_detection() const;
     bool misc1() const;
     bool vrr_config() const;
-
-    /// Trunk stable server flags ///
-    bool late_boot_misc2() const;
-    bool dont_skip_on_early() const;
+    bool hotplug2() const;
+    bool hdcp_level_hal() const;
+    bool multithreaded_present() const;
 
 protected:
     // overridden for unit tests
@@ -66,6 +69,9 @@
 
     FlagManager(const FlagManager&) = delete;
 
+    void dumpFlag(std::string& result, bool readonly, const char* name,
+                  std::function<bool()> getter) const;
+
     std::atomic_bool mBootCompleted = false;
     std::atomic_bool mUnitTestMode = false;
 
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 7a85da0..38974a2 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -332,6 +332,14 @@
     return geomBufferSize.toFloatRect();
 }
 
+bool LayerSnapshot::isFrontBuffered() const {
+    if (!externalTexture) {
+        return false;
+    }
+
+    return externalTexture->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
+}
+
 Hwc2::IComposerClient::BlendMode LayerSnapshot::getBlendMode(
         const RequestedLayerState& requested) const {
     auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 4fd6495..73ee22f 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -143,6 +143,7 @@
     std::string getIsVisibleReason() const;
     bool hasInputInfo() const;
     FloatRect sourceBounds() const;
+    bool isFrontBuffered() const;
     Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const;
     friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj);
     void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 700baa2..66ea15c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -4024,10 +4024,10 @@
     return getAlpha() > 0.0f || hasBlur();
 }
 
-void Layer::onPostComposition(const DisplayDevice* display,
-                              const std::shared_ptr<FenceTime>& glDoneFence,
-                              const std::shared_ptr<FenceTime>& presentFence,
-                              const CompositorTiming& compositorTiming) {
+void Layer::onCompositionPresented(const DisplayDevice* display,
+                                   const std::shared_ptr<FenceTime>& glDoneFence,
+                                   const std::shared_ptr<FenceTime>& presentFence,
+                                   const CompositorTiming& compositorTiming) {
     // mFrameLatencyNeeded is true when a new frame was latched for the
     // composition.
     if (!mBufferInfo.mFrameLatencyNeeded) return;
@@ -4230,6 +4230,14 @@
     return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace;
 }
 
+bool Layer::isFrontBuffered() const {
+    if (mBufferInfo.mBuffer == nullptr) {
+        return false;
+    }
+
+    return mBufferInfo.mBuffer->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
+}
+
 ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) {
     ui::Dataspace updatedDataspace = dataspace;
     // translate legacy dataspaces to modern dataspaces
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index dd91adc..f715910 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -342,6 +342,8 @@
     //
     ui::Dataspace getDataSpace() const;
 
+    virtual bool isFrontBuffered() const;
+
     virtual sp<LayerFE> getCompositionEngineLayerFE() const;
     virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
     sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
@@ -433,13 +435,10 @@
     void updateCloneBufferInfo();
     uint64_t mPreviousFrameNumber = 0;
 
-    /*
-     * called after composition.
-     * returns true if the layer latched a new buffer this frame.
-     */
-    void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/,
-                           const std::shared_ptr<FenceTime>& /*presentFence*/,
-                           const CompositorTiming&);
+    void onCompositionPresented(const DisplayDevice*,
+                                const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+                                const std::shared_ptr<FenceTime>& /*presentFence*/,
+                                const CompositorTiming&);
 
     // If a buffer was replaced this frame, release the former buffer
     void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/);
@@ -915,14 +914,13 @@
     void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now);
     void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now);
     auto getLayerProps() const {
-        return scheduler::LayerProps{
-                .visible = isVisible(),
-                .bounds = getBounds(),
-                .transform = getTransform(),
-                .setFrameRateVote = getFrameRateForLayerTree(),
-                .frameRateSelectionPriority = getFrameRateSelectionPriority(),
-                .isSmallDirty = mSmallDirty,
-        };
+        return scheduler::LayerProps{.visible = isVisible(),
+                                     .bounds = getBounds(),
+                                     .transform = getTransform(),
+                                     .setFrameRateVote = getFrameRateForLayerTree(),
+                                     .frameRateSelectionPriority = getFrameRateSelectionPriority(),
+                                     .isSmallDirty = mSmallDirty,
+                                     .isFrontBuffered = isFrontBuffered()};
     };
     bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
     void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 21bbb08..8fc9cba 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -51,6 +51,11 @@
         return true;
     }
 
+    // Make all front buffered layers active
+    if (FlagManager::getInstance().vrr_config() && info.isFrontBuffered() && info.isVisible()) {
+        return true;
+    }
+
     return info.isVisible() && info.getLastUpdatedTime() >= threshold;
 }
 
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 54e9022..8d18769 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -334,6 +334,14 @@
         return votes;
     }
 
+    // Vote for max refresh rate whenever we're front-buffered.
+    if (FlagManager::getInstance().vrr_config() && isFrontBuffered()) {
+        ATRACE_FORMAT_INSTANT("front buffered");
+        ALOGV("%s is front-buffered", mName.c_str());
+        votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
+        return votes;
+    }
+
     const LayerInfo::Frequent frequent = isFrequent(now);
     mIsFrequencyConclusive = frequent.isConclusive;
     if (!frequent.isFrequent) {
@@ -394,6 +402,10 @@
     return mLayerProps->frameRateSelectionPriority;
 }
 
+bool LayerInfo::isFrontBuffered() const {
+    return mLayerProps->isFrontBuffered;
+}
+
 FloatRect LayerInfo::getBounds() const {
     return mLayerProps->bounds;
 }
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 7d3cffa..03ab0df 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -200,6 +200,7 @@
     FrameRate getSetFrameRateVote() const;
     bool isVisible() const;
     int32_t getFrameRateSelectionPriority() const;
+    bool isFrontBuffered() const;
     FloatRect getBounds() const;
     ui::Transform getTransform() const;
 
@@ -360,6 +361,7 @@
     LayerInfo::FrameRate setFrameRateVote;
     int32_t frameRateSelectionPriority = -1;
     bool isSmallDirty = false;
+    bool isFrontBuffered = false;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1a8713d..56a4ae2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -67,11 +67,12 @@
 namespace android::scheduler {
 
 Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features,
-                     sp<VsyncModulator> modulatorPtr)
+                     sp<VsyncModulator> modulatorPtr, IVsyncTrackerCallback& vsyncTrackerCallback)
       : impl::MessageQueue(compositor),
         mFeatures(features),
         mVsyncModulator(std::move(modulatorPtr)),
-        mSchedulerCallback(callback) {}
+        mSchedulerCallback(callback),
+        mVsyncTrackerCallback(vsyncTrackerCallback) {}
 
 Scheduler::~Scheduler() {
     // MessageQueue depends on VsyncSchedule, so first destroy it.
@@ -116,10 +117,10 @@
 }
 
 void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
-    auto schedulePtr = std::make_shared<VsyncSchedule>(displayId, mFeatures,
-                                                       [this](PhysicalDisplayId id, bool enable) {
-                                                           onHardwareVsyncRequest(id, enable);
-                                                       });
+    auto schedulePtr = std::make_shared<VsyncSchedule>(
+            displayId, mFeatures,
+            [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); },
+            mVsyncTrackerCallback);
 
     registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr));
 }
@@ -562,7 +563,19 @@
     ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
           to_string(mode.modePtr->getVsyncRate()).c_str());
 
-    display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
+    display.schedulePtr->getTracker().setDisplayModeData(
+            {.renderRate = renderFrameRate,
+             .notifyExpectedPresentTimeoutOpt = getNotifyExpectedPresentTimeout(mode)});
+}
+
+std::optional<Period> Scheduler::getNotifyExpectedPresentTimeout(const FrameRateMode& mode) {
+    if (mode.modePtr->getVrrConfig() && mode.modePtr->getVrrConfig()->notifyExpectedPresentConfig) {
+        return Period::fromNs(
+                mode.modePtr->getVrrConfig()
+                        ->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs);
+    } else {
+        return std::nullopt;
+    }
 }
 
 void Scheduler::resync() {
@@ -1165,7 +1178,7 @@
     }
 }
 
-bool Scheduler::onPostComposition(nsecs_t presentTime) {
+bool Scheduler::onCompositionPresented(nsecs_t presentTime) {
     std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
     if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
         if (presentTime < mLastVsyncPeriodChangeTimeline->refreshTimeNanos) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index b0520a6..a02180a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -102,7 +102,8 @@
     using Impl = android::impl::MessageQueue;
 
 public:
-    Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>);
+    Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>,
+              IVsyncTrackerCallback&);
     virtual ~Scheduler();
 
     void startTimers();
@@ -281,8 +282,8 @@
     // Notifies the scheduler about a refresh rate timeline change.
     void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
 
-    // Notifies the scheduler post composition. Returns if recomposite is needed.
-    bool onPostComposition(nsecs_t presentTime);
+    // Notifies the scheduler once the composition is presented. Returns if recomposite is needed.
+    bool onCompositionPresented(nsecs_t presentTime);
 
     // Notifies the scheduler when the display size has changed. Called from SF's main thread
     void onActiveDisplayAreaChanged(uint32_t displayArea);
@@ -429,6 +430,9 @@
     Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
     void resync() override EXCLUDES(mDisplayLock);
 
+    std::optional<Period> getNotifyExpectedPresentTimeout(const FrameRateMode&)
+            REQUIRES(mDisplayLock);
+
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
         sp<EventThreadConnection> connection;
@@ -462,6 +466,8 @@
 
     ISchedulerCallback& mSchedulerCallback;
 
+    IVsyncTrackerCallback& mVsyncTrackerCallback;
+
     // mDisplayLock may be locked while under mPolicyLock.
     mutable std::mutex mPolicyLock;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 186a6bc..3e7ec49 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -38,19 +38,16 @@
 
 namespace {
 
-nsecs_t getExpectedCallbackTime(nsecs_t now, nsecs_t nextVsyncTime,
+nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
                                 const VSyncDispatch::ScheduleTiming& timing) {
-    const auto expectedCallbackTime = nextVsyncTime - timing.readyDuration - timing.workDuration;
-    const auto baseTime =
-            FlagManager::getInstance().dont_skip_on_early() ? now : expectedCallbackTime;
-    return std::max(baseTime, expectedCallbackTime);
+    return nextVsyncTime - timing.readyDuration - timing.workDuration;
 }
 
 nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
                                 const VSyncDispatch::ScheduleTiming& timing) {
     const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
             std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
-    return getExpectedCallbackTime(now, nextVsyncTime, timing);
+    return getExpectedCallbackTime(nextVsyncTime, timing);
 }
 
 } // namespace
@@ -106,21 +103,23 @@
             mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
     if (FlagManager::getInstance().dont_skip_on_early()) {
         if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
-            return getExpectedCallbackTime(now, mArmedInfo->mActualVsyncTime, timing);
+            nextVsyncTime = mArmedInfo->mActualVsyncTime;
+        } else {
+            nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
         }
+        nextWakeupTime = std::max(now, nextVsyncTime - timing.workDuration - timing.readyDuration);
     } else {
         if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
-            return getExpectedCallbackTime(now, nextVsyncTime, timing);
+            return getExpectedCallbackTime(nextVsyncTime, timing);
         }
+        nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
+        nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
     }
 
-    nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
-    nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
-
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mScheduleTiming = timing;
     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
-    return getExpectedCallbackTime(now, nextVsyncTime, timing);
+    return nextWakeupTime;
 }
 
 void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index e969fdc..57aa010 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -35,6 +35,7 @@
 #include <gui/TraceUtils.h>
 #include <utils/Log.h>
 
+#include "FlagManager.h"
 #include "RefreshRateSelector.h"
 #include "VSyncPredictor.h"
 
@@ -47,12 +48,14 @@
 VSyncPredictor::~VSyncPredictor() = default;
 
 VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize,
-                               size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
+                               size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent,
+                               IVsyncTrackerCallback& callback)
       : mId(id),
         mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
         kHistorySize(historySize),
         kMinimumSamplesForPrediction(minimumSamplesForPrediction),
         kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
+        mVsyncTrackerCallback(callback),
         mIdealPeriod(idealPeriod) {
     resetModel();
 }
@@ -275,11 +278,11 @@
     mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
 
     const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
-        if (!mRenderRate) return 0;
+        if (!mDisplayModeDataOpt) return 0;
 
         const auto divisor =
                 RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod),
-                                                         *mRenderRate);
+                                                         mDisplayModeDataOpt->renderRate);
         if (divisor <= 1) return 0;
 
         const int mod = mLastVsyncSequence->seq % divisor;
@@ -289,12 +292,29 @@
     }();
 
     if (renderRatePhase == 0) {
-        return mLastVsyncSequence->vsyncTime;
+        const auto vsyncTime = mLastVsyncSequence->vsyncTime;
+        if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) {
+            const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
+            ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__,
+                          ticks<std::milli, float>(vsyncTimePoint - TimePoint::now()));
+            mVsyncTrackerCallback.onVsyncGenerated(mId, vsyncTimePoint, *mDisplayModeDataOpt,
+                                                   Period::fromNs(mIdealPeriod));
+        }
+        return vsyncTime;
     }
 
     auto const [slope, intercept] = getVSyncPredictionModelLocked();
     const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
-    return nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
+    const auto nextAnticipatedVsyncTime =
+            nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
+    if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) {
+        const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime);
+        ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__,
+                      ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now()));
+        mVsyncTrackerCallback.onVsyncGenerated(mId, nextAnticipatedVsyncTimePoint,
+                                               *mDisplayModeDataOpt, Period::fromNs(mIdealPeriod));
+    }
+    return nextAnticipatedVsyncTime;
 }
 
 /*
@@ -332,10 +352,14 @@
     return vsyncSequence.seq % divisor == 0;
 }
 
-void VSyncPredictor::setRenderRate(Fps fps) {
-    ALOGV("%s %s: %s", __func__, to_string(mId).c_str(), to_string(fps).c_str());
+void VSyncPredictor::setDisplayModeData(const DisplayModeData& displayModeData) {
+    ALOGV("%s %s: RenderRate %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(),
+          to_string(displayModeData.renderRate).c_str(),
+          displayModeData.notifyExpectedPresentTimeoutOpt
+                  ? std::to_string(displayModeData.notifyExpectedPresentTimeoutOpt->ns()).c_str()
+                  : "N/A");
     std::lock_guard lock(mMutex);
-    mRenderRate = fps;
+    mDisplayModeDataOpt = displayModeData;
 }
 
 VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
@@ -358,6 +382,7 @@
         mRateMap.erase(mRateMap.begin());
     }
 
+    // TODO(b/308610306) mIdealPeriod to be updated with setDisplayModeData
     mIdealPeriod = period;
     if (mRateMap.find(period) == mRateMap.end()) {
         mRateMap[mIdealPeriod] = {period, 0};
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index c01c44d..c271eb7 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -36,9 +36,11 @@
      * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
      * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
      * samples that fall outlierTolerancePercent from an anticipated vsync event.
+     * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker.
      */
     VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize,
-                   size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent);
+                   size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent,
+                   IVsyncTrackerCallback&);
     ~VSyncPredictor();
 
     bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
@@ -69,7 +71,7 @@
 
     bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
 
-    void setRenderRate(Fps) final EXCLUDES(mMutex);
+    void setDisplayModeData(const DisplayModeData&) final EXCLUDES(mMutex);
 
     void dump(std::string& result) const final EXCLUDES(mMutex);
 
@@ -99,6 +101,7 @@
     size_t const kHistorySize;
     size_t const kMinimumSamplesForPrediction;
     size_t const kOutlierTolerancePercent;
+    IVsyncTrackerCallback& mVsyncTrackerCallback;
     std::mutex mutable mMutex;
 
     nsecs_t mIdealPeriod GUARDED_BY(mMutex);
@@ -110,7 +113,7 @@
     size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
     std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
 
-    std::optional<Fps> mRenderRate GUARDED_BY(mMutex);
+    std::optional<DisplayModeData> mDisplayModeDataOpt GUARDED_BY(mMutex);
 
     mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
 };
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index bc0e3bc..7eedc31 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <ui/DisplayId.h>
 #include <utils/Timers.h>
 
 #include <scheduler/Fps.h>
@@ -23,6 +24,23 @@
 #include "VSyncDispatch.h"
 
 namespace android::scheduler {
+
+struct DisplayModeData {
+    Fps renderRate;
+    std::optional<Period> notifyExpectedPresentTimeoutOpt;
+
+    bool operator==(const DisplayModeData& other) const {
+        return isApproxEqual(renderRate, other.renderRate) &&
+                notifyExpectedPresentTimeoutOpt == other.notifyExpectedPresentTimeoutOpt;
+    }
+};
+
+struct IVsyncTrackerCallback {
+    virtual ~IVsyncTrackerCallback() = default;
+    virtual void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime,
+                                  const DisplayModeData&, Period vsyncPeriod) = 0;
+};
+
 /*
  * VSyncTracker is an interface for providing estimates on future Vsync signal times based on
  * historical vsync timing data.
@@ -80,16 +98,20 @@
     virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
 
     /*
-     * Sets a render rate on the tracker. If the render rate is not a divisor
-     * of the period, the render rate is ignored until the period changes.
+     * Sets the metadata about the currently active display mode such as VRR
+     * timeout period, vsyncPeriod and framework property such as render rate.
+     * If the render rate is not a divisor of the period, the render rate is
+     * ignored until the period changes.
      * The tracker will continue to track the vsync timeline and expect it
      * to match the current period, however, nextAnticipatedVSyncTimeFrom will
      * return vsyncs according to the render rate set. Setting a render rate is useful
      * when a display is running at 120Hz but the render frame rate is 60Hz.
+     * When IVsyncTrackerCallback::onVsyncGenerated callback is made we will pass along
+     * the vsyncPeriod, render rate and timeoutNs.
      *
-     * \param [in] Fps   The render rate the tracker should operate at.
+     * \param [in] DisplayModeData The DisplayModeData the tracker will use.
      */
-    virtual void setRenderRate(Fps) = 0;
+    virtual void setDisplayModeData(const DisplayModeData&) = 0;
 
     virtual void dump(std::string& result) const = 0;
 
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index ff3f29d..5fb53f9 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -54,10 +54,11 @@
 };
 
 VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features,
-                             RequestHardwareVsync requestHardwareVsync)
+                             RequestHardwareVsync requestHardwareVsync,
+                             IVsyncTrackerCallback& callback)
       : mId(id),
         mRequestHardwareVsync(std::move(requestHardwareVsync)),
-        mTracker(createTracker(id)),
+        mTracker(createTracker(id, callback)),
         mDispatch(createDispatch(mTracker)),
         mController(createController(id, *mTracker, features)),
         mTracer(features.test(Feature::kTracePredictedVsync)
@@ -100,7 +101,8 @@
     mDispatch->dump(out);
 }
 
-VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) {
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id,
+                                                       IVsyncTrackerCallback& callback) {
     // TODO(b/144707443): Tune constants.
     constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
     constexpr size_t kHistorySize = 20;
@@ -108,7 +110,8 @@
     constexpr uint32_t kDiscardOutlierPercent = 20;
 
     return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize,
-                                            kMinSamplesForPrediction, kDiscardOutlierPercent);
+                                            kMinSamplesForPrediction, kDiscardOutlierPercent,
+                                            callback);
 }
 
 VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) {
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 47e92e1..ca61f87 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -31,6 +31,7 @@
 #include <scheduler/Time.h>
 
 #include "ThreadContext.h"
+#include "VSyncTracker.h"
 
 namespace android {
 class EventThreadTest;
@@ -56,7 +57,7 @@
 public:
     using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>;
 
-    VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync);
+    VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync, IVsyncTrackerCallback&);
     ~VsyncSchedule();
 
     // IVsyncSource overrides:
@@ -124,7 +125,7 @@
     friend class android::VsyncScheduleTest;
     friend class android::fuzz::SchedulerFuzzer;
 
-    static TrackerPtr createTracker(PhysicalDisplayId);
+    static TrackerPtr createTracker(PhysicalDisplayId, IVsyncTrackerCallback&);
     static DispatchPtr createDispatch(TrackerPtr);
     static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 62eb17d..4d02b44 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2247,6 +2247,7 @@
                 .setFrameRateVote = snapshot->frameRate,
                 .frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
                 .isSmallDirty = snapshot->isSmallDirty,
+                .isFrontBuffered = snapshot->isFrontBuffered(),
         };
 
         if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
@@ -2747,11 +2748,11 @@
         mPowerAdvisor->reportActualWorkDuration();
     }
 
-    if (mScheduler->onPostComposition(presentTime)) {
+    if (mScheduler->onCompositionPresented(presentTime)) {
         scheduleComposite(FrameHint::kNone);
     }
 
-    postComposition(pacesetterId, frameTargeters, presentTime);
+    onCompositionPresented(pacesetterId, frameTargeters, presentTime);
 
     const bool hadGpuComposited =
             multiDisplayUnion(mCompositionCoverage).test(CompositionCoverage::Gpu);
@@ -2908,9 +2909,9 @@
     return ui::ROTATION_0;
 }
 
-void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId,
-                                     const scheduler::FrameTargeters& frameTargeters,
-                                     nsecs_t presentStartTime) {
+void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId,
+                                            const scheduler::FrameTargeters& frameTargeters,
+                                            nsecs_t presentStartTime) {
     ATRACE_CALL();
     ALOGV(__func__);
 
@@ -3004,8 +3005,9 @@
     mLayersWithBuffersRemoved.clear();
 
     for (const auto& layer: mLayersWithQueuedFrames) {
-        layer->onPostComposition(pacesetterDisplay.get(), pacesetterGpuCompositionDoneFenceTime,
-                                 pacesetterPresentFenceTime, compositorTiming);
+        layer->onCompositionPresented(pacesetterDisplay.get(),
+                                      pacesetterGpuCompositionDoneFenceTime,
+                                      pacesetterPresentFenceTime, compositorTiming);
         layer->releasePendingBuffer(presentTime.ns());
     }
 
@@ -4036,6 +4038,21 @@
     }
 }
 
+void SurfaceFlinger::onVsyncGenerated(PhysicalDisplayId displayId, TimePoint expectedPresentTime,
+                                      const scheduler::DisplayModeData& displayModeData,
+                                      Period vsyncPeriod) {
+    const auto status =
+            getHwComposer()
+                    .notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime,
+                                                     displayModeData.renderRate,
+                                                     displayModeData
+                                                             .notifyExpectedPresentTimeoutOpt);
+    if (status != NO_ERROR) {
+        ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__,
+              displayId.value);
+    }
+}
+
 void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
     using namespace scheduler;
 
@@ -4074,8 +4091,12 @@
 
     mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
                                              static_cast<ISchedulerCallback&>(*this), features,
-                                             std::move(modulatorPtr));
+                                             std::move(modulatorPtr),
+                                             static_cast<IVsyncTrackerCallback&>(*this));
     mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
+    if (FlagManager::getInstance().vrr_config()) {
+        mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps);
+    }
     mScheduler->startTimers();
 
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
@@ -4880,6 +4901,7 @@
                         .transform = layer->getTransform(),
                         .setFrameRateVote = layer->getFrameRateForLayerTree(),
                         .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+                        .isFrontBuffered = layer->isFrontBuffered(),
                 };
                 layer->recordLayerHistoryAnimationTx(layerProps, now);
             }
@@ -5965,7 +5987,9 @@
                                 });
 
                         out << "\nLayer Hierarchy\n"
-                            << mLayerHierarchyBuilder.getHierarchy() << "\n\n";
+                            << mLayerHierarchyBuilder.getHierarchy()
+                            << "\nOffscreen Hierarchy\n"
+                            << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n";
                         compositionLayers = out.str();
                         dumpHwcLayersMinidump(compositionLayers);
                     }
@@ -6245,7 +6269,9 @@
                         });
 
                 out << "\nLayer Hierarchy\n"
-                    << mLayerHierarchyBuilder.getHierarchy().dump() << "\n\n";
+                    << mLayerHierarchyBuilder.getHierarchy().dump()
+                    << "\nOffscreen Hierarchy\n"
+                    << mLayerHierarchyBuilder.getOffscreenHierarchy().dump() << "\n\n";
                 result.append(out.str());
             })
             .get();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 520bd22..1e90340 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -195,7 +195,8 @@
                        private HWC2::ComposerCallback,
                        private ICompositor,
                        private scheduler::ISchedulerCallback,
-                       private compositionengine::ICEPowerCallback {
+                       private compositionengine::ICEPowerCallback,
+                       private scheduler::IVsyncTrackerCallback {
 public:
     struct SkipInitializationTag {};
 
@@ -656,6 +657,10 @@
     // ICEPowerCallback overrides:
     void notifyCpuLoadUp() override;
 
+    // IVsyncTrackerCallback overrides
+    void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime,
+                          const scheduler::DisplayModeData&, Period vsyncPeriod) override;
+
     // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
     void toggleKernelIdleTimer() REQUIRES(mStateLock);
 
@@ -982,8 +987,8 @@
     /*
      * Compositing
      */
-    void postComposition(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&,
-                         nsecs_t presentStartTime) REQUIRES(kMainThreadContext);
+    void onCompositionPresented(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&,
+                                nsecs_t presentStartTime) REQUIRES(kMainThreadContext);
 
     /*
      * Display management
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index 910e685..243b8e0 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -74,9 +74,17 @@
     ],
     fuzz_config: {
         cc: [
-            "android-media-fuzzing-reports@google.com",
+            "android-cogs-eng@google.com",
         ],
-        componentid: 155276,
+        componentid: 1075131,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libsurfaceflinger library",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
     },
 }
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index c4077df..9b2d453 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -225,16 +225,19 @@
 class TestableScheduler : public Scheduler, private ICompositor {
 public:
     TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
-                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback,
+                      IVsyncTrackerCallback& vsyncTrackerCallback)
           : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
                               std::make_shared<android::mock::VSyncTracker>(), selectorPtr,
-                              std::move(modulatorPtr), callback) {}
+                              std::move(modulatorPtr), callback, vsyncTrackerCallback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       VsyncSchedule::TrackerPtr tracker,
                       std::shared_ptr<RefreshRateSelector> selectorPtr,
-                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
-          : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback,
+                      IVsyncTrackerCallback& vsyncTrackerCallback)
+          : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr),
+                      vsyncTrackerCallback) {
         const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
         registerDisplayInternal(displayId, std::move(selectorPtr),
                                 std::shared_ptr<VsyncSchedule>(
@@ -400,7 +403,8 @@
 } // namespace surfaceflinger::test
 
 // TODO(b/189053744) : Create a common test/mock library for surfaceflinger
-class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback {
+class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback,
+                                     private scheduler::IVsyncTrackerCallback {
 public:
     using HotplugEvent = SurfaceFlinger::HotplugEvent;
 
@@ -610,8 +614,8 @@
             mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
 
             scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool());
-            mFlinger->postComposition(displayId, ftl::init::map(displayId, &frameTargeter),
-                                      mFdp.ConsumeIntegral<nsecs_t>());
+            mFlinger->onCompositionPresented(displayId, ftl::init::map(displayId, &frameTargeter),
+                                             mFdp.ConsumeIntegral<nsecs_t>());
         }
 
         mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -656,6 +660,7 @@
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
                         scheduler::ISchedulerCallback* callback = nullptr,
+                        scheduler::IVsyncTrackerCallback* vsyncTrackerCallback = nullptr,
                         bool hasMultipleModes = false) {
         constexpr DisplayModeId kModeId60{0};
         DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
@@ -678,7 +683,8 @@
 
         mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
                                                       std::move(vsyncTracker), mRefreshRateSelector,
-                                                      std::move(modulatorPtr), *(callback ?: this));
+                                                      std::move(modulatorPtr), *(callback ?: this),
+                                                      *(vsyncTrackerCallback ?: this));
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -799,6 +805,10 @@
     void triggerOnFrameRateOverridesChanged() override {}
     void onChoreographerAttached() override {}
 
+    // IVsyncTrackerCallback overrides
+    void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&,
+                          Period) override {}
+
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger =
             sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 39a7ee5..7aae3c4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -133,7 +133,7 @@
                             ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>()));
 
     layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
-    layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming);
+    layer->onCompositionPresented(nullptr, fenceTime, fenceTime, compositorTiming);
 
     layer->setTransform(mFdp.ConsumeIntegral<uint32_t>());
     layer->setTransformToDisplayInverse(mFdp.ConsumeBool());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index a8727f9..8fcfd81 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -178,14 +178,23 @@
     dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp);
 }
 
+struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback {
+    void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&,
+                          Period) override {}
+};
+
 void SchedulerFuzzer::fuzzVSyncPredictor() {
     uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
     uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
     uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
     nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX);
-    scheduler::VSyncPredictor tracker{kDisplayId, idealPeriod, historySize,
+    VsyncTrackerCallback callback;
+    scheduler::VSyncPredictor tracker{kDisplayId,
+                                      idealPeriod,
+                                      historySize,
                                       minimumSamplesForPrediction,
-                                      mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
+                                      mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/,
+                                      callback};
     uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
     tracker.setPeriod(period);
     for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 8061a8f..728708f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -100,7 +100,7 @@
         return true;
     }
 
-    void setRenderRate(Fps) override {}
+    void setDisplayModeData(const scheduler::DisplayModeData&) override {}
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
         if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 19d194f..a81f9b8 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -52,4 +52,21 @@
   description: "Feature flag for SmallAreaDetection"
   bug: "283055450"
   is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+  name: "hotplug2"
+  namespace: "core_graphics"
+  description: "Feature flag for using hotplug2 HAL API"
+  bug: "303460805"
+  is_fixed_read_only: true
+}
+
+flag {
+  name: "hdcp_level_hal"
+  namespace: "core_graphics"
+  description: "Feature flag for adding a HAL API to commuicate hdcp levels"
+  bug: "285359126"
+  is_fixed_read_only: true
+}
+
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index fa31643..1379665 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -80,7 +80,8 @@
                             std::unique_ptr<EventThread>(mEventThread),
                             std::unique_ptr<EventThread>(mSFEventThread),
                             TestableSurfaceFlinger::DefaultDisplayMode{displayId},
-                            TestableSurfaceFlinger::SchedulerCallbackImpl::kMock);
+                            TestableSurfaceFlinger::SchedulerCallbackImpl::kMock,
+                            TestableSurfaceFlinger::VsyncTrackerCallbackImpl::kMock);
 }
 
 void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 4f545a9..58d7a40 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -79,10 +79,13 @@
         EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
     }
 
-    void setDisplayData(HalDisplayId displayId, nsecs_t lastExpectedPresentTimestamp) {
+    void setDisplayData(HalDisplayId displayId, TimePoint lastExpectedPresentTimestamp,
+                        Fps lastFrameInterval) {
         ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end());
         auto& displayData = mHwc.mDisplayData.at(displayId);
+        std::scoped_lock lock{displayData.expectedPresentLock};
         displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp;
+        displayData.lastFrameInterval = lastFrameInterval;
     }
 };
 
@@ -322,48 +325,137 @@
     ASSERT_TRUE(info);
 
     auto expectedPresentTime = systemTime() + ms2ns(10);
-    const int32_t frameIntervalNs = static_cast<Fps>(60_Hz).getPeriodNsecs();
-    static constexpr nsecs_t kTimeoutNs = ms2ns(30);
+    static constexpr Fps Fps60Hz = 60_Hz;
+    static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs();
+    static constexpr int32_t kFrameInterval60HzNs = Fps60Hz.getPeriodNsecs();
+    static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs();
+    static constexpr Period kVsyncPeriod =
+            Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs());
+    static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs);
+    static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0);
 
-    ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, /* lastExpectedPresentTimestamp= */ 0));
+    ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, kLastExpectedPresentTimestamp, Fps60Hz));
 
     {
         // Very first ExpectedPresent after idle, no previous timestamp
         EXPECT_CALL(*mHal,
-                    notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+                    notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
                 .WillOnce(Return(HalError::NONE));
-        mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+        mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+                                             TimePoint::fromNs(expectedPresentTime), Fps60Hz,
                                              kTimeoutNs);
     }
     {
-        // ExpectedPresent is after the timeoutNs
-        expectedPresentTime += ms2ns(50);
+        // Absent timeoutNs
+        expectedPresentTime += 2 * kFrameInterval5HzNs;
+        EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+        mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+                                             TimePoint::fromNs(expectedPresentTime), Fps60Hz,
+                                             /*timeoutOpt*/ std::nullopt);
+    }
+    {
+        // Timeout is 0
+        expectedPresentTime += kFrameInterval60HzNs;
         EXPECT_CALL(*mHal,
-                    notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+                    notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
                 .WillOnce(Return(HalError::NONE));
-        mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+        mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+                                             TimePoint::fromNs(expectedPresentTime), Fps60Hz,
+                                             Period::fromNs(0));
+    }
+    {
+        // ExpectedPresent is after the timeoutNs
+        expectedPresentTime += 2 * kFrameInterval5HzNs;
+        EXPECT_CALL(*mHal,
+                    notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
+                .WillOnce(Return(HalError::NONE));
+        mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+                                             TimePoint::fromNs(expectedPresentTime), Fps60Hz,
+                                             kTimeoutNs);
+    }
+    {
+        // ExpectedPresent has not changed
+        EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+        mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+                                             TimePoint::fromNs(expectedPresentTime), Fps60Hz,
                                              kTimeoutNs);
     }
     {
         // ExpectedPresent is after the last reported ExpectedPresent.
-        expectedPresentTime += ms2ns(10);
+        expectedPresentTime += kFrameInterval60HzNs;
         EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
-        mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+        mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+                                             TimePoint::fromNs(expectedPresentTime), Fps60Hz,
                                              kTimeoutNs);
     }
     {
         // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs,
         // representing we changed our decision and want to present earlier than previously
         // reported.
-        expectedPresentTime -= ms2ns(20);
+        expectedPresentTime -= kFrameInterval120HzNs;
         EXPECT_CALL(*mHal,
-                    notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
+                    notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
                 .WillOnce(Return(HalError::NONE));
-        mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
+        mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+                                             TimePoint::fromNs(expectedPresentTime), Fps60Hz,
                                              kTimeoutNs);
     }
 }
 
+TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) {
+    constexpr hal::HWDisplayId kHwcDisplayId = 2;
+    expectHotplugConnect(kHwcDisplayId);
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+    ASSERT_TRUE(info);
+
+    const auto now = systemTime();
+    auto expectedPresentTime = now;
+    static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
+
+    ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, TimePoint::fromNs(now), Fps::fromValue(0)));
+    static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs();
+    static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs();
+    static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs();
+    static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs();
+    static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs();
+    static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs();
+    static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs();
+    static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs();
+    static constexpr Period kVsyncPeriod =
+            Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs());
+
+    struct FrameRateIntervalTestData {
+        int32_t frameIntervalNs;
+        bool callExpectedPresent;
+    };
+    const std::vector<FrameRateIntervalTestData> frameIntervals = {
+            {kFrameIntervalNs60Hz, true},  {kFrameIntervalNs96Hz, true},
+            {kFrameIntervalNs80Hz, true},  {kFrameIntervalNs120Hz, true},
+            {kFrameIntervalNs80Hz, true},  {kFrameIntervalNs60Hz, true},
+            {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false},
+            {kFrameIntervalNs24Hz, true},  {kFrameIntervalNs40Hz, true},
+            {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true},
+            {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true},
+    };
+
+    for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) {
+        {
+            expectedPresentTime += frameIntervalNs;
+            if (callExpectedPresent) {
+                EXPECT_CALL(*mHal,
+                            notifyExpectedPresent(kHwcDisplayId, expectedPresentTime,
+                                                  frameIntervalNs))
+                        .WillOnce(Return(HalError::NONE));
+            } else {
+                EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+            }
+            mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod,
+                                                 TimePoint::fromNs(expectedPresentTime),
+                                                 Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs);
+        }
+    }
+}
+
 struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
     MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection));
     MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId));
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 2f6058f..190c0e8 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -165,8 +165,10 @@
                                                   DisplayModeId(0));
 
     mock::SchedulerCallback mSchedulerCallback;
+    mock::VsyncTrackerCallback mVsyncTrackerCallback;
 
-    TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+    TestableScheduler* mScheduler =
+            new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
 
     TestableSurfaceFlinger mFlinger;
 };
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index e8831ab..1adf14f 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -144,7 +144,9 @@
 
     mock::SchedulerCallback mSchedulerCallback;
 
-    TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+    mock::VsyncTrackerCallback mVsyncTrackerCallback;
+    TestableScheduler* mScheduler =
+            new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
 
     TestableSurfaceFlinger mFlinger;
 };
@@ -925,6 +927,43 @@
     EXPECT_EQ(1, animatingLayerCount(time));
 }
 
+TEST_F(LayerHistoryTest, frontBufferedLayerVotesMax) {
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, isFrontBuffered()).WillRepeatedly(Return(true));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    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(0, animatingLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    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(0, animatingLayerCount(time));
+}
+
 TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) {
     auto layer = createLayer();
 
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 11072bc..047ef5a 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -21,6 +21,7 @@
 
 #include <scheduler/Fps.h>
 
+#include "FlagUtils.h"
 #include "FpsOps.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/LayerInfo.h"
@@ -28,6 +29,8 @@
 #include "TestableSurfaceFlinger.h"
 #include "mock/MockSchedulerCallback.h"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 namespace android::scheduler {
 
 using android::mock::createDisplayMode;
@@ -61,12 +64,16 @@
                                                                               HI_FPS)),
                                                   DisplayModeId(0));
     mock::SchedulerCallback mSchedulerCallback;
-    TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+    mock::VsyncTrackerCallback mVsyncTrackerCallback;
+    TestableScheduler* mScheduler =
+            new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback);
     TestableSurfaceFlinger mFlinger;
 };
 
 namespace {
 
+using namespace com::android::graphics::surfaceflinger;
+
 TEST_F(LayerInfoTest, prefersPresentTime) {
     std::deque<FrameTimeData> frameTimes;
     constexpr auto kExpectedFps = 50_Hz;
@@ -261,5 +268,18 @@
     ASSERT_EQ(actualVotes[0].fps, vote.fps);
 }
 
+TEST_F(LayerInfoTest, isFrontBuffered) {
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    ASSERT_FALSE(layerInfo.isFrontBuffered());
+
+    LayerProps prop = {.isFrontBuffered = true};
+    layerInfo.setLastPresentTime(0, 0, LayerHistory::LayerUpdateType::Buffer, true, prop);
+    ASSERT_TRUE(layerInfo.isFrontBuffered());
+
+    prop.isFrontBuffered = false;
+    layerInfo.setLastPresentTime(0, 0, LayerHistory::LayerUpdateType::Buffer, true, prop);
+    ASSERT_FALSE(layerInfo.isFrontBuffered());
+}
+
 } // namespace
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index e784eb7..57babaf 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -898,4 +898,24 @@
             gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
 }
 
+TEST_F(LayerSnapshotTest, isFrontBuffered) {
+    setBuffer(1,
+              std::make_shared<renderengine::mock::FakeExternalTexture>(
+                      1U /*width*/, 1U /*height*/, 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888,
+                      GRALLOC_USAGE_HW_TEXTURE | AHARDWAREBUFFER_USAGE_FRONT_BUFFER /*usage*/));
+
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->isFrontBuffered());
+
+    setBuffer(1,
+              std::make_shared<
+                      renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                               1ULL /* bufferId */,
+                                                               HAL_PIXEL_FORMAT_RGBA_8888,
+                                                               GRALLOC_USAGE_HW_TEXTURE /*usage*/));
+
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->isFrontBuffered());
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 87fae2c..b5eb777 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -89,7 +89,9 @@
                                                   kDisplay1Mode60->getId());
 
     mock::SchedulerCallback mSchedulerCallback;
-    TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
+    mock::VsyncTrackerCallback mVsyncTrackerCallback;
+    TestableScheduler* mScheduler =
+            new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback};
     surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
 
     ConnectionHandle mConnectionHandle;
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3d1c900..8b6f0f1 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -36,18 +36,21 @@
 
 class TestableScheduler : public Scheduler, private ICompositor {
 public:
-    TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
+    TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback,
+                      IVsyncTrackerCallback& vsyncTrackerCallback)
           : TestableScheduler(std::make_unique<mock::VsyncController>(),
                               std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
-                              sp<VsyncModulator>::make(VsyncConfigSet{}), callback) {}
+                              sp<VsyncModulator>::make(VsyncConfigSet{}), callback,
+                              vsyncTrackerCallback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
-                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
-          : Scheduler(*this, callback,
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& schedulerCallback,
+                      IVsyncTrackerCallback& vsyncTrackerCallback)
+          : Scheduler(*this, schedulerCallback,
                       (FeatureFlags)Feature::kContentDetection |
                               Feature::kSmallDirtyContentDetection,
-                      std::move(modulatorPtr)) {
+                      std::move(modulatorPtr), vsyncTrackerCallback) {
         const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
         registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
                         std::move(tracker));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 03af56c..d0b2199 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -53,6 +53,7 @@
 #include "mock/MockFrameTimeline.h"
 #include "mock/MockFrameTracer.h"
 #include "mock/MockSchedulerCallback.h"
+#include "mock/MockVsyncTrackerCallback.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 #include "Scheduler/VSyncTracker.h"
@@ -204,6 +205,8 @@
 
     enum class SchedulerCallbackImpl { kNoOp, kMock };
 
+    enum class VsyncTrackerCallbackImpl { kNoOp, kMock };
+
     struct DefaultDisplayMode {
         // The ID of the injected RefreshRateSelector and its default display mode.
         PhysicalDisplayId displayId;
@@ -213,13 +216,14 @@
 
     using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>;
 
-    void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
-                        std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
-                        std::unique_ptr<EventThread> appEventThread,
-                        std::unique_ptr<EventThread> sfEventThread,
-                        DisplayModesVariant modesVariant,
-                        SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
-                        bool useNiceMock = false) {
+    void setupScheduler(
+            std::unique_ptr<scheduler::VsyncController> vsyncController,
+            std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
+            std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread,
+            DisplayModesVariant modesVariant,
+            SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
+            VsyncTrackerCallbackImpl vsyncTrackerCallbackImpl = VsyncTrackerCallbackImpl::kNoOp,
+            bool useNiceMock = false) {
         RefreshRateSelectorPtr selectorPtr = ftl::match(
                 modesVariant,
                 [](DefaultDisplayMode arg) {
@@ -239,10 +243,16 @@
 
         mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
 
-        using Callback = scheduler::ISchedulerCallback;
-        Callback& callback = callbackImpl == SchedulerCallbackImpl::kNoOp
-                ? static_cast<Callback&>(mNoOpSchedulerCallback)
-                : static_cast<Callback&>(mSchedulerCallback);
+        using ISchedulerCallback = scheduler::ISchedulerCallback;
+        ISchedulerCallback& schedulerCallback = callbackImpl == SchedulerCallbackImpl::kNoOp
+                ? static_cast<ISchedulerCallback&>(mNoOpSchedulerCallback)
+                : static_cast<ISchedulerCallback&>(mSchedulerCallback);
+
+        using VsyncTrackerCallback = scheduler::IVsyncTrackerCallback;
+        VsyncTrackerCallback& vsyncTrackerCallback =
+                vsyncTrackerCallbackImpl == VsyncTrackerCallbackImpl::kNoOp
+                ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback)
+                : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback);
 
         auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -253,12 +263,14 @@
                                                                         std::move(vsyncTracker),
                                                                         std::move(selectorPtr),
                                                                         std::move(modulatorPtr),
-                                                                        callback);
+                                                                        schedulerCallback,
+                                                                        vsyncTrackerCallback);
         } else {
             mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
                                                           std::move(vsyncTracker),
                                                           std::move(selectorPtr),
-                                                          std::move(modulatorPtr), callback);
+                                                          std::move(modulatorPtr),
+                                                          schedulerCallback, vsyncTrackerCallback);
         }
 
         mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms);
@@ -297,7 +309,8 @@
         EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
         setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread),
                        std::move(sfEventThread), DefaultDisplayMode{options.displayId},
-                       SchedulerCallbackImpl::kNoOp, options.useNiceMock);
+                       SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp,
+                       options.useNiceMock);
     }
 
     void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
@@ -1071,6 +1084,8 @@
     sp<SurfaceFlinger> mFlinger;
     scheduler::mock::SchedulerCallback mSchedulerCallback;
     scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
+    scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback;
+    scheduler::mock::NoOpVsyncTrackerCallback mNoOpVsyncTrackerCallback;
     std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
     scheduler::TestableScheduler* mScheduler = nullptr;
     Hwc2::mock::PowerAdvisor mPowerAdvisor;
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 00b5bf0..d4d5b32 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -112,7 +112,7 @@
         EXPECT_CALL(*mFlinger.getFrameTracer(),
                     traceFence(layerId, bufferId, frameNumber, presentFence,
                                FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0));
-        layer->onPostComposition(nullptr, glDoneFence, presentFence, compositorTiming);
+        layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming);
     }
 };
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 41866a1..4be07a1 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -54,7 +54,7 @@
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
-    void setRenderRate(Fps) final {}
+    void setDisplayModeData(const DisplayModeData&) final {}
     void dump(std::string&) const final {}
 
 private:
@@ -92,7 +92,7 @@
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
-    void setRenderRate(Fps) final {}
+    void setDisplayModeData(const DisplayModeData&) final {}
     void dump(std::string&) const final {}
 
 private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 1dc5498..8310866 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -59,7 +59,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
-    MOCK_METHOD(void, setRenderRate, (Fps), (override));
+    MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
@@ -783,7 +783,9 @@
 TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTargetVSync) {
     SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
 
-    EXPECT_CALL(mMockClock, alarmAt(_, 500));
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 400)).InSequence(seq);
     CountingCallback cb(mDispatch);
     auto result =
             mDispatch->schedule(cb,
@@ -873,7 +875,9 @@
 TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) {
     SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
 
-    EXPECT_CALL(mMockClock, alarmAt(_, 600));
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq);
 
     CountingCallback cb(mDispatch);
     auto result =
@@ -1119,6 +1123,7 @@
 
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq);
 
     CountingCallback cb(mDispatch);
 
@@ -1132,7 +1137,7 @@
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
     EXPECT_THAT(cb.mCalls[0], Eq(1000));
     ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
-    EXPECT_THAT(cb.mWakeupTime[0], Eq(600));
+    EXPECT_THAT(cb.mWakeupTime[0], Eq(0));
     ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
     EXPECT_THAT(cb.mReadyTime[0], Eq(1000));
 }
@@ -1161,7 +1166,9 @@
 TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) {
     SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
 
-    EXPECT_CALL(mMockClock, alarmAt(_, 500));
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 300)).InSequence(seq);
     CountingCallback cb(mDispatch);
     auto result =
             mDispatch->schedule(cb,
@@ -1177,6 +1184,11 @@
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(1000));
+    ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
+    EXPECT_THAT(cb.mWakeupTime[0], Eq(300));
+    ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
+    EXPECT_THAT(cb.mReadyTime[0], Eq(1000));
 }
 
 class VSyncDispatchTimerQueueEntryTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 43d683d..30a2855 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -23,7 +23,9 @@
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 #define LOG_NDEBUG 0
 
+#include "FlagUtils.h"
 #include "Scheduler/VSyncPredictor.h"
+#include "mock/MockVsyncTrackerCallback.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -31,8 +33,11 @@
 #include <chrono>
 #include <utility>
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 using namespace testing;
 using namespace std::literals;
+using namespace com::android::graphics::surfaceflinger;
 
 namespace android::scheduler {
 
@@ -52,13 +57,18 @@
 struct VSyncPredictorTest : testing::Test {
     nsecs_t mNow = 0;
     nsecs_t mPeriod = 1000;
+    scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback;
     static constexpr size_t kHistorySize = 10;
     static constexpr size_t kMinimumSamplesForPrediction = 6;
     static constexpr size_t kOutlierTolerancePercent = 25;
     static constexpr nsecs_t mMaxRoundingError = 100;
 
-    VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, kHistorySize, kMinimumSamplesForPrediction,
-                           kOutlierTolerancePercent};
+    VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
+                           mPeriod,
+                           kHistorySize,
+                           kMinimumSamplesForPrediction,
+                           kOutlierTolerancePercent,
+                           mVsyncTrackerCallback};
 };
 
 TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
@@ -378,8 +388,12 @@
 
 // See b/151146131
 TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
-    VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, 20, kMinimumSamplesForPrediction,
-                           kOutlierTolerancePercent};
+    VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
+                           mPeriod,
+                           20,
+                           kMinimumSamplesForPrediction,
+                           kOutlierTolerancePercent,
+                           mVsyncTrackerCallback};
     std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
                                                840923581635, 840940161584, 840956868096,
                                                840973702473, 840990256277, 841007116851,
@@ -566,7 +580,7 @@
         tracker.addVsyncTimestamp(mNow);
     }
 
-    tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod));
+    tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3 * mPeriod)});
 
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
@@ -588,12 +602,12 @@
 
     const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
 
-    tracker.setRenderRate(refreshRate / 4);
+    tracker.setDisplayModeData({.renderRate = refreshRate / 4});
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod));
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod));
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod));
 
-    tracker.setRenderRate(refreshRate / 2);
+    tracker.setDisplayModeData({.renderRate = refreshRate / 2});
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod));
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod));
@@ -601,7 +615,7 @@
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod));
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod));
 
-    tracker.setRenderRate(refreshRate / 6);
+    tracker.setDisplayModeData({.renderRate = refreshRate / 6});
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod));
 }
@@ -615,7 +629,7 @@
         tracker.addVsyncTimestamp(mNow);
     }
 
-    tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod));
+    tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3.5f * mPeriod)});
 
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
@@ -626,6 +640,39 @@
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
 }
 
+TEST_F(VSyncPredictorTest, vsyncTrackerCallback) {
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
+    DisplayModeData displayModeData =
+            DisplayModeData{.renderRate = refreshRate,
+                            .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)};
+    tracker.setDisplayModeData(displayModeData);
+    auto last = mNow;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_CALL(mVsyncTrackerCallback,
+                    onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(last + mPeriod),
+                                     displayModeData, Period::fromNs(mPeriod)))
+                .Times(1);
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+    }
+
+    displayModeData = DisplayModeData{.renderRate = refreshRate / 2,
+                                      .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)};
+    tracker.setDisplayModeData(displayModeData);
+    {
+        // out of render rate phase
+        EXPECT_CALL(mVsyncTrackerCallback,
+                    onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(mNow + 3 * mPeriod),
+                                     displayModeData, Period::fromNs(mPeriod)))
+                .Times(1);
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod),
+                    Eq(mNow + 3 * mPeriod));
+    }
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 122192b..aca3ccc 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -50,7 +50,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
-    MOCK_METHOD(void, setRenderRate, (Fps), (override));
+    MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 4cc78fe..3dfb649 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -37,6 +37,7 @@
     MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility());
     MOCK_CONST_METHOD0(getOwnerUid, uid_t());
     MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
+    MOCK_METHOD(bool, isFrontBuffered, (), (const, override));
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index dcf25e1..31eb86e 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -34,7 +34,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
-    MOCK_METHOD(void, setRenderRate, (Fps), (override));
+    MOCK_METHOD(void, setDisplayModeData, (const scheduler::DisplayModeData&), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h
new file mode 100644
index 0000000..b8e24e0
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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 <gmock/gmock.h>
+
+#include "Scheduler/VSyncTracker.h"
+
+namespace android::scheduler::mock {
+
+struct VsyncTrackerCallback final : IVsyncTrackerCallback {
+    MOCK_METHOD(void, onVsyncGenerated,
+                (PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, Period),
+                (override));
+};
+
+struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback {
+    void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&,
+                          Period) override{};
+};
+} // namespace android::scheduler::mock