Merge "SF: Decouple MessageQueue"
diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp
index 3e8e3f6..21ac11b 100644
--- a/cmds/service/Android.bp
+++ b/cmds/service/Android.bp
@@ -52,3 +52,21 @@
         "-Werror",
     ],
 }
+
+cc_binary_host {
+    name: "aservice",
+
+    srcs: ["service.cpp"],
+
+    shared_libs: [
+        "libcutils",
+        "libutils",
+        "libbinder",
+    ],
+
+    cflags: [
+        "-DXP_UNIX",
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 18b6b58..0b00c2d 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -50,6 +50,7 @@
 {
     if (service != nullptr) {
         Parcel data, reply;
+        data.markForBinder(service);
         status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply);
         if (err == NO_ERROR) {
             return reply.readString16();
@@ -96,6 +97,9 @@
 #ifdef VENDORSERVICES
     ProcessState::initWithDriver("/dev/vndbinder");
 #endif
+#ifndef __ANDROID__
+    setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+#endif
     sp<IServiceManager> sm = defaultServiceManager();
     fflush(stdout);
     if (sm == nullptr) {
@@ -138,6 +142,7 @@
                 int32_t code = atoi(argv[optind++]);
                 if (service != nullptr && ifName.size() > 0) {
                     Parcel data, reply;
+                    data.markForBinder(service);
 
                     // the interface name is first
                     data.writeInterfaceToken(ifName);
@@ -229,7 +234,7 @@
                             int afd = ashmem_create_region("test", statbuf.st_size);
                             void* ptr = mmap(NULL, statbuf.st_size,
                                    PROT_READ | PROT_WRITE, MAP_SHARED, afd, 0);
-                            read(fd, ptr, statbuf.st_size);
+                            (void)read(fd, ptr, statbuf.st_size);
                             close(fd);
                             data.writeFileDescriptor(afd, true /* take ownership */);
                         } else if (strcmp(argv[optind], "nfd") == 0) {
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 235990a..5fe4ea1 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -185,6 +185,12 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.wifi.passpoint.prebuilt.xml",
+    src: "android.hardware.wifi.passpoint.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.software.device_id_attestation.prebuilt.xml",
     src: "android.software.device_id_attestation.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 89a31c5..2ecb895 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -153,10 +153,6 @@
         export_aidl_headers: true,
     },
 
-    // TODO(b/142684679): for com.android.media which is compiled
-    // as vendor and used as system code.
-    use_apex_name_macro: true,
-
     cflags: [
         "-Wall",
         "-Wextra",
@@ -303,6 +299,14 @@
         "aidl/android/content/pm/ApexStagedEvent.aidl",
         "aidl/android/content/pm/StagedApexInfo.aidl",
     ],
+    backend: {
+        rust: {
+            apex_available: [
+                "com.android.virt",
+            ],
+            enabled: true,
+        },
+    },
 }
 
 aidl_interface {
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index aff9e0d..81e61da 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -448,21 +448,27 @@
 // on-device service manager.
 class ServiceManagerHostShim : public ServiceManagerShim {
 public:
-    using ServiceManagerShim::ServiceManagerShim;
+    ServiceManagerHostShim(const sp<AidlServiceManager>& impl,
+                           const RpcDelegateServiceManagerOptions& options)
+          : ServiceManagerShim(impl), mOptions(options) {}
     // ServiceManagerShim::getService is based on checkService, so no need to override it.
     sp<IBinder> checkService(const String16& name) const override {
-        return getDeviceService({String8(name).c_str()});
+        return getDeviceService({String8(name).c_str()}, mOptions);
     }
 
 protected:
     // Override realGetService for ServiceManagerShim::waitForService.
     Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
-        *_aidl_return = getDeviceService({"-g", name});
+        *_aidl_return = getDeviceService({"-g", name}, mOptions);
         return Status::ok();
     }
+
+private:
+    RpcDelegateServiceManagerOptions mOptions;
 };
-sp<IServiceManager> createRpcDelegateServiceManager() {
-    auto binder = getDeviceService({"manager"});
+sp<IServiceManager> createRpcDelegateServiceManager(
+        const RpcDelegateServiceManagerOptions& options) {
+    auto binder = getDeviceService({"manager"}, options);
     if (binder == nullptr) {
         ALOGE("getDeviceService(\"manager\") returns null");
         return nullptr;
@@ -472,7 +478,7 @@
         ALOGE("getDeviceService(\"manager\") returns non service manager");
         return nullptr;
     }
-    return sp<ServiceManagerHostShim>::make(interface);
+    return sp<ServiceManagerHostShim>::make(interface, options);
 }
 #endif
 
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index 350994a..1c8bdea 100644
--- a/libs/binder/OWNERS
+++ b/libs/binder/OWNERS
@@ -1,5 +1,7 @@
+# Bug component: 32456
 arve@google.com
 ctate@google.com
 hackbod@google.com
 maco@google.com
 smoreland@google.com
+tkjos@google.com
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 07555f6..805e576 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -98,9 +98,8 @@
     BLOB_ASHMEM_MUTABLE = 2,
 };
 
-static void acquire_object(const sp<ProcessState>& proc,
-    const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
-{
+static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
+                           const void* who) {
     switch (obj.hdr.type) {
         case BINDER_TYPE_BINDER:
             if (obj.binder) {
@@ -117,13 +116,6 @@
             return;
         }
         case BINDER_TYPE_FD: {
-            if ((obj.cookie != 0) && (outAshmemSize != nullptr) && ashmem_valid(obj.handle)) {
-                // If we own an ashmem fd, keep track of how much memory it refers to.
-                int size = ashmem_get_size_region(obj.handle);
-                if (size > 0) {
-                    *outAshmemSize += size;
-                }
-            }
             return;
         }
     }
@@ -131,9 +123,8 @@
     ALOGD("Invalid object type 0x%08x", obj.hdr.type);
 }
 
-static void release_object(const sp<ProcessState>& proc,
-    const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
-{
+static void release_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
+                           const void* who) {
     switch (obj.hdr.type) {
         case BINDER_TYPE_BINDER:
             if (obj.binder) {
@@ -151,16 +142,6 @@
         }
         case BINDER_TYPE_FD: {
             if (obj.cookie != 0) { // owned
-                if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) {
-                    int size = ashmem_get_size_region(obj.handle);
-                    if (size > 0) {
-                        // ashmem size might have changed since last time it was accounted for, e.g.
-                        // in acquire_object(). Value of *outAshmemSize is not critical since we are
-                        // releasing the object anyway. Check for integer overflow condition.
-                        *outAshmemSize -= std::min(*outAshmemSize, static_cast<size_t>(size));
-                    }
-                }
-
                 close(obj.handle);
             }
             return;
@@ -430,8 +411,9 @@
 
 status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
 {
-    if (parcel->isForRpc() != isForRpc()) {
-        ALOGE("Cannot append Parcel of one format to another.");
+    if (mSession != parcel->mSession) {
+        ALOGE("Cannot append Parcel from one context to another. They may be different formats, "
+              "and objects are specific to a context.");
         return BAD_TYPE;
     }
 
@@ -512,7 +494,7 @@
 
             flat_binder_object* flat
                 = reinterpret_cast<flat_binder_object*>(mData + off);
-            acquire_object(proc, *flat, this, &mOpenAshmemSize);
+            acquire_object(proc, *flat, this);
 
             if (flat->hdr.type == BINDER_TYPE_FD) {
                 // If this is a file descriptor, we need to dup it so the
@@ -566,6 +548,36 @@
     return mHasFds;
 }
 
+status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool& result) const {
+    if (len > INT32_MAX || offset > INT32_MAX) {
+        // Don't accept size_t values which may have come from an inadvertent conversion from a
+        // negative int.
+        return BAD_VALUE;
+    }
+    size_t limit = offset + len;
+    if (offset > mDataSize || len > mDataSize || limit > mDataSize || offset > limit) {
+        return BAD_VALUE;
+    }
+    result = hasFileDescriptorsInRangeUnchecked(offset, len);
+    return NO_ERROR;
+}
+
+bool Parcel::hasFileDescriptorsInRangeUnchecked(size_t offset, size_t len) const {
+    for (size_t i = 0; i < mObjectsSize; i++) {
+        size_t pos = mObjects[i];
+        if (pos < offset) continue;
+        if (pos + sizeof(flat_binder_object) > offset + len) {
+          if (mObjectsSorted) break;
+          else continue;
+        }
+        const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + pos);
+        if (flat->hdr.type == BINDER_TYPE_FD) {
+            return true;
+        }
+    }
+    return false;
+}
+
 void Parcel::markSensitive() const
 {
     mDeallocZero = true;
@@ -1335,7 +1347,7 @@
         // Need to write meta-data?
         if (nullMetaData || val.binder != 0) {
             mObjects[mObjectsSize] = mDataPos;
-            acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
+            acquire_object(ProcessState::self(), val, this);
             mObjectsSize++;
         }
 
@@ -2225,7 +2237,7 @@
         i--;
         const flat_binder_object* flat
             = reinterpret_cast<flat_binder_object*>(data+objects[i]);
-        release_object(proc, *flat, this, &mOpenAshmemSize);
+        release_object(proc, *flat, this);
     }
 }
 
@@ -2242,7 +2254,7 @@
         i--;
         const flat_binder_object* flat
             = reinterpret_cast<flat_binder_object*>(data+objects[i]);
-        acquire_object(proc, *flat, this, &mOpenAshmemSize);
+        acquire_object(proc, *flat, this);
     }
 }
 
@@ -2447,7 +2459,7 @@
                     // will need to rescan because we may have lopped off the only FDs
                     mFdsKnown = false;
                 }
-                release_object(proc, *flat, this, &mOpenAshmemSize);
+                release_object(proc, *flat, this);
             }
 
             if (objectsSize == 0) {
@@ -2540,7 +2552,6 @@
     mAllowFds = true;
     mDeallocZero = false;
     mOwner = nullptr;
-    mOpenAshmemSize = 0;
     mWorkSourceRequestHeaderPosition = 0;
     mRequestHeaderPresent = false;
 
@@ -2559,16 +2570,7 @@
 
 void Parcel::scanForFds() const
 {
-    bool hasFds = false;
-    for (size_t i=0; i<mObjectsSize; i++) {
-        const flat_binder_object* flat
-            = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
-        if (flat->hdr.type == BINDER_TYPE_FD) {
-            hasFds = true;
-            break;
-        }
-    }
-    mHasFds = hasFds;
+    mHasFds = hasFileDescriptorsInRangeUnchecked(0, dataSize());
     mFdsKnown = true;
 }
 
@@ -2576,13 +2578,28 @@
 {
     // This used to return the size of all blobs that were written to ashmem, now we're returning
     // the ashmem currently referenced by this Parcel, which should be equivalent.
-    // TODO: Remove method once ABI can be changed.
-    return mOpenAshmemSize;
+    // TODO(b/202029388): Remove method once ABI can be changed.
+    return getOpenAshmemSize();
 }
 
 size_t Parcel::getOpenAshmemSize() const
 {
-    return mOpenAshmemSize;
+    size_t openAshmemSize = 0;
+    for (size_t i = 0; i < mObjectsSize; i++) {
+        const flat_binder_object* flat =
+                reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
+
+        // cookie is compared against zero for historical reasons
+        // > obj.cookie = takeOwnership ? 1 : 0;
+        if (flat->hdr.type == BINDER_TYPE_FD && flat->cookie != 0 && ashmem_valid(flat->handle)) {
+            int size = ashmem_get_size_region(flat->handle);
+            if (__builtin_add_overflow(openAshmemSize, size, &openAshmemSize)) {
+                ALOGE("Overflow when computing ashmem size.");
+                return SIZE_MAX;
+            }
+        }
+    }
+    return openAshmemSize;
 }
 
 // --- Parcel::Blob ---
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 44b588b..967b8e3 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -381,7 +381,7 @@
             } while (server->mSessions.end() != server->mSessions.find(sessionId));
 
             session = RpcSession::make();
-            session->setMaxThreads(server->mMaxThreads);
+            session->setMaxIncomingThreads(server->mMaxThreads);
             if (!session->setProtocolVersion(protocolVersion)) return;
             if (!session->setForServer(server,
                                        sp<RpcServer::EventListener>::fromExisting(
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 4465b8e..9eef3e8 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -61,7 +61,7 @@
     LOG_RPC_DETAIL("RpcSession destroyed %p", this);
 
     std::lock_guard<std::mutex> _l(mMutex);
-    LOG_ALWAYS_FATAL_IF(mThreadState.mIncomingConnections.size() != 0,
+    LOG_ALWAYS_FATAL_IF(mConnections.mIncoming.size() != 0,
                         "Should not be able to destroy a session with servers in use.");
 }
 
@@ -76,20 +76,32 @@
     return sp<RpcSession>::make(std::move(ctx));
 }
 
-void RpcSession::setMaxThreads(size_t threads) {
+void RpcSession::setMaxIncomingThreads(size_t threads) {
     std::lock_guard<std::mutex> _l(mMutex);
-    LOG_ALWAYS_FATAL_IF(!mThreadState.mOutgoingConnections.empty() ||
-                                !mThreadState.mIncomingConnections.empty(),
-                        "Must set max threads before setting up connections, but has %zu client(s) "
-                        "and %zu server(s)",
-                        mThreadState.mOutgoingConnections.size(),
-                        mThreadState.mIncomingConnections.size());
-    mMaxThreads = threads;
+    LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(),
+                        "Must set max incoming threads before setting up connections, but has %zu "
+                        "client(s) and %zu server(s)",
+                        mConnections.mOutgoing.size(), mConnections.mIncoming.size());
+    mMaxIncomingThreads = threads;
 }
 
-size_t RpcSession::getMaxThreads() {
+size_t RpcSession::getMaxIncomingThreads() {
     std::lock_guard<std::mutex> _l(mMutex);
-    return mMaxThreads;
+    return mMaxIncomingThreads;
+}
+
+void RpcSession::setMaxOutgoingThreads(size_t threads) {
+    std::lock_guard<std::mutex> _l(mMutex);
+    LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(),
+                        "Must set max outgoing threads before setting up connections, but has %zu "
+                        "client(s) and %zu server(s)",
+                        mConnections.mOutgoing.size(), mConnections.mIncoming.size());
+    mMaxOutgoingThreads = threads;
+}
+
+size_t RpcSession::getMaxOutgoingThreads() {
+    std::lock_guard<std::mutex> _l(mMutex);
+    return mMaxOutgoingThreads;
 }
 
 bool RpcSession::setProtocolVersion(uint32_t version) {
@@ -197,7 +209,7 @@
         LOG_ALWAYS_FATAL_IF(mShutdownListener == nullptr, "Shutdown listener not installed");
         mShutdownListener->waitForShutdown(_l, sp<RpcSession>::fromExisting(this));
 
-        LOG_ALWAYS_FATAL_IF(!mThreadState.mThreads.empty(), "Shutdown failed");
+        LOG_ALWAYS_FATAL_IF(!mConnections.mThreads.empty(), "Shutdown failed");
     }
 
     _l.unlock();
@@ -263,11 +275,11 @@
 
 void RpcSession::WaitForShutdownListener::waitForShutdown(std::unique_lock<std::mutex>& lock,
                                                           const sp<RpcSession>& session) {
-    while (session->mThreadState.mIncomingConnections.size() > 0) {
+    while (session->mConnections.mIncoming.size() > 0) {
         if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) {
             ALOGE("Waiting for RpcSession to shut down (1s w/o progress): %zu incoming connections "
                   "still.",
-                  session->mThreadState.mIncomingConnections.size());
+                  session->mConnections.mIncoming.size());
         }
     }
 }
@@ -277,7 +289,7 @@
 
     {
         std::lock_guard<std::mutex> _l(mMutex);
-        mThreadState.mThreads[thread.get_id()] = std::move(thread);
+        mConnections.mThreads[thread.get_id()] = std::move(thread);
     }
 }
 
@@ -380,10 +392,10 @@
     sp<RpcSession::EventListener> listener;
     {
         std::lock_guard<std::mutex> _l(session->mMutex);
-        auto it = session->mThreadState.mThreads.find(std::this_thread::get_id());
-        LOG_ALWAYS_FATAL_IF(it == session->mThreadState.mThreads.end());
+        auto it = session->mConnections.mThreads.find(std::this_thread::get_id());
+        LOG_ALWAYS_FATAL_IF(it == session->mConnections.mThreads.end());
         it->second.detach();
-        session->mThreadState.mThreads.erase(it);
+        session->mConnections.mThreads.erase(it);
 
         listener = session->mEventListener.promote();
     }
@@ -414,9 +426,9 @@
                                                               bool incoming)>& connectAndInit) {
     {
         std::lock_guard<std::mutex> _l(mMutex);
-        LOG_ALWAYS_FATAL_IF(mThreadState.mOutgoingConnections.size() != 0,
+        LOG_ALWAYS_FATAL_IF(mConnections.mOutgoing.size() != 0,
                             "Must only setup session once, but already has %zu clients",
-                            mThreadState.mOutgoingConnections.size());
+                            mConnections.mOutgoing.size());
     }
 
     if (auto status = initShutdownTrigger(); status != OK) return status;
@@ -439,7 +451,7 @@
         // downgrade again
         mProtocolVersion = oldProtocolVersion;
 
-        mThreadState = {};
+        mConnections = {};
     });
 
     if (status_t status = connectAndInit({}, false /*incoming*/); status != OK) return status;
@@ -475,6 +487,12 @@
         return status;
     }
 
+    size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
+    ALOGI_IF(outgoingThreads != numThreadsAvailable,
+             "Server hints client to start %zu outgoing threads, but client will only start %zu "
+             "because it is preconfigured to start at most %zu outgoing threads.",
+             numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads);
+
     // TODO(b/189955605): we should add additional sessions dynamically
     // instead of all at once - the other side should be responsible for setting
     // up additional connections. We need to create at least one (unless 0 are
@@ -482,11 +500,14 @@
     // any requests at all.
 
     // we've already setup one client
-    for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
+    LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu "
+                   "incoming threads",
+                   outgoingThreads, numThreadsAvailable, mMaxIncomingThreads);
+    for (size_t i = 0; i + 1 < outgoingThreads; i++) {
         if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
     }
 
-    for (size_t i = 0; i < mMaxThreads; i++) {
+    for (size_t i = 0; i < mMaxIncomingThreads; i++) {
         if (status_t status = connectAndInit(mId, true /*incoming*/); status != OK) return status;
     }
 
@@ -662,7 +683,7 @@
         std::lock_guard<std::mutex> _l(mMutex);
         connection->rpcTransport = std::move(rpcTransport);
         connection->exclusiveTid = gettid();
-        mThreadState.mOutgoingConnections.push_back(connection);
+        mConnections.mOutgoing.push_back(connection);
     }
 
     status_t status = OK;
@@ -699,9 +720,9 @@
         std::unique_ptr<RpcTransport> rpcTransport) {
     std::lock_guard<std::mutex> _l(mMutex);
 
-    if (mThreadState.mIncomingConnections.size() >= mMaxThreads) {
+    if (mConnections.mIncoming.size() >= mMaxIncomingThreads) {
         ALOGE("Cannot add thread to session with %zu threads (max is set to %zu)",
-              mThreadState.mIncomingConnections.size(), mMaxThreads);
+              mConnections.mIncoming.size(), mMaxIncomingThreads);
         return nullptr;
     }
 
@@ -709,7 +730,7 @@
     // happens when new connections are still being established as part of a
     // very short-lived session which shuts down after it already started
     // accepting new connections.
-    if (mThreadState.mIncomingConnections.size() < mThreadState.mMaxIncomingConnections) {
+    if (mConnections.mIncoming.size() < mConnections.mMaxIncoming) {
         return nullptr;
     }
 
@@ -717,19 +738,19 @@
     session->rpcTransport = std::move(rpcTransport);
     session->exclusiveTid = gettid();
 
-    mThreadState.mIncomingConnections.push_back(session);
-    mThreadState.mMaxIncomingConnections = mThreadState.mIncomingConnections.size();
+    mConnections.mIncoming.push_back(session);
+    mConnections.mMaxIncoming = mConnections.mIncoming.size();
 
     return session;
 }
 
 bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) {
     std::unique_lock<std::mutex> _l(mMutex);
-    if (auto it = std::find(mThreadState.mIncomingConnections.begin(),
-                            mThreadState.mIncomingConnections.end(), connection);
-        it != mThreadState.mIncomingConnections.end()) {
-        mThreadState.mIncomingConnections.erase(it);
-        if (mThreadState.mIncomingConnections.size() == 0) {
+    if (auto it =
+                std::find(mConnections.mIncoming.begin(), mConnections.mIncoming.end(), connection);
+        it != mConnections.mIncoming.end()) {
+        mConnections.mIncoming.erase(it);
+        if (mConnections.mIncoming.size() == 0) {
             sp<EventListener> listener = mEventListener.promote();
             if (listener) {
                 _l.unlock();
@@ -754,7 +775,7 @@
     pid_t tid = gettid();
     std::unique_lock<std::mutex> _l(session->mMutex);
 
-    session->mThreadState.mWaitingThreads++;
+    session->mConnections.mWaitingThreads++;
     while (true) {
         sp<RpcConnection> exclusive;
         sp<RpcConnection> available;
@@ -762,11 +783,11 @@
         // CHECK FOR DEDICATED CLIENT SOCKET
         //
         // A server/looper should always use a dedicated connection if available
-        findConnection(tid, &exclusive, &available, session->mThreadState.mOutgoingConnections,
-                       session->mThreadState.mOutgoingConnectionsOffset);
+        findConnection(tid, &exclusive, &available, session->mConnections.mOutgoing,
+                       session->mConnections.mOutgoingOffset);
 
         // WARNING: this assumes a server cannot request its client to send
-        // a transaction, as mIncomingConnections is excluded below.
+        // a transaction, as mIncoming is excluded below.
         //
         // Imagine we have more than one thread in play, and a single thread
         // sends a synchronous, then an asynchronous command. Imagine the
@@ -776,9 +797,8 @@
         // command. So, we move to considering the second available thread
         // for subsequent calls.
         if (use == ConnectionUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
-            session->mThreadState.mOutgoingConnectionsOffset =
-                    (session->mThreadState.mOutgoingConnectionsOffset + 1) %
-                    session->mThreadState.mOutgoingConnections.size();
+            session->mConnections.mOutgoingOffset = (session->mConnections.mOutgoingOffset + 1) %
+                    session->mConnections.mOutgoing.size();
         }
 
         // USE SERVING SOCKET (e.g. nested transaction)
@@ -786,7 +806,7 @@
             sp<RpcConnection> exclusiveIncoming;
             // server connections are always assigned to a thread
             findConnection(tid, &exclusiveIncoming, nullptr /*available*/,
-                           session->mThreadState.mIncomingConnections, 0 /* index hint */);
+                           session->mConnections.mIncoming, 0 /* index hint */);
 
             // asynchronous calls cannot be nested, we currently allow ref count
             // calls to be nested (so that you can use this without having extra
@@ -815,20 +835,20 @@
             break;
         }
 
-        if (session->mThreadState.mOutgoingConnections.size() == 0) {
+        if (session->mConnections.mOutgoing.size() == 0) {
             ALOGE("Session has no client connections. This is required for an RPC server to make "
                   "any non-nested (e.g. oneway or on another thread) calls. Use: %d. Server "
                   "connections: %zu",
-                  static_cast<int>(use), session->mThreadState.mIncomingConnections.size());
+                  static_cast<int>(use), session->mConnections.mIncoming.size());
             return WOULD_BLOCK;
         }
 
         LOG_RPC_DETAIL("No available connections (have %zu clients and %zu servers). Waiting...",
-                       session->mThreadState.mOutgoingConnections.size(),
-                       session->mThreadState.mIncomingConnections.size());
+                       session->mConnections.mOutgoing.size(),
+                       session->mConnections.mIncoming.size());
         session->mAvailableConnectionCv.wait(_l);
     }
-    session->mThreadState.mWaitingThreads--;
+    session->mConnections.mWaitingThreads--;
 
     return OK;
 }
@@ -867,7 +887,7 @@
     if (!mReentrant && mConnection != nullptr) {
         std::unique_lock<std::mutex> _l(mSession->mMutex);
         mConnection->exclusiveTid = std::nullopt;
-        if (mSession->mThreadState.mWaitingThreads > 0) {
+        if (mSession->mConnections.mWaitingThreads > 0) {
             _l.unlock();
             mSession->mAvailableConnectionCv.notify_one();
         }
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index ef62f20..9ba64f3 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -855,7 +855,7 @@
 
             switch (transaction->code) {
                 case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: {
-                    replyStatus = reply.writeInt32(session->getMaxThreads());
+                    replyStatus = reply.writeInt32(session->getMaxIncomingThreads());
                     break;
                 }
                 case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: {
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 27cc563..194254a 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -124,7 +124,8 @@
 
 } // namespace
 
-sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs) {
+sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs,
+                             const RpcDelegateServiceManagerOptions& options) {
     std::vector<std::string> prefix{"adb", "shell", "servicedispatcher"};
     serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end());
 
@@ -158,6 +159,10 @@
     LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
 
     auto rpcSession = RpcSession::make();
+    if (options.maxOutgoingThreads.has_value()) {
+        rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads);
+    }
+
     if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort());
         status != OK) {
         ALOGE("Unable to set up inet client on host port %u: %s", *forwardResult->hostPort(),
diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h
index e59724c..c5310da 100644
--- a/libs/binder/ServiceManagerHost.h
+++ b/libs/binder/ServiceManagerHost.h
@@ -21,11 +21,14 @@
 
 namespace android {
 
+struct RpcDelegateServiceManagerOptions;
+
 // Get a service on device by running servicedispatcher with the given args, e.g.
 //     getDeviceService({"foo"});
 // Return nullptr on any error.
 // When the returned binder object is destroyed, remove adb forwarding and kills
 // the long-running servicedispatcher process.
-sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs);
+sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs,
+                             const RpcDelegateServiceManagerOptions& options);
 
 } // namespace android
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index d71f496..7c99f76 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -144,5 +144,5 @@
      * Get information of APEX which is staged ready for installation.
      * Returns null if no such APEX is found.
      */
-    StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName);
+    @nullable StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName);
 }
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index a48075d..240e3c2 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -188,7 +188,16 @@
 //        // ...
 //    }
 // Resources are cleaned up when the object is destroyed.
-sp<IServiceManager> createRpcDelegateServiceManager();
+//
+// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated.
+// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked
+// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads.
+// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|.
+struct RpcDelegateServiceManagerOptions {
+    std::optional<size_t> maxOutgoingThreads;
+};
+sp<IServiceManager> createRpcDelegateServiceManager(
+        const RpcDelegateServiceManagerOptions& options);
 #endif
 
 } // namespace android
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index fd8ac62..cf30f17 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -87,6 +87,7 @@
     void                restoreAllowFds(bool lastValue);
 
     bool                hasFileDescriptors() const;
+    status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool& result) const;
 
     // Zeros data when reallocating. Other mitigations may be added
     // in the future.
@@ -575,6 +576,7 @@
 
     status_t            writeRawNullableParcelable(const Parcelable*
                                                    parcelable);
+    bool hasFileDescriptorsInRangeUnchecked(size_t offset, size_t length) const;
 
     //-----------------------------------------------------------------------------
     // Generic type read and write methods for Parcel:
@@ -1141,6 +1143,7 @@
     release_func        mOwner;
 
     sp<RpcSession> mSession;
+    size_t mReserved;
 
     class Blob {
     public:
@@ -1225,13 +1228,17 @@
         inline void* data() { return mData; }
     };
 
-private:
-    size_t mOpenAshmemSize;
-
-public:
-    // TODO: Remove once ABI can be changed.
-    size_t getBlobAshmemSize() const;
+    /**
+     * Returns the total amount of ashmem memory owned by this object.
+     *
+     * Note: for historical reasons, this does not include ashmem memory which
+     * is referenced by this Parcel, but which this parcel doesn't own (e.g.
+     * writeFileDescriptor is called without 'takeOwnership' true).
+     */
     size_t getOpenAshmemSize() const;
+
+    // TODO(b/202029388): Remove 'getBlobAshmemSize' once ABI can be changed.
+    size_t getBlobAshmemSize() const;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 19888b7..f5505da 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -50,6 +50,8 @@
  */
 class RpcSession final : public virtual RefBase {
 public:
+    static constexpr size_t kDefaultMaxOutgoingThreads = 10;
+
     // Create an RpcSession with default configuration (raw sockets).
     static sp<RpcSession> make();
 
@@ -59,7 +61,7 @@
     static sp<RpcSession> make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory);
 
     /**
-     * Set the maximum number of threads allowed to be made (for things like callbacks).
+     * Set the maximum number of incoming threads allowed to be made (for things like callbacks).
      * By default, this is 0. This must be called before setting up this connection as a client.
      * Server sessions will inherits this value from RpcServer.
      *
@@ -68,8 +70,20 @@
      *
      * TODO(b/189955605): start these dynamically
      */
-    void setMaxThreads(size_t threads);
-    size_t getMaxThreads();
+    void setMaxIncomingThreads(size_t threads);
+    size_t getMaxIncomingThreads();
+
+    /**
+     * Set the maximum number of outgoing threads allowed to be made.
+     * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this
+     * connection as a client.
+     *
+     * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession
+     * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where
+     * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|.
+     */
+    void setMaxOutgoingThreads(size_t threads);
+    size_t getMaxOutgoingThreads();
 
     /**
      * By default, the minimum of the supported versions of the client and the
@@ -281,13 +295,13 @@
 
     const std::unique_ptr<RpcTransportCtx> mCtx;
 
-    // On the other side of a session, for each of mOutgoingConnections here, there should
-    // be one of mIncomingConnections on the other side (and vice versa).
+    // On the other side of a session, for each of mOutgoing here, there should
+    // be one of mIncoming on the other side (and vice versa).
     //
     // For the simplest session, a single server with one client, you would
     // have:
-    //  - the server has a single 'mIncomingConnections' and a thread listening on this
-    //  - the client has a single 'mOutgoingConnections' and makes calls to this
+    //  - the server has a single 'mIncoming' and a thread listening on this
+    //  - the client has a single 'mOutgoing' and makes calls to this
     //  - here, when the client makes a call, the server can call back into it
     //    (nested calls), but outside of this, the client will only ever read
     //    calls from the server when it makes a call itself.
@@ -307,7 +321,8 @@
 
     std::mutex mMutex; // for all below
 
-    size_t mMaxThreads = 0;
+    size_t mMaxIncomingThreads = 0;
+    size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
     std::optional<uint32_t> mProtocolVersion;
 
     std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
@@ -315,12 +330,12 @@
     struct ThreadState {
         size_t mWaitingThreads = 0;
         // hint index into clients, ++ when sending an async transaction
-        size_t mOutgoingConnectionsOffset = 0;
-        std::vector<sp<RpcConnection>> mOutgoingConnections;
-        size_t mMaxIncomingConnections = 0;
-        std::vector<sp<RpcConnection>> mIncomingConnections;
+        size_t mOutgoingOffset = 0;
+        std::vector<sp<RpcConnection>> mOutgoing;
+        size_t mMaxIncoming = 0;
+        std::vector<sp<RpcConnection>> mIncoming;
         std::map<std::thread::id, std::thread> mThreads;
-    } mThreadState;
+    } mConnections;
 };
 
 } // namespace android
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 563d011..4a7b664 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -543,6 +543,28 @@
 }
 
 /**
+ * Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
+ */
+template <typename P>
+binder_status_t AParcel_writeNullableStdVectorParcelableElement(AParcel* parcel,
+                                                                const void* vectorData,
+                                                                size_t index) {
+    const std::optional<std::vector<P>>* vector =
+            static_cast<const std::optional<std::vector<P>*>>(vectorData);
+    return AParcel_writeNullableParcelable(parcel, vector->at(index));
+}
+
+/**
+ * Reads a parcelable object of type P inside a std::vector<P> at index 'index' from 'parcel'.
+ */
+template <typename P>
+binder_status_t AParcel_readNullableStdVectorParcelableElement(const AParcel* parcel,
+                                                               void* vectorData, size_t index) {
+    std::optional<std::vector<P>>* vector = static_cast<std::optional<std::vector<P>>*>(vectorData);
+    return AParcel_readNullableParcelable(parcel, &vector->at(index));
+}
+
+/**
  * Writes a ScopedFileDescriptor object inside a std::vector<ScopedFileDescriptor> at index 'index'
  * to 'parcel'.
  */
@@ -579,6 +601,54 @@
 }
 
 /**
+ * Writes an SpAIBinder object inside a std::vector<SpAIBinder> at index 'index'
+ * to 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_writeStdVectorParcelableElement<SpAIBinder>(AParcel* parcel,
+                                                                           const void* vectorData,
+                                                                           size_t index) {
+    const std::vector<SpAIBinder>* vector = static_cast<const std::vector<SpAIBinder>*>(vectorData);
+    return AParcel_writeRequiredStrongBinder(parcel, vector->at(index));
+}
+
+/**
+ * Reads an SpAIBinder object inside a std::vector<SpAIBinder> at index 'index'
+ * from 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_readStdVectorParcelableElement<SpAIBinder>(const AParcel* parcel,
+                                                                          void* vectorData,
+                                                                          size_t index) {
+    std::vector<SpAIBinder>* vector = static_cast<std::vector<SpAIBinder>*>(vectorData);
+    return AParcel_readRequiredStrongBinder(parcel, &vector->at(index));
+}
+
+/**
+ * Writes an SpAIBinder object inside a std::optional<std::vector<SpAIBinder>> at index 'index'
+ * to 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_writeNullableStdVectorParcelableElement<SpAIBinder>(
+        AParcel* parcel, const void* vectorData, size_t index) {
+    const std::optional<std::vector<SpAIBinder>>* vector =
+            static_cast<const std::optional<std::vector<SpAIBinder>>*>(vectorData);
+    return AParcel_writeNullableStrongBinder(parcel, (*vector)->at(index));
+}
+
+/**
+ * Reads an SpAIBinder object inside a std::optional<std::vector<SpAIBinder>> at index 'index'
+ * from 'parcel'.
+ */
+template <>
+inline binder_status_t AParcel_readNullableStdVectorParcelableElement<SpAIBinder>(
+        const AParcel* parcel, void* vectorData, size_t index) {
+    std::optional<std::vector<SpAIBinder>>* vector =
+            static_cast<std::optional<std::vector<SpAIBinder>>*>(vectorData);
+    return AParcel_readNullableStrongBinder(parcel, &(*vector)->at(index));
+}
+
+/**
  * Convenience API for writing a std::vector<P>
  */
 template <typename P>
@@ -598,6 +668,30 @@
                                        AParcel_readStdVectorParcelableElement<P>);
 }
 
+/**
+ * Convenience API for writing a std::optional<std::vector<P>>
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeVector(AParcel* parcel,
+                                                  const std::optional<std::vector<P>>& vec) {
+    if (!vec) return AParcel_writeInt32(parcel, -1);
+    const void* vectorData = static_cast<const void*>(&vec);
+    return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()),
+                                        AParcel_writeNullableStdVectorParcelableElement<P>);
+}
+
+/**
+ * Convenience API for reading a std::optional<std::vector<P>>
+ */
+template <typename P>
+static inline binder_status_t AParcel_readVector(const AParcel* parcel,
+                                                 std::optional<std::vector<P>>* vec) {
+    void* vectorData = static_cast<void*>(vec);
+    return AParcel_readParcelableArray(parcel, vectorData,
+                                       AParcel_nullableStdVectorExternalAllocator<P>,
+                                       AParcel_readNullableStdVectorParcelableElement<P>);
+}
+
 // @START
 /**
  * Writes a vector of int32_t to the next location in a non-null parcel.
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index d9d7caf..ecb044e 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -17,6 +17,7 @@
     rustlibs: [
         "liblibc",
         "libbinder_ndk_sys",
+        "libdowncast_rs",
     ],
     host_supported: true,
     target: {
@@ -133,6 +134,7 @@
     rustlibs: [
         "liblibc",
         "libbinder_ndk_sys",
+        "libdowncast_rs",
     ],
 }
 
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 41ceee5..854b1f9 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -23,6 +23,7 @@
 
 use std::borrow::Borrow;
 use std::cmp::Ordering;
+use std::convert::TryFrom;
 use std::ffi::{c_void, CStr, CString};
 use std::fmt;
 use std::fs::File;
@@ -70,6 +71,7 @@
 /// An interface can promise to be a stable vendor interface ([`Vintf`]), or
 /// makes no stability guarantees ([`Local`]). [`Local`] is
 /// currently the default stability.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
 pub enum Stability {
     /// Default stability, visible to other modules in the same compilation
     /// context (e.g. modules on system.img)
@@ -85,6 +87,28 @@
     }
 }
 
+impl From<Stability> for i32 {
+    fn from(stability: Stability) -> i32 {
+        use Stability::*;
+        match stability {
+            Local => 0,
+            Vintf => 1,
+        }
+    }
+}
+
+impl TryFrom<i32> for Stability {
+    type Error = StatusCode;
+    fn try_from(stability: i32) -> Result<Stability> {
+        use Stability::*;
+        match stability {
+            0 => Ok(Local),
+            1 => Ok(Vintf),
+            _ => Err(StatusCode::BAD_VALUE)
+        }
+    }
+}
+
 /// A local service that can be remotable via Binder.
 ///
 /// An object that implement this interface made be made into a Binder service
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 7e8e3a5..d1d37d7 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -127,7 +127,7 @@
 
 /// The public API usable outside AIDL-generated interface crates.
 pub mod public_api {
-    pub use super::parcel::ParcelFileDescriptor;
+    pub use super::parcel::{ParcelFileDescriptor, ParcelableHolder};
     pub use super::{
         add_service, force_lazy_services_persist, get_interface, register_lazy_service,
         wait_for_interface,
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index dad89ec..7391561 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -29,11 +29,14 @@
 
 mod file_descriptor;
 mod parcelable;
+mod parcelable_holder;
 
 pub use self::file_descriptor::ParcelFileDescriptor;
 pub use self::parcelable::{
     Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption,
+    Parcelable, NON_NULL_PARCELABLE_FLAG, NULL_PARCELABLE_FLAG,
 };
+pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata};
 
 /// Container for a message (data and object references) that can be sent
 /// through Binder.
@@ -68,6 +71,21 @@
 }
 
 impl Parcel {
+    /// Create a new empty `Parcel`.
+    ///
+    /// Creates a new owned empty parcel that can be written to
+    /// using the serialization methods and appended to and
+    /// from using `append_from` and `append_from_all`.
+    pub fn new() -> Parcel {
+        let parcel = unsafe {
+            // Safety: If `AParcel_create` succeeds, it always returns
+            // a valid pointer. If it fails, the process will crash.
+            sys::AParcel_create()
+        };
+        assert!(!parcel.is_null());
+        Self::Owned(parcel)
+    }
+
     /// Create a borrowed reference to a parcel object from a raw pointer.
     ///
     /// # Safety
@@ -106,6 +124,22 @@
     }
 }
 
+impl Default for Parcel {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl Clone for Parcel {
+    fn clone(&self) -> Self {
+        let mut new_parcel = Self::new();
+        new_parcel
+            .append_all_from(self)
+            .expect("Failed to append from Parcel");
+        new_parcel
+    }
+}
+
 // Data serialization methods
 impl Parcel {
     /// Data written to parcelable is zero'd before being deleted or reallocated.
@@ -213,6 +247,30 @@
     pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
         status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
     }
+
+    /// Append a subset of another `Parcel`.
+    ///
+    /// This appends `size` bytes of data from `other` starting at offset
+    /// `start` to the current `Parcel`, or returns an error if not possible.
+    pub fn append_from(&mut self, other: &Self, start: i32, size: i32) -> Result<()> {
+        let status = unsafe {
+            // Safety: `Parcel::appendFrom` from C++ checks that `start`
+            // and `size` are in bounds, and returns an error otherwise.
+            // Both `self` and `other` always contain valid pointers.
+            sys::AParcel_appendFrom(
+                other.as_native(),
+                self.as_native_mut(),
+                start,
+                size,
+            )
+        };
+        status_result(status)
+    }
+
+    /// Append the contents of another `Parcel`.
+    pub fn append_all_from(&mut self, other: &Self) -> Result<()> {
+        self.append_from(other, 0, other.get_data_size())
+    }
 }
 
 /// A segment of a writable parcel, used for [`Parcel::sized_write`].
@@ -427,43 +485,9 @@
     }
 }
 
-#[cfg(test)]
-impl Parcel {
-    /// Create a new parcel tied to a bogus binder. TESTING ONLY!
-    ///
-    /// This can only be used for testing! All real parcel operations must be
-    /// done in the callback to [`IBinder::transact`] or in
-    /// [`Remotable::on_transact`] using the parcels provided to these methods.
-    pub(crate) fn new_for_test(binder: &mut SpIBinder) -> Result<Self> {
-        let mut input = ptr::null_mut();
-        let status = unsafe {
-            // Safety: `SpIBinder` guarantees that `binder` always contains a
-            // valid pointer to an `AIBinder`. We pass a valid, mutable out
-            // pointer to receive a newly constructed parcel. When successful
-            // this function assigns a new pointer to an `AParcel` to `input`
-            // and transfers ownership of this pointer to the caller. Thus,
-            // after this call, `input` will either be null or point to a valid,
-            // owned `AParcel`.
-            sys::AIBinder_prepareTransaction(binder.as_native_mut(), &mut input)
-        };
-        status_result(status)?;
-        unsafe {
-            // Safety: `input` is either null or a valid, owned pointer to an
-            // `AParcel`, so is valid to safe to
-            // `Parcel::owned`. `Parcel::owned` takes ownership of the parcel
-            // pointer.
-            Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)
-        }
-    }
-}
-
 #[test]
 fn test_read_write() {
-    use crate::binder::Interface;
-    use crate::native::Binder;
-
-    let mut service = Binder::new(()).as_binder();
-    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let mut parcel = Parcel::new();
     let start = parcel.get_data_position();
 
     assert_eq!(parcel.read::<bool>(), Err(StatusCode::NOT_ENOUGH_DATA));
@@ -493,11 +517,7 @@
 #[test]
 #[allow(clippy::float_cmp)]
 fn test_read_data() {
-    use crate::binder::Interface;
-    use crate::native::Binder;
-
-    let mut service = Binder::new(()).as_binder();
-    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let mut parcel = Parcel::new();
     let str_start = parcel.get_data_position();
 
     parcel.write(&b"Hello, Binder!\0"[..]).unwrap();
@@ -572,11 +592,7 @@
 
 #[test]
 fn test_utf8_utf16_conversions() {
-    use crate::binder::Interface;
-    use crate::native::Binder;
-
-    let mut service = Binder::new(()).as_binder();
-    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let mut parcel = Parcel::new();
     let start = parcel.get_data_position();
 
     assert!(parcel.write("Hello, Binder!").is_ok());
@@ -636,11 +652,7 @@
 
 #[test]
 fn test_sized_write() {
-    use crate::binder::Interface;
-    use crate::native::Binder;
-
-    let mut service = Binder::new(()).as_binder();
-    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let mut parcel = Parcel::new();
     let start = parcel.get_data_position();
 
     let arr = [1i32, 2i32, 3i32];
@@ -668,3 +680,43 @@
         &arr,
     );
 }
+
+#[test]
+fn test_append_from() {
+    let mut parcel1 = Parcel::new();
+    parcel1.write(&42i32).expect("Could not perform write");
+
+    let mut parcel2 = Parcel::new();
+    assert_eq!(Ok(()), parcel2.append_all_from(&parcel1));
+    assert_eq!(4, parcel2.get_data_size());
+    assert_eq!(Ok(()), parcel2.append_all_from(&parcel1));
+    assert_eq!(8, parcel2.get_data_size());
+    unsafe {
+        parcel2.set_data_position(0).unwrap();
+    }
+    assert_eq!(Ok(42), parcel2.read::<i32>());
+    assert_eq!(Ok(42), parcel2.read::<i32>());
+
+    let mut parcel2 = Parcel::new();
+    assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2));
+    assert_eq!(Ok(()), parcel2.append_from(&parcel1, 2, 2));
+    assert_eq!(4, parcel2.get_data_size());
+    unsafe {
+        parcel2.set_data_position(0).unwrap();
+    }
+    assert_eq!(Ok(42), parcel2.read::<i32>());
+
+    let mut parcel2 = Parcel::new();
+    assert_eq!(Ok(()), parcel2.append_from(&parcel1, 0, 2));
+    assert_eq!(2, parcel2.get_data_size());
+    unsafe {
+        parcel2.set_data_position(0).unwrap();
+    }
+    assert_eq!(Err(StatusCode::NOT_ENOUGH_DATA), parcel2.read::<i32>());
+
+    let mut parcel2 = Parcel::new();
+    assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 4, 2));
+    assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 2, 4));
+    assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, -1, 4));
+    assert_eq!(Err(StatusCode::BAD_VALUE), parcel2.append_from(&parcel1, 2, -1));
+}
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 56c6165..499ef09 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -14,19 +14,42 @@
  * limitations under the License.
  */
 
-use crate::binder::{AsNative, FromIBinder, Strong};
+use crate::binder::{AsNative, FromIBinder, Stability, Strong};
 use crate::error::{status_result, status_t, Result, Status, StatusCode};
 use crate::parcel::Parcel;
 use crate::proxy::SpIBinder;
 use crate::sys;
 
-use std::convert::TryInto;
+use std::convert::{TryFrom, TryInto};
 use std::ffi::c_void;
 use std::os::raw::{c_char, c_ulong};
 use std::mem::{self, MaybeUninit};
 use std::ptr;
 use std::slice;
 
+/// Super-trait for Binder parcelables.
+///
+/// This trait is equivalent `android::Parcelable` in C++,
+/// and defines a common interface that all parcelables need
+/// to implement.
+pub trait Parcelable {
+    /// Internal serialization function for parcelables.
+    ///
+    /// This method is mainly for internal use.
+    /// `Serialize::serialize` and its variants are generally
+    /// preferred over this function, since the former also
+    /// prepend a header.
+    fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()>;
+
+    /// Internal deserialization function for parcelables.
+    ///
+    /// This method is mainly for internal use.
+    /// `Deserialize::deserialize` and its variants are generally
+    /// preferred over this function, since the former also
+    /// parse the additional header.
+    fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()>;
+}
+
 /// A struct whose instances can be written to a [`Parcel`].
 // Might be able to hook this up as a serde backend in the future?
 pub trait Serialize {
@@ -162,6 +185,18 @@
     StatusCode::OK as status_t
 }
 
+/// Flag that specifies that the following parcelable is present.
+///
+/// This is the Rust equivalent of `Parcel::kNonNullParcelableFlag`
+/// from `include/binder/Parcel.h` in C++.
+pub const NON_NULL_PARCELABLE_FLAG: i32 = 1;
+
+/// Flag that specifies that the following parcelable is absent.
+///
+/// This is the Rust equivalent of `Parcel::kNullParcelableFlag`
+/// from `include/binder/Parcel.h` in C++.
+pub const NULL_PARCELABLE_FLAG: i32 = 0;
+
 /// Helper trait for types that can be nullable when serialized.
 // We really need this trait instead of implementing `Serialize for Option<T>`
 // because of the Rust orphan rule which prevents us from doing
@@ -173,10 +208,10 @@
     /// Serialize an Option of this type into the given [`Parcel`].
     fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
         if let Some(inner) = this {
-            parcel.write(&1i32)?;
+            parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
             parcel.write(inner)
         } else {
-            parcel.write(&0i32)
+            parcel.write(&NULL_PARCELABLE_FLAG)
         }
     }
 }
@@ -186,7 +221,7 @@
     /// Deserialize an Option of this type from the given [`Parcel`].
     fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
         let null: i32 = parcel.read()?;
-        if null == 0 {
+        if null == NULL_PARCELABLE_FLAG {
             Ok(None)
         } else {
             parcel.read().map(Some)
@@ -608,6 +643,18 @@
     }
 }
 
+impl Serialize for Stability {
+    fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+        i32::from(*self).serialize(parcel)
+    }
+}
+
+impl Deserialize for Stability {
+    fn deserialize(parcel: &Parcel) -> Result<Self> {
+        i32::deserialize(parcel).and_then(Stability::try_from)
+    }
+}
+
 impl Serialize for Status {
     fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
         unsafe {
@@ -699,19 +746,53 @@
     }
 }
 
+/// Implement `Serialize` trait and friends for a parcelable
+///
+/// This is an internal macro used by the AIDL compiler to implement
+/// `Serialize`, `SerializeArray` and `SerializeOption` for
+/// structured parcelables. The target type must implement the
+/// `Parcelable` trait.
+/// ```
+#[macro_export]
+macro_rules! impl_serialize_for_parcelable {
+    ($parcelable:ident) => {
+        impl $crate::parcel::Serialize for $parcelable {
+            fn serialize(
+                &self,
+                parcel: &mut $crate::parcel::Parcel,
+            ) -> $crate::Result<()> {
+                <Self as $crate::parcel::SerializeOption>::serialize_option(
+                    Some(self),
+                    parcel,
+                )
+            }
+        }
+
+        impl $crate::parcel::SerializeArray for $parcelable {}
+
+        impl $crate::parcel::SerializeOption for $parcelable {
+            fn serialize_option(
+                this: Option<&Self>,
+                parcel: &mut $crate::parcel::Parcel,
+            ) -> $crate::Result<()> {
+                if let Some(this) = this {
+                    use $crate::parcel::Parcelable;
+                    parcel.write(&$crate::parcel::NON_NULL_PARCELABLE_FLAG)?;
+                    this.write_to_parcel(parcel)
+                } else {
+                    parcel.write(&$crate::parcel::NULL_PARCELABLE_FLAG)
+                }
+            }
+        }
+    }
+}
+
 /// Implement `Deserialize` trait and friends for a parcelable
 ///
 /// This is an internal macro used by the AIDL compiler to implement
 /// `Deserialize`, `DeserializeArray` and `DeserializeOption` for
-/// structured parcelables. The target type must implement a
-/// `deserialize_parcelable` method with the following signature:
-/// ```no_run
-/// fn deserialize_parcelable(
-///     &mut self,
-///     parcel: &binder::parcel::Parcelable,
-/// ) -> binder::Result<()> {
-///     // ...
-/// }
+/// structured parcelables. The target type must implement the
+/// `Parcelable` trait.
 /// ```
 #[macro_export]
 macro_rules! impl_deserialize_for_parcelable {
@@ -729,10 +810,11 @@
                 parcel: &$crate::parcel::Parcel,
             ) -> $crate::Result<()> {
                 let status: i32 = parcel.read()?;
-                if status == 0 {
+                if status == $crate::parcel::NULL_PARCELABLE_FLAG {
                     Err($crate::StatusCode::UNEXPECTED_NULL)
                 } else {
-                    self.deserialize_parcelable(parcel)
+                    use $crate::parcel::Parcelable;
+                    self.read_from_parcel(parcel)
                 }
             }
         }
@@ -752,12 +834,13 @@
                 parcel: &$crate::parcel::Parcel,
             ) -> $crate::Result<()> {
                 let status: i32 = parcel.read()?;
-                if status == 0 {
+                if status == $crate::parcel::NULL_PARCELABLE_FLAG {
                     *this = None;
                     Ok(())
                 } else {
+                    use $crate::parcel::Parcelable;
                     this.get_or_insert_with(Self::default)
-                        .deserialize_parcelable(parcel)
+                        .read_from_parcel(parcel)
                 }
             }
         }
@@ -790,10 +873,6 @@
 
 #[test]
 fn test_custom_parcelable() {
-    use crate::binder::Interface;
-    use crate::native::Binder;
-    let mut service = Binder::new(()).as_binder();
-
     struct Custom(u32, bool, String, Vec<String>);
 
     impl Serialize for Custom {
@@ -826,7 +905,7 @@
 
     let custom = Custom(123_456_789, true, string8, strs);
 
-    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let mut parcel = Parcel::new();
     let start = parcel.get_data_position();
 
     assert!(custom.serialize(&mut parcel).is_ok());
@@ -846,13 +925,9 @@
 #[test]
 #[allow(clippy::excessive_precision)]
 fn test_slice_parcelables() {
-    use crate::binder::Interface;
-    use crate::native::Binder;
-    let mut service = Binder::new(()).as_binder();
-
     let bools = [true, false, false, true];
 
-    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let mut parcel = Parcel::new();
     let start = parcel.get_data_position();
 
     assert!(bools.serialize(&mut parcel).is_ok());
@@ -876,7 +951,7 @@
 
     let u8s = [101u8, 255, 42, 117];
 
-    let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+    let mut parcel = Parcel::new();
     let start = parcel.get_data_position();
 
     assert!(parcel.write(&u8s[..]).is_ok());
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
new file mode 100644
index 0000000..3e75d1b
--- /dev/null
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::Stability;
+use crate::error::{Result, StatusCode};
+use crate::parcel::{Parcel, Parcelable};
+use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
+
+use downcast_rs::{impl_downcast, Downcast};
+use std::any::Any;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+/// Metadata that `ParcelableHolder` needs for all parcelables.
+///
+/// The compiler auto-generates implementations of this trait
+/// for AIDL parcelables.
+pub trait ParcelableMetadata {
+    /// The Binder parcelable descriptor string.
+    ///
+    /// This string is a unique identifier for a Binder parcelable.
+    fn get_descriptor() -> &'static str;
+
+    /// The Binder parcelable stability.
+    fn get_stability(&self) -> Stability {
+        Stability::Local
+    }
+}
+
+trait AnyParcelable: Downcast + Parcelable + std::fmt::Debug {}
+impl_downcast!(AnyParcelable);
+impl<T> AnyParcelable for T where T: Downcast + Parcelable + std::fmt::Debug {}
+
+#[derive(Debug, Clone)]
+enum ParcelableHolderData {
+    Empty,
+    Parcelable {
+        parcelable: Rc<dyn AnyParcelable>,
+        name: String,
+    },
+    Parcel(Parcel),
+}
+
+impl Default for ParcelableHolderData {
+    fn default() -> Self {
+        ParcelableHolderData::Empty
+    }
+}
+
+/// A container that can hold any arbitrary `Parcelable`.
+///
+/// This type is currently used for AIDL parcelable fields.
+///
+/// `ParcelableHolder` is currently not thread-safe (neither
+/// `Send` nor `Sync`), mainly because it internally contains
+/// a `Parcel` which in turn is not thread-safe.
+#[derive(Debug, Default, Clone)]
+pub struct ParcelableHolder {
+    // This is a `RefCell` because of `get_parcelable`
+    // which takes `&self` for consistency with C++.
+    // We could make `get_parcelable` take a `&mut self`
+    // and get rid of the `RefCell` here for a performance
+    // improvement, but then callers would require a mutable
+    // `ParcelableHolder` even for that getter method.
+    data: RefCell<ParcelableHolderData>,
+    stability: Stability,
+}
+
+impl ParcelableHolder {
+    /// Construct a new `ParcelableHolder` with the given stability.
+    pub fn new(stability: Stability) -> Self {
+        Self {
+            data: RefCell::new(ParcelableHolderData::Empty),
+            stability,
+        }
+    }
+
+    /// Reset the contents of this `ParcelableHolder`.
+    ///
+    /// Note that this method does not reset the stability,
+    /// only the contents.
+    pub fn reset(&mut self) {
+        *self.data.get_mut() = ParcelableHolderData::Empty;
+        // We could also clear stability here, but C++ doesn't
+    }
+
+    /// Set the parcelable contained in this `ParcelableHolder`.
+    pub fn set_parcelable<T>(&mut self, p: Rc<T>) -> Result<()>
+    where
+        T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug,
+    {
+        if self.stability > p.get_stability() {
+            return Err(StatusCode::BAD_VALUE);
+        }
+
+        *self.data.get_mut() = ParcelableHolderData::Parcelable {
+            parcelable: p,
+            name: T::get_descriptor().into(),
+        };
+
+        Ok(())
+    }
+
+    /// Retrieve the parcelable stored in this `ParcelableHolder`.
+    ///
+    /// This method attempts to retrieve the parcelable inside
+    /// the current object as a parcelable of type `T`.
+    /// The object is validated against `T` by checking that
+    /// its parcelable descriptor matches the one returned
+    /// by `T::get_descriptor()`.
+    ///
+    /// Returns one of the following:
+    /// * `Err(_)` in case of error
+    /// * `Ok(None)` if the holder is empty or the descriptor does not match
+    /// * `Ok(Some(_))` if the object holds a parcelable of type `T`
+    ///   with the correct descriptor
+    pub fn get_parcelable<T>(&self) -> Result<Option<Rc<T>>>
+    where
+        T: Any + Parcelable + ParcelableMetadata + Default + std::fmt::Debug,
+    {
+        let parcelable_desc = T::get_descriptor();
+        let mut data = self.data.borrow_mut();
+        match *data {
+            ParcelableHolderData::Empty => Ok(None),
+            ParcelableHolderData::Parcelable {
+                ref parcelable,
+                ref name,
+            } => {
+                if name != parcelable_desc {
+                    return Err(StatusCode::BAD_VALUE);
+                }
+
+                match Rc::clone(parcelable).downcast_rc::<T>() {
+                    Err(_) => Err(StatusCode::BAD_VALUE),
+                    Ok(x) => Ok(Some(x)),
+                }
+            }
+            ParcelableHolderData::Parcel(ref parcel) => {
+                unsafe {
+                    // Safety: 0 should always be a valid position.
+                    parcel.set_data_position(0)?;
+                }
+
+                let name: String = parcel.read()?;
+                if name != parcelable_desc {
+                    return Ok(None);
+                }
+
+                let mut parcelable = T::default();
+                parcelable.read_from_parcel(parcel)?;
+
+                let parcelable = Rc::new(parcelable);
+                let result = Rc::clone(&parcelable);
+                *data = ParcelableHolderData::Parcelable { parcelable, name };
+
+                Ok(Some(result))
+            }
+        }
+    }
+
+    /// Return the stability value of this object.
+    pub fn get_stability(&self) -> Stability {
+        self.stability
+    }
+}
+
+impl_serialize_for_parcelable!(ParcelableHolder);
+impl_deserialize_for_parcelable!(ParcelableHolder);
+
+impl Parcelable for ParcelableHolder {
+    fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> {
+        parcel.write(&self.stability)?;
+
+        match *self.data.borrow() {
+            ParcelableHolderData::Empty => parcel.write(&0i32),
+            ParcelableHolderData::Parcelable {
+                ref parcelable,
+                ref name,
+            } => {
+                let length_start = parcel.get_data_position();
+                parcel.write(&0i32)?;
+
+                let data_start = parcel.get_data_position();
+                parcel.write(name)?;
+                parcelable.write_to_parcel(parcel)?;
+
+                let end = parcel.get_data_position();
+                unsafe {
+                    // Safety: we got the position from `get_data_position`.
+                    parcel.set_data_position(length_start)?;
+                }
+
+                assert!(end >= data_start);
+                parcel.write(&(end - data_start))?;
+                unsafe {
+                    // Safety: we got the position from `get_data_position`.
+                    parcel.set_data_position(end)?;
+                }
+
+                Ok(())
+            }
+            ParcelableHolderData::Parcel(ref p) => {
+                parcel.write(&p.get_data_size())?;
+                parcel.append_all_from(p)
+            }
+        }
+    }
+
+    fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()> {
+        self.stability = parcel.read()?;
+
+        let data_size: i32 = parcel.read()?;
+        if data_size < 0 {
+            // C++ returns BAD_VALUE here,
+            // while Java returns ILLEGAL_ARGUMENT
+            return Err(StatusCode::BAD_VALUE);
+        }
+        if data_size == 0 {
+            *self.data.get_mut() = ParcelableHolderData::Empty;
+            return Ok(());
+        }
+
+        // TODO: C++ ParcelableHolder accepts sizes up to SIZE_MAX here, but we
+        // only go up to i32::MAX because that's what our API uses everywhere
+        let data_start = parcel.get_data_position();
+        let data_end = data_start
+            .checked_add(data_size)
+            .ok_or(StatusCode::BAD_VALUE)?;
+
+        let mut new_parcel = Parcel::new();
+        new_parcel.append_from(parcel, data_start, data_size)?;
+        *self.data.get_mut() = ParcelableHolderData::Parcel(new_parcel);
+
+        unsafe {
+            // Safety: `append_from` checks if `data_size` overflows
+            // `parcel` and returns `BAD_VALUE` if that happens. We also
+            // explicitly check for negative and zero `data_size` above,
+            // so `data_end` is guaranteed to be greater than `data_start`.
+            parcel.set_data_position(data_end)?;
+        }
+
+        Ok(())
+    }
+}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 68fa34b..6a4af07 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -430,6 +430,7 @@
 
 impl SerializeArray for SpIBinder {}
 impl SerializeArray for Option<&SpIBinder> {}
+impl SerializeArray for Option<SpIBinder> {}
 
 impl Deserialize for SpIBinder {
     fn deserialize(parcel: &Parcel) -> Result<SpIBinder> {
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 680f0ed..86da588 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -87,6 +87,7 @@
     srcs: ["binderParcelUnitTest.cpp", "binderBinderUnitTest.cpp"],
     shared_libs: [
         "libbinder",
+        "libcutils",
         "libutils",
     ],
     test_suites: ["general-tests"],
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index eec3b44..464da60 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -65,7 +65,9 @@
 
 void initHostRpcServiceManagerOnce() {
     static std::once_flag gSmOnce;
-    std::call_once(gSmOnce, [] { setDefaultServiceManager(createRpcDelegateServiceManager()); });
+    std::call_once(gSmOnce, [] {
+        setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+    });
 }
 
 // Test for host service manager.
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 841d47b..4950b23 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#include <binder/Parcel.h>
 #include <binder/IPCThreadState.h>
+#include <binder/Parcel.h>
+#include <cutils/ashmem.h>
 #include <gtest/gtest.h>
 
 using android::IPCThreadState;
@@ -146,3 +147,18 @@
 TEST_READ_WRITE_INVERSE(int8_t, Byte, {-1, 0, 1});
 TEST_READ_WRITE_INVERSE(String8, String8, {String8(), String8("a"), String8("asdf")});
 TEST_READ_WRITE_INVERSE(String16, String16, {String16(), String16("a"), String16("asdf")});
+
+TEST(Parcel, GetOpenAshmemSize) {
+    constexpr size_t kSize = 1024;
+    constexpr size_t kCount = 3;
+
+    Parcel p;
+
+    for (size_t i = 0; i < kCount; i++) {
+        int fd = ashmem_create_region("test-getOpenAshmemSize", kSize);
+        ASSERT_GE(fd, 0);
+        ASSERT_EQ(OK, p.writeFileDescriptor(fd, true /* take ownership */));
+
+        ASSERT_EQ((kSize * (i + 1)), p.getOpenAshmemSize());
+    }
+}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index a1058bc..8267702 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -481,6 +481,7 @@
         size_t numThreads = 1;
         size_t numSessions = 1;
         size_t numIncomingConnections = 0;
+        size_t numOutgoingConnections = SIZE_MAX;
     };
 
     static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
@@ -613,7 +614,8 @@
         status_t status;
 
         for (const auto& session : sessions) {
-            session->setMaxThreads(options.numIncomingConnections);
+            session->setMaxIncomingThreads(options.numIncomingConnections);
+            session->setMaxOutgoingThreads(options.numOutgoingConnections);
 
             switch (socketType) {
                 case SocketType::PRECONNECTED:
@@ -655,6 +657,9 @@
 
         return ret;
     }
+
+    void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
+                                     size_t sleepMs = 500);
 };
 
 TEST_P(BinderRpc, Ping) {
@@ -685,13 +690,21 @@
 }
 
 TEST_P(BinderRpc, AppendSeparateFormats) {
-    auto proc = createRpcTestSocketServerProcess({});
+    auto proc1 = createRpcTestSocketServerProcess({});
+    auto proc2 = createRpcTestSocketServerProcess({});
+
+    Parcel pRaw;
 
     Parcel p1;
-    p1.markForBinder(proc.rootBinder);
+    p1.markForBinder(proc1.rootBinder);
     p1.writeInt32(3);
 
+    EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, p1.dataSize()));
+    EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
+
     Parcel p2;
+    p2.markForBinder(proc2.rootBinder);
+    p2.writeInt32(7);
 
     EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
     EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
@@ -996,28 +1009,39 @@
     for (auto& t : ts) t.join();
 }
 
-TEST_P(BinderRpc, ThreadPoolOverSaturated) {
-    constexpr size_t kNumThreads = 10;
-    constexpr size_t kNumCalls = kNumThreads + 3;
-    constexpr size_t kSleepMs = 500;
-
-    auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
-
+void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
+                                            size_t sleepMs) {
     size_t epochMsBefore = epochMillis();
 
     std::vector<std::thread> ts;
-    for (size_t i = 0; i < kNumCalls; i++) {
-        ts.push_back(std::thread([&] { proc.rootIface->sleepMs(kSleepMs); }));
+    for (size_t i = 0; i < numCalls; i++) {
+        ts.push_back(std::thread([&] { iface->sleepMs(sleepMs); }));
     }
 
     for (auto& t : ts) t.join();
 
     size_t epochMsAfter = epochMillis();
 
-    EXPECT_GE(epochMsAfter, epochMsBefore + 2 * kSleepMs);
+    EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs);
 
     // Potential flake, but make sure calls are handled in parallel.
-    EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs);
+    EXPECT_LE(epochMsAfter, epochMsBefore + 3 * sleepMs);
+}
+
+TEST_P(BinderRpc, ThreadPoolOverSaturated) {
+    constexpr size_t kNumThreads = 10;
+    constexpr size_t kNumCalls = kNumThreads + 3;
+    auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+    testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+}
+
+TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
+    constexpr size_t kNumThreads = 20;
+    constexpr size_t kNumOutgoingConnections = 10;
+    constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
+    auto proc = createRpcTestSocketServerProcess(
+            {.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
+    testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
 }
 
 TEST_P(BinderRpc, ThreadingStressTest) {
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index f1fe164..55eb847 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -301,6 +301,14 @@
         FUZZ_LOG() << "ParcelableHolder status: " << status;
     },
     PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
+    [] (const ::android::Parcel& p, uint8_t /* data */) {
+        FUZZ_LOG() << "about to call hasFileDescriptorsInRange() with status";
+        size_t offset = p.readUint32();
+        size_t length = p.readUint32();
+        bool result;
+        status_t status = p.hasFileDescriptorsInRange(offset, length, result);
+        FUZZ_LOG() << " status: " << status  << " result: " << result;
+    },
 };
 // clang-format on
 #pragma clang diagnostic pop
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index e2f072a..61e6657 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -1134,6 +1134,18 @@
                           decodeByteVector);
 }
 
+status_t encodeSmpte2094_10(const std::optional<std::vector<uint8_t>>& smpte2094_10,
+                            hidl_vec<uint8_t>* outSmpte2094_10) {
+    return encodeOptionalMetadata(MetadataType_Smpte2094_10, smpte2094_10, outSmpte2094_10,
+                                  encodeByteVector);
+}
+
+status_t decodeSmpte2094_10(const hidl_vec<uint8_t>& smpte2094_10,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_10) {
+    return decodeOptionalMetadata(MetadataType_Smpte2094_10, smpte2094_10, outSmpte2094_10,
+                                  decodeByteVector);
+}
+
 status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
                       hidl_vec<uint8_t>* output) {
     return encodeMetadata(metadataType, input, output, encodeInteger);
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 2f418ac..deaffad 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -134,6 +134,12 @@
                                              aidl::android::hardware::graphics::common::
                                                      StandardMetadataType::SMPTE2094_40)};
 
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Smpte2094_10 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                     static_cast<int64_t>(
+                                             aidl::android::hardware::graphics::common::
+                                                     StandardMetadataType::SMPTE2094_10)};
+
 /*---------------------------------------------------------------------------------------------*/
 
 /**
@@ -327,6 +333,11 @@
 status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
                             std::optional<std::vector<uint8_t>>* outSmpte2094_40);
 
+status_t encodeSmpte2094_10(const std::optional<std::vector<uint8_t>>& smpte2094_10,
+                            android::hardware::hidl_vec<uint8_t>* outSmpte2094_10);
+status_t decodeSmpte2094_10(const android::hardware::hidl_vec<uint8_t>& smpte2094_10,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_10);
+
 /**
  * The functions below can be used to encode and decode vendor metadata types.
  */
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
index 89cbf4a..94e344f 100644
--- a/libs/gralloc/types/tests/Gralloc4_test.cpp
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -455,6 +455,32 @@
     ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
 }
 
+class Gralloc4TestSmpte2094_10
+      : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> {};
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestSmpte2094_10Params, Gralloc4TestSmpte2094_10,
+        ::testing::Values(
+                std::optional<std::vector<uint8_t>>({}),
+                std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+                std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+                                                     std::numeric_limits<uint8_t>::min() + 1,
+                                                     std::numeric_limits<uint8_t>::min() + 2,
+                                                     std::numeric_limits<uint8_t>::min() + 3,
+                                                     std::numeric_limits<uint8_t>::min() + 4}),
+                std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+                                                     std::numeric_limits<uint8_t>::max() - 1,
+                                                     std::numeric_limits<uint8_t>::max() - 2,
+                                                     std::numeric_limits<uint8_t>::max() - 3,
+                                                     std::numeric_limits<uint8_t>::max() - 4}),
+                std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_10, Smpte2094_10) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(),
+                                                             gralloc4::encodeSmpte2094_10,
+                                                             gralloc4::decodeSmpte2094_10));
+}
+
 class Gralloc4TestBufferDescriptorInfo : public testing::TestWithParam<BufferDescriptorInfo> { };
 
 INSTANTIATE_TEST_CASE_P(
@@ -491,6 +517,7 @@
     ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
     ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
     ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_10({{}}, nullptr));
 }
 
 TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
@@ -516,6 +543,7 @@
     ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
     ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
     ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_10(vec, nullptr));
 }
 
 TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 2b17616..dfbb863 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -46,6 +46,8 @@
 namespace android {
 
 // Macros to include adapter info in log messages
+#define BQA_LOGD(x, ...) \
+    ALOGD("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
 #define BQA_LOGV(x, ...) \
     ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
 // enable logs for a single layer
@@ -162,6 +164,7 @@
 
     ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
     mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
+    mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
 
     mTransformHint = mSurfaceControl->getTransformHint();
     mBufferItemConsumer->setTransformHint(mTransformHint);
@@ -243,6 +246,67 @@
     }
 }
 
+static std::optional<SurfaceControlStats> findMatchingStat(
+        const std::vector<SurfaceControlStats>& stats, const sp<SurfaceControl>& sc) {
+    for (auto stat : stats) {
+        if (SurfaceControl::isSameSurface(sc, stat.surfaceControl)) {
+            return stat;
+        }
+    }
+    return std::nullopt;
+}
+
+static void transactionCommittedCallbackThunk(void* context, nsecs_t latchTime,
+                                              const sp<Fence>& presentFence,
+                                              const std::vector<SurfaceControlStats>& stats) {
+    if (context == nullptr) {
+        return;
+    }
+    sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
+    bq->transactionCommittedCallback(latchTime, presentFence, stats);
+}
+
+void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/,
+                                                    const sp<Fence>& /*presentFence*/,
+                                                    const std::vector<SurfaceControlStats>& stats) {
+    {
+        std::unique_lock _lock{mMutex};
+        ATRACE_CALL();
+        BQA_LOGV("transactionCommittedCallback");
+        if (!mSurfaceControlsWithPendingCallback.empty()) {
+            sp<SurfaceControl> pendingSC = mSurfaceControlsWithPendingCallback.front();
+            std::optional<SurfaceControlStats> stat = findMatchingStat(stats, pendingSC);
+            if (stat) {
+                uint64_t currFrameNumber = stat->frameEventStats.frameNumber;
+
+                // We need to check if we were waiting for a transaction callback in order to
+                // process any pending buffers and unblock. It's possible to get transaction
+                // callbacks for previous requests so we need to ensure the frame from this
+                // transaction callback matches the last acquired buffer. Since acquireNextBuffer
+                // will stop processing buffers when mWaitForTransactionCallback is set, we know
+                // that mLastAcquiredFrameNumber is the frame we're waiting on.
+                // We also want to check if mNextTransaction is null because it's possible another
+                // sync request came in while waiting, but it hasn't started processing yet. In that
+                // case, we don't actually want to flush the frames in between since they will get
+                // processed and merged with the sync transaction and released earlier than if they
+                // were sent to SF
+                if (mWaitForTransactionCallback && mNextTransaction == nullptr &&
+                    currFrameNumber >= mLastAcquiredFrameNumber) {
+                    mWaitForTransactionCallback = false;
+                    flushShadowQueue();
+                }
+            } else {
+                BQA_LOGE("Failed to find matching SurfaceControl in transaction callback");
+            }
+        } else {
+            BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
+                     "empty.");
+        }
+
+        decStrong((void*)transactionCommittedCallbackThunk);
+    }
+}
+
 static void transactionCallbackThunk(void* context, nsecs_t latchTime,
                                      const sp<Fence>& presentFence,
                                      const std::vector<SurfaceControlStats>& stats) {
@@ -266,12 +330,9 @@
         if (!mSurfaceControlsWithPendingCallback.empty()) {
             sp<SurfaceControl> pendingSC = mSurfaceControlsWithPendingCallback.front();
             mSurfaceControlsWithPendingCallback.pop();
-            bool found = false;
-            for (auto stat : stats) {
-                if (!SurfaceControl::isSameSurface(pendingSC, stat.surfaceControl)) {
-                    continue;
-                }
-
+            std::optional<SurfaceControlStats> statsOptional = findMatchingStat(stats, pendingSC);
+            if (statsOptional) {
+                SurfaceControlStats stat = *statsOptional;
                 mTransformHint = stat.transformHint;
                 mBufferItemConsumer->setTransformHint(mTransformHint);
                 BQA_LOGV("updated mTransformHint=%d", mTransformHint);
@@ -299,12 +360,7 @@
                     transactionCompleteCallback = std::move(mTransactionCompleteCallback);
                     mTransactionCompleteFrameNumber = 0;
                 }
-
-                found = true;
-                break;
-            }
-
-            if (!found) {
+            } else {
                 BQA_LOGE("Failed to find matching SurfaceControl in transaction callback");
             }
         } else {
@@ -325,31 +381,32 @@
 // So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
 // Otherwise, this is a no-op.
 static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id,
-                                       const sp<Fence>& releaseFence, uint32_t transformHint,
-                                       uint32_t currentMaxAcquiredBufferCount) {
+                                       const sp<Fence>& releaseFence,
+                                       std::optional<uint32_t> currentMaxAcquiredBufferCount) {
     sp<BLASTBufferQueue> blastBufferQueue = context.promote();
     if (blastBufferQueue) {
-        blastBufferQueue->releaseBufferCallback(id, releaseFence, transformHint,
-                                                currentMaxAcquiredBufferCount);
+        blastBufferQueue->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
     } else {
         ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
     }
 }
 
-void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id,
-                                             const sp<Fence>& releaseFence, uint32_t transformHint,
-                                             uint32_t currentMaxAcquiredBufferCount) {
+void BLASTBufferQueue::flushShadowQueue() {
+    BQA_LOGV("flushShadowQueue");
+    int numFramesToFlush = mNumFrameAvailable;
+    while (numFramesToFlush > 0) {
+        acquireNextBufferLocked(std::nullopt);
+        numFramesToFlush--;
+    }
+}
+
+void BLASTBufferQueue::releaseBufferCallback(
+        const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+        std::optional<uint32_t> currentMaxAcquiredBufferCount) {
     ATRACE_CALL();
     std::unique_lock _lock{mMutex};
     BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
 
-    if (mSurfaceControl != nullptr) {
-        mTransformHint = transformHint;
-        mSurfaceControl->setTransformHint(transformHint);
-        mBufferItemConsumer->setTransformHint(mTransformHint);
-        BQA_LOGV("updated mTransformHint=%d", mTransformHint);
-    }
-
     // Calculate how many buffers we need to hold before we release them back
     // to the buffer queue. This will prevent higher latency when we are running
     // on a lower refresh rate than the max supported. We only do that for EGL
@@ -359,8 +416,12 @@
         return it != mSubmitted.end() && it->second.mApi == NATIVE_WINDOW_API_EGL;
     }();
 
+    if (currentMaxAcquiredBufferCount) {
+        mCurrentMaxAcquiredBufferCount = *currentMaxAcquiredBufferCount;
+    }
+
     const auto numPendingBuffersToHold =
-            isEGL ? std::max(0u, mMaxAcquiredBuffers - currentMaxAcquiredBufferCount) : 0;
+            isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
     mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
 
     // Release all buffers that are beyond the ones that we need to hold
@@ -374,10 +435,14 @@
             return;
         }
         mNumAcquired--;
-        BQA_LOGV("released %s", id.to_string().c_str());
+        BQA_LOGV("released %s", releaseBuffer.callbackId.to_string().c_str());
         mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
         mSubmitted.erase(it);
-        processNextBufferLocked(false /* useNextTransaction */);
+        // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
+        // onFrameAvailable handle processing them since it will merge with the nextTransaction.
+        if (!mWaitForTransactionCallback) {
+            acquireNextBufferLocked(std::nullopt);
+        }
     }
 
     ATRACE_INT("PendingRelease", mPendingRelease.size());
@@ -386,13 +451,15 @@
     mCallbackCV.notify_all();
 }
 
-void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
+void BLASTBufferQueue::acquireNextBufferLocked(
+        const std::optional<SurfaceComposerClient::Transaction*> transaction) {
     ATRACE_CALL();
     // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
     // include the extra buffer when checking if we can acquire the next buffer.
-    const bool includeExtraAcquire = !useNextTransaction;
-    if (mNumFrameAvailable == 0 || maxBuffersAcquired(includeExtraAcquire)) {
-        mCallbackCV.notify_all();
+    const bool includeExtraAcquire = !transaction;
+    const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire);
+    if (mNumFrameAvailable == 0 || maxAcquired) {
+        BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired));
         return;
     }
 
@@ -404,9 +471,8 @@
     SurfaceComposerClient::Transaction localTransaction;
     bool applyTransaction = true;
     SurfaceComposerClient::Transaction* t = &localTransaction;
-    if (mNextTransaction != nullptr && useNextTransaction) {
-        t = mNextTransaction;
-        mNextTransaction = nullptr;
+    if (transaction) {
+        t = *transaction;
         applyTransaction = false;
     }
 
@@ -436,7 +502,7 @@
                  mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height,
                  buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
         mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
-        processNextBufferLocked(useNextTransaction);
+        acquireNextBufferLocked(transaction);
         return;
     }
 
@@ -455,6 +521,7 @@
 
     // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
     incStrong((void*)transactionCallbackThunk);
+    incStrong((void*)transactionCommittedCallbackThunk);
 
     const bool sizeHasChanged = mRequestedSize != mSize;
     mSize = mRequestedSize;
@@ -466,8 +533,7 @@
 
     auto releaseBufferCallback =
             std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
-                      std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
-                      std::placeholders::_4);
+                      std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
     sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
     t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseCallbackId,
                  releaseBufferCallback);
@@ -475,6 +541,7 @@
     t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
     t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
+    t->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast<void*>(this));
     mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
 
     if (updateDestinationFrame) {
@@ -512,7 +579,7 @@
         t->setApplyToken(mApplyToken).apply();
     }
 
-    BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
+    BQA_LOGV("acquireNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
              " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d"
              " graphicBufferId=%" PRIu64 "%s transform=%d",
              mSize.width, mSize.height, bufferItem.mFrameNumber, boolToString(applyTransaction),
@@ -528,17 +595,50 @@
     return item.mCrop;
 }
 
+void BLASTBufferQueue::acquireAndReleaseBuffer() {
+    BufferItem bufferItem;
+    status_t status =
+            mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
+    if (status != OK) {
+        BQA_LOGE("Failed to acquire a buffer in acquireAndReleaseBuffer, err=%s",
+                 statusToString(status).c_str());
+        return;
+    }
+    mNumFrameAvailable--;
+    mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
+}
+
 void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
     ATRACE_CALL();
     std::unique_lock _lock{mMutex};
 
     const bool nextTransactionSet = mNextTransaction != nullptr;
+    BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet));
     if (nextTransactionSet) {
-        while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
-            BQA_LOGV("waiting in onFrameAvailable...");
+        if (mWaitForTransactionCallback) {
+            // We are waiting on a previous sync's transaction callback so allow another sync
+            // transaction to proceed.
+            //
+            // We need to first flush out the transactions that were in between the two syncs.
+            // We do this by merging them into mNextTransaction so any buffer merging will get
+            // a release callback invoked. The release callback will be async so we need to wait
+            // on max acquired to make sure we have the capacity to acquire another buffer.
+            if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+                BQA_LOGD("waiting to flush shadow queue...");
+                mCallbackCV.wait(_lock);
+            }
+            while (mNumFrameAvailable > 0) {
+                // flush out the shadow queue
+                acquireAndReleaseBuffer();
+            }
+        }
+
+        while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+            BQA_LOGD("waiting for free buffer.");
             mCallbackCV.wait(_lock);
         }
     }
+
     // add to shadow queue
     mNumFrameAvailable++;
     ATRACE_INT(mQueuedBufferTrace.c_str(),
@@ -546,7 +646,14 @@
 
     BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
              boolToString(nextTransactionSet));
-    processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
+
+    if (nextTransactionSet) {
+        acquireNextBufferLocked(std::move(mNextTransaction));
+        mNextTransaction = nullptr;
+        mWaitForTransactionCallback = true;
+    } else if (!mWaitForTransactionCallback) {
+        acquireNextBufferLocked(std::nullopt);
+    }
 }
 
 void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) {
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 98e8b54..aa7ebc9 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -254,11 +254,10 @@
     }
 
     void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
-                         uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override {
+                         uint32_t currentMaxAcquiredBufferCount) override {
         callRemoteAsync<decltype(
                 &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
                                                                   callbackId, releaseFence,
-                                                                  transformHint,
                                                                   currentMaxAcquiredBufferCount);
     }
 };
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index aca59b6..2713be0 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -214,12 +214,6 @@
     mReleaseBufferCallbacks[callbackId] = listener;
 }
 
-void TransactionCompletedListener::removeReleaseBufferCallback(
-        const ReleaseCallbackId& callbackId) {
-    std::scoped_lock<std::mutex> lock(mMutex);
-    mReleaseBufferCallbacks.erase(callbackId);
-}
-
 void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
         sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
     std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
@@ -343,7 +337,6 @@
                                  surfaceStats.previousReleaseFence
                                          ? surfaceStats.previousReleaseFence
                                          : Fence::NO_FENCE,
-                                 surfaceStats.transformHint,
                                  surfaceStats.currentMaxAcquiredBufferCount);
                     }
                 }
@@ -389,7 +382,7 @@
 }
 
 void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
-                                                   sp<Fence> releaseFence, uint32_t transformHint,
+                                                   sp<Fence> releaseFence,
                                                    uint32_t currentMaxAcquiredBufferCount) {
     ReleaseBufferCallback callback;
     {
@@ -401,7 +394,11 @@
               callbackId.to_string().c_str());
         return;
     }
-    callback(callbackId, releaseFence, transformHint, currentMaxAcquiredBufferCount);
+    std::optional<uint32_t> optionalMaxAcquiredBufferCount =
+            currentMaxAcquiredBufferCount == UINT_MAX
+            ? std::nullopt
+            : std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
+    callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
 }
 
 ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -712,11 +709,34 @@
     return NO_ERROR;
 }
 
+void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_state_t& state) {
+    if (!(state.what & layer_state_t::eBufferChanged)) {
+        return;
+    }
+
+    auto listener = state.bufferData.releaseBufferListener;
+    sp<Fence> fence =
+            state.bufferData.acquireFence ? state.bufferData.acquireFence : Fence::NO_FENCE;
+    if (state.bufferData.releaseBufferEndpoint ==
+        IInterface::asBinder(TransactionCompletedListener::getIInstance())) {
+        // if the callback is in process, run on a different thread to avoid any lock contigency
+        // issues in the client.
+        SurfaceComposerClient::getDefault()
+                ->mReleaseCallbackThread.addReleaseCallback(state.bufferData.releaseCallbackId,
+                                                            fence);
+    } else {
+        listener->onReleaseBuffer(state.bufferData.releaseCallbackId, fence, UINT_MAX);
+    }
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
     for (auto const& [handle, composerState] : other.mComposerStates) {
         if (mComposerStates.count(handle) == 0) {
             mComposerStates[handle] = composerState;
         } else {
+            if (composerState.state.what & layer_state_t::eBufferChanged) {
+                releaseBufferIfOverwriting(mComposerStates[handle].state);
+            }
             mComposerStates[handle].state.merge(composerState.state);
         }
     }
@@ -1296,7 +1316,9 @@
         mStatus = BAD_INDEX;
         return *this;
     }
-    removeReleaseBufferCallback(s);
+
+    releaseBufferIfOverwriting(*s);
+
     BufferData bufferData;
     bufferData.buffer = buffer;
     if (frameNumber) {
@@ -1321,15 +1343,6 @@
     return *this;
 }
 
-void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) {
-    if (!(s->what & layer_state_t::eBufferChanged)) {
-        return;
-    }
-
-    auto listener = TransactionCompletedListener::getInstance();
-    listener->removeReleaseBufferCallback(s->bufferData.releaseCallbackId);
-}
-
 void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData,
                                                                   const ReleaseCallbackId& id,
                                                                   ReleaseBufferCallback callback) {
@@ -2210,4 +2223,43 @@
     return s->captureLayers(captureArgs, captureListener);
 }
 
+// ---------------------------------------------------------------------------------
+
+void ReleaseCallbackThread::addReleaseCallback(const ReleaseCallbackId callbackId,
+                                               sp<Fence> releaseFence) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    if (!mStarted) {
+        mThread = std::thread(&ReleaseCallbackThread::threadMain, this);
+        mStarted = true;
+    }
+
+    mCallbackInfos.emplace(callbackId, std::move(releaseFence));
+    mReleaseCallbackPending.notify_one();
+}
+
+void ReleaseCallbackThread::threadMain() {
+    const auto listener = TransactionCompletedListener::getInstance();
+    std::queue<std::tuple<const ReleaseCallbackId, const sp<Fence>>> callbackInfos;
+    while (true) {
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            callbackInfos = std::move(mCallbackInfos);
+            mCallbackInfos = {};
+        }
+
+        while (!callbackInfos.empty()) {
+            auto [callbackId, releaseFence] = callbackInfos.front();
+            listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+            callbackInfos.pop();
+        }
+
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            if (mCallbackInfos.size() == 0) {
+                mReleaseCallbackPending.wait(lock);
+            }
+        }
+    }
+}
+
 } // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 1f517f6..4a1d475 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -88,10 +88,12 @@
     void onFrameDequeued(const uint64_t) override;
     void onFrameCancelled(const uint64_t) override;
 
+    void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+                                      const std::vector<SurfaceControlStats>& stats);
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
             const std::vector<SurfaceControlStats>& stats);
     void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
-                               uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
+                               std::optional<uint32_t> currentMaxAcquiredBufferCount);
     void setNextTransaction(SurfaceComposerClient::Transaction *t);
     void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
     void applyPendingTransactions(uint64_t frameNumber);
@@ -120,7 +122,8 @@
     void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                            sp<IGraphicBufferConsumer>* outConsumer);
 
-    void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
+    void acquireNextBufferLocked(
+            const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex);
     Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
     // Return true if we need to reject the buffer based on the scaling mode and the buffer size.
     bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
@@ -129,6 +132,9 @@
     void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
             REQUIRES(mMutex);
 
+    void flushShadowQueue() REQUIRES(mMutex);
+    void acquireAndReleaseBuffer() REQUIRES(mMutex);
+
     std::string mName;
     // Represents the queued buffer count from buffer queue,
     // pre-BLAST. This is mNumFrameAvailable (buffers that queued to blast) +
@@ -236,6 +242,9 @@
     // Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a
     // callback for them.
     std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
+
+    uint32_t mCurrentMaxAcquiredBufferCount;
+    bool mWaitForTransactionCallback = false;
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 937095c..0df5822 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -192,7 +192,6 @@
     virtual void onTransactionCompleted(ListenerStats stats) = 0;
 
     virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
-                                 uint32_t transformHint,
                                  uint32_t currentMaxAcquiredBufferCount) = 0;
 };
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 82249a3..e62c76e 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <set>
+#include <thread>
 #include <unordered_map>
 #include <unordered_set>
 
@@ -84,7 +85,7 @@
                            const std::vector<SurfaceControlStats>& /*stats*/)>;
 using ReleaseBufferCallback =
         std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
-                           uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount)>;
+                           std::optional<uint32_t> currentMaxAcquiredBufferCount)>;
 
 using SurfaceStatsCallback =
         std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
@@ -93,6 +94,22 @@
 
 // ---------------------------------------------------------------------------
 
+class ReleaseCallbackThread {
+public:
+    void addReleaseCallback(const ReleaseCallbackId, sp<Fence>);
+    void threadMain();
+
+private:
+    std::thread mThread;
+    std::mutex mMutex;
+    bool mStarted GUARDED_BY(mMutex) = false;
+    std::condition_variable mReleaseCallbackPending;
+    std::queue<std::tuple<const ReleaseCallbackId, const sp<Fence>>> mCallbackInfos
+            GUARDED_BY(mMutex);
+};
+
+// ---------------------------------------------------------------------------
+
 class SurfaceComposerClient : public RefBase
 {
     friend class Composer;
@@ -350,6 +367,7 @@
     private:
         static std::atomic<uint32_t> idCounter;
         int64_t generateId();
+        void releaseBufferIfOverwriting(const layer_state_t& state);
 
     protected:
         std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
@@ -400,7 +418,6 @@
         void cacheBuffers();
         void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
         void setReleaseBufferCallback(BufferData*, const ReleaseCallbackId&, ReleaseBufferCallback);
-        void removeReleaseBufferCallback(layer_state_t*);
 
     public:
         Transaction();
@@ -625,6 +642,9 @@
     status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
     status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
 
+protected:
+    ReleaseCallbackThread mReleaseCallbackThread;
+
 private:
     virtual void onFirstRef();
 
@@ -725,11 +745,10 @@
     void removeSurfaceStatsListener(void* context, void* cookie);
 
     void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
-    void removeReleaseBufferCallback(const ReleaseCallbackId&);
 
     // BnTransactionCompletedListener overrides
     void onTransactionCompleted(ListenerStats stats) override;
-    void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint,
+    void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
                          uint32_t currentMaxAcquiredBufferCount) override;
 
     // For Testing Only
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
similarity index 96%
rename from services/surfaceflinger/tests/utils/CallbackUtils.h
rename to libs/gui/include/gui/test/CallbackUtils.h
index f4a3425..6403208 100644
--- a/services/surfaceflinger/tests/utils/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -134,10 +134,8 @@
 
         void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
                                        nsecs_t latchTime) const {
-            const auto&
-                    [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
-                     transformHint,
-                     frameEvents] = surfaceControlStats;
+            const auto& [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
+                         transformHint, frameEvents] = surfaceControlStats;
 
             ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
                     << "bad acquire time";
@@ -199,7 +197,7 @@
         std::this_thread::sleep_for(500ms);
 
         std::lock_guard lock(mMutex);
-        EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+        EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received";
         mCallbackDataQueue = {};
     }
 
@@ -209,5 +207,5 @@
     std::condition_variable mConditionVariable;
     std::queue<CallbackData> mCallbackDataQueue;
 };
-}
+} // namespace
 } // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index b474086..8e4898d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -27,6 +27,7 @@
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SyncScreenCaptureListener.h>
+#include <gui/test/CallbackUtils.h>
 #include <private/gui/ComposerService.h>
 #include <ui/DisplayMode.h>
 #include <ui/GraphicBuffer.h>
@@ -42,6 +43,29 @@
 using Transaction = SurfaceComposerClient::Transaction;
 using android::hardware::graphics::common::V1_2::BufferUsage;
 
+class CountProducerListener : public BnProducerListener {
+public:
+    void onBufferReleased() override {
+        std::scoped_lock<std::mutex> lock(mMutex);
+        mNumReleased++;
+        mReleaseCallback.notify_one();
+    }
+
+    void waitOnNumberReleased(int32_t expectedNumReleased) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (mNumReleased < expectedNumReleased) {
+            ASSERT_NE(mReleaseCallback.wait_for(lock, std::chrono::seconds(3)),
+                      std::cv_status::timeout)
+                    << "did not receive release";
+        }
+    }
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mReleaseCallback;
+    int32_t mNumReleased GUARDED_BY(mMutex) = 0;
+};
+
 class BLASTBufferQueueHelper {
 public:
     BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
@@ -152,18 +176,19 @@
         mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
     }
 
-    void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer) {
+    void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer,
+                       int32_t maxBufferCount = 2) {
         producer = adapter.getIGraphicBufferProducer();
-        setUpProducer(producer);
+        setUpProducer(producer, maxBufferCount);
     }
 
-    void setUpProducer(sp<IGraphicBufferProducer>& igbProducer) {
+    void setUpProducer(sp<IGraphicBufferProducer>& igbProducer, int32_t maxBufferCount) {
         ASSERT_NE(nullptr, igbProducer.get());
-        ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
+        ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(maxBufferCount));
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        mProducerListener = new CountProducerListener();
         ASSERT_EQ(NO_ERROR,
-                  igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false,
-                                       &qbOutput));
+                  igbProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput));
         ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
     }
 
@@ -287,6 +312,7 @@
 
     DisplayCaptureArgs mCaptureArgs;
     ScreenCaptureResults mCaptureResults;
+    sp<CountProducerListener> mProducerListener;
 };
 
 TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
@@ -749,6 +775,240 @@
                                {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight / 2}));
 }
 
+TEST_F(BLASTBufferQueueTest, SyncThenNoSync) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    Transaction next;
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, 0, 255, 0, 0);
+
+    // queue non sync buffer, so this one should get blocked
+    // Add a present delay to allow the first screenshot to get taken.
+    nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+    queueBuffer(igbProducer, r, g, b, presentTimeDelay);
+
+    CallbackHelper transactionCallback;
+    next.addTransactionCompletedCallback(transactionCallback.function,
+                                         transactionCallback.getContext())
+            .apply();
+
+    CallbackData callbackData;
+    transactionCallback.getCallbackData(&callbackData);
+
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+
+    mProducerListener->waitOnNumberReleased(1);
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, MultipleSyncTransactions) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    Transaction mainTransaction;
+
+    Transaction next;
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, 0, 255, 0, 0);
+
+    mainTransaction.merge(std::move(next));
+
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, r, g, b, 0);
+
+    mainTransaction.merge(std::move(next));
+    // Expect 1 buffer to be released even before sending to SurfaceFlinger
+    mProducerListener->waitOnNumberReleased(1);
+
+    CallbackHelper transactionCallback;
+    mainTransaction
+            .addTransactionCompletedCallback(transactionCallback.function,
+                                             transactionCallback.getContext())
+            .apply();
+
+    CallbackData callbackData;
+    transactionCallback.getCallbackData(&callbackData);
+
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, MultipleSyncTransactionWithNonSync) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    Transaction mainTransaction;
+
+    Transaction next;
+    // queue a sync transaction
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, 0, 255, 0, 0);
+
+    mainTransaction.merge(std::move(next));
+
+    // queue another buffer without setting next transaction
+    queueBuffer(igbProducer, 0, 0, 255, 0);
+
+    // queue another sync transaction
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, r, g, b, 0);
+    // Expect 1 buffer to be released because the non sync transaction should merge
+    // with the sync
+    mProducerListener->waitOnNumberReleased(1);
+
+    mainTransaction.merge(std::move(next));
+    // Expect 2 buffers to be released due to merging the two syncs.
+    mProducerListener->waitOnNumberReleased(2);
+
+    CallbackHelper transactionCallback;
+    mainTransaction
+            .addTransactionCompletedCallback(transactionCallback.function,
+                                             transactionCallback.getContext())
+            .apply();
+
+    CallbackData callbackData;
+    transactionCallback.getCallbackData(&callbackData);
+
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, MultipleSyncRunOutOfBuffers) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer, 3);
+
+    Transaction mainTransaction;
+
+    Transaction next;
+    // queue a sync transaction
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, 0, 255, 0, 0);
+
+    mainTransaction.merge(std::move(next));
+
+    // queue a few buffers without setting next transaction
+    queueBuffer(igbProducer, 0, 0, 255, 0);
+    queueBuffer(igbProducer, 0, 0, 255, 0);
+    queueBuffer(igbProducer, 0, 0, 255, 0);
+
+    // queue another sync transaction
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, r, g, b, 0);
+    // Expect 3 buffers to be released because the non sync transactions should merge
+    // with the sync
+    mProducerListener->waitOnNumberReleased(3);
+
+    mainTransaction.merge(std::move(next));
+    // Expect 4 buffers to be released due to merging the two syncs.
+    mProducerListener->waitOnNumberReleased(4);
+
+    CallbackHelper transactionCallback;
+    mainTransaction
+            .addTransactionCompletedCallback(transactionCallback.function,
+                                             transactionCallback.getContext())
+            .apply();
+
+    CallbackData callbackData;
+    transactionCallback.getCallbackData(&callbackData);
+
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+// Tests BBQ with a sync transaction when the buffers acquired reaches max and the only way to
+// continue processing is for a release callback from SurfaceFlinger.
+// This is done by sending a buffer to SF so it can release the previous one and allow BBQ to
+// continue acquiring buffers.
+TEST_F(BLASTBufferQueueTest, RunOutOfBuffersWaitingOnSF) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer, 4);
+
+    Transaction mainTransaction;
+
+    // Send a buffer to SF
+    queueBuffer(igbProducer, 0, 255, 0, 0);
+
+    Transaction next;
+    // queue a sync transaction
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, 0, 255, 0, 0);
+
+    mainTransaction.merge(std::move(next));
+
+    // queue a few buffers without setting next transaction
+    queueBuffer(igbProducer, 0, 0, 255, 0);
+    queueBuffer(igbProducer, 0, 0, 255, 0);
+    queueBuffer(igbProducer, 0, 0, 255, 0);
+
+    // apply the first synced buffer to ensure we have to wait on SF
+    mainTransaction.apply();
+
+    // queue another sync transaction
+    adapter.setNextTransaction(&next);
+    queueBuffer(igbProducer, r, g, b, 0);
+    // Expect 2 buffers to be released because the non sync transactions should merge
+    // with the sync
+    mProducerListener->waitOnNumberReleased(3);
+
+    mainTransaction.merge(std::move(next));
+
+    CallbackHelper transactionCallback;
+    mainTransaction
+            .addTransactionCompletedCallback(transactionCallback.function,
+                                             transactionCallback.getContext())
+            .apply();
+
+    CallbackData callbackData;
+    transactionCallback.getCallbackData(&callbackData);
+
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
 class TestProducerListener : public BnProducerListener {
 public:
     sp<IGraphicBufferProducer> mIgbp;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 80f6c82..8ac08fb 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -634,6 +634,12 @@
                outSmpte2094_40);
 }
 
+status_t Gralloc4Mapper::getSmpte2094_10(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) const {
+    return get(bufferHandle, gralloc4::MetadataType_Smpte2094_10, gralloc4::decodeSmpte2094_10,
+               outSmpte2094_10);
+}
+
 template <class T>
 status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
                                     uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index d20bd7a..82d6cd5 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -301,6 +301,11 @@
     return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
 }
 
+status_t GraphicBufferMapper::getSmpte2094_10(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) {
+    return mMapper->getSmpte2094_10(bufferHandle, outSmpte2094_10);
+}
+
 status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
                                                           PixelFormat format, uint32_t layerCount,
                                                           uint64_t usage,
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index e199648..753b0a6 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -178,6 +178,11 @@
             std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
         return INVALID_OPERATION;
     }
+    virtual status_t getSmpte2094_10(
+            buffer_handle_t /*bufferHandle*/,
+            std::optional<std::vector<uint8_t>>* /*outSmpte2094_10*/) const {
+        return INVALID_OPERATION;
+    }
 
     virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
                                                  PixelFormat /*format*/, uint32_t /*layerCount*/,
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 4729cba..62f9e4a 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -108,6 +108,8 @@
                          std::optional<ui::Cta861_3>* outCta861_3) const override;
     status_t getSmpte2094_40(buffer_handle_t bufferHandle,
                              std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
+    status_t getSmpte2094_10(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_10) const override;
 
     status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
                                          uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 837e3d8..257c155 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -126,6 +126,8 @@
     status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
     status_t getSmpte2094_40(buffer_handle_t bufferHandle,
                              std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+    status_t getSmpte2094_10(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_10);
 
     /**
      * Gets the default metadata for a gralloc buffer allocated with the given parameters.
diff --git a/libs/vibrator/OWNERS b/libs/vibrator/OWNERS
index 0997e9f..d073e2b 100644
--- a/libs/vibrator/OWNERS
+++ b/libs/vibrator/OWNERS
@@ -1,2 +1 @@
-lsandrade@google.com
-michaelwr@google.com
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3db3907..25b3005 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -88,6 +88,9 @@
 // Log debug messages about input focus tracking.
 constexpr bool DEBUG_FOCUS = false;
 
+// Log debug messages about touch mode event
+constexpr bool DEBUG_TOUCH_MODE = false;
+
 // Log debug messages about touch occlusion
 // STOPSHIP(b/169067926): Set to false
 constexpr bool DEBUG_TOUCH_OCCLUSION = true;
@@ -534,6 +537,23 @@
     return transformedXy - transformedOrigin;
 }
 
+// Returns true if the event type passed as argument represents a user activity.
+bool isUserActivityEvent(const EventEntry& eventEntry) {
+    switch (eventEntry.type) {
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG:
+        case EventEntry::Type::TOUCH_MODE_CHANGED:
+        case EventEntry::Type::SENSOR:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+            return false;
+        case EventEntry::Type::DEVICE_RESET:
+        case EventEntry::Type::KEY:
+        case EventEntry::Type::MOTION:
+            return true;
+    }
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -552,7 +572,7 @@
         // mInTouchMode will be initialized by the WindowManager to the default device config.
         // To avoid leaking stack in case that call never comes, and for tests,
         // initialize it here anyways.
-        mInTouchMode(true),
+        mInTouchMode(kDefaultInTouchMode),
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
@@ -1789,9 +1809,9 @@
             displayId = motionEntry.displayId;
             break;
         }
+        case EventEntry::Type::TOUCH_MODE_CHANGED:
         case EventEntry::Type::POINTER_CAPTURE_CHANGED:
         case EventEntry::Type::FOCUS:
-        case EventEntry::Type::TOUCH_MODE_CHANGED:
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
@@ -2757,11 +2777,8 @@
 }
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
-    if (eventEntry.type == EventEntry::Type::FOCUS ||
-        eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
-        eventEntry.type == EventEntry::Type::DRAG) {
-        // Focus or pointer capture changed events are passed to apps, but do not represent user
-        // activity.
+    if (!isUserActivityEvent(eventEntry)) {
+        // Not poking user activity if the event type does not represent a user activity
         return;
     }
     int32_t displayId = getTargetDisplayId(eventEntry);
@@ -2797,16 +2814,7 @@
             eventType = USER_ACTIVITY_EVENT_BUTTON;
             break;
         }
-        case EventEntry::Type::TOUCH_MODE_CHANGED: {
-            break;
-        }
-
-        case EventEntry::Type::FOCUS:
-        case EventEntry::Type::CONFIGURATION_CHANGED:
-        case EventEntry::Type::DEVICE_RESET:
-        case EventEntry::Type::SENSOR:
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
-        case EventEntry::Type::DRAG: {
+        default: {
             LOG_ALWAYS_FATAL("%s events are not user activity",
                              ftl::enum_string(eventEntry.type).c_str());
             break;
@@ -3781,7 +3789,7 @@
         ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
     }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -3876,7 +3884,7 @@
               std::to_string(t.duration().count()).c_str());
     }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         mLock.lock();
 
@@ -3953,7 +3961,7 @@
               std::to_string(t.duration().count()).c_str());
     }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         mLock.lock();
 
@@ -4018,7 +4026,7 @@
               ftl::enum_string(args->sensorType).c_str());
     }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         mLock.lock();
 
@@ -4068,7 +4076,7 @@
               args->deviceId);
     }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -4088,7 +4096,7 @@
               args->request.enable ? "true" : "false");
     }
 
-    bool needWake;
+    bool needWake = false;
     { // acquire lock
         std::scoped_lock _l(mLock);
         auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
@@ -4909,9 +4917,29 @@
 }
 
 void InputDispatcher::setInTouchMode(bool inTouchMode) {
-    std::scoped_lock lock(mLock);
-    mInTouchMode = inTouchMode;
-    // TODO(b/193718270): Fire TouchModeEvent here.
+    bool needWake = false;
+    {
+        std::scoped_lock lock(mLock);
+        if (mInTouchMode == inTouchMode) {
+            return;
+        }
+        if (DEBUG_TOUCH_MODE) {
+            ALOGD("Request to change touch mode from %s to %s", toString(mInTouchMode),
+                  toString(inTouchMode));
+            // TODO(b/198487159): Also print the current last interacted apps.
+        }
+
+        // TODO(b/198499018): Store touch mode per display.
+        mInTouchMode = inTouchMode;
+
+        // TODO(b/198487159): Enforce that only last interacted apps can change touch mode.
+        auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode);
+        needWake = enqueueInboundEventLocked(std::move(entry));
+    } // release lock
+
+    if (needWake) {
+        mLooper->wake();
+    }
 }
 
 void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 55ca6c9..8a551cf 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -82,6 +82,8 @@
  */
 class InputDispatcher : public android::InputDispatcherInterface {
 public:
+    static constexpr bool kDefaultInTouchMode = true;
+
     explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
     ~InputDispatcher() override;
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 84e4276..1879cec 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -817,6 +817,9 @@
             case AINPUT_EVENT_TYPE_CAPTURE: {
                 FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
             }
+            case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+                FAIL() << "Use 'consumeTouchModeEvent' for TOUCH_MODE events";
+            }
             case AINPUT_EVENT_TYPE_DRAG: {
                 FAIL() << "Use 'consumeDragEvent' for DRAG events";
             }
@@ -874,6 +877,20 @@
         EXPECT_EQ(y, dragEvent.getY());
     }
 
+    void consumeTouchModeEvent(bool inTouchMode) {
+        const InputEvent* event = consume();
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType())
+                << "Got " << inputEventTypeToString(event->getType())
+                << " event instead of TOUCH_MODE event";
+
+        ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+                << mName.c_str() << ": event displayId should always be NONE.";
+        const auto& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+        EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode());
+    }
+
     void assertNoEvents() {
         InputEvent* event = consume();
         if (event == nullptr) {
@@ -895,6 +912,10 @@
             const auto& captureEvent = static_cast<CaptureEvent&>(*event);
             ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = "
                           << (captureEvent.getPointerCaptureEnabled() ? "true" : "false");
+        } else if (event->getType() == AINPUT_EVENT_TYPE_TOUCH_MODE) {
+            const auto& touchModeEvent = static_cast<TouchModeEvent&>(*event);
+            ADD_FAILURE() << "Received touch mode event, inTouchMode = "
+                          << (touchModeEvent.isInTouchMode() ? "true" : "false");
         }
         FAIL() << mName.c_str()
                << ": should not have received any events, so consume() should return NULL";
@@ -1091,6 +1112,12 @@
         mInputReceiver->consumeDragEvent(isExiting, x, y);
     }
 
+    void consumeTouchModeEvent(bool inTouchMode) {
+        ASSERT_NE(mInputReceiver, nullptr)
+                << "Cannot consume events from a window with no receiver";
+        mInputReceiver->consumeTouchModeEvent(inTouchMode);
+    }
+
     std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
         if (mInputReceiver == nullptr) {
             ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -2857,7 +2884,6 @@
     SCOPED_TRACE("Check default value of touch mode");
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     setFocusedWindow(window);
-
     window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
 
     SCOPED_TRACE("Remove the window to trigger focus loss");
@@ -2867,6 +2893,7 @@
 
     SCOPED_TRACE("Disable touch mode");
     mDispatcher->setInTouchMode(false);
+    window->consumeTouchModeEvent(false);
     window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     setFocusedWindow(window);
@@ -2879,6 +2906,7 @@
 
     SCOPED_TRACE("Enable touch mode again");
     mDispatcher->setInTouchMode(true);
+    window->consumeTouchModeEvent(true);
     window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     setFocusedWindow(window);
@@ -6024,4 +6052,43 @@
     window->assertNoEvents();
 }
 
+class InputDispatcherTouchModeChangedTests : public InputDispatcherTest {
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApp;
+    sp<FakeWindowHandle> mWindow;
+    sp<FakeWindowHandle> mSecondWindow;
+
+    void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        mApp = std::make_shared<FakeApplicationHandle>();
+        mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+        mWindow->setFocusable(true);
+        mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+        mSecondWindow->setFocusable(true);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+
+        setFocusedWindow(mWindow);
+        mWindow->consumeFocusEvent(true);
+    }
+
+    void changeAndVerifyTouchMode(bool inTouchMode) {
+        mDispatcher->setInTouchMode(inTouchMode);
+        mWindow->consumeTouchModeEvent(inTouchMode);
+        mSecondWindow->consumeTouchModeEvent(inTouchMode);
+    }
+};
+
+TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchModeOnFocusedWindow) {
+    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode);
+}
+
+TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) {
+    mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index bba6e22..1be5a96 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -77,7 +77,6 @@
         "libsensor",
         "libsensorprivacy",
         "libpermission",
-        "packagemanager_aidl-cpp",
     ],
 }
 
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index af012cd..75b2ba9 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -102,7 +102,7 @@
     // TODO (marissaw): this library is not used by surfaceflinger. This is here so
     // the library compiled in a way that is accessible to system partition when running
     // IMapper's VTS.
-    required: ["libgralloctypes"]
+    required: ["libgralloctypes"],
 }
 
 cc_defaults {
@@ -191,6 +191,7 @@
         "SurfaceFlingerDefaultFactory.cpp",
         "SurfaceInterceptor.cpp",
         "SurfaceTracing.cpp",
+        "Tracing/TransactionProtoParser.cpp",
         "TransactionCallbackInvoker.cpp",
         "TunnelModeEnabledReporter.cpp",
     ],
@@ -249,7 +250,7 @@
         "libSurfaceFlingerProp",
     ],
 
-     logtags: ["EventLog/EventLogTags.logtags"],
+    logtags: ["EventLog/EventLogTags.logtags"],
 }
 
 subdirs = [
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 160b2ea..546d8ca 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -223,7 +223,7 @@
          * of a camera where the buffer remains in native orientation,
          * we want the pixels to always be upright.
          */
-        sp<Layer> p = mDrawingParent.promote();
+        auto p = mDrawingParent;
         if (p != nullptr) {
             const auto parentTransform = p->getTransform();
             tr = tr * inverseOrientation(parentTransform.getOrientation());
@@ -346,13 +346,13 @@
 }
 } // namespace
 
-bool BufferLayer::onPostComposition(const DisplayDevice* display,
+void BufferLayer::onPostComposition(const DisplayDevice* display,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
                                     const std::shared_ptr<FenceTime>& presentFence,
                                     const CompositorTiming& compositorTiming) {
     // mFrameLatencyNeeded is true when a new frame was latched for the
     // composition.
-    if (!mBufferInfo.mFrameLatencyNeeded) return false;
+    if (!mBufferInfo.mFrameLatencyNeeded) return;
 
     // Update mFrameEventHistory.
     {
@@ -426,7 +426,6 @@
 
     mFrameTracker.advanceFrame();
     mBufferInfo.mFrameLatencyNeeded = false;
-    return true;
 }
 
 void BufferLayer::gatherBufferInfo() {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 2da73f8..61e7ac5 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -77,7 +77,7 @@
 
     bool isHdrY410() const override;
 
-    bool onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
+    void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
                            const std::shared_ptr<FenceTime>& presentFence,
                            const CompositorTiming&) override;
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index bf38177..52bd420 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -65,16 +65,6 @@
     mConsumer->setTransformHint(mTransformHint);
 }
 
-std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(bool forceFlush) {
-    std::vector<OccupancyTracker::Segment> history;
-    status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
-    if (result != NO_ERROR) {
-        ALOGW("[%s] Failed to obtain occupancy history (%d)", getDebugName(), result);
-        return {};
-    }
-    return history;
-}
-
 void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     if (!mConsumer->releasePendingBuffer()) {
         return;
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index be2902b..a3bd725 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -44,8 +44,6 @@
 
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
 
-    std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
-
     // If a buffer was replaced this frame, release the former buffer
     void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index f0099c2..fda7a92 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -40,13 +40,13 @@
 namespace {
 void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
                                const sp<GraphicBuffer>& buffer, uint64_t framenumber,
-                               const sp<Fence>& releaseFence, uint32_t transformHint,
+                               const sp<Fence>& releaseFence,
                                uint32_t currentMaxAcquiredBufferCount) {
     if (!listener) {
         return;
     }
     listener->onReleaseBuffer({buffer->getId(), framenumber},
-                              releaseFence ? releaseFence : Fence::NO_FENCE, transformHint,
+                              releaseFence ? releaseFence : Fence::NO_FENCE,
                               currentMaxAcquiredBufferCount);
 }
 } // namespace
@@ -64,7 +64,7 @@
     if (mBufferInfo.mBuffer != nullptr && !isClone()) {
         callReleaseBufferCallback(mDrawingState.releaseBufferListener,
                                   mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
-                                  mBufferInfo.mFence, mTransformHint,
+                                  mBufferInfo.mFence,
                                   mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
                                           mOwnerUid));
     }
@@ -479,7 +479,7 @@
             // call any release buffer callbacks if set.
             callReleaseBufferCallback(mDrawingState.releaseBufferListener,
                                       mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mDrawingState.acquireFence, mTransformHint,
+                                      mDrawingState.acquireFence,
                                       mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
                                               mOwnerUid));
             decrementPendingBufferCount();
@@ -491,9 +491,9 @@
         } else if (mLastClientCompositionFence != nullptr) {
             callReleaseBufferCallback(mDrawingState.releaseBufferListener,
                                       mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mLastClientCompositionFence, mTransformHint,
+                                      mLastClientCompositionFence,
                                       mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
-                                          mOwnerUid));
+                                              mOwnerUid));
             mLastClientCompositionFence = nullptr;
         }
     }
@@ -989,7 +989,7 @@
  * how to go from screen space back to window space.
  */
 ui::Transform BufferStateLayer::getInputTransform() const {
-    sp<Layer> parent = mDrawingParent.promote();
+    auto parent = mDrawingParent;
     if (parent == nullptr) {
         return ui::Transform();
     }
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 57837d4..5707c67 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -179,9 +179,23 @@
     if (mDrawingState.sidebandStream != nullptr) {
         mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
     }
+
     if (mHadClonedChild) {
         mFlinger->mNumClones--;
     }
+
+    for (auto const& child : mCurrentChildren) {
+        if (child->mCurrentParent == this) child->mCurrentParent = nullptr;
+        if (child->mDrawingParent == this) {
+            child->mDrawingParent = nullptr;
+        }
+    }
+    for (auto const& child : mDrawingChildren) {
+        if (child->mCurrentParent == this) child->mCurrentParent = nullptr;
+        if (child->mDrawingParent == this) {
+            child->mDrawingParent = nullptr;
+        }
+    }
 }
 
 LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
@@ -237,7 +251,7 @@
 }
 
 sp<Layer> Layer::getRootLayer() {
-    sp<Layer> parent = getParent();
+    auto parent = getParent();
     if (parent == nullptr) {
         return this;
     }
@@ -662,7 +676,7 @@
         return true;
     }
 
-    const auto p = mDrawingParent.promote();
+    const auto p = mDrawingParent;
     return (p != nullptr) ? p->isSecure() : false;
 }
 
@@ -845,7 +859,7 @@
     if (getDrawingState().isTrustedOverlay) {
         return true;
     }
-    const auto& p = mDrawingParent.promote();
+    const auto p = mDrawingParent;
     return (p != nullptr) && p->isTrustedOverlay();
 }
 
@@ -1025,7 +1039,7 @@
         return mDrawingState.frameRateSelectionPriority;
     }
     // If not, search whether its parents have it set.
-    sp<Layer> parent = getParent();
+    auto parent = getParent();
     if (parent != nullptr) {
         return parent->getFrameRateSelectionPriority();
     }
@@ -1038,10 +1052,11 @@
 };
 
 ui::LayerStack Layer::getLayerStack() const {
-    if (const auto parent = mDrawingParent.promote()) {
-        return parent->getLayerStack();
+    auto p = mDrawingParent;
+    if (p == nullptr) {
+        return getDrawingState().layerStack;
     }
-    return getDrawingState().layerStack;
+    return mDrawingParent->getLayerStack();
 }
 
 bool Layer::setShadowRadius(float shadowRadius) {
@@ -1086,7 +1101,7 @@
         return mDrawingState.stretchEffect;
     }
 
-    sp<Layer> parent = getParent();
+    auto parent = mDrawingParent;
     if (parent != nullptr) {
         auto effect = parent->getStretchEffect();
         if (effect.hasEffect()) {
@@ -1301,7 +1316,7 @@
 
 bool Layer::isHiddenByPolicy() const {
     const State& s(mDrawingState);
-    const auto& parent = mDrawingParent.promote();
+    auto parent = mDrawingParent;
     if (parent != nullptr && parent->isHiddenByPolicy()) {
         return true;
     }
@@ -1348,7 +1363,7 @@
     LayerDebugInfo info;
     const State& ds = getDrawingState();
     info.mName = getName();
-    sp<Layer> parent = mDrawingParent.promote();
+    auto parent = mDrawingParent;
     info.mParentName = parent ? parent->getName() : "none"s;
     info.mType = getType();
     info.mTransparentRegion = ds.activeTransparentRegion_legacy;
@@ -1580,7 +1595,7 @@
 
 void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
     for (const sp<Layer>& child : mDrawingChildren) {
-        child->mDrawingParent = newParent;
+        child->mDrawingParent = newParent.get();
         child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
                              newParent->mEffectiveShadowRadius);
     }
@@ -1600,7 +1615,7 @@
         }
     }
 
-    sp<Layer> parent = getParent();
+    auto parent = getParent();
     if (parent != nullptr) {
         parent->removeChild(this);
     }
@@ -1635,7 +1650,7 @@
 
 mat4 Layer::getColorTransform() const {
     mat4 colorTransform = mat4(getDrawingState().colorTransform);
-    if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
+    if (auto parent = mDrawingParent; parent != nullptr) {
         colorTransform = parent->getColorTransform() * colorTransform;
     }
     return colorTransform;
@@ -1643,7 +1658,7 @@
 
 bool Layer::hasColorTransform() const {
     bool hasColorTransform = getDrawingState().hasColorTransform;
-    if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
+    if (auto parent = mDrawingParent; parent != nullptr) {
         hasColorTransform = hasColorTransform || parent->hasColorTransform();
     }
     return hasColorTransform;
@@ -1657,7 +1672,7 @@
 }
 
 void Layer::setParent(const sp<Layer>& layer) {
-    mCurrentParent = layer;
+    mCurrentParent = layer.get();
 }
 
 int32_t Layer::getZ(LayerVector::StateSet) const {
@@ -1861,7 +1876,7 @@
 }
 
 half Layer::getAlpha() const {
-    const auto& p = mDrawingParent.promote();
+    auto p = mDrawingParent;
 
     half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
     return parentAlpha * getDrawingState().color.a;
@@ -1872,7 +1887,7 @@
     if (fixedTransformHint != ui::Transform::ROT_INVALID) {
         return fixedTransformHint;
     }
-    const auto& p = mCurrentParent.promote();
+    auto p = mCurrentParent;
     if (!p) return fixedTransformHint;
     return p->getFixedTransformHint();
 }
@@ -1883,7 +1898,7 @@
 }
 
 int32_t Layer::getBackgroundBlurRadius() const {
-    const auto& p = mDrawingParent.promote();
+    auto p = mDrawingParent;
 
     half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
     return parentAlpha * getDrawingState().backgroundBlurRadius;
@@ -1901,9 +1916,8 @@
 Layer::RoundedCornerState Layer::getRoundedCornerState() const {
     // Get parent settings
     RoundedCornerState parentSettings;
-    const auto& parent = mDrawingParent.promote();
-    if (parent != nullptr) {
-        parentSettings = parent->getRoundedCornerState();
+    if (mDrawingParent != nullptr) {
+        parentSettings = mDrawingParent->getRoundedCornerState();
         if (parentSettings.radius > 0) {
             ui::Transform t = getActiveTransform(getDrawingState());
             t = t.inverse();
@@ -1922,8 +1936,9 @@
     Rect layerCropRect = getCroppedBufferSize(getDrawingState());
     const float radius = getDrawingState().cornerRadius;
     RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
+    const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid();
 
-    if (layerSettings.radius > 0 && parentSettings.radius > 0) {
+    if (layerSettingsValid && parentSettings.radius > 0) {
         // If the parent and the layer have rounded corner settings, use the parent settings if the
         // parent crop is entirely inside the layer crop.
         // This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
@@ -1935,7 +1950,7 @@
         } else {
             return layerSettings;
         }
-    } else if (layerSettings.radius > 0) {
+    } else if (layerSettingsValid) {
         return layerSettings;
     } else if (parentSettings.radius > 0) {
         return parentSettings;
@@ -2118,7 +2133,7 @@
         LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
                                                  layerInfo->mutable_requested_transform());
 
-        auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
+        auto parent = useDrawing ? mDrawingParent : mCurrentParent;
         if (parent != nullptr) {
             layerInfo->set_parent(parent->sequence);
         } else {
@@ -2265,9 +2280,9 @@
 }
 
 void Layer::fillTouchOcclusionMode(WindowInfo& info) {
-    sp<Layer> p = this;
+    Layer* p = this;
     while (p != nullptr && !p->hasInputInfo()) {
-        p = p->mDrawingParent.promote();
+        p = p->mDrawingParent;
     }
     if (p != nullptr) {
         info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode;
@@ -2279,9 +2294,8 @@
     if (mode == gui::DropInputMode::ALL) {
         return mode;
     }
-    sp<Layer> parent = mDrawingParent.promote();
-    if (parent) {
-        gui::DropInputMode parentMode = parent->getDropInputMode();
+    if (mDrawingParent) {
+        gui::DropInputMode parentMode = mDrawingParent->getDropInputMode();
         if (parentMode != gui::DropInputMode::NONE) {
             return parentMode;
         }
@@ -2308,8 +2322,7 @@
     }
 
     // Check if the parent has set an alpha on the layer
-    sp<Layer> parent = mDrawingParent.promote();
-    if (parent && parent->getAlpha() != 1.0_hf) {
+    if (mDrawingParent && mDrawingParent->getAlpha() != 1.0_hf) {
         info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
         ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
               static_cast<float>(getAlpha()));
@@ -2407,10 +2420,10 @@
     if (mClonedChild != nullptr) {
         return this;
     }
-    if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
+    if (mDrawingParent == nullptr) {
         return nullptr;
     }
-    return mDrawingParent.promote()->getClonedRoot();
+    return mDrawingParent->getClonedRoot();
 }
 
 bool Layer::hasInputInfo() const {
@@ -2597,8 +2610,7 @@
         return true;
     }
 
-    sp<Layer> parent = mDrawingParent.promote();
-    return parent && parent->isInternalDisplayOverlay();
+    return mDrawingParent && mDrawingParent->isInternalDisplayOverlay();
 }
 
 void Layer::setClonedChild(const sp<Layer>& clonedChild) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d17c05b..07b2eb5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -525,12 +525,10 @@
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual bool onPostComposition(const DisplayDevice*,
+    virtual void onPostComposition(const DisplayDevice*,
                                    const std::shared_ptr<FenceTime>& /*glDoneFence*/,
                                    const std::shared_ptr<FenceTime>& /*presentFence*/,
-                                   const CompositorTiming&) {
-        return false;
-    }
+                                   const CompositorTiming&) {}
 
     // If a buffer was replaced this frame, release the former buffer
     virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
@@ -591,10 +589,6 @@
     }
     virtual FrameRate getFrameRateForLayerTree() const;
 
-    virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
-        return {};
-    }
-
     virtual bool getTransformToDisplayInverse() const { return false; }
 
     // Returns how rounded corners should be drawn for this layer.
@@ -801,12 +795,12 @@
     // Returns index if removed, or negative value otherwise
     // for symmetry with Vector::remove
     ssize_t removeChild(const sp<Layer>& layer);
-    sp<Layer> getParent() const { return mCurrentParent.promote(); }
 
     // Should be called with the surfaceflinger statelock held
     bool isAtRoot() const { return mIsAtRoot; }
     void setIsAtRoot(bool isAtRoot) { mIsAtRoot = isAtRoot; }
 
+    Layer* getParent() const { return mCurrentParent; }
     bool hasParent() const { return getParent() != nullptr; }
     Rect getScreenBounds(bool reduceTransparentRegion = true) const;
     bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
@@ -1013,8 +1007,8 @@
     LayerVector mCurrentChildren{LayerVector::StateSet::Current};
     LayerVector mDrawingChildren{LayerVector::StateSet::Drawing};
 
-    wp<Layer> mCurrentParent;
-    wp<Layer> mDrawingParent;
+    Layer* mCurrentParent = nullptr;
+    Layer* mDrawingParent = nullptr;
 
     // Window types from WindowManager.LayoutParams
     const gui::WindowInfo::Type mWindowType;
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 1062126..ee23561 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -53,28 +53,52 @@
         return;
     }
 
+    writeToProto(region, getRegionProto());
+}
+
+void LayerProtoHelper::writeToProto(const Region& region, RegionProto* regionProto) {
+    if (region.isEmpty()) {
+        return;
+    }
+
     Region::const_iterator head = region.begin();
     Region::const_iterator const tail = region.end();
     // Use a lambda do avoid writing the object header when the object is empty
-    RegionProto* regionProto = getRegionProto();
     while (head != tail) {
-        std::function<RectProto*()> getProtoRect = [&]() { return regionProto->add_rect(); };
-        writeToProto(*head, getProtoRect);
+        writeToProto(*head, regionProto->add_rect());
         head++;
     }
 }
 
+void LayerProtoHelper::readFromProto(const RegionProto& regionProto, Region& outRegion) {
+    for (int i = 0; i < regionProto.rect_size(); i++) {
+        Rect rect;
+        readFromProto(regionProto.rect(i), rect);
+        outRegion.orSelf(rect);
+    }
+}
+
 void LayerProtoHelper::writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto) {
     if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) {
         // Use a lambda do avoid writing the object header when the object is empty
-        RectProto* rectProto = getRectProto();
-        rectProto->set_left(rect.left);
-        rectProto->set_top(rect.top);
-        rectProto->set_bottom(rect.bottom);
-        rectProto->set_right(rect.right);
+        writeToProto(rect, getRectProto());
     }
 }
 
+void LayerProtoHelper::writeToProto(const Rect& rect, RectProto* rectProto) {
+    rectProto->set_left(rect.left);
+    rectProto->set_top(rect.top);
+    rectProto->set_bottom(rect.bottom);
+    rectProto->set_right(rect.right);
+}
+
+void LayerProtoHelper::readFromProto(const RectProto& proto, Rect& outRect) {
+    outRect.left = proto.left();
+    outRect.top = proto.top();
+    outRect.bottom = proto.bottom();
+    outRect.right = proto.right();
+}
+
 void LayerProtoHelper::writeToProto(const FloatRect& rect,
                                     std::function<FloatRectProto*()> getFloatRectProto) {
     if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) {
@@ -189,6 +213,39 @@
     }
 }
 
+void LayerProtoHelper::readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix) {
+    for (int i = 0; i < mat4::ROW_SIZE; i++) {
+        for (int j = 0; j < mat4::COL_SIZE; j++) {
+            matrix[i][j] = colorTransformProto.val(i * mat4::COL_SIZE + j);
+        }
+    }
+}
+
+void LayerProtoHelper::writeToProto(const android::BlurRegion region, BlurRegion* proto) {
+    proto->set_blur_radius(region.blurRadius);
+    proto->set_corner_radius_tl(region.cornerRadiusTL);
+    proto->set_corner_radius_tr(region.cornerRadiusTR);
+    proto->set_corner_radius_bl(region.cornerRadiusBL);
+    proto->set_corner_radius_br(region.cornerRadiusBR);
+    proto->set_alpha(region.alpha);
+    proto->set_left(region.left);
+    proto->set_top(region.top);
+    proto->set_right(region.right);
+    proto->set_bottom(region.bottom);
+}
+
+void LayerProtoHelper::readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion) {
+    outRegion.blurRadius = proto.blur_radius();
+    outRegion.cornerRadiusTL = proto.corner_radius_tl();
+    outRegion.cornerRadiusTR = proto.corner_radius_tr();
+    outRegion.cornerRadiusBL = proto.corner_radius_bl();
+    outRegion.cornerRadiusBR = proto.corner_radius_br();
+    outRegion.alpha = proto.alpha();
+    outRegion.left = proto.left();
+    outRegion.top = proto.top();
+    outRegion.right = proto.right();
+    outRegion.bottom = proto.bottom();
+}
 } // namespace surfaceflinger
 } // namespace android
 
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 36e0647..249ec42 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -19,6 +19,7 @@
 #include <Layer.h>
 #include <gui/WindowInfo.h>
 #include <math/vec4.h>
+#include <ui/BlurRegion.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -33,9 +34,13 @@
     static void writeSizeToProto(const uint32_t w, const uint32_t h,
                                  std::function<SizeProto*()> getSizeProto);
     static void writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto);
+    static void writeToProto(const Rect& rect, RectProto* rectProto);
+    static void readFromProto(const RectProto& proto, Rect& outRect);
     static void writeToProto(const FloatRect& rect,
                              std::function<FloatRectProto*()> getFloatRectProto);
     static void writeToProto(const Region& region, std::function<RegionProto*()> getRegionProto);
+    static void writeToProto(const Region& region, RegionProto* regionProto);
+    static void readFromProto(const RegionProto& regionProto, Region& outRegion);
     static void writeToProto(const half4 color, std::function<ColorProto*()> getColorProto);
     // This writeToProto for transform is incorrect, but due to backwards compatibility, we can't
     // update Layers to use it. Use writeTransformToProto for any new transform proto data.
@@ -49,6 +54,9 @@
                              const wp<Layer>& touchableRegionBounds,
                              std::function<InputWindowInfoProto*()> getInputWindowInfoProto);
     static void writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto);
+    static void readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix);
+    static void writeToProto(const android::BlurRegion region, BlurRegion*);
+    static void readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion);
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bb65bae..e9665bd 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2263,13 +2263,9 @@
     }
 
     for (const auto& layer: mLayersWithQueuedFrames) {
-        const bool frameLatched =
-                layer->onPostComposition(display, glCompositionDoneFenceTime,
-                                         mPreviousPresentFences[0].fenceTime, compositorTiming);
+        layer->onPostComposition(display, glCompositionDoneFenceTime,
+                                 mPreviousPresentFences[0].fenceTime, compositorTiming);
         layer->releasePendingBuffer(/*dequeueReadyTime*/ now);
-        if (frameLatched) {
-            recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
-        }
     }
 
     std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
@@ -3199,8 +3195,6 @@
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
         for (const auto& l : mLayersPendingRemoval) {
-            recordBufferingStats(l->getName(), l->getOccupancyHistory(true));
-
             // Ensure any buffers set to display on any children are released.
             if (l->isRemovedFromCurrentState()) {
                 l->latchAndReleaseBuffer();
@@ -3372,6 +3366,7 @@
     states.add(composerState);
 
     lbc->updateTransformHint(mActiveDisplayTransformHint);
+
     if (outTransformHint) {
         *outTransformHint = mActiveDisplayTransformHint;
     }
@@ -3916,7 +3911,7 @@
     }
     if (what & layer_state_t::eLayerChanged) {
         // NOTE: index needs to be calculated before we update the state
-        const auto& p = layer->getParent();
+        auto p = layer->getParent();
         if (p == nullptr) {
             ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
             if (layer->setLayer(s.z) && idx >= 0) {
@@ -3934,7 +3929,7 @@
     }
     if (what & layer_state_t::eRelativeLayerChanged) {
         // NOTE: index needs to be calculated before we update the state
-        const auto& p = layer->getParent();
+        auto p = layer->getParent();
         const auto& relativeHandle = s.relativeLayerSurfaceControl ?
                 s.relativeLayerSurfaceControl->getHandle() : nullptr;
         if (p == nullptr) {
@@ -4768,24 +4763,6 @@
                   bucketTimeSec, percent);
 }
 
-void SurfaceFlinger::recordBufferingStats(const std::string& layerName,
-                                          std::vector<OccupancyTracker::Segment>&& history) {
-    Mutex::Autolock lock(getBE().mBufferingStatsMutex);
-    auto& stats = getBE().mBufferingStats[layerName];
-    for (const auto& segment : history) {
-        if (!segment.usedThirdBuffer) {
-            stats.twoBufferTime += segment.totalTime;
-        }
-        if (segment.occupancyAverage < 1.0f) {
-            stats.doubleBufferedTime += segment.totalTime;
-        } else if (segment.occupancyAverage < 2.0f) {
-            stats.tripleBufferedTime += segment.totalTime;
-        }
-        ++stats.numSegments;
-        stats.totalTime += segment.totalTime;
-    }
-}
-
 void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
     result.append("Layer frame timestamps:\n");
     // Traverse all layers to dump frame-events for each layer
@@ -4793,38 +4770,6 @@
         [&] (Layer* layer) { layer->dumpFrameEvents(result); });
 }
 
-void SurfaceFlinger::dumpBufferingStats(std::string& result) const {
-    result.append("Buffering stats:\n");
-    result.append("  [Layer name] <Active time> <Two buffer> "
-            "<Double buffered> <Triple buffered>\n");
-    Mutex::Autolock lock(getBE().mBufferingStatsMutex);
-    typedef std::tuple<std::string, float, float, float> BufferTuple;
-    std::map<float, BufferTuple, std::greater<float>> sorted;
-    for (const auto& statsPair : getBE().mBufferingStats) {
-        const char* name = statsPair.first.c_str();
-        const SurfaceFlingerBE::BufferingStats& stats = statsPair.second;
-        if (stats.numSegments == 0) {
-            continue;
-        }
-        float activeTime = ns2ms(stats.totalTime) / 1000.0f;
-        float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
-                stats.totalTime;
-        float doubleBufferRatio = static_cast<float>(
-                stats.doubleBufferedTime) / stats.totalTime;
-        float tripleBufferRatio = static_cast<float>(
-                stats.tripleBufferedTime) / stats.totalTime;
-        sorted.insert({activeTime, {name, twoBufferRatio,
-                doubleBufferRatio, tripleBufferRatio}});
-    }
-    for (const auto& sortedPair : sorted) {
-        float activeTime = sortedPair.first;
-        const BufferTuple& values = sortedPair.second;
-        StringAppendF(&result, "  [%s] %.2f %.3f %.3f %.3f\n", std::get<0>(values).c_str(),
-                      activeTime, std::get<1>(values), std::get<2>(values), std::get<3>(values));
-    }
-    result.append("\n");
-}
-
 void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
         const auto displayId = PhysicalDisplayId::tryCast(display->getId());
@@ -5016,8 +4961,6 @@
     StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load());
     StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load());
 
-    dumpBufferingStats(result);
-
     /*
      * Dump the visible layer list
      */
@@ -5464,15 +5407,12 @@
                     mClientColorMatrix = mat4();
                 }
 
-                // TODO(b/193487656): Restore once HWASan bug is fixed.
-#if 0
                 // Check that supplied matrix's last row is {0,0,0,1} so we can avoid
                 // the division by w in the fragment shader
                 float4 lastRow(transpose(mClientColorMatrix)[3]);
                 if (any(greaterThan(abs(lastRow - float4{0, 0, 0, 1}), float4{1e-4f}))) {
                     ALOGE("The color transform's last row must be (0, 0, 0, 1)");
                 }
-#endif
 
                 updateColorMatrixLocked();
                 return NO_ERROR;
@@ -6137,7 +6077,7 @@
                 return;
             }
 
-            sp<Layer> p = layer;
+            auto p = layer;
             while (p != nullptr) {
                 if (excludeLayers.count(p) != 0) {
                     return;
@@ -6799,7 +6739,7 @@
     return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
 }
 
-void SurfaceFlinger::TransactionState::traverseStatesWithBuffers(
+void TransactionState::traverseStatesWithBuffers(
         std::function<void(const layer_state_t&)> visitor) {
     for (const auto& state : states) {
         if (state.state.hasBufferChanges() && state.state.hasValidBuffer() && state.state.surface) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 43f7b36..cb16110 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -32,7 +32,6 @@
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
-#include <gui/OccupancyTracker.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
 #include <renderengine/LayerSettings.h>
@@ -66,6 +65,7 @@
 #include "SurfaceTracing.h"
 #include "TracedOrdinal.h"
 #include "TransactionCallbackInvoker.h"
+#include "TransactionState.h"
 
 #include <atomic>
 #include <cstdint>
@@ -156,22 +156,6 @@
     nsecs_t mFrameBuckets[NUM_BUCKETS] = {};
     nsecs_t mTotalTime = 0;
     std::atomic<nsecs_t> mLastSwapTime = 0;
-
-    // Double- vs. triple-buffering stats
-    struct BufferingStats {
-        size_t numSegments = 0;
-        nsecs_t totalTime = 0;
-
-        // "Two buffer" means that a third buffer was never used, whereas
-        // "double-buffered" means that on average the segment only used two
-        // buffers (though it may have used a third for some part of the
-        // segment)
-        nsecs_t twoBufferTime = 0;
-        nsecs_t doubleBufferedTime = 0;
-        nsecs_t tripleBufferedTime = 0;
-    };
-    mutable Mutex mBufferingStatsMutex;
-    std::unordered_map<std::string, BufferingStats> mBufferingStats;
 };
 
 class SurfaceFlinger : public BnSurfaceComposer,
@@ -478,96 +462,6 @@
         hal::Connection connection = hal::Connection::INVALID;
     };
 
-    class CountDownLatch {
-    public:
-        enum {
-            eSyncTransaction = 1 << 0,
-            eSyncInputWindows = 1 << 1,
-        };
-        explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
-
-        // True if there is no waiting condition after count down.
-        bool countDown(uint32_t flag) {
-            std::unique_lock<std::mutex> lock(mMutex);
-            if (mFlags == 0) {
-                return true;
-            }
-            mFlags &= ~flag;
-            if (mFlags == 0) {
-                mCountDownComplete.notify_all();
-                return true;
-            }
-            return false;
-        }
-
-        // Return true if triggered.
-        bool wait_until(const std::chrono::seconds& timeout) const {
-            std::unique_lock<std::mutex> lock(mMutex);
-            const auto untilTime = std::chrono::system_clock::now() + timeout;
-            while (mFlags != 0) {
-                // Conditional variables can be woken up sporadically, so we check count
-                // to verify the wakeup was triggered by |countDown|.
-                if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-    private:
-        uint32_t mFlags;
-        mutable std::condition_variable mCountDownComplete;
-        mutable std::mutex mMutex;
-    };
-
-    struct TransactionState {
-        TransactionState(const FrameTimelineInfo& frameTimelineInfo,
-                         const Vector<ComposerState>& composerStates,
-                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
-                         const sp<IBinder>& applyToken,
-                         const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-                         bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
-                         int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
-                         std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
-                         int originUid, uint64_t transactionId)
-              : frameTimelineInfo(frameTimelineInfo),
-                states(composerStates),
-                displays(displayStates),
-                flags(transactionFlags),
-                applyToken(applyToken),
-                inputWindowCommands(inputWindowCommands),
-                desiredPresentTime(desiredPresentTime),
-                isAutoTimestamp(isAutoTimestamp),
-                buffer(uncacheBuffer),
-                postTime(postTime),
-                permissions(permissions),
-                hasListenerCallbacks(hasListenerCallbacks),
-                listenerCallbacks(listenerCallbacks),
-                originPid(originPid),
-                originUid(originUid),
-                id(transactionId) {}
-
-        void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
-
-        FrameTimelineInfo frameTimelineInfo;
-        Vector<ComposerState> states;
-        Vector<DisplayState> displays;
-        uint32_t flags;
-        sp<IBinder> applyToken;
-        InputWindowCommands inputWindowCommands;
-        const int64_t desiredPresentTime;
-        const bool isAutoTimestamp;
-        client_cache_t buffer;
-        const int64_t postTime;
-        uint32_t permissions;
-        bool hasListenerCallbacks;
-        std::vector<ListenerCallbacks> listenerCallbacks;
-        int originPid;
-        int originUid;
-        uint64_t id;
-        std::shared_ptr<CountDownLatch> transactionCommittedSignal;
-    };
-
     template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
     static Dumper dumper(F&& dump) {
         using namespace std::placeholders;
@@ -1150,10 +1044,6 @@
     void dumpStaticScreenStats(std::string& result) const;
     // Not const because each Layer needs to query Fences and cache timestamps.
     void dumpFrameEventsLocked(std::string& result);
-
-    void recordBufferingStats(const std::string& layerName,
-                              std::vector<OccupancyTracker::Segment>&& history);
-    void dumpBufferingStats(std::string& result) const;
     void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock);
     void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
     void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
new file mode 100644
index 0000000..fb1d43b
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/SurfaceComposerClient.h>
+#include <ui/Rect.h>
+
+#include "LayerProtoHelper.h"
+#include "TransactionProtoParser.h"
+
+namespace android::surfaceflinger {
+
+proto::TransactionState TransactionProtoParser::toProto(
+        const TransactionState& t, std::function<int32_t(const sp<IBinder>&)> getLayerId,
+        std::function<int32_t(const sp<IBinder>&)> getDisplayId) {
+    proto::TransactionState proto;
+    proto.set_pid(t.originPid);
+    proto.set_uid(t.originUid);
+    proto.set_vsync_id(t.frameTimelineInfo.vsyncId);
+    proto.set_input_event_id(t.frameTimelineInfo.inputEventId);
+    proto.set_post_time(t.postTime);
+
+    for (auto& layerState : t.states) {
+        proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state, getLayerId)));
+    }
+
+    for (auto& displayState : t.displays) {
+        proto.mutable_display_changes()->Add(std::move(toProto(displayState, getDisplayId)));
+    }
+    return proto;
+}
+
+proto::LayerState TransactionProtoParser::toProto(
+        const layer_state_t& layer, std::function<int32_t(const sp<IBinder>&)> getLayerId) {
+    proto::LayerState proto;
+    proto.set_layer_id(layer.layerId);
+    proto.set_what(layer.what);
+
+    if (layer.what & layer_state_t::ePositionChanged) {
+        proto.set_x(layer.x);
+        proto.set_y(layer.y);
+    }
+    if (layer.what & layer_state_t::eLayerChanged) {
+        proto.set_z(layer.z);
+    }
+    if (layer.what & layer_state_t::eSizeChanged) {
+        proto.set_w(layer.w);
+        proto.set_h(layer.h);
+    }
+    if (layer.what & layer_state_t::eLayerStackChanged) {
+        proto.set_layer_stack(layer.layerStack.id);
+    }
+    if (layer.what & layer_state_t::eFlagsChanged) {
+        proto.set_flags(layer.flags);
+        proto.set_mask(layer.mask);
+    }
+    if (layer.what & layer_state_t::eMatrixChanged) {
+        proto::LayerState_Matrix22* matrixProto = proto.mutable_matrix();
+        matrixProto->set_dsdx(layer.matrix.dsdx);
+        matrixProto->set_dsdy(layer.matrix.dsdy);
+        matrixProto->set_dtdx(layer.matrix.dtdx);
+        matrixProto->set_dtdy(layer.matrix.dtdy);
+    }
+    if (layer.what & layer_state_t::eCornerRadiusChanged) {
+        proto.set_corner_radius(layer.cornerRadius);
+    }
+    if (layer.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+        proto.set_background_blur_radius(layer.backgroundBlurRadius);
+    }
+
+    if (layer.what & layer_state_t::eAlphaChanged) {
+        proto.set_alpha(layer.alpha);
+    }
+
+    if (layer.what & layer_state_t::eColorChanged) {
+        proto::LayerState_Color3* colorProto = proto.mutable_color();
+        colorProto->set_r(layer.color.r);
+        colorProto->set_g(layer.color.g);
+        colorProto->set_b(layer.color.b);
+    }
+    if (layer.what & layer_state_t::eTransparentRegionChanged) {
+        LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region());
+    }
+    if (layer.what & layer_state_t::eTransformChanged) {
+        proto.set_transform(layer.transform);
+    }
+    if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) {
+        proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
+    }
+    if (layer.what & layer_state_t::eCropChanged) {
+        LayerProtoHelper::writeToProto(layer.crop, proto.mutable_crop());
+    }
+    if (layer.what & layer_state_t::eBufferChanged) {
+        proto::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
+        if (layer.bufferData.buffer) {
+            bufferProto->set_buffer_id(layer.bufferData.buffer->getId());
+            bufferProto->set_width(layer.bufferData.buffer->getWidth());
+            bufferProto->set_height(layer.bufferData.buffer->getHeight());
+        }
+        bufferProto->set_frame_number(layer.bufferData.frameNumber);
+        bufferProto->set_flags(layer.bufferData.flags.get());
+        bufferProto->set_cached_buffer_id(layer.bufferData.cachedBuffer.id);
+    }
+    if (layer.what & layer_state_t::eSidebandStreamChanged) {
+        proto.set_has_sideband_stream(layer.sidebandStream != nullptr);
+    }
+
+    if (layer.what & layer_state_t::eApiChanged) {
+        proto.set_api(layer.api);
+    }
+
+    if (layer.what & layer_state_t::eColorTransformChanged) {
+        LayerProtoHelper::writeToProto(layer.colorTransform, proto.mutable_color_transform());
+    }
+    if (layer.what & layer_state_t::eBlurRegionsChanged) {
+        for (auto& region : layer.blurRegions) {
+            LayerProtoHelper::writeToProto(region, proto.add_blur_regions());
+        }
+    }
+
+    if (layer.what & layer_state_t::eReparent) {
+        int32_t layerId = layer.parentSurfaceControlForChild
+                ? getLayerId(layer.parentSurfaceControlForChild->getHandle())
+                : -1;
+        proto.set_parent_id(layerId);
+    }
+    if (layer.what & layer_state_t::eRelativeLayerChanged) {
+        int32_t layerId = layer.relativeLayerSurfaceControl
+                ? getLayerId(layer.relativeLayerSurfaceControl->getHandle())
+                : -1;
+        proto.set_relative_parent_id(layerId);
+    }
+
+    if (layer.what & layer_state_t::eInputInfoChanged) {
+        if (layer.windowInfoHandle) {
+            const gui::WindowInfo* inputInfo = layer.windowInfoHandle->getInfo();
+            proto::LayerState_WindowInfo* windowInfoProto = proto.mutable_window_info_handle();
+            windowInfoProto->set_layout_params_flags(inputInfo->flags.get());
+            windowInfoProto->set_layout_params_type(static_cast<int32_t>(inputInfo->type));
+            LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
+                                           windowInfoProto->mutable_touchable_region());
+            windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
+            windowInfoProto->set_focusable(inputInfo->focusable);
+            windowInfoProto->set_has_wallpaper(inputInfo->hasWallpaper);
+            windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
+            proto::LayerState_Transform* transformProto = windowInfoProto->mutable_transform();
+            transformProto->set_dsdx(inputInfo->transform.dsdx());
+            transformProto->set_dtdx(inputInfo->transform.dtdx());
+            transformProto->set_dtdy(inputInfo->transform.dtdy());
+            transformProto->set_dsdy(inputInfo->transform.dsdy());
+            transformProto->set_tx(inputInfo->transform.tx());
+            transformProto->set_ty(inputInfo->transform.ty());
+            windowInfoProto->set_replace_touchable_region_with_crop(
+                    inputInfo->replaceTouchableRegionWithCrop);
+            windowInfoProto->set_crop_layer_id(
+                    getLayerId(inputInfo->touchableRegionCropHandle.promote()));
+        }
+    }
+    if (layer.what & layer_state_t::eBackgroundColorChanged) {
+        proto.set_bg_color_alpha(layer.bgColorAlpha);
+        proto.set_bg_color_dataspace(static_cast<int32_t>(layer.bgColorDataspace));
+        proto::LayerState_Color3* colorProto = proto.mutable_color();
+        colorProto->set_r(layer.color.r);
+        colorProto->set_g(layer.color.g);
+        colorProto->set_b(layer.color.b);
+    }
+    if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) {
+        proto.set_color_space_agnostic(layer.colorSpaceAgnostic);
+    }
+    if (layer.what & layer_state_t::eShadowRadiusChanged) {
+        proto.set_shadow_radius(layer.shadowRadius);
+    }
+    if (layer.what & layer_state_t::eFrameRateSelectionPriority) {
+        proto.set_frame_rate_selection_priority(layer.frameRateSelectionPriority);
+    }
+    if (layer.what & layer_state_t::eFrameRateChanged) {
+        proto.set_frame_rate(layer.frameRate);
+        proto.set_frame_rate_compatibility(layer.frameRateCompatibility);
+        proto.set_change_frame_rate_strategy(layer.changeFrameRateStrategy);
+    }
+    if (layer.what & layer_state_t::eFixedTransformHintChanged) {
+        proto.set_fixed_transform_hint(layer.fixedTransformHint);
+    }
+    if (layer.what & layer_state_t::eAutoRefreshChanged) {
+        proto.set_auto_refresh(layer.autoRefresh);
+    }
+    if (layer.what & layer_state_t::eTrustedOverlayChanged) {
+        proto.set_is_trusted_overlay(layer.isTrustedOverlay);
+    }
+    if (layer.what & layer_state_t::eBufferCropChanged) {
+        LayerProtoHelper::writeToProto(layer.bufferCrop, proto.mutable_buffer_crop());
+    }
+    if (layer.what & layer_state_t::eDestinationFrameChanged) {
+        LayerProtoHelper::writeToProto(layer.destinationFrame, proto.mutable_destination_frame());
+    }
+    if (layer.what & layer_state_t::eDropInputModeChanged) {
+        proto.set_drop_input_mode(
+                static_cast<proto::LayerState_DropInputMode>(layer.dropInputMode));
+    }
+    return proto;
+}
+
+proto::DisplayState TransactionProtoParser::toProto(
+        const DisplayState& display, std::function<int32_t(const sp<IBinder>&)> getDisplayId) {
+    proto::DisplayState proto;
+    proto.set_what(display.what);
+    proto.set_id(getDisplayId(display.token));
+
+    if (display.what & DisplayState::eLayerStackChanged) {
+        proto.set_layer_stack(display.layerStack.id);
+    }
+    if (display.what & DisplayState::eDisplayProjectionChanged) {
+        proto.set_orientation(static_cast<uint32_t>(display.orientation));
+        LayerProtoHelper::writeToProto(display.orientedDisplaySpaceRect,
+                                       proto.mutable_oriented_display_space_rect());
+        LayerProtoHelper::writeToProto(display.layerStackSpaceRect,
+                                       proto.mutable_layer_stack_space_rect());
+    }
+    if (display.what & DisplayState::eDisplaySizeChanged) {
+        proto.set_width(display.width);
+        proto.set_height(display.height);
+    }
+    if (display.what & DisplayState::eFlagsChanged) {
+        proto.set_flags(display.flags);
+    }
+    return proto;
+}
+
+TransactionState TransactionProtoParser::fromProto(
+        const proto::TransactionState& proto, std::function<sp<IBinder>(int32_t)> getLayerHandle,
+        std::function<sp<IBinder>(int32_t)> getDisplayHandle) {
+    TransactionState t;
+    t.originPid = proto.pid();
+    t.originUid = proto.uid();
+    t.frameTimelineInfo.vsyncId = proto.vsync_id();
+    t.frameTimelineInfo.inputEventId = proto.input_event_id();
+    t.postTime = proto.post_time();
+    int32_t layerCount = proto.layer_changes_size();
+    t.states.reserve(static_cast<size_t>(layerCount));
+    for (int i = 0; i < layerCount; i++) {
+        ComposerState s;
+        s.state = std::move(fromProto(proto.layer_changes(i), getLayerHandle));
+        t.states.add(s);
+    }
+
+    int32_t displayCount = proto.display_changes_size();
+    t.displays.reserve(static_cast<size_t>(displayCount));
+    for (int i = 0; i < displayCount; i++) {
+        t.displays.add(fromProto(proto.display_changes(i), getDisplayHandle));
+    }
+    return t;
+}
+
+layer_state_t TransactionProtoParser::fromProto(
+        const proto::LayerState& proto, std::function<sp<IBinder>(int32_t)> getLayerHandle) {
+    layer_state_t layer;
+    layer.layerId = proto.layer_id();
+    layer.what = proto.what();
+
+    if (layer.what & layer_state_t::ePositionChanged) {
+        layer.x = proto.x();
+        layer.y = proto.y();
+    }
+    if (layer.what & layer_state_t::eLayerChanged) {
+        layer.z = proto.z();
+    }
+    if (layer.what & layer_state_t::eSizeChanged) {
+        layer.w = proto.w();
+        layer.h = proto.h();
+    }
+    if (layer.what & layer_state_t::eLayerStackChanged) {
+        layer.layerStack.id = proto.layer_stack();
+    }
+    if (layer.what & layer_state_t::eFlagsChanged) {
+        layer.flags = proto.flags();
+        layer.mask = proto.mask();
+    }
+    if (layer.what & layer_state_t::eMatrixChanged) {
+        const proto::LayerState_Matrix22& matrixProto = proto.matrix();
+        layer.matrix.dsdx = matrixProto.dsdx();
+        layer.matrix.dsdy = matrixProto.dsdy();
+        layer.matrix.dtdx = matrixProto.dtdx();
+        layer.matrix.dtdy = matrixProto.dtdy();
+    }
+    if (layer.what & layer_state_t::eCornerRadiusChanged) {
+        layer.cornerRadius = proto.corner_radius();
+    }
+    if (layer.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+        layer.backgroundBlurRadius = proto.background_blur_radius();
+    }
+
+    if (layer.what & layer_state_t::eAlphaChanged) {
+        layer.alpha = proto.alpha();
+    }
+
+    if (layer.what & layer_state_t::eColorChanged) {
+        const proto::LayerState_Color3& colorProto = proto.color();
+        layer.color.r = colorProto.r();
+        layer.color.g = colorProto.g();
+        layer.color.b = colorProto.b();
+    }
+    if (layer.what & layer_state_t::eTransparentRegionChanged) {
+        LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
+    }
+    if (layer.what & layer_state_t::eTransformChanged) {
+        layer.transform = proto.transform();
+    }
+    if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) {
+        layer.transformToDisplayInverse = proto.transform_to_display_inverse();
+    }
+    if (layer.what & layer_state_t::eCropChanged) {
+        LayerProtoHelper::readFromProto(proto.crop(), layer.crop);
+    }
+    if (layer.what & layer_state_t::eBufferChanged) {
+        const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
+        layer.bufferData.buffer = new GraphicBuffer(bufferProto.width(), bufferProto.height(),
+                                                    HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+        layer.bufferData.frameNumber = bufferProto.frame_number();
+        layer.bufferData.flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
+        layer.bufferData.cachedBuffer.id = bufferProto.cached_buffer_id();
+    }
+    if (layer.what & layer_state_t::eSidebandStreamChanged) {
+        native_handle_t* handle = native_handle_create(0, 0);
+        layer.sidebandStream =
+                proto.has_sideband_stream() ? NativeHandle::create(handle, true) : nullptr;
+    }
+
+    if (layer.what & layer_state_t::eApiChanged) {
+        layer.api = proto.api();
+    }
+
+    if (layer.what & layer_state_t::eColorTransformChanged) {
+        LayerProtoHelper::readFromProto(proto.color_transform(), layer.colorTransform);
+    }
+    if (layer.what & layer_state_t::eBlurRegionsChanged) {
+        layer.blurRegions.reserve(static_cast<size_t>(proto.blur_regions_size()));
+        for (int i = 0; i < proto.blur_regions_size(); i++) {
+            android::BlurRegion region;
+            LayerProtoHelper::readFromProto(proto.blur_regions(i), region);
+            layer.blurRegions.push_back(region);
+        }
+    }
+
+    if (layer.what & layer_state_t::eReparent) {
+        int32_t layerId = proto.parent_id();
+        layer.parentSurfaceControlForChild =
+                new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+                                   nullptr, layerId);
+    }
+    if (layer.what & layer_state_t::eRelativeLayerChanged) {
+        int32_t layerId = proto.relative_parent_id();
+        layer.relativeLayerSurfaceControl =
+                new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+                                   nullptr, layerId);
+    }
+
+    if ((layer.what & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) {
+        gui::WindowInfo inputInfo;
+        const proto::LayerState_WindowInfo& windowInfoProto = proto.window_info_handle();
+
+        inputInfo.flags = static_cast<gui::WindowInfo::Flag>(windowInfoProto.layout_params_flags());
+        inputInfo.type = static_cast<gui::WindowInfo::Type>(windowInfoProto.layout_params_type());
+        LayerProtoHelper::readFromProto(windowInfoProto.touchable_region(),
+                                        inputInfo.touchableRegion);
+        inputInfo.surfaceInset = windowInfoProto.surface_inset();
+        inputInfo.focusable = windowInfoProto.focusable();
+        inputInfo.hasWallpaper = windowInfoProto.has_wallpaper();
+        inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
+        const proto::LayerState_Transform& transformProto = windowInfoProto.transform();
+        inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
+                                transformProto.dsdy());
+        inputInfo.transform.set(transformProto.tx(), transformProto.ty());
+        inputInfo.replaceTouchableRegionWithCrop =
+                windowInfoProto.replace_touchable_region_with_crop();
+        int32_t layerId = windowInfoProto.crop_layer_id();
+        inputInfo.touchableRegionCropHandle = getLayerHandle(layerId);
+        layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
+    }
+    if (layer.what & layer_state_t::eBackgroundColorChanged) {
+        layer.bgColorAlpha = proto.bg_color_alpha();
+        layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace());
+        const proto::LayerState_Color3& colorProto = proto.color();
+        layer.color.r = colorProto.r();
+        layer.color.g = colorProto.g();
+        layer.color.b = colorProto.b();
+    }
+    if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) {
+        layer.colorSpaceAgnostic = proto.color_space_agnostic();
+    }
+    if (layer.what & layer_state_t::eShadowRadiusChanged) {
+        layer.shadowRadius = proto.shadow_radius();
+    }
+    if (layer.what & layer_state_t::eFrameRateSelectionPriority) {
+        layer.frameRateSelectionPriority = proto.frame_rate_selection_priority();
+    }
+    if (layer.what & layer_state_t::eFrameRateChanged) {
+        layer.frameRate = proto.frame_rate();
+        layer.frameRateCompatibility = static_cast<int8_t>(proto.frame_rate_compatibility());
+        layer.changeFrameRateStrategy = static_cast<int8_t>(proto.change_frame_rate_strategy());
+    }
+    if (layer.what & layer_state_t::eFixedTransformHintChanged) {
+        layer.fixedTransformHint =
+                static_cast<ui::Transform::RotationFlags>(proto.fixed_transform_hint());
+    }
+    if (layer.what & layer_state_t::eAutoRefreshChanged) {
+        layer.autoRefresh = proto.auto_refresh();
+    }
+    if (layer.what & layer_state_t::eTrustedOverlayChanged) {
+        layer.isTrustedOverlay = proto.is_trusted_overlay();
+    }
+    if (layer.what & layer_state_t::eBufferCropChanged) {
+        LayerProtoHelper::readFromProto(proto.buffer_crop(), layer.bufferCrop);
+    }
+    if (layer.what & layer_state_t::eDestinationFrameChanged) {
+        LayerProtoHelper::readFromProto(proto.destination_frame(), layer.destinationFrame);
+    }
+    if (layer.what & layer_state_t::eDropInputModeChanged) {
+        layer.dropInputMode = static_cast<gui::DropInputMode>(proto.drop_input_mode());
+    }
+    return layer;
+}
+
+DisplayState TransactionProtoParser::fromProto(
+        const proto::DisplayState& proto, std::function<sp<IBinder>(int32_t)> getDisplayHandle) {
+    DisplayState display;
+    display.what = proto.what();
+    display.token = getDisplayHandle(proto.id());
+
+    if (display.what & DisplayState::eLayerStackChanged) {
+        display.layerStack.id = proto.layer_stack();
+    }
+    if (display.what & DisplayState::eDisplayProjectionChanged) {
+        display.orientation = static_cast<ui::Rotation>(proto.orientation());
+        LayerProtoHelper::readFromProto(proto.oriented_display_space_rect(),
+                                        display.orientedDisplaySpaceRect);
+        LayerProtoHelper::readFromProto(proto.layer_stack_space_rect(),
+                                        display.layerStackSpaceRect);
+    }
+    if (display.what & DisplayState::eDisplaySizeChanged) {
+        display.width = proto.width();
+        display.height = proto.height();
+    }
+    if (display.what & DisplayState::eFlagsChanged) {
+        display.flags = proto.flags();
+    }
+    return display;
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
new file mode 100644
index 0000000..a2b8889
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <layerproto/TransactionProto.h>
+#include <utils/RefBase.h>
+
+#include "TransactionState.h"
+
+namespace android::surfaceflinger {
+class TransactionProtoParser {
+public:
+    static proto::TransactionState toProto(
+            const TransactionState&, std::function<int32_t(const sp<IBinder>&)> getLayerIdFn,
+            std::function<int32_t(const sp<IBinder>&)> getDisplayIdFn);
+    static TransactionState fromProto(const proto::TransactionState&,
+                                      std::function<sp<IBinder>(int32_t)> getLayerHandleFn,
+                                      std::function<sp<IBinder>(int32_t)> getDisplayHandleFn);
+
+private:
+    static proto::LayerState toProto(const layer_state_t&,
+                                     std::function<int32_t(const sp<IBinder>&)> getLayerId);
+    static proto::DisplayState toProto(const DisplayState&,
+                                       std::function<int32_t(const sp<IBinder>&)> getDisplayId);
+    static layer_state_t fromProto(const proto::LayerState&,
+                                   std::function<sp<IBinder>(int32_t)> getLayerHandle);
+    static DisplayState fromProto(const proto::DisplayState&,
+                                  std::function<sp<IBinder>(int32_t)> getDisplayHandle);
+};
+
+} // namespace android::surfaceflinger
\ No newline at end of file
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 4b12a26..c1eb896 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -49,6 +49,31 @@
     return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT;
 }
 
+TransactionCallbackInvoker::TransactionCallbackInvoker() {
+    mThread = std::thread([&]() {
+          std::unique_lock lock(mCallbackThreadMutex);
+
+        while (mKeepRunning) {
+          while (mCallbackThreadWork.size() > 0) {
+              mCallbackThreadWork.front()();
+              mCallbackThreadWork.pop();
+          }
+          mCallbackConditionVariable.wait(lock);
+        }
+    });
+}
+
+TransactionCallbackInvoker::~TransactionCallbackInvoker() {
+    {
+          std::unique_lock lock(mCallbackThreadMutex);
+          mKeepRunning = false;
+          mCallbackConditionVariable.notify_all();
+    }
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
 void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) {
     auto& [listener, callbackIds] = listenerCallbacks;
     auto& transactionStatsDeque = mCompletedTransactions[listener];
@@ -180,8 +205,15 @@
                 // keep it as an IBinder due to consistency reasons: if we
                 // interface_cast at the IPC boundary when reading a Parcel,
                 // we get pointers that compare unequal in the SF process.
-                interface_cast<ITransactionCompletedListener>(listenerStats.listener)
-                        ->onTransactionCompleted(listenerStats);
+                {
+                    std::unique_lock lock(mCallbackThreadMutex);
+                    mCallbackThreadWork.push(
+                        [stats = std::move(listenerStats)]() {
+                          interface_cast<ITransactionCompletedListener>(stats.listener)
+                              ->onTransactionCompleted(stats);
+                    });
+                    mCallbackConditionVariable.notify_all();
+                }
             }
         }
         completedTransactionsItr++;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 71ca6e5..7e879e1 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -18,6 +18,7 @@
 
 #include <condition_variable>
 #include <deque>
+#include <queue>
 #include <mutex>
 #include <thread>
 #include <unordered_map>
@@ -56,8 +57,11 @@
 
 class TransactionCallbackInvoker {
 public:
+    TransactionCallbackInvoker();
+    ~TransactionCallbackInvoker();
+
     status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
-                                            const std::vector<JankData>& jankData);
+                                const std::vector<JankData>& jankData);
     status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
                                              std::deque<sp<CallbackHandle>>& outRemainingHandles);
 
@@ -88,6 +92,12 @@
         mCompletedTransactions;
 
     sp<Fence> mPresentFence;
+
+    std::mutex mCallbackThreadMutex;
+    std::condition_variable mCallbackConditionVariable;
+    std::thread mThread;
+    bool mKeepRunning = true;
+    std::queue<std::function<void()>> mCallbackThreadWork;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
new file mode 100644
index 0000000..fe3f3fc
--- /dev/null
+++ b/services/surfaceflinger/TransactionState.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/LayerState.h>
+
+namespace android {
+class CountDownLatch;
+
+struct TransactionState {
+    TransactionState(const FrameTimelineInfo& frameTimelineInfo,
+                     const Vector<ComposerState>& composerStates,
+                     const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+                     const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
+                     int64_t desiredPresentTime, bool isAutoTimestamp,
+                     const client_cache_t& uncacheBuffer, int64_t postTime, uint32_t permissions,
+                     bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
+                     int originPid, int originUid, uint64_t transactionId)
+          : frameTimelineInfo(frameTimelineInfo),
+            states(composerStates),
+            displays(displayStates),
+            flags(transactionFlags),
+            applyToken(applyToken),
+            inputWindowCommands(inputWindowCommands),
+            desiredPresentTime(desiredPresentTime),
+            isAutoTimestamp(isAutoTimestamp),
+            buffer(uncacheBuffer),
+            postTime(postTime),
+            permissions(permissions),
+            hasListenerCallbacks(hasListenerCallbacks),
+            listenerCallbacks(listenerCallbacks),
+            originPid(originPid),
+            originUid(originUid),
+            id(transactionId) {}
+
+    TransactionState() {}
+
+    void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
+
+    FrameTimelineInfo frameTimelineInfo;
+    Vector<ComposerState> states;
+    Vector<DisplayState> displays;
+    uint32_t flags;
+    sp<IBinder> applyToken;
+    InputWindowCommands inputWindowCommands;
+    int64_t desiredPresentTime;
+    bool isAutoTimestamp;
+    client_cache_t buffer;
+    int64_t postTime;
+    uint32_t permissions;
+    bool hasListenerCallbacks;
+    std::vector<ListenerCallbacks> listenerCallbacks;
+    int originPid;
+    int originUid;
+    uint64_t id;
+    std::shared_ptr<CountDownLatch> transactionCommittedSignal;
+};
+
+class CountDownLatch {
+public:
+    enum {
+        eSyncTransaction = 1 << 0,
+        eSyncInputWindows = 1 << 1,
+    };
+    explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
+
+    // True if there is no waiting condition after count down.
+    bool countDown(uint32_t flag) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        if (mFlags == 0) {
+            return true;
+        }
+        mFlags &= ~flag;
+        if (mFlags == 0) {
+            mCountDownComplete.notify_all();
+            return true;
+        }
+        return false;
+    }
+
+    // Return true if triggered.
+    bool wait_until(const std::chrono::seconds& timeout) const {
+        std::unique_lock<std::mutex> lock(mMutex);
+        const auto untilTime = std::chrono::system_clock::now() + timeout;
+        while (mFlags != 0) {
+            // Conditional variables can be woken up sporadically, so we check count
+            // to verify the wakeup was triggered by |countDown|.
+            if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+private:
+    uint32_t mFlags;
+    mutable std::condition_variable mCountDownComplete;
+    mutable std::mutex mMutex;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/layerproto/common.proto b/services/surfaceflinger/layerproto/common.proto
index 1c73a9f..a6d8d61 100644
--- a/services/surfaceflinger/layerproto/common.proto
+++ b/services/surfaceflinger/layerproto/common.proto
@@ -18,6 +18,11 @@
 option optimize_for = LITE_RUNTIME;
 package android.surfaceflinger;
 
+message RegionProto {
+    reserved 1; // Previously: uint64 id
+    repeated RectProto rect = 2;
+}
+
 message RectProto {
   int32 left   = 1;
   int32 top    = 2;
@@ -36,4 +41,51 @@
   float dsdy = 3;
   float dtdy = 4;
   int32 type = 5;
-}
\ No newline at end of file
+}
+
+message ColorProto {
+    float r = 1;
+    float g = 2;
+    float b = 3;
+    float a = 4;
+}
+
+message InputWindowInfoProto {
+    uint32 layout_params_flags = 1;
+    int32 layout_params_type = 2;
+    RectProto frame = 3;
+    RegionProto touchable_region = 4;
+
+    int32 surface_inset = 5;
+    bool visible = 6;
+    bool can_receive_keys = 7 [deprecated = true];
+    bool focusable = 8;
+    bool has_wallpaper = 9;
+
+    float global_scale_factor = 10;
+    float window_x_scale = 11 [deprecated = true];
+    float window_y_scale = 12 [deprecated = true];
+
+    int32 crop_layer_id = 13;
+    bool replace_touchable_region_with_crop = 14;
+    RectProto touchable_region_crop = 15;
+    TransformProto transform = 16;
+}
+
+message BlurRegion {
+    uint32 blur_radius = 1;
+    uint32 corner_radius_tl = 2;
+    uint32 corner_radius_tr = 3;
+    uint32 corner_radius_bl = 4;
+    float corner_radius_br = 5;
+    float alpha = 6;
+    int32 left = 7;
+    int32 top = 8;
+    int32 right = 9;
+    int32 bottom = 10;
+}
+
+message ColorTransformProto {
+    // This will be a 4x4 matrix of float values
+    repeated float val = 1;
+}
diff --git a/services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h b/services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h
new file mode 100644
index 0000000..3e9ca52
--- /dev/null
+++ b/services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// disable the warnings emitted from the protobuf headers. This file should be included instead of
+// directly including the generated header file
+#pragma GCC system_header
+#include <transactions.pb.h>
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 057eabb..4529905 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -143,11 +143,6 @@
   float y = 2;
 }
 
-message RegionProto {
-  reserved 1;  // Previously: uint64 id
-  repeated RectProto rect = 2;
-}
-
 message FloatRectProto {
   float left = 1;
   float top = 2;
@@ -162,13 +157,6 @@
   int32 format = 4;
 }
 
-message ColorProto {
-  float r = 1;
-  float g = 2;
-  float b = 3;
-  float a = 4;
-}
-
 message BarrierLayerProto {
   // layer id the barrier is waiting on.
   int32 id = 1;
@@ -176,42 +164,3 @@
   uint64 frame_number = 2;
 }
 
-message InputWindowInfoProto {
-    uint32 layout_params_flags = 1;
-    uint32 layout_params_type = 2;
-    RectProto frame = 3;
-    RegionProto touchable_region = 4;
-
-    uint32 surface_inset = 5;
-    bool visible = 6;
-    bool can_receive_keys = 7  [deprecated=true];
-    bool focusable = 8;
-    bool has_wallpaper = 9;
-
-    float global_scale_factor = 10;
-    float window_x_scale = 11 [deprecated=true];
-    float window_y_scale = 12 [deprecated=true];
-
-    uint32 crop_layer_id = 13;
-    bool replace_touchable_region_with_crop = 14;
-    RectProto touchable_region_crop = 15;
-    TransformProto transform = 16;
-}
-
-message ColorTransformProto {
-  // This will be a 4x4 matrix of float values
-  repeated float val = 1;
-}
-
-message BlurRegion {
-    uint32 blur_radius = 1;
-    uint32 corner_radius_tl = 2;
-    uint32 corner_radius_tr = 3;
-    uint32 corner_radius_bl = 4;
-    float corner_radius_br = 5;
-    float alpha = 6;
-    int32 left = 7;
-    int32 top = 8;
-    int32 right = 9;
-    int32 bottom = 10;
-}
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
new file mode 100644
index 0000000..e7fb180
--- /dev/null
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+option optimize_for = LITE_RUNTIME;
+
+import "frameworks/native/services/surfaceflinger/layerproto/common.proto";
+
+package android.surfaceflinger.proto;
+
+/* Represents a file full of surface flinger transactions.
+   Encoded, it should start with 0x54 0x4E 0x58 0x54 0x52 0x41 0x43 0x45 (.TNXTRACE), such
+   that they can be easily identified. */
+message TransactionTraceFile {
+    /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+       (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+        constants into .proto files. */
+    enum MagicNumber {
+        INVALID = 0;
+        MAGIC_NUMBER_L = 0x54584E54; /* TNXT (little-endian ASCII) */
+        MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */
+    }
+
+    fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+    repeated TransactionTraceEntry entry = 2;
+}
+
+message TransactionTraceEntry {
+    int64 elapsed_time = 1;
+    int64 vsync_id = 2;
+    repeated TransactionState transactions = 3;
+}
+
+message TransactionState {
+    string tag = 2;
+    int32 pid = 3;
+    int32 uid = 4;
+    int64 vsync_id = 5;
+    int32 input_event_id = 6;
+    int64 post_time = 7;
+    repeated LayerState layer_changes = 9;
+    repeated DisplayState new_displays = 10;
+    repeated DisplayState display_changes = 11;
+}
+
+// Keep insync with layer_state_t
+message LayerState {
+    int32 layer_id = 1;
+    // Changes are split into ChangesLsb and ChangesMsb. First 32 bits are in ChangesLsb
+    // and the next 32 bits are in ChangesMsb. This is needed because enums have to be
+    // 32 bits and there's no nice way to put 64bit constants into .proto files.
+    enum ChangesLsb {
+        eChangesLsbNone = 0;
+        ePositionChanged = 0x00000001;
+        eLayerChanged = 0x00000002;
+        eSizeChanged = 0x00000004;
+        eAlphaChanged = 0x00000008;
+        eMatrixChanged = 0x00000010;
+        eTransparentRegionChanged = 0x00000020;
+        eFlagsChanged = 0x00000040;
+        eLayerStackChanged = 0x00000080;
+        eReleaseBufferListenerChanged = 0x00000400;
+        eShadowRadiusChanged = 0x00000800;
+        eLayerCreated = 0x00001000;
+        eBufferCropChanged = 0x00002000;
+        eRelativeLayerChanged = 0x00004000;
+        eReparent = 0x00008000;
+        eColorChanged = 0x00010000;
+        eDestroySurface = 0x00020000;
+        eTransformChanged = 0x00040000;
+        eTransformToDisplayInverseChanged = 0x00080000;
+        eCropChanged = 0x00100000;
+        eBufferChanged = 0x00200000;
+        eAcquireFenceChanged = 0x00400000;
+        eDataspaceChanged = 0x00800000;
+        eHdrMetadataChanged = 0x01000000;
+        eSurfaceDamageRegionChanged = 0x02000000;
+        eApiChanged = 0x04000000;
+        eSidebandStreamChanged = 0x08000000;
+        eColorTransformChanged = 0x10000000;
+        eHasListenerCallbacksChanged = 0x20000000;
+        eInputInfoChanged = 0x40000000;
+        eCornerRadiusChanged = -2147483648; // 0x80000000; (proto stores enums as signed int)
+    };
+    enum ChangesMsb {
+        eChangesMsbNone = 0;
+        eDestinationFrameChanged = 0x1;
+        eCachedBufferChanged = 0x2;
+        eBackgroundColorChanged = 0x4;
+        eMetadataChanged = 0x8;
+        eColorSpaceAgnosticChanged = 0x10;
+        eFrameRateSelectionPriority = 0x20;
+        eFrameRateChanged = 0x40;
+        eBackgroundBlurRadiusChanged = 0x80;
+        eProducerDisconnect = 0x100;
+        eFixedTransformHintChanged = 0x200;
+        eFrameNumberChanged = 0x400;
+        eBlurRegionsChanged = 0x800;
+        eAutoRefreshChanged = 0x1000;
+        eStretchChanged = 0x2000;
+        eTrustedOverlayChanged = 0x4000;
+        eDropInputModeChanged = 0x8000;
+    };
+    uint64 what = 2;
+    float x = 3;
+    float y = 4;
+    int32 z = 5;
+    uint32 w = 6;
+    uint32 h = 7;
+    uint32 layer_stack = 8;
+
+    enum Flags {
+        eFlagsNone = 0;
+        eLayerHidden = 0x01;
+        eLayerOpaque = 0x02;
+        eLayerSkipScreenshot = 0x40;
+        eLayerSecure = 0x80;
+        eEnableBackpressure = 0x100;
+    };
+    uint32 flags = 10;
+    uint32 mask = 11;
+
+    message Matrix22 {
+        float dsdx = 1;
+        float dtdx = 2;
+        float dtdy = 3;
+        float dsdy = 4;
+    };
+    Matrix22 matrix = 12;
+    float corner_radius = 13;
+    uint32 background_blur_radius = 14;
+    int32 parent_id = 15;
+    int32 relative_parent_id = 16;
+
+    float alpha = 50;
+    message Color3 {
+        float r = 1;
+        float g = 2;
+        float b = 3;
+    }
+    Color3 color = 18;
+    RegionProto transparent_region = 19;
+    uint32 transform = 20;
+    bool transform_to_display_inverse = 21;
+    RectProto crop = 49;
+
+    message BufferData {
+        uint64 buffer_id = 1;
+        uint32 width = 2;
+        uint32 height = 3;
+        uint64 frame_number = 5;
+
+        enum BufferDataChange {
+            BufferDataChangeNone = 0;
+            fenceChanged = 0x01;
+            frameNumberChanged = 0x02;
+            cachedBufferChanged = 0x04;
+        }
+        uint32 flags = 6;
+        uint64 cached_buffer_id = 7;
+    }
+    BufferData buffer_data = 23;
+    int32 api = 24;
+    bool has_sideband_stream = 25;
+    ColorTransformProto color_transform = 26;
+    repeated BlurRegion blur_regions = 27;
+
+    message Transform {
+        float dsdx = 1;
+        float dtdx = 2;
+        float dtdy = 3;
+        float dsdy = 4;
+        float tx = 5;
+        float ty = 6;
+    }
+    message WindowInfo {
+        uint32 layout_params_flags = 1;
+        int32 layout_params_type = 2;
+        RegionProto touchable_region = 4;
+        int32 surface_inset = 5;
+        bool focusable = 8;
+        bool has_wallpaper = 9;
+        float global_scale_factor = 10;
+        int32 crop_layer_id = 13;
+        bool replace_touchable_region_with_crop = 14;
+        RectProto touchable_region_crop = 15;
+        Transform transform = 16;
+    }
+    WindowInfo window_info_handle = 28;
+    float bg_color_alpha = 31;
+    int32 bg_color_dataspace = 32;
+    bool color_space_agnostic = 33;
+    float shadow_radius = 34;
+    int32 frame_rate_selection_priority = 35;
+    float frame_rate = 36;
+    int32 frame_rate_compatibility = 37;
+    int32 change_frame_rate_strategy = 38;
+    uint32 fixed_transform_hint = 39;
+    uint64 frame_number = 40;
+    bool auto_refresh = 41;
+    bool is_trusted_overlay = 42;
+    RectProto buffer_crop = 44;
+    RectProto destination_frame = 45;
+
+    enum DropInputMode {
+        NONE = 0;
+        ALL = 1;
+        OBSCURED = 2;
+    };
+    DropInputMode drop_input_mode = 48;
+}
+
+message DisplayState {
+    enum Changes {
+        eChangesNone = 0;
+        eSurfaceChanged = 0x01;
+        eLayerStackChanged = 0x02;
+        eDisplayProjectionChanged = 0x04;
+        eDisplaySizeChanged = 0x08;
+        eFlagsChanged = 0x10;
+    };
+    int32 id = 1;
+    uint32 what = 2;
+    uint32 flags = 3;
+    uint32 layer_stack = 4;
+    uint32 orientation = 5;
+    RectProto layer_stack_space_rect = 6;
+    RectProto oriented_display_space_rect = 7;
+    uint32 width = 8;
+    uint32 height = 9;
+}
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 94e1e0c..ce94dab 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -28,8 +28,8 @@
 
 #include <limits>
 
+#include <gui/test/CallbackUtils.h>
 #include "BufferGenerator.h"
-#include "utils/CallbackUtils.h"
 #include "utils/ColorUtils.h"
 #include "utils/TransactionUtils.h"
 
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index e8759e5..9ddbed2 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -18,8 +18,8 @@
 
 #include <gui/DisplayEventReceiver.h>
 
+#include <gui/test/CallbackUtils.h>
 #include "LayerTransactionTest.h"
-#include "utils/CallbackUtils.h"
 
 using namespace std::chrono_literals;
 
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index e5872c1..9cb617a 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -208,6 +208,37 @@
     }
 }
 
+// b/200781179 - don't round a layer without a valid crop
+// This behaviour should be fixed since we treat buffer layers differently than
+// effect or container layers.
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusInvalidCrop) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::GREEN, size, size));
+    ASSERT_NO_FATAL_FAILURE(child = createColorLayer("child", Color::RED));
+
+    Transaction().setCornerRadius(child, cornerRadius).reparent(child, parent).show(child).apply();
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        std::this_thread::sleep_for(std::chrono::seconds(5));
+        // Solid corners since we don't round a layer without a valid crop
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::RED);
+        shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::RED);
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::RED);
+        shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::RED);
+        // Solid center
+        shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+                               size / 2 + testArea / 2, size / 2 + testArea / 2),
+                          Color::RED);
+    }
+}
+
 TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) {
     sp<SurfaceControl> parent;
     sp<SurfaceControl> child;
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index 3847a51..f6b0def 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
+#include <gui/test/CallbackUtils.h>
 #include "LayerTransactionTest.h"
-#include "utils/CallbackUtils.h"
 
 using namespace std::chrono_literals;
 
@@ -31,7 +31,7 @@
 public:
     static void function(void* callbackContext, ReleaseCallbackId callbackId,
                          const sp<Fence>& releaseFence,
-                         uint32_t /*currentMaxAcquiredBufferCount*/) {
+                         std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {
         if (!callbackContext) {
             FAIL() << "failed to get callback context";
         }
@@ -61,7 +61,7 @@
         std::this_thread::sleep_for(300ms);
 
         std::lock_guard lock(mMutex);
-        EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+        EXPECT_EQ(mCallbackDataQueue.size(), 0U) << "extra callbacks received";
         mCallbackDataQueue = {};
     }
 
@@ -385,4 +385,84 @@
     ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
 
+TEST_F(ReleaseBufferCallbackTest, DISABLED_SetBuffer_OverwriteBuffers) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+    // Create transaction with a buffer.
+    Transaction transaction;
+    transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+                          firstBufferCallbackId, releaseCallback->getCallback());
+
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+    // Call setBuffer on the same transaction with a different buffer.
+    transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+                          secondBufferCallbackId, releaseCallback->getCallback());
+
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) {
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+    // Create transaction with a buffer.
+    Transaction transaction1;
+    transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+                           firstBufferCallbackId, releaseCallback->getCallback());
+
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+    // Create a second transaction with a new buffer for the same layer.
+    Transaction transaction2;
+    transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+                           secondBufferCallbackId, releaseCallback->getCallback());
+
+    // merge transaction1 into transaction2 so ensure we get a proper buffer release callback.
+    transaction1.merge(std::move(transaction2));
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) {
+    sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
+    sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();
+
+    TransactionCompletedListener::setInstance(firstCompletedListener);
+
+    sp<SurfaceControl> layer = createBufferStateLayer();
+    ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+    sp<GraphicBuffer> firstBuffer = getBuffer();
+    ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+    Transaction transaction1;
+    transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+                           firstBufferCallbackId, releaseCallback->getCallback());
+
+    // Sent a second buffer to allow the first buffer to get released.
+    sp<GraphicBuffer> secondBuffer = getBuffer();
+    ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+    Transaction transaction2;
+    transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+                           secondBufferCallbackId, releaseCallback->getCallback());
+
+    // Set a different TransactionCompletedListener to mimic a second process
+    TransactionCompletedListener::setInstance(secondCompletedListener);
+    Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply();
+
+    // Make sure we can still get the release callback even though the merge happened in a different
+    // process.
+    ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 078b0d4..da019a3 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -91,6 +91,7 @@
         "TimerTest.cpp",
         "TransactionApplicationTest.cpp",
         "TransactionFrameTracerTest.cpp",
+        "TransactionProtoParserTest.cpp",
         "TransactionSurfaceFrameTest.cpp",
         "TunnelModeEnabledReporterTest.cpp",
         "StrongTypingTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index de1cade..b89da60 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -289,7 +289,7 @@
     }
 
     static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) {
-        layer->mDrawingParent = drawingParent;
+        layer->mDrawingParent = drawingParent.get();
     }
 
     /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index d8e68b8..05551b4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -103,7 +103,7 @@
         static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
     };
 
-    void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) {
+    void checkEqual(TransactionInfo info, TransactionState state) {
         EXPECT_EQ(0u, info.states.size());
         EXPECT_EQ(0u, state.states.size());
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
new file mode 100644
index 0000000..cebd451
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <limits> // std::numeric_limits
+
+#include <gui/SurfaceComposerClient.h>
+
+#include "Tracing/TransactionProtoParser.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+TEST(TransactionProtoParserTest, parse) {
+    const sp<IBinder> layerHandle = new BBinder();
+    const sp<IBinder> displayHandle = new BBinder();
+    TransactionState t1;
+    t1.originPid = 1;
+    t1.originUid = 2;
+    t1.frameTimelineInfo.vsyncId = 3;
+    t1.frameTimelineInfo.inputEventId = 4;
+    t1.postTime = 5;
+
+    layer_state_t layer;
+    layer.layerId = 6;
+    layer.what = std::numeric_limits<uint64_t>::max();
+    layer.x = 7;
+    layer.matrix.dsdx = 15;
+
+    size_t layerCount = 2;
+    t1.states.reserve(layerCount);
+    for (uint32_t i = 0; i < layerCount; i++) {
+        ComposerState s;
+        if (i == 1) {
+            layer.parentSurfaceControlForChild =
+                    new SurfaceControl(SurfaceComposerClient::getDefault(), layerHandle, nullptr,
+                                       42);
+        }
+        s.state = layer;
+        t1.states.add(s);
+    }
+
+    size_t displayCount = 2;
+    t1.displays.reserve(displayCount);
+    for (uint32_t i = 0; i < displayCount; i++) {
+        DisplayState display;
+        display.what = std::numeric_limits<uint32_t>::max();
+        if (i == 0) {
+            display.token = displayHandle;
+        } else {
+            display.token = nullptr;
+        }
+        display.width = 85;
+        t1.displays.add(display);
+    }
+
+    std::function<int32_t(const sp<IBinder>&)> getLayerIdFn = [&](const sp<IBinder>& handle) {
+        return (handle == layerHandle) ? 42 : -1;
+    };
+    std::function<int32_t(const sp<IBinder>&)> getDisplayIdFn = [&](const sp<IBinder>& handle) {
+        return (handle == displayHandle) ? 43 : -1;
+    };
+    std::function<sp<IBinder>(int32_t)> getLayerHandleFn = [&](int32_t id) {
+        return (id == 42) ? layerHandle : nullptr;
+    };
+    std::function<sp<IBinder>(int32_t)> getDisplayHandleFn = [&](int32_t id) {
+        return (id == 43) ? displayHandle : nullptr;
+    };
+
+    proto::TransactionState proto =
+            TransactionProtoParser::toProto(t1, getLayerIdFn, getDisplayIdFn);
+    TransactionState t2 =
+            TransactionProtoParser::fromProto(proto, getLayerHandleFn, getDisplayHandleFn);
+
+    ASSERT_EQ(t1.originPid, t2.originPid);
+    ASSERT_EQ(t1.originUid, t2.originUid);
+    ASSERT_EQ(t1.frameTimelineInfo.vsyncId, t2.frameTimelineInfo.vsyncId);
+    ASSERT_EQ(t1.frameTimelineInfo.inputEventId, t2.frameTimelineInfo.inputEventId);
+    ASSERT_EQ(t1.postTime, t2.postTime);
+    ASSERT_EQ(t1.states.size(), t2.states.size());
+    ASSERT_EQ(t1.states[0].state.x, t2.states[0].state.x);
+    ASSERT_EQ(t1.states[0].state.matrix.dsdx, t2.states[0].state.matrix.dsdx);
+    ASSERT_EQ(t1.states[1].state.parentSurfaceControlForChild->getHandle(),
+              t2.states[1].state.parentSurfaceControlForChild->getHandle());
+
+    ASSERT_EQ(t1.displays.size(), t2.displays.size());
+    ASSERT_EQ(t1.displays[1].width, t2.displays[1].width);
+    ASSERT_EQ(t1.displays[0].token, t2.displays[0].token);
+}
+
+} // namespace android
diff --git a/services/vibratorservice/OWNERS b/services/vibratorservice/OWNERS
new file mode 100644
index 0000000..d073e2b
--- /dev/null
+++ b/services/vibratorservice/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS