Merge changes I53f65dae,I4bbfe4c5 into main

* changes:
  Count active touches in MultiTouchMotionAccumulator
  Record last keypress timestamp on physical keyboard while typing
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 4f80190..17e5cc1 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1089,8 +1089,14 @@
     // This function copies into the .zip the system trace that was snapshotted
     // by the early call to MaybeSnapshotSystemTrace(), if any background
     // tracing was happening.
-    if (!ds.has_system_trace_) {
-        // No background trace was happening at the time dumpstate was invoked.
+    bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
+    if (!system_trace_exists) {
+        // No background trace was happening at the time MaybeSnapshotSystemTrace() was invoked.
+        if (!PropertiesHelper::IsUserBuild()) {
+            MYLOGI(
+                "No system traces found. Check for previously uploaded traces by looking for "
+                "go/trace-uuid in logcat")
+        }
         return;
     }
     ds.AddZipEntry(
@@ -1650,8 +1656,6 @@
         dump_board = ds.dump_pool_->enqueueTaskWithFd(
             DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
         dump_checkins = ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
-        post_process_ui_traces = ds.dump_pool_->enqueueTask(
-            POST_PROCESS_UI_TRACES_TASK, &Dumpstate::MaybePostProcessUiTraces, &ds);
     }
 
     // Dump various things. Note that anything that takes "long" (i.e. several seconds) should
@@ -1861,12 +1865,6 @@
                 DumpIncidentReport);
     }
 
-    if (ds.dump_pool_) {
-        WaitForTask(std::move(post_process_ui_traces));
-    } else {
-        RUN_SLOW_FUNCTION_AND_LOG(POST_PROCESS_UI_TRACES_TASK, MaybePostProcessUiTraces);
-    }
-
     MaybeAddUiTracesToZip();
 
     return Dumpstate::RunStatus::OK;
@@ -3078,6 +3076,7 @@
 }
 
 void Dumpstate::PreDumpUiData() {
+    MaybeSnapshotSystemTrace();
     MaybeSnapshotUiTraces();
 }
 
@@ -3264,25 +3263,23 @@
     // duration is logged into MYLOG instead.
     PrintHeader();
 
-    bool is_dumpstate_restricted = options_->telephony_only
-                                   || options_->wifi_only
-                                   || options_->limited_only;
-    if (!is_dumpstate_restricted) {
-        // Invoke critical dumpsys first to preserve system state, before doing anything else.
-        RunDumpsysCritical();
-    }
-    MaybeTakeEarlyScreenshot();
-
+    bool is_dumpstate_restricted =
+        options_->telephony_only || options_->wifi_only || options_->limited_only;
     if (!is_dumpstate_restricted) {
         // Snapshot the system trace now (if running) to avoid that dumpstate's
         // own activity pushes out interesting data from the trace ring buffer.
         // The trace file is added to the zip by MaybeAddSystemTraceToZip().
         MaybeSnapshotSystemTrace();
 
+        // Invoke critical dumpsys to preserve system state, before doing anything else.
+        RunDumpsysCritical();
+
         // Snapshot the UI traces now (if running).
         // The trace files will be added to bugreport later.
         MaybeSnapshotUiTraces();
     }
+
+    MaybeTakeEarlyScreenshot();
     onUiIntensiveBugreportDumpsFinished(calling_uid);
     MaybeCheckUserConsent(calling_uid, calling_package);
     if (options_->telephony_only) {
@@ -3379,6 +3376,19 @@
 }
 
 void Dumpstate::MaybeSnapshotSystemTrace() {
+    // When capturing traces via bugreport handler (BH), this function will be invoked twice:
+    // 1) When BH invokes IDumpstate::PreDumpUiData()
+    // 2) When BH invokes IDumpstate::startBugreport(flags = BUGREPORT_USE_PREDUMPED_UI_DATA)
+    // In this case we don't want to re-invoke perfetto in step 2.
+    // In all other standard invocation states, this function is invoked once
+    // without the flag BUGREPORT_USE_PREDUMPED_UI_DATA.
+    if (options_->use_predumped_ui_data) {
+        return;
+    }
+
+    // If a stale file exists already, remove it.
+    unlink(SYSTEM_TRACE_SNAPSHOT);
+
     // If a background system trace is happening and is marked as "suitable for
     // bugreport" (i.e. bugreport_score > 0 in the trace config), this command
     // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely)
@@ -3386,14 +3396,8 @@
     // Note: this should not be enqueued as we need to freeze the trace before
     // dumpstate starts. Otherwise the trace ring buffers will contain mostly
     // the dumpstate's own activity which is irrelevant.
-    int res = RunCommand(
-        "SERIALIZE PERFETTO TRACE",
-        {"perfetto", "--save-for-bugreport"},
-        CommandOptions::WithTimeout(10)
-            .DropRoot()
-            .CloseAllFileDescriptorsOnExec()
-            .Build());
-    has_system_trace_ = res == 0;
+    RunCommand("SERIALIZE PERFETTO TRACE", {"perfetto", "--save-for-bugreport"},
+               CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build());
     // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
     // file in the later stages.
 }
@@ -3420,33 +3424,6 @@
             "", command,
             CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
     }
-
-    // This command needs to be run as root
-    static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector<std::string> {
-        "service", "call", "SurfaceFlinger", "1042"
-    };
-    // Empty name because it's not intended to be classified as a bugreport section.
-    // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
-    RunCommand(
-        "", SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES,
-        CommandOptions::WithTimeout(10).Always().AsRoot().RedirectStderr().Build());
-}
-
-void Dumpstate::MaybePostProcessUiTraces() {
-    if (PropertiesHelper::IsUserBuild()) {
-        return;
-    }
-
-    RunCommand(
-        // Empty name because it's not intended to be classified as a bugreport section.
-        // Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
-        "", {
-            "/system/xbin/su", "system",
-            "/system/bin/layertracegenerator",
-            "/data/misc/wmtrace/transactions_trace.winscope",
-            "/data/misc/wmtrace/layers_trace_from_transactions.winscope"
-        },
-        CommandOptions::WithTimeout(120).Always().RedirectStderr().Build());
 }
 
 void Dumpstate::MaybeAddUiTracesToZip() {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index d01cfce..c66fd1c 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -476,11 +476,6 @@
     // Whether it should take an screenshot earlier in the process.
     bool do_early_screenshot_ = false;
 
-    // This is set to true when the trace snapshot request in the early call to
-    // MaybeSnapshotSystemTrace(). When this is true, the later stages of
-    // dumpstate will append the trace to the zip archive.
-    bool has_system_trace_ = false;
-
     std::unique_ptr<Progress> progress_;
 
     // When set, defines a socket file-descriptor use to report progress to bugreportz
@@ -574,7 +569,6 @@
     void MaybeTakeEarlyScreenshot();
     void MaybeSnapshotSystemTrace();
     void MaybeSnapshotUiTraces();
-    void MaybePostProcessUiTraces();
     void MaybeAddUiTracesToZip();
 
     void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index a417837..fc82886 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -1000,7 +1000,6 @@
 TEST_F(DumpstateTest, PreDumpUiData) {
     // These traces are always enabled, i.e. they are always pre-dumped
     const std::vector<std::filesystem::path> uiTraces = {
-        std::filesystem::path{"/data/misc/wmtrace/transactions_trace.winscope"},
         std::filesystem::path{"/data/misc/wmtrace/wm_transition_trace.winscope"},
         std::filesystem::path{"/data/misc/wmtrace/shell_transition_trace.winscope"},
     };
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 822ab7f..8eb7458 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -514,6 +514,8 @@
         // Make sure dex2oat is run with background priority.
         dexopt_flags |= DEXOPT_BOOTCOMPLETE | DEXOPT_IDLE_BACKGROUND_JOB;
 
+        parameters_.compilation_reason = "ab-ota";
+
         int res = dexopt(parameters_.apk_path,
                          parameters_.uid,
                          parameters_.pkgName,
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/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/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/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/Parcel.cpp b/libs/binder/Parcel.cpp
index a3ff7d2..6d2e12b 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>
@@ -43,11 +44,8 @@
 #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"
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 8ec4af9..58203c1 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -189,7 +189,7 @@
 
 void ProcessState::startThreadPool()
 {
-    AutoMutex _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
     if (!mThreadPoolStarted) {
         if (mMaxThreads == 0) {
             // see also getThreadPoolMaxTotalThreadCount
@@ -203,7 +203,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 +310,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 +374,7 @@
 
 void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
 {
-    AutoMutex _l(mLock);
+    std::unique_lock<std::mutex> _l(mLock);
 
     handle_entry* e = lookupHandleLocked(handle);
 
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 07ab093..1011571 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -30,7 +30,6 @@
 #include <binder/RpcServer.h>
 #include <binder/RpcTransportRaw.h>
 #include <log/log.h>
-#include <utils/Compat.h>
 
 #include "BuildFlags.h"
 #include "FdTrigger.h"
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index fa8f2b5..c8aff63 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -33,7 +33,6 @@
 #include <binder/RpcServer.h>
 #include <binder/RpcTransportRaw.h>
 #include <binder/Stability.h>
-#include <utils/Compat.h>
 #include <utils/String8.h>
 
 #include "BuildFlags.h"
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 5046253..749c2f8 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -20,7 +20,6 @@
 
 #include <android-base/macros.h>
 #include <android-base/scopeguard.h>
-#include <android-base/stringprintf.h>
 #include <binder/BpBinder.h>
 #include <binder/IPCThreadState.h>
 #include <binder/RpcServer.h>
@@ -30,6 +29,7 @@
 #include "Utils.h"
 
 #include <random>
+#include <sstream>
 
 #include <inttypes.h>
 
@@ -39,8 +39,6 @@
 
 namespace android {
 
-using base::StringPrintf;
-
 #if RPC_FLAKE_PRONE
 void rpcMaybeWaitToFlake() {
     [[clang::no_destroy]] static std::random_device r;
@@ -329,8 +327,10 @@
         desc = "(not promotable)";
     }
 
-    return StringPrintf("node{%p times sent: %zu times recd: %zu type: %s}",
-                        this->binder.unsafe_get(), this->timesSent, this->timesRecd, desc);
+    std::stringstream ss;
+    ss << "node{" << intptr_t(this->binder.unsafe_get()) << " times sent: " << this->timesSent
+       << " times recd: " << this->timesRecd << " type: " << desc << "}";
+    return ss.str();
 }
 
 RpcState::CommandData::CommandData(size_t size) : mSize(size) {
@@ -412,10 +412,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 "
@@ -1220,10 +1218,11 @@
     uint32_t protocolVersion = session->getProtocolVersion().value();
     if (protocolVersion < RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE &&
         !rpcFields->mObjectPositions.empty()) {
-        *errorMsg = StringPrintf("Parcel has attached objects but the session's protocol version "
-                                 "(%" PRIu32 ") is too old, must be at least %" PRIu32,
-                                 protocolVersion,
-                                 RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE);
+        std::stringstream ss;
+        ss << "Parcel has attached objects but the session's protocol version (" << protocolVersion
+           << ") is too old, must be at least "
+           << RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE;
+        *errorMsg = ss.str();
         return BAD_VALUE;
     }
 
@@ -1236,9 +1235,10 @@
             case RpcSession::FileDescriptorTransportMode::UNIX: {
                 constexpr size_t kMaxFdsPerMsg = 253;
                 if (rpcFields->mFds->size() > kMaxFdsPerMsg) {
-                    *errorMsg = StringPrintf("Too many file descriptors in Parcel for unix "
-                                             "domain socket: %zu (max is %zu)",
-                                             rpcFields->mFds->size(), kMaxFdsPerMsg);
+                    std::stringstream ss;
+                    ss << "Too many file descriptors in Parcel for unix domain socket: "
+                       << rpcFields->mFds->size() << " (max is " << kMaxFdsPerMsg << ")";
+                    *errorMsg = ss.str();
                     return BAD_VALUE;
                 }
                 break;
@@ -1249,9 +1249,10 @@
                 // available on Android
                 constexpr size_t kMaxFdsPerMsg = 8;
                 if (rpcFields->mFds->size() > kMaxFdsPerMsg) {
-                    *errorMsg = StringPrintf("Too many file descriptors in Parcel for Trusty "
-                                             "IPC connection: %zu (max is %zu)",
-                                             rpcFields->mFds->size(), kMaxFdsPerMsg);
+                    std::stringstream ss;
+                    ss << "Too many file descriptors in Parcel for Trusty IPC connection: "
+                       << rpcFields->mFds->size() << " (max is " << kMaxFdsPerMsg << ")";
+                    *errorMsg = ss.str();
                     return BAD_VALUE;
                 }
                 break;
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index dd632c0..8942c31 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -22,6 +22,19 @@
 #include <log/log.h>
 #include <utils/Errors.h>
 
+/* 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)) {                          \
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/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/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 5bf9680..f2693dd 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -22,7 +22,6 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/stringprintf.h>
 #include <android/debug/BnAdbCallback.h>
 #include <android/debug/IAdbManager.h>
 #include <android/os/BnServiceManager.h>
@@ -46,7 +45,6 @@
 using android::base::LogId;
 using android::base::LogSeverity;
 using android::base::StdioLogger;
-using android::base::StringPrintf;
 using std::string_view_literals::operator""sv;
 
 namespace {
@@ -57,11 +55,12 @@
 
 int Usage(const char* program) {
     auto basename = Basename(program);
-    auto format = R"(dispatch calls to RPC service.
+    // clang-format off
+    LOG(ERROR) << R"(dispatch calls to RPC service.
 Usage:
-  %s [-g] [-i <ip_address>] <service_name>
+  )" << basename << R"( [-g] [-i <ip_address>] <service_name>
     <service_name>: the service to connect to.
-  %s [-g] manager
+  )" << basename << R"( [-g] manager
     Runs an RPC-friendly service that redirects calls to servicemanager.
 
   -g: use getService() instead of checkService().
@@ -71,7 +70,7 @@
   blocks until killed.
   Otherwise, writes error message to stderr and exits with non-zero code.
 )";
-    LOG(ERROR) << StringPrintf(format, basename.c_str(), basename.c_str());
+    // clang-format on
     return EX_USAGE;
 }
 
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 341e9ce..659943a 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -40,6 +40,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/RpcServer.h>
 #include <binder/RpcSession.h>
+#include <utils/Flattenable.h>
 
 #include <linux/sched.h>
 #include <sys/epoll.h>
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 1340ea1..bc34d4c 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -18,7 +18,6 @@
 // only used on NDK tests outside of vendor
 #include <aidl/IBinderRpcTest.h>
 #endif
-#include <android-base/stringprintf.h>
 
 #include <chrono>
 #include <cstdlib>
@@ -59,12 +58,12 @@
 
 static std::string WaitStatusToString(int wstatus) {
     if (WIFEXITED(wstatus)) {
-        return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
+        return std::format("exit status {}", WEXITSTATUS(wstatus));
     }
     if (WIFSIGNALED(wstatus)) {
-        return base::StringPrintf("term signal %d", WTERMSIG(wstatus));
+        return std::format("term signal {}", WTERMSIG(wstatus));
     }
-    return base::StringPrintf("unexpected state %d", wstatus);
+    return std::format("unexpected state {}", wstatus);
 }
 
 static void debugBacktrace(pid_t pid) {
@@ -260,9 +259,9 @@
     bool noKernel = GetParam().noKernel;
 
     std::string path = android::base::GetExecutableDirectory();
-    auto servicePath = android::base::StringPrintf("%s/binder_rpc_test_service%s%s", path.c_str(),
-                                                   singleThreaded ? "_single_threaded" : "",
-                                                   noKernel ? "_no_kernel" : "");
+    auto servicePath =
+            std::format("{}/binder_rpc_test_service{}{}", path,
+                        singleThreaded ? "_single_threaded" : "", noKernel ? "_no_kernel" : "");
 
     base::unique_fd bootstrapClientFd, socketFd;
 
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index eceff35..c3070dd 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -22,7 +22,6 @@
 #include <BnBinderRpcCallback.h>
 #include <BnBinderRpcSession.h>
 #include <BnBinderRpcTest.h>
-#include <android-base/stringprintf.h>
 #include <binder/Binder.h>
 #include <binder/BpBinder.h>
 #include <binder/IPCThreadState.h>
@@ -58,6 +57,7 @@
 #include "../BuildFlags.h"
 #include "../FdTrigger.h"
 #include "../RpcState.h" // for debugging
+#include "format.h"
 #include "utils/Errors.h"
 
 namespace android {
@@ -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
 }
 
@@ -91,7 +90,7 @@
 }
 
 static inline std::string trustyIpcPort(uint32_t serverVersion) {
-    return base::StringPrintf("com.android.trusty.binderRpcTestService.V%" PRIu32, serverVersion);
+    return std::format("com.android.trusty.binderRpcTestService.V{}", serverVersion);
 }
 
 enum class SocketType {
diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
index cb632e9..aaca8d0 100644
--- a/libs/binder/tests/binderRpcTestServiceTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
@@ -16,7 +16,6 @@
 
 #define TLOG_TAG "binderRpcTestService"
 
-#include <android-base/stringprintf.h>
 #include <binder/RpcServerTrusty.h>
 #include <inttypes.h>
 #include <lib/tipc/tipc.h>
@@ -28,7 +27,6 @@
 #include "binderRpcTestCommon.h"
 
 using namespace android;
-using android::base::StringPrintf;
 using binder::Status;
 
 static int gConnectionCounter = 0;
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index fcb83bd..8acaae6 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -16,7 +16,6 @@
 
 #define LOG_TAG "binderRpcTest"
 
-#include <android-base/stringprintf.h>
 #include <binder/RpcTransportTipcTrusty.h>
 #include <trusty-gtest.h>
 #include <trusty_ipc.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/format.h b/libs/binder/tests/format.h
new file mode 100644
index 0000000..b5440a4
--- /dev/null
+++ b/libs/binder/tests/format.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+// TODO(b/302723053): remove this header and replace with <format> once b/175635923 is done
+// ETA for this blocker is 2023-10-27~2023-11-10.
+// Also, remember to remove fmtlib's format.cc from trusty makefiles.
+
+#if __has_include(<format>)
+#include <format>
+#else
+#include <fmt/format.h>
+
+namespace std {
+using fmt::format;
+}
+#endif
\ No newline at end of file
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..f3006cd 100644
--- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -35,7 +35,7 @@
     if (transaction.has_value()) {
         intermediateFile = std::tmpfile();
 
-        android::base::unique_fd fdForWriting(fileno(intermediateFile));
+        android::base::unique_fd fdForWriting(dup(fileno(intermediateFile)));
         auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
 
         std::fclose(intermediateFile);
diff --git a/libs/binder/trusty/binderRpcTest/rules.mk b/libs/binder/trusty/binderRpcTest/rules.mk
index 975f689..e46ccfb 100644
--- a/libs/binder/trusty/binderRpcTest/rules.mk
+++ b/libs/binder/trusty/binderRpcTest/rules.mk
@@ -21,6 +21,7 @@
 MANIFEST := $(LOCAL_DIR)/manifest.json
 
 MODULE_SRCS += \
+	$(FMTLIB_DIR)/src/format.cc \
 	$(LIBBINDER_TESTS_DIR)/binderRpcUniversalTests.cpp \
 	$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
 	$(LIBBINDER_TESTS_DIR)/binderRpcTestTrusty.cpp \
diff --git a/libs/binder/trusty/binderRpcTest/service/rules.mk b/libs/binder/trusty/binderRpcTest/service/rules.mk
index 5d1a51d..50ae3d2 100644
--- a/libs/binder/trusty/binderRpcTest/service/rules.mk
+++ b/libs/binder/trusty/binderRpcTest/service/rules.mk
@@ -21,6 +21,7 @@
 MANIFEST := $(LOCAL_DIR)/manifest.json
 
 MODULE_SRCS := \
+	$(FMTLIB_DIR)/src/format.cc \
 	$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
 	$(LIBBINDER_TESTS_DIR)/binderRpcTestServiceTrusty.cpp \
 
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/bufferstreams/examples/app/Android.bp b/libs/bufferstreams/examples/app/Android.bp
index 0ecf94c..d6305f8 100644
--- a/libs/bufferstreams/examples/app/Android.bp
+++ b/libs/bufferstreams/examples/app/Android.bp
@@ -14,14 +14,33 @@
 
 android_app {
     name: "BufferStreamsDemoApp",
-    srcs: ["java/**/*.java"],
+    srcs: ["java/**/*.kt"],
     sdk_version: "current",
 
     jni_uses_platform_apis: true,
     jni_libs: ["libbufferstreamdemoapp"],
     use_embedded_native_libs: true,
+    kotlincflags: [
+        "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
+    ],
+
+    resource_dirs: ["res"],
 
     static_libs: [
+        "androidx.activity_activity-compose",
         "androidx.appcompat_appcompat",
+        "androidx.compose.foundation_foundation",
+        "androidx.compose.material3_material3",
+        "androidx.compose.runtime_runtime",
+        "androidx.compose.ui_ui",
+        "androidx.compose.ui_ui-graphics",
+        "androidx.compose.ui_ui-tooling-preview",
+        "androidx.core_core-ktx",
+        "androidx.lifecycle_lifecycle-runtime-ktx",
+        "androidx.navigation_navigation-common-ktx",
+        "androidx.navigation_navigation-compose",
+        "androidx.navigation_navigation-fragment-ktx",
+        "androidx.navigation_navigation-runtime-ktx",
+        "androidx.navigation_navigation-ui-ktx",
     ],
 }
diff --git a/libs/bufferstreams/examples/app/AndroidManifest.xml b/libs/bufferstreams/examples/app/AndroidManifest.xml
index 872193c..a5e2fa8 100644
--- a/libs/bufferstreams/examples/app/AndroidManifest.xml
+++ b/libs/bufferstreams/examples/app/AndroidManifest.xml
@@ -9,14 +9,15 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:theme="@style/Theme.AppCompat.Light"
+        android:theme="@style/Theme.Jetpack"
         tools:targetApi="34">
         <activity
             android:name=".MainActivity"
-            android:exported="true">
+            android:exported="true"
+            android:label="@string/app_name"
+            android:theme="@style/Theme.Jetpack">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
-
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt
new file mode 100644
index 0000000..ff3ae5a
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt
@@ -0,0 +1,40 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+
+@Composable
+fun BufferDemosAppBar(
+        currentScreen: BufferDemoScreen,
+        canNavigateBack: Boolean,
+        navigateUp: () -> Unit,
+        modifier: Modifier = Modifier
+) {
+    TopAppBar(
+            title = { Text(stringResource(currentScreen.title)) },
+            colors =
+            TopAppBarDefaults.mediumTopAppBarColors(
+                    containerColor = MaterialTheme.colorScheme.primaryContainer
+            ),
+            modifier = modifier,
+            navigationIcon = {
+                if (canNavigateBack) {
+                    IconButton(onClick = navigateUp) {
+                        Icon(
+                                imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+                                contentDescription = stringResource(R.string.back_button)
+                        )
+                    }
+                }
+            }
+    )
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt
new file mode 100644
index 0000000..a2db934
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferStreamJNI.kt
@@ -0,0 +1,27 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+class BufferStreamJNI {
+    // Used to load the 'bufferstreamsdemoapp' library on application startup.
+    init {
+        System.loadLibrary("bufferstreamdemoapp")
+    }
+
+    /**
+     * A native method that is implemented by the 'bufferstreamsdemoapp' native library, which is
+     * packaged with this application.
+     */
+    external fun stringFromJNI()
+    external fun testBufferQueueCreation()
+
+    companion object {
+        fun companion_stringFromJNI() {
+            val instance = BufferStreamJNI()
+            instance.stringFromJNI()
+        }
+
+        fun companion_testBufferQueueCreation() {
+            val instance = BufferStreamJNI()
+            instance.testBufferQueueCreation()
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt
new file mode 100644
index 0000000..46ce028
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt
@@ -0,0 +1,33 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun DemoScreen1(modifier: Modifier = Modifier) {
+    Column(modifier = modifier, verticalArrangement = Arrangement.SpaceBetween) {
+        Card(modifier = Modifier.fillMaxWidth().weight(1f, false).padding(16.dp).height(400.dp)) {
+            Text("Log output", modifier = Modifier.padding(16.dp))
+        }
+        Row(modifier = Modifier.weight(1f, false).padding(16.dp)) {
+            Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
+                Button(
+                        modifier = Modifier.fillMaxWidth(),
+                        onClick = { BufferStreamJNI.companion_testBufferQueueCreation() }
+                ) { Text("Run") }
+                OutlinedButton(modifier = Modifier.fillMaxWidth(), onClick = {}) { Text("Clear") }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt
new file mode 100644
index 0000000..5efee92
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen2.kt
@@ -0,0 +1,7 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.runtime.Composable
+
+@Composable
+fun DemoScreen2() {
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt
new file mode 100644
index 0000000..8cba857
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen3.kt
@@ -0,0 +1,7 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.runtime.Composable
+
+@Composable
+fun DemoScreen3() {
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java
deleted file mode 100644
index 67b95a5..0000000
--- a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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.
-
-package com.android.graphics.bufferstreamsdemoapp;
-
-import android.os.Bundle;
-import android.widget.TextView;
-import androidx.appcompat.app.AppCompatActivity;
-
-public class MainActivity extends AppCompatActivity {
-    // Used to load the 'bufferstreamsdemoapp' library on application startup.
-    static { System.loadLibrary("bufferstreamdemoapp"); }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        RunBufferQueue();
-        System.out.println("stringFromJNI: " + stringFromJNI());
-    }
-
-    /**
-     * A native method that is implemented by the 'bufferstreamsdemoapp' native
-     * library, which is packaged with this application.
-     */
-    public native String stringFromJNI();
-    public native void RunBufferQueue();
-}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt
new file mode 100644
index 0000000..f3f4404
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt
@@ -0,0 +1,136 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.annotation.StringRes
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.material3.Button
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import com.android.graphics.bufferstreamsdemoapp.ui.theme.JetpackTheme
+
+class MainActivity : ComponentActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            JetpackTheme {
+                Surface(
+                        modifier = Modifier.fillMaxSize(),
+                        color = MaterialTheme.colorScheme.background
+                ) { BufferDemosApp() }
+            }
+        }
+    }
+}
+
+enum class BufferDemoScreen(val route: String, @StringRes val title: Int) {
+    Start(route = "start", title = R.string.start),
+    Demo1(route = "demo1", title = R.string.demo1),
+    Demo2(route = "demo2", title = R.string.demo2),
+    Demo3(route = "demo3", title = R.string.demo3);
+
+    companion object {
+        fun findByRoute(route: String): BufferDemoScreen {
+            return values().find { it.route == route }!!
+        }
+    }
+}
+
+@Composable
+fun BufferDemosApp() {
+    var navController: NavHostController = rememberNavController()
+    // Get current back stack entry
+    val backStackEntry by navController.currentBackStackEntryAsState()
+    // Get the name of the current screen
+    val currentScreen =
+            BufferDemoScreen.findByRoute(
+                    backStackEntry?.destination?.route ?: BufferDemoScreen.Start.route
+            )
+
+    Scaffold(
+            topBar = {
+                BufferDemosAppBar(
+                        currentScreen = currentScreen,
+                        canNavigateBack = navController.previousBackStackEntry != null,
+                        navigateUp = { navController.navigateUp() }
+                )
+            }
+    ) {
+        NavHost(
+                navController = navController,
+                startDestination = BufferDemoScreen.Start.route,
+                modifier = Modifier.padding(10.dp)
+        ) {
+            composable(route = BufferDemoScreen.Start.route) {
+                DemoList(
+                        onButtonClicked = {
+                            navController.navigate(it)
+                        },
+                )
+            }
+            composable(route = BufferDemoScreen.Demo1.route) {
+                DemoScreen1(modifier = Modifier.fillMaxHeight().padding(top = 100.dp))
+            }
+            composable(route = BufferDemoScreen.Demo2.route) { DemoScreen2() }
+            composable(route = BufferDemoScreen.Demo3.route) { DemoScreen3() }
+        }
+    }
+}
+
+@Composable
+fun DemoList(onButtonClicked: (String) -> Unit) {
+    var modifier = Modifier.fillMaxSize().padding(16.dp)
+
+    Column(modifier = modifier, verticalArrangement = Arrangement.SpaceBetween) {
+        Column(
+                modifier = Modifier.fillMaxWidth(),
+                horizontalAlignment = Alignment.CenterHorizontally,
+                verticalArrangement = Arrangement.spacedBy(8.dp)
+        ) {
+            Spacer(modifier = Modifier.height(100.dp))
+            Text(text = "Buffer Demos", style = MaterialTheme.typography.titleLarge)
+            Spacer(modifier = Modifier.height(8.dp))
+        }
+        Row(modifier = Modifier.weight(2f, false)) {
+            Column(
+                    modifier = Modifier.fillMaxWidth(),
+                    horizontalAlignment = Alignment.CenterHorizontally,
+                    verticalArrangement = Arrangement.spacedBy(16.dp)
+            ) {
+                for (item in BufferDemoScreen.values()) {
+                    if (item.route != BufferDemoScreen.Start.route)
+                        SelectDemoButton(name = stringResource(item.title), onClick = { onButtonClicked(item.route) })
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun SelectDemoButton(name: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
+    Button(onClick = onClick, modifier = modifier.widthIn(min = 250.dp)) { Text(name) }
+}
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt
new file mode 100644
index 0000000..d85ea72
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Color.kt
@@ -0,0 +1,11 @@
+package com.android.graphics.bufferstreamsdemoapp.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt
new file mode 100644
index 0000000..fccd93a
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Theme.kt
@@ -0,0 +1,60 @@
+package com.android.graphics.bufferstreamsdemoapp.ui.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+private val DarkColorScheme = darkColorScheme(
+  primary = Purple80,
+  secondary = PurpleGrey80,
+  tertiary = Pink80
+)
+
+private val LightColorScheme = lightColorScheme(
+  primary = Purple40,
+  secondary = PurpleGrey40,
+  tertiary = Pink40
+)
+
+@Composable
+fun JetpackTheme(
+  darkTheme: Boolean = isSystemInDarkTheme(),
+  // Dynamic color is available on Android 12+
+  dynamicColor: Boolean = true,
+  content: @Composable () -> Unit
+) {
+  val colorScheme = when {
+    dynamicColor -> {
+      val context = LocalContext.current
+      if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+    }
+
+    darkTheme -> DarkColorScheme
+    else -> LightColorScheme
+  }
+  val view = LocalView.current
+  if (!view.isInEditMode) {
+    SideEffect {
+      val window = (view.context as Activity).window
+      window.statusBarColor = colorScheme.primary.toArgb()
+      WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
+    }
+  }
+
+  MaterialTheme(
+    colorScheme = colorScheme,
+    typography = Typography,
+    content = content
+  )
+}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt
new file mode 100644
index 0000000..06814ea
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/ui/Type.kt
@@ -0,0 +1,18 @@
+package com.android.graphics.bufferstreamsdemoapp.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+  bodyLarge = TextStyle(
+    fontFamily = FontFamily.Default,
+    fontWeight = FontWeight.Normal,
+    fontSize = 16.sp,
+    lineHeight = 24.sp,
+    letterSpacing = 0.5.sp
+  )
+)
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/jni/main.cpp b/libs/bufferstreams/examples/app/jni/main.cpp
index 34e0eb4..3d3fee4 100644
--- a/libs/bufferstreams/examples/app/jni/main.cpp
+++ b/libs/bufferstreams/examples/app/jni/main.cpp
@@ -19,16 +19,16 @@
 extern "C"
 {
     JNIEXPORT jstring JNICALL
-    Java_com_android_graphics_bufferstreamsdemoapp_MainActivity_stringFromJNI(
-            JNIEnv *env,
+    Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_stringFromJNI(
+            JNIEnv* env,
             jobject /* this */) {
         const char* hello = "Hello from C++";
         return env->NewStringUTF(hello);
     }
 
     JNIEXPORT void JNICALL
-    Java_com_android_graphics_bufferstreamsdemoapp_MainActivity_RunBufferQueue(
-            JNIEnv *env,
+    Java_com_android_graphics_bufferstreamsdemoapp_BufferStreamJNI_testBufferQueueCreation(
+            JNIEnv* /* env */,
             jobject /* this */) {
         android::sp<android::IGraphicBufferProducer> producer;
         android::sp<android::IGraphicBufferConsumer> consumer;
diff --git a/libs/bufferstreams/examples/app/res/layout/activity_main.xml b/libs/bufferstreams/examples/app/res/layout/activity_main.xml
deleted file mode 100644
index 79fb331..0000000
--- a/libs/bufferstreams/examples/app/res/layout/activity_main.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context=".MainActivity">
-
-    <TextView
-        android:id="@+id/sample_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="Hello World!"
-        tools:layout_editor_absoluteX="100dp"
-        tools:layout_editor_absoluteY="100dp" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/res/values/strings.xml b/libs/bufferstreams/examples/app/res/values/strings.xml
index e652102..75c8ab5 100644
--- a/libs/bufferstreams/examples/app/res/values/strings.xml
+++ b/libs/bufferstreams/examples/app/res/values/strings.xml
@@ -1,3 +1,8 @@
 <resources>
     <string name="app_name">Buffer Demos</string>
+    <string name="start">Start</string>
+    <string name="demo1">Demo 1</string>
+    <string name="demo2">Demo 2</string>
+    <string name="demo3">Demo 3</string>
+    <string name="back_button">Back</string>
 </resources>
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/res/values/themes.xml b/libs/bufferstreams/examples/app/res/values/themes.xml
new file mode 100644
index 0000000..eeb308a
--- /dev/null
+++ b/libs/bufferstreams/examples/app/res/values/themes.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="Theme.Jetpack" parent="android:Theme.Material.Light.NoActionBar" />
+</resources>
\ No newline at end of file
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 9ebaf16..47607a0 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, // cover both system ANGLE and ANGLE APK
+        ANGLE = 5,
     };
 
     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/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 7b2f39c..04e2fff 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -567,7 +567,6 @@
         return nullptr;
     }
 
-    // use ANGLE APK driver
     android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE);
     driver_t* hnd = nullptr;
 
@@ -636,13 +635,7 @@
 Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
                                                         const bool exact) {
     ATRACE_CALL();
-    if (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);
-    }
-
+    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/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index d447d1e..f06a045 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -163,11 +163,11 @@
         addLoadingTime(driver, driverLoadingTime, &appInfo);
         appInfo.appPackageName = appPackageName;
         appInfo.driverVersionCode = driverVersionCode;
-        appInfo.angleInUse = driver == GpuStatsInfo::Driver::ANGLE;
+        appInfo.angleInUse = driverPackageName == "angle";
         appInfo.lastAccessTime = std::chrono::system_clock::now();
         mAppStats.insert({appStatsKey, appInfo});
     } else {
-        mAppStats[appStatsKey].angleInUse = driver == GpuStatsInfo::Driver::ANGLE;
+        mAppStats[appStatsKey].angleInUse = driverPackageName == "angle";
         addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
         mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now();
     }
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/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/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7dfbf94..47b9a0c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -756,10 +756,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 +770,6 @@
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
-        mStaleEventTimeout(staleEventTimeout),
         mLatencyAggregator(),
         mLatencyTracker(&mLatencyAggregator) {
     mLooper = sp<Looper>::make(false);
@@ -956,20 +951,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 (!input_flags::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 (!input_flags::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 +1067,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 (!input_flags::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 +1090,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 (!input_flags::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 +1108,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 (!input_flags::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 +1137,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 +1213,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 (!input_flags::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.
@@ -2032,10 +2040,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 +2461,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 +2873,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(
@@ -2910,8 +2924,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;
+    }
 
     it->addPointers(pointerIds, windowInfo->transform);
 }
@@ -4419,7 +4438,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;
                 }
             }
@@ -4531,6 +4551,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);
@@ -4672,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>
@@ -5797,6 +5842,9 @@
         dump += INDENT "Connections: <none>\n";
     }
 
+    dump += "input_flags::remove_app_switch_drops() = ";
+    dump += toString(input_flags::remove_app_switch_drops());
+    dump += "\n";
     if (isAppSwitchPendingLocked()) {
         dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
                              ns2ms(mAppSwitchDueTime - now()));
@@ -6733,6 +6781,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..e428c4e 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);
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/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3c87f71..2509c60 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
@@ -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";
@@ -391,6 +391,8 @@
 
     std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
 
+    std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
+
     BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
 
     // All three ANR-related callbacks behave the same way, so we use this generic function to wait
@@ -545,6 +547,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 +592,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());
@@ -2160,6 +2167,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.
@@ -7529,6 +7599,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/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 1775a7a..f32fb3a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -30,8 +30,8 @@
 #include <scheduler/Fps.h>
 
 #include "DisplayHardware/Hal.h"
+#include "FlagManager.h"
 #include "Scheduler/StrongTyping.h"
-#include "Utils/FlagUtils.h"
 
 namespace android {
 
@@ -50,7 +50,6 @@
 
 using DisplayModes = ftl::SmallMap<DisplayModeId, DisplayModePtr, 3>;
 using DisplayModeIterator = DisplayModes::const_iterator;
-using namespace com::android::graphics::surfaceflinger;
 
 class DisplayMode {
 public:
@@ -140,7 +139,7 @@
     // Peak refresh rate represents the highest refresh rate that can be used
     // for the presentation.
     Fps getPeakFps() const {
-        return flagutils::vrrConfigEnabled() && mVrrConfig
+        return FlagManager::getInstance().vrr_config() && mVrrConfig
                 ? Fps::fromPeriodNsecs(mVrrConfig->minFrameIntervalNs)
                 : mVsyncRate;
     }
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
index f8ad8f6..11a5e0d 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -26,37 +26,21 @@
 #include <server_configurable_flags/get_flags.h>
 #include <cinttypes>
 
-namespace android {
-static constexpr const char* kExperimentNamespace = "surface_flinger_native_boot";
-static constexpr const int64_t kDemoFlag = -1;
+#include <com_android_graphics_surfaceflinger_flags.h>
 
+namespace android {
+using namespace com::android::graphics::surfaceflinger;
+
+static constexpr const char* kExperimentNamespace = "surface_flinger_native_boot";
+
+std::unique_ptr<FlagManager> FlagManager::mInstance;
+std::once_flag FlagManager::mOnce;
+
+FlagManager::FlagManager(ConstructorTag) {}
 FlagManager::~FlagManager() = default;
 
-void FlagManager::dump(std::string& result) const {
-    base::StringAppendF(&result, "FlagManager values: \n");
-    base::StringAppendF(&result, "demo_flag: %" PRId64 "\n", demo_flag());
-    base::StringAppendF(&result, "use_adpf_cpu_hint: %s\n", use_adpf_cpu_hint() ? "true" : "false");
-    base::StringAppendF(&result, "use_skia_tracing: %s\n", use_skia_tracing() ? "true" : "false");
-}
-
 namespace {
-template <typename T>
-std::optional<T> doParse(const char* str);
-
-template <>
-[[maybe_unused]] std::optional<int32_t> doParse(const char* str) {
-    int32_t ret;
-    return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
-}
-
-template <>
-[[maybe_unused]] std::optional<int64_t> doParse(const char* str) {
-    int64_t ret;
-    return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
-}
-
-template <>
-[[maybe_unused]] std::optional<bool> doParse(const char* str) {
+std::optional<bool> parseBool(const char* str) {
     base::ParseBoolResult parseResult = base::ParseBool(str);
     switch (parseResult) {
         case base::ParseBoolResult::kTrue:
@@ -67,44 +51,133 @@
             return std::nullopt;
     }
 }
+
+bool getFlagValue(std::function<bool()> getter, std::optional<bool> overrideValue) {
+    if (overrideValue.has_value()) {
+        return *overrideValue;
+    }
+
+    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
 
-std::string FlagManager::getServerConfigurableFlag(const std::string& experimentFlagName) const {
-    return server_configurable_flags::GetServerConfigurableFlag(kExperimentNamespace,
-                                                                experimentFlagName, "");
+const FlagManager& FlagManager::getInstance() {
+    return getMutableInstance();
 }
 
-template int32_t FlagManager::getValue<int32_t>(const std::string&, std::optional<int32_t>,
-                                                int32_t) const;
-template int64_t FlagManager::getValue<int64_t>(const std::string&, std::optional<int64_t>,
-                                                int64_t) const;
-template bool FlagManager::getValue<bool>(const std::string&, std::optional<bool>, bool) const;
-template <typename T>
-T FlagManager::getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
-                        T defaultValue) const {
-    // System property takes precedence over the experiment config server value.
-    if (systemPropertyOpt.has_value()) {
-        return *systemPropertyOpt;
+FlagManager& FlagManager::getMutableInstance() {
+    std::call_once(mOnce, [&] {
+        LOG_ALWAYS_FATAL_IF(mInstance, "Instance already created");
+        mInstance = std::make_unique<FlagManager>(ConstructorTag{});
+    });
+
+    return *mInstance;
+}
+
+void FlagManager::markBootCompleted() {
+    mBootCompleted = true;
+}
+
+void FlagManager::setUnitTestMode() {
+    mUnitTestMode = true;
+
+    // Also set boot completed as we don't really care about it in unit testing
+    mBootCompleted = true;
+}
+
+void FlagManager::dump(std::string& result) const {
+#define DUMP_FLAG(name) dumpFlag(result, #name, std::bind(&FlagManager::name, this))
+
+    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);
+    DUMP_FLAG(hotplug2);
+    DUMP_FLAG(hdcp_level_hal);
+
+#undef DUMP_FLAG
+}
+
+std::optional<bool> FlagManager::getBoolProperty(const char* property) const {
+    return parseBool(base::GetProperty(property, "").c_str());
+}
+
+bool FlagManager::getServerConfigurableFlag(const char* experimentFlagName) const {
+    const auto value = server_configurable_flags::GetServerConfigurableFlag(kExperimentNamespace,
+                                                                            experimentFlagName, "");
+    const auto res = parseBool(value.c_str());
+    return res.has_value() && res.value();
+}
+
+#define FLAG_MANAGER_LEGACY_SERVER_FLAG(name, syspropOverride, serverFlagName)              \
+    bool FlagManager::name() const {                                                        \
+        LOG_ALWAYS_FATAL_IF(!mBootCompleted,                                                \
+                            "Can't read %s before boot completed as it is server writable", \
+                            __func__);                                                      \
+        const auto debugOverride = getBoolProperty(syspropOverride);                        \
+        if (debugOverride.has_value()) return debugOverride.value();                        \
+        return getServerConfigurableFlag(serverFlagName);                                   \
     }
-    std::string str = getServerConfigurableFlag(experimentFlagName);
-    return str.empty() ? defaultValue : doParse<T>(str.c_str()).value_or(defaultValue);
-}
 
-int64_t FlagManager::demo_flag() const {
-    std::optional<int64_t> sysPropVal = std::nullopt;
-    return getValue("DemoFeature__demo_flag", sysPropVal, kDemoFlag);
-}
+#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted)                \
+    bool FlagManager::name() const {                                                            \
+        if (checkForBootCompleted) {                                                            \
+            LOG_ALWAYS_FATAL_IF(!mBootCompleted,                                                \
+                                "Can't read %s before boot completed as it is server writable", \
+                                __func__);                                                      \
+        }                                                                                       \
+        static std::optional<bool> debugOverride = getBoolProperty(syspropOverride);            \
+        static bool value = getFlagValue([] { return flags::name(); }, debugOverride);          \
+        if (mUnitTestMode) {                                                                    \
+            /*                                                                                  \
+             * When testing, we don't want to rely on the cached values stored in the static    \
+             * variables.                                                                       \
+             */                                                                                 \
+            debugOverride = getBoolProperty(syspropOverride);                                   \
+            value = getFlagValue([] { return flags::name(); }, debugOverride);                  \
+        }                                                                                       \
+        return value;                                                                           \
+    }
 
-bool FlagManager::use_adpf_cpu_hint() const {
-    std::optional<bool> sysPropVal =
-            doParse<bool>(base::GetProperty("debug.sf.enable_adpf_cpu_hint", "").c_str());
-    return getValue("AdpfFeature__adpf_cpu_hint", sysPropVal, false);
-}
+#define FLAG_MANAGER_SERVER_FLAG(name, syspropOverride) \
+    FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true)
 
-bool FlagManager::use_skia_tracing() const {
-    std::optional<bool> sysPropVal =
-            doParse<bool>(base::GetProperty(PROPERTY_SKIA_ATRACE_ENABLED, "").c_str());
-    return getValue("SkiaTracingFeature__use_skia_tracing", sysPropVal, false);
+#define FLAG_MANAGER_READ_ONLY_FLAG(name, syspropOverride) \
+    FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false)
+
+/// Legacy server flags ///
+FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
+FLAG_MANAGER_LEGACY_SERVER_FLAG(use_adpf_cpu_hint, "debug.sf.enable_adpf_cpu_hint",
+                                "AdpfFeature__adpf_cpu_hint")
+FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED,
+                                "SkiaTracingFeature__use_skia_tracing")
+
+/// Trunk stable readonly flags ///
+FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
+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, "")
+
+/// Trunk stable server flags ///
+FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
+
+/// Exceptions ///
+bool FlagManager::dont_skip_on_early() const {
+    // Even though this is a server writable flag, we do call it before boot completed, but that's
+    // fine since the decision is done per frame. We can't do caching though.
+    return flags::dont_skip_on_early();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
index e834142..1a68993 100644
--- a/services/surfaceflinger/FlagManager.h
+++ b/services/surfaceflinger/FlagManager.h
@@ -17,32 +17,61 @@
 #pragma once
 
 #include <cstdint>
+#include <mutex>
 #include <optional>
 #include <string>
 
 namespace android {
 // Manages flags for SurfaceFlinger, including default values, system properties, and Mendel
-// experiment configuration values.
+// experiment configuration values. Can be called from any thread.
 class FlagManager {
+private:
+    // Effectively making the constructor private, while allowing std::make_unique to work
+    struct ConstructorTag {};
+
 public:
-    FlagManager() = default;
+    static const FlagManager& getInstance();
+    static FlagManager& getMutableInstance();
+
+    FlagManager(ConstructorTag);
     virtual ~FlagManager();
+
+    void markBootCompleted();
     void dump(std::string& result) const;
 
-    int64_t demo_flag() const;
+    void setUnitTestMode();
 
+    /// Legacy server flags ///
+    bool test_flag() const;
     bool use_adpf_cpu_hint() const;
-
     bool use_skia_tracing() const;
 
+    /// Trunk stable readonly flags ///
+    bool connected_display() const;
+    bool enable_small_area_detection() const;
+    bool misc1() const;
+    bool vrr_config() const;
+    bool hotplug2() const;
+    bool hdcp_level_hal() const;
+
+    /// Trunk stable server flags ///
+    bool late_boot_misc2() const;
+    bool dont_skip_on_early() const;
+
+protected:
+    // overridden for unit tests
+    virtual std::optional<bool> getBoolProperty(const char*) const;
+    virtual bool getServerConfigurableFlag(const char*) const;
+
 private:
-    friend class FlagManagerTest;
+    friend class TestableFlagManager;
 
-    // Wrapper for mocking in test.
-    virtual std::string getServerConfigurableFlag(const std::string& experimentFlagName) const;
+    FlagManager(const FlagManager&) = delete;
 
-    template <typename T>
-    T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
-               T defaultValue) const;
+    std::atomic_bool mBootCompleted = false;
+    std::atomic_bool mUnitTestMode = false;
+
+    static std::unique_ptr<FlagManager> mInstance;
+    static std::once_flag mOnce;
 };
 } // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 42676c6..2ac7319 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -17,9 +17,9 @@
 #include <algorithm>
 
 #include "Client.h"
+#include "FlagManager.h"
 #include "Layer.h"
 #include "RefreshRateOverlay.h"
-#include "Utils/FlagUtils.h"
 
 #include <SkSurface.h>
 
@@ -268,7 +268,8 @@
 }
 
 void RefreshRateOverlay::changeRenderRate(Fps renderFps) {
-    if (mFeatures.test(Features::RenderRate) && mVsyncRate && flagutils::vrrConfigEnabled()) {
+    if (mFeatures.test(Features::RenderRate) && mVsyncRate &&
+        FlagManager::getInstance().vrr_config()) {
         mRenderFps = renderFps;
         const auto buffer = getOrCreateBuffers(*mVsyncRate, renderFps)[mFrame];
         createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 9a55c94..7f627f8 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -45,6 +45,7 @@
 
 #include <scheduler/VsyncConfig.h>
 #include "DisplayHardware/DisplayMode.h"
+#include "FlagManager.h"
 #include "FrameTimeline.h"
 #include "VSyncDispatch.h"
 #include "VSyncTracker.h"
@@ -308,7 +309,7 @@
     auto connection = sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
                                                       IPCThreadState::self()->getCallingUid(),
                                                       eventRegistration);
-    if (flags::misc1()) {
+    if (FlagManager::getInstance().misc1()) {
         const int policy = SCHED_FIFO;
         connection->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
     }
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ff82914..21bbb08 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -21,7 +21,6 @@
 #include "LayerHistory.h"
 
 #include <android-base/stringprintf.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
 #include <cutils/properties.h>
 #include <gui/TraceUtils.h>
 #include <utils/Log.h>
@@ -34,16 +33,15 @@
 
 #include "../Layer.h"
 #include "EventThread.h"
+#include "FlagManager.h"
 #include "LayerInfo.h"
 
 namespace android::scheduler {
 
 namespace {
 
-using namespace com::android::graphics::surfaceflinger;
-
 bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
-    if (flags::misc1() && !info.isVisible()) {
+    if (FlagManager::getInstance().misc1() && !info.isVisible()) {
         return false;
     }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index eb69d0b..5892b2b 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -38,7 +38,6 @@
 
 #include "../SurfaceFlingerProperties.h"
 #include "RefreshRateSelector.h"
-#include "Utils/FlagUtils.h"
 
 #include <com_android_graphics_surfaceflinger_flags.h>
 
@@ -115,7 +114,7 @@
     using fps_approx_ops::operator/;
     // use signed type as `fps / range.max` might be 0
     auto start = std::max(1, static_cast<int>(peakFps / range.max) - 1);
-    if (flagutils::vrrConfigEnabled()) {
+    if (FlagManager::getInstance().vrr_config()) {
         start = std::max(1,
                          static_cast<int>(vsyncRate /
                                           std::min(range.max, peakFps, fps_approx_ops::operator<)) -
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5b36a5e..1a8713d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -47,6 +47,7 @@
 
 #include "../Layer.h"
 #include "EventThread.h"
+#include "FlagManager.h"
 #include "FrameRateOverrideMappings.h"
 #include "FrontEnd/LayerHandle.h"
 #include "OneShotTimer.h"
@@ -210,7 +211,7 @@
         targeters.try_emplace(id, &targeter);
     }
 
-    if (flagutils::vrrConfigEnabled() &&
+    if (FlagManager::getInstance().vrr_config() &&
         CC_UNLIKELY(mPacesetterFrameDurationFractionToSkip > 0.f)) {
         const auto period = pacesetterTargeter.target().expectedFrameDuration();
         const auto skipDuration = Duration::fromNs(
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index f467670..186a6bc 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -25,16 +25,14 @@
 
 #include <scheduler/TimeKeeper.h>
 
+#include "FlagManager.h"
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncTracker.h"
 
-#include <com_android_graphics_surfaceflinger_flags.h>
-
 #undef LOG_TAG
 #define LOG_TAG "VSyncDispatch"
 
 namespace android::scheduler {
-using namespace com::android::graphics::surfaceflinger;
 
 using base::StringAppendF;
 
@@ -43,7 +41,8 @@
 nsecs_t getExpectedCallbackTime(nsecs_t now, nsecs_t nextVsyncTime,
                                 const VSyncDispatch::ScheduleTiming& timing) {
     const auto expectedCallbackTime = nextVsyncTime - timing.readyDuration - timing.workDuration;
-    const auto baseTime = flags::dont_skip_on_early() ? now : expectedCallbackTime;
+    const auto baseTime =
+            FlagManager::getInstance().dont_skip_on_early() ? now : expectedCallbackTime;
     return std::max(baseTime, expectedCallbackTime);
 }
 
@@ -105,7 +104,7 @@
             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
     bool const wouldSkipAWakeup =
             mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
-    if (flags::dont_skip_on_early()) {
+    if (FlagManager::getInstance().dont_skip_on_early()) {
         if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
             return getExpectedCallbackTime(now, mArmedInfo->mActualVsyncTime, timing);
         }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 48be33c..62eb17d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -157,15 +157,12 @@
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
 #include "Utils/Dumper.h"
-#include "Utils/FlagUtils.h"
 #include "WindowInfosListenerInvoker.h"
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
 
-#include <com_android_graphics_surfaceflinger_flags.h>
-
 #undef NO_THREAD_SAFETY_ANALYSIS
 #define NO_THREAD_SAFETY_ANALYSIS \
     _Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"")
@@ -175,8 +172,6 @@
 #define DOES_CONTAIN_BORDER false
 
 namespace android {
-using namespace com::android::graphics::surfaceflinger;
-
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 using namespace std::string_view_literals;
@@ -509,11 +504,6 @@
             base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true);
     mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
             base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
-
-    // Trunk-Stable flags
-    mMiscFlagValue = flags::misc1();
-    mConnectedDisplayFlagValue = flags::connected_display();
-    mMisc2FlagEarlyBootValue = flags::late_boot_misc2();
 }
 
 LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -689,6 +679,7 @@
         return;
     }
     mBootFinished = true;
+    FlagManager::getMutableInstance().markBootCompleted();
     if (mStartPropertySetThread->join() != NO_ERROR) {
         ALOGE("Join StartPropertySetThread failed!");
     }
@@ -702,7 +693,7 @@
 
     mFrameTracer->initialize();
     mFrameTimeline->onBootFinished();
-    getRenderEngine().setEnableTracing(mFlagManager.use_skia_tracing());
+    getRenderEngine().setEnableTracing(FlagManager::getInstance().use_skia_tracing());
 
     // wait patiently for the window manager death
     const String16 name("window");
@@ -731,7 +722,7 @@
 
         readPersistentProperties();
         mPowerAdvisor->onBootFinished();
-        const bool hintSessionEnabled = mFlagManager.use_adpf_cpu_hint();
+        const bool hintSessionEnabled = FlagManager::getInstance().use_adpf_cpu_hint();
         mPowerAdvisor->enablePowerHintSession(hintSessionEnabled);
         const bool hintSessionUsed = mPowerAdvisor->usePowerHintSession();
         ALOGD("Power hint is %s",
@@ -755,10 +746,6 @@
             enableRefreshRateOverlay(true);
         }
     }));
-
-    LOG_ALWAYS_FATAL_IF(flags::misc1() != mMiscFlagValue, "misc1 flag is not boot stable!");
-
-    mMisc2FlagLateBootValue = flags::late_boot_misc2();
 }
 
 static std::optional<renderengine::RenderEngine::RenderEngineType>
@@ -2101,7 +2088,7 @@
 
 void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                         std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
-    if (mConnectedDisplayFlagValue) {
+    if (FlagManager::getInstance().connected_display()) {
         // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32
         if (mIsHotplugErrViaNegVsync && timestamp < 0 && vsyncPeriod.has_value() &&
             vsyncPeriod.value() == ~0) {
@@ -4065,7 +4052,7 @@
 
     if (sysprop::use_content_detection_for_refresh_rate(false)) {
         features |= Feature::kContentDetection;
-        if (flags::enable_small_area_detection()) {
+        if (FlagManager::getInstance().enable_small_area_detection()) {
             features |= Feature::kSmallDirtyContentDetection;
         }
     }
@@ -6475,17 +6462,6 @@
     result.append("SurfaceFlinger global state:\n");
     colorizer.reset(result);
 
-    StringAppendF(&result, "MiscFlagValue: %s\n", mMiscFlagValue ? "true" : "false");
-    StringAppendF(&result, "ConnectedDisplayFlagValue: %s\n",
-                  mConnectedDisplayFlagValue ? "true" : "false");
-    StringAppendF(&result, "Misc2FlagValue: %s (%s after boot)\n",
-                  mMisc2FlagLateBootValue ? "true" : "false",
-                  mMisc2FlagEarlyBootValue == mMisc2FlagLateBootValue ? "stable" : "modified");
-    StringAppendF(&result, "VrrConfigFlagValue: %s\n",
-                  flagutils::vrrConfigEnabled() ? "true" : "false");
-    StringAppendF(&result, "DontSkipOnEarlyFlagValue: %s\n",
-                  flags::dont_skip_on_early() ? "true" : "false");
-
     getRenderEngine().dump(result);
 
     result.append("ClientCache state:\n");
@@ -6562,7 +6538,7 @@
     /*
      * Dump flag/property manager state
      */
-    mFlagManager.dump(result);
+    FlagManager::getInstance().dump(result);
 
     result.append(mTimeStats->miniDump());
     result.append("\n");
@@ -7227,7 +7203,7 @@
             // Second argument is a delay in ms for triggering the jank. This is useful for working
             // with tools that steal the adb connection. This argument is optional.
             case 1045: {
-                if (flagutils::vrrConfigEnabled()) {
+                if (FlagManager::getInstance().vrr_config()) {
                     float jankAmount = data.readFloat();
                     int32_t jankDelayMs = 0;
                     if (data.readInt32(&jankDelayMs) != NO_ERROR) {
@@ -9078,7 +9054,7 @@
     const sp<Client> client = sp<Client>::make(mFlinger);
     if (client->initCheck() == NO_ERROR) {
         *outClient = client;
-        if (flags::misc1()) {
+        if (FlagManager::getInstance().misc1()) {
             const int policy = SCHED_FIFO;
             client->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
         }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 96b67b8..520bd22 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1421,8 +1421,6 @@
 
     const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
 
-    FlagManager mFlagManager;
-
     // returns the framerate of the layer with the given sequence ID
     float getLayerFramerate(nsecs_t now, int32_t id) const {
         return mScheduler->getLayerFramerate(now, id);
@@ -1459,12 +1457,6 @@
     void sfdo_setDebugFlash(int delay);
     void sfdo_scheduleComposite();
     void sfdo_scheduleCommit();
-
-    // Trunk-Stable flags
-    bool mMiscFlagValue;
-    bool mConnectedDisplayFlagValue;
-    bool mMisc2FlagEarlyBootValue;
-    bool mMisc2FlagLateBootValue;
 };
 
 class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/Tracing/LayerDataSource.cpp b/services/surfaceflinger/Tracing/LayerDataSource.cpp
index 25e768e..ed1e2ec 100644
--- a/services/surfaceflinger/Tracing/LayerDataSource.cpp
+++ b/services/surfaceflinger/Tracing/LayerDataSource.cpp
@@ -51,8 +51,9 @@
     if (config.has_mode() && config.mode() != LayerTracing::Mode::MODE_UNSPECIFIED) {
         mMode = static_cast<LayerTracing::Mode>(config.mode());
     } else {
-        mMode = LayerTracing::Mode::MODE_GENERATED;
-        ALOGD("Received config with unspecified 'mode'. Using 'GENERATED' as default");
+        mMode = LayerTracing::Mode::MODE_GENERATED_BUGREPORT_ONLY;
+        ALOGD("Received config with unspecified 'mode'."
+              " Using 'MODE_GENERATED_BUGREPORT_ONLY' as default");
     }
 
     mFlags = 0;
@@ -68,10 +69,16 @@
     }
 }
 
-void LayerDataSource::OnFlush(const LayerDataSource::FlushArgs&) {
-    ALOGD("Received OnFlush event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
+void LayerDataSource::OnFlush(const LayerDataSource::FlushArgs& args) {
+    ALOGD("Received OnFlush event"
+          " (mode = 0x%02x, flags = 0x%02x, reason = 0x%" PRIx64 ", clone_target = 0x%0" PRIx64 ")",
+          mMode, mFlags, args.flush_flags.reason(), args.flush_flags.clone_target());
+
+    bool isBugreport = args.flush_flags.reason() == perfetto::FlushFlags::Reason::kTraceClone &&
+            args.flush_flags.clone_target() == perfetto::FlushFlags::CloneTarget::kBugreport;
+
     if (auto* p = mLayerTracing.load()) {
-        p->onFlush(mMode, mFlags);
+        p->onFlush(mMode, mFlags, isBugreport);
     }
 }
 
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 403e105..41bcdf0 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -67,9 +67,27 @@
             break;
         }
         case Mode::MODE_GENERATED: {
+            // This tracing mode processes the buffer of transactions (owned by TransactionTracing),
+            // generates layers snapshots and writes them to perfetto. This happens every time an
+            // OnFlush event is received.
             ALOGD("Started generated tracing (waiting for OnFlush event to generated layers)");
             break;
         }
+        case Mode::MODE_GENERATED_BUGREPORT_ONLY: {
+            // Same as MODE_GENERATED, but only when the received OnFlush event is due to a
+            // bugreport being taken. This mode exists because the generated layers trace is very
+            // large (hundreds of MB), hence we want to include it only in bugreports and not in
+            // field uploads.
+            //
+            // Note that perfetto communicates only whether the OnFlush event is due to a bugreport
+            // or not, hence we need an additional "bugreport only" tracing mode.
+            // If perfetto had communicated when the OnFlush is due to a field upload, then we could
+            // have had a single "generated" tracing mode that would have been a noop in case of
+            // field uploads.
+            ALOGD("Started 'generated bugreport only' tracing"
+                  " (waiting for bugreport's OnFlush event to generate layers)");
+            break;
+        }
         case Mode::MODE_DUMP: {
             auto snapshot = mTakeLayersSnapshotProto(flags);
             addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP);
@@ -82,10 +100,18 @@
     }
 }
 
-void LayerTracing::onFlush(Mode mode, uint32_t flags) {
+void LayerTracing::onFlush(Mode mode, uint32_t flags, bool isBugreport) {
     // In "generated" mode process the buffer of transactions (owned by TransactionTracing),
-    // generate a sequence of layers snapshots and write them to perfetto.
-    if (mode != Mode::MODE_GENERATED) {
+    // generate layers snapshots and write them to perfetto.
+    if (mode != Mode::MODE_GENERATED && mode != Mode::MODE_GENERATED_BUGREPORT_ONLY) {
+        ALOGD("Skipping layers trace generation (not a 'generated' tracing session)");
+        return;
+    }
+
+    // In "generated bugreport only" mode skip the layers snapshot generation
+    // if the perfetto's OnFlush event is not due to a bugreport being taken.
+    if (mode == Mode::MODE_GENERATED_BUGREPORT_ONLY && !isBugreport) {
+        ALOGD("Skipping layers trace generation (not a bugreport OnFlush event)");
         return;
     }
 
@@ -147,14 +173,23 @@
 }
 
 void LayerTracing::writeSnapshotToPerfetto(const perfetto::protos::LayersSnapshotProto& snapshot,
-                                           Mode mode) {
+                                           Mode srcMode) {
     const auto snapshotBytes = snapshot.SerializeAsString();
 
     LayerDataSource::Trace([&](LayerDataSource::TraceContext context) {
-        if (mode != context.GetCustomTlsState()->mMode) {
+        auto dstMode = context.GetCustomTlsState()->mMode;
+        if (srcMode == Mode::MODE_GENERATED) {
+            // Layers snapshots produced by LayerTraceGenerator have srcMode == MODE_GENERATED
+            // and should be written to tracing sessions with MODE_GENERATED
+            // or MODE_GENERATED_BUGREPORT_ONLY.
+            if (dstMode != Mode::MODE_GENERATED && dstMode != Mode::MODE_GENERATED_BUGREPORT_ONLY) {
+                return;
+            }
+        } else if (srcMode != dstMode) {
             return;
         }
-        if (!checkAndUpdateLastVsyncIdWrittenToPerfetto(mode, snapshot.vsync_id())) {
+
+        if (!checkAndUpdateLastVsyncIdWrittenToPerfetto(srcMode, snapshot.vsync_id())) {
             return;
         }
         {
@@ -176,7 +211,7 @@
     // In some situations (e.g. two bugreports taken shortly one after the other) the generated
     // sequence of layers snapshots might overlap. Here we check the snapshot's vsyncid to make
     // sure that in generated tracing mode a given snapshot is written only once to perfetto.
-    if (mode != Mode::MODE_GENERATED) {
+    if (mode != Mode::MODE_GENERATED && mode != Mode::MODE_GENERATED_BUGREPORT_ONLY) {
         return true;
     }
 
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index fe7f06d..2895ba7 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -55,7 +55,9 @@
  * and written to perfetto.
  *
  *
- * E.g. start active mode tracing:
+ * E.g. start active mode tracing
+ * (replace mode value with MODE_DUMP, MODE_GENERATED or MODE_GENERATED_BUGREPORT_ONLY to enable
+ * different tracing modes):
  *
    adb shell -t perfetto \
      -c - --txt \
@@ -79,7 +81,7 @@
            }
        }
    }
-   EOF
+EOF
  *
  */
 class LayerTracing {
@@ -106,7 +108,7 @@
     // Start event from perfetto data source
     void onStart(Mode mode, uint32_t flags);
     // Flush event from perfetto data source
-    void onFlush(Mode mode, uint32_t flags);
+    void onFlush(Mode mode, uint32_t flags, bool isBugreport);
     // Stop event from perfetto data source
     void onStop(Mode mode);
 
diff --git a/services/surfaceflinger/Utils/FlagUtils.h b/services/surfaceflinger/Utils/FlagUtils.h
deleted file mode 100644
index 8435f04..0000000
--- a/services/surfaceflinger/Utils/FlagUtils.h
+++ /dev/null
@@ -1,33 +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
-
-#include <android-base/properties.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
-#include <string>
-
-namespace android::flagutils {
-
-using namespace std::literals::string_literals;
-using namespace com::android::graphics::surfaceflinger;
-
-inline bool vrrConfigEnabled() {
-    static const bool enable_vrr_config =
-            base::GetBoolProperty("debug.sf.enable_vrr_config"s, false);
-    return flags::vrr_config() || enable_vrr_config;
-}
-} // namespace android::flagutils
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 9599452..9889cb9 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -34,6 +34,7 @@
 #include <errno.h>
 #include <hidl/LegacySupport.h>
 #include <processgroup/sched_policy.h>
+#include "FlagManager.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceFlingerProperties.h"
@@ -149,7 +150,7 @@
 
     // publish gui::ISurfaceComposer, the new AIDL interface
     sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
-    if (flags::misc1()) {
+    if (FlagManager::getInstance().misc1()) {
         composerAIDL->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
     }
     sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
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/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index 0905cd1..aa37754 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -14,130 +14,153 @@
  * limitations under the License.
  */
 
-#include <cstdint>
 #undef LOG_TAG
 #define LOG_TAG "FlagManagerTest"
 
 #include "FlagManager.h"
+#include "FlagUtils.h"
 
-#include <android-base/properties.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
-#include <server_configurable_flags/get_flags.h>
-#include <optional>
+
+#include <com_android_graphics_surfaceflinger_flags.h>
 
 namespace android {
 
 using testing::Return;
 
-class MockFlagManager : public FlagManager {
+class TestableFlagManager : public FlagManager {
 public:
-    MockFlagManager() = default;
-    ~MockFlagManager() = default;
+    TestableFlagManager() : FlagManager(ConstructorTag{}) { markBootCompleted(); }
+    ~TestableFlagManager() = default;
 
-    MOCK_METHOD(std::string, getServerConfigurableFlag, (const std::string& experimentFlagName),
-                (const, override));
+    MOCK_METHOD(std::optional<bool>, getBoolProperty, (const char*), (const, override));
+    MOCK_METHOD(bool, getServerConfigurableFlag, (const char*), (const, override));
+
+    void markBootIncomplete() { mBootCompleted = false; }
 };
 
 class FlagManagerTest : public testing::Test {
 public:
-    FlagManagerTest();
-    ~FlagManagerTest() override;
-    std::unique_ptr<MockFlagManager> mFlagManager;
+    FlagManagerTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+    ~FlagManagerTest() override {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
 
-    template <typename T>
-    T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
-               T defaultValue);
+    TestableFlagManager mFlagManager;
 };
 
-FlagManagerTest::FlagManagerTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-    mFlagManager = std::make_unique<MockFlagManager>();
+TEST_F(FlagManagerTest, isSingleton) {
+    EXPECT_EQ(&FlagManager::getInstance(), &FlagManager::getInstance());
 }
 
-FlagManagerTest::~FlagManagerTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+TEST_F(FlagManagerTest, legacyCreashesIfQueriedBeforeBoot) {
+    mFlagManager.markBootIncomplete();
+    EXPECT_DEATH(FlagManager::getInstance().test_flag(), "");
 }
 
-template <typename T>
-T FlagManagerTest::getValue(const std::string& experimentFlagName,
-                            std::optional<T> systemPropertyOpt, T defaultValue) {
-    return mFlagManager->getValue(experimentFlagName, systemPropertyOpt, defaultValue);
+TEST_F(FlagManagerTest, legacyReturnsOverride) {
+    EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(true));
+    EXPECT_EQ(true, mFlagManager.test_flag());
+
+    EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
+    EXPECT_EQ(false, mFlagManager.test_flag());
 }
 
-namespace {
-TEST_F(FlagManagerTest, getValue_bool_default) {
-    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
-    const bool defaultValue = false;
-    std::optional<bool> systemPropertyValue = std::nullopt;
-    const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, defaultValue);
+TEST_F(FlagManagerTest, legacyReturnsValue) {
+    EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt));
+
+    EXPECT_CALL(mFlagManager, getServerConfigurableFlag).WillOnce(Return(true));
+    EXPECT_EQ(true, mFlagManager.test_flag());
+
+    EXPECT_CALL(mFlagManager, getServerConfigurableFlag).WillOnce(Return(false));
+    EXPECT_EQ(false, mFlagManager.test_flag());
 }
 
-TEST_F(FlagManagerTest, getValue_bool_sysprop) {
-    const bool defaultValue = false;
-    std::optional<bool> systemPropertyValue = std::make_optional(true);
-    const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, true);
+TEST_F(FlagManagerTest, creashesIfQueriedBeforeBoot) {
+    mFlagManager.markBootIncomplete();
+    EXPECT_DEATH(FlagManager::getInstance().late_boot_misc2(), "");
 }
 
-TEST_F(FlagManagerTest, getValue_bool_experiment) {
-    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("1"));
-    const bool defaultValue = false;
-    std::optional<bool> systemPropertyValue = std::nullopt;
-    const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, true);
+TEST_F(FlagManagerTest, returnsOverride) {
+    mFlagManager.setUnitTestMode();
+
+    // Twice, since the first call is to initialize the static variable
+    EXPECT_CALL(mFlagManager, getBoolProperty)
+            .Times((2))
+            .WillOnce(Return(true))
+            .WillOnce(Return(true));
+    EXPECT_EQ(true, mFlagManager.late_boot_misc2());
+
+    EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
+    EXPECT_EQ(false, mFlagManager.late_boot_misc2());
 }
 
-TEST_F(FlagManagerTest, getValue_int32_default) {
-    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
-    int32_t defaultValue = 30;
-    std::optional<int32_t> systemPropertyValue = std::nullopt;
-    int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, defaultValue);
+TEST_F(FlagManagerTest, returnsValue) {
+    mFlagManager.setUnitTestMode();
+
+    EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt));
+
+    {
+        SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, true);
+        EXPECT_EQ(true, mFlagManager.late_boot_misc2());
+    }
+
+    {
+        SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, false);
+        EXPECT_EQ(false, mFlagManager.late_boot_misc2());
+    }
 }
 
-TEST_F(FlagManagerTest, getValue_int32_sysprop) {
-    int32_t defaultValue = 30;
-    std::optional<int32_t> systemPropertyValue = std::make_optional(10);
-    int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, 10);
+TEST_F(FlagManagerTest, readonlyReturnsOverride) {
+    mFlagManager.setUnitTestMode();
+
+    // Twice, since the first call is to initialize the static variable
+    EXPECT_CALL(mFlagManager, getBoolProperty)
+            .Times(2)
+            .WillOnce(Return(true))
+            .WillOnce(Return(true));
+    EXPECT_EQ(true, mFlagManager.misc1());
+
+    EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
+    EXPECT_EQ(false, mFlagManager.misc1());
 }
 
-TEST_F(FlagManagerTest, getValue_int32_experiment) {
-    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50"));
-    std::int32_t defaultValue = 30;
-    std::optional<std::int32_t> systemPropertyValue = std::nullopt;
-    std::int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, 50);
+TEST_F(FlagManagerTest, readonlyReturnsValue) {
+    mFlagManager.setUnitTestMode();
+
+    EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt));
+
+    {
+        SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::misc1, true);
+        EXPECT_EQ(true, mFlagManager.misc1());
+    }
+
+    {
+        SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::misc1, false);
+        EXPECT_EQ(false, mFlagManager.misc1());
+    }
 }
 
-TEST_F(FlagManagerTest, getValue_int64_default) {
-    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
-    int64_t defaultValue = 30;
-    std::optional<int64_t> systemPropertyValue = std::nullopt;
-    int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, defaultValue);
+TEST_F(FlagManagerTest, dontSkipOnEarlyIsNotCached) {
+    EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt));
+
+    const auto initialValue = com::android::graphics::surfaceflinger::flags::dont_skip_on_early();
+
+    com::android::graphics::surfaceflinger::flags::dont_skip_on_early(true);
+    EXPECT_EQ(true, mFlagManager.dont_skip_on_early());
+
+    com::android::graphics::surfaceflinger::flags::dont_skip_on_early(false);
+    EXPECT_EQ(false, mFlagManager.dont_skip_on_early());
+
+    com::android::graphics::surfaceflinger::flags::dont_skip_on_early(initialValue);
 }
 
-TEST_F(FlagManagerTest, getValue_int64_sysprop) {
-    int64_t defaultValue = 30;
-    std::optional<int64_t> systemPropertyValue = std::make_optional(10);
-    int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, 10);
-}
-
-TEST_F(FlagManagerTest, getValue_int64_experiment) {
-    EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50"));
-    int64_t defaultValue = 30;
-    std::optional<int64_t> systemPropertyValue = std::nullopt;
-    int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
-    ASSERT_EQ(result, 50);
-}
-} // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/tests/unittests/FlagUtils.h
index 7103684..333e4e7 100644
--- a/services/surfaceflinger/tests/unittests/FlagUtils.h
+++ b/services/surfaceflinger/tests/unittests/FlagUtils.h
@@ -16,12 +16,16 @@
 
 #pragma once
 
+#include "FlagManager.h"
+
 #define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value))
 
 namespace android {
 class TestFlagSetter {
 public:
     TestFlagSetter(bool (*getter)(), void((*setter)(bool)), bool flagValue) {
+        FlagManager::getMutableInstance().setUnitTestMode();
+
         const bool initialValue = getter();
         setter(flagValue);
         mResetFlagValue = [=] { setter(initialValue); };