Merge "Fix java SurfaceControl BBQ wrapper update path" into sc-dev
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreport/OWNERS
+++ b/cmds/bugreport/OWNERS
@@ -1,4 +1,5 @@
 set noparent
 
+gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/bugreportz/OWNERS
+++ b/cmds/bugreportz/OWNERS
@@ -1,4 +1,5 @@
 set noparent
 
+gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 2a9b681..5f56531 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -1,4 +1,5 @@
 set noparent
 
+gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index ae96e66..8ac4ff8 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1964,6 +1964,8 @@
 
     RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
+    RunDumpsys("DUMPSYS", {"vcn_management"}, CommandOptions::WithTimeout(90).Build(),
+               SEC_TO_MSEC(10));
     if (include_sensitive_info) {
         // Carrier apps' services will be dumped below in dumpsys activity service all-non-platform.
         RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(),
@@ -3187,6 +3189,11 @@
         // Since we do not have user consent to share the bugreport it does not get
         // copied over to the calling app but remains in the internal directory from
         // where the user can manually pull it.
+        std::string final_path = GetPath(".zip");
+        bool copy_succeeded = android::os::CopyFileToFile(path_, final_path);
+        if (copy_succeeded) {
+            android::os::UnlinkAndLogOnError(path_);
+        }
         return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT;
     }
     // Unknown result; must be a programming error.
diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS
index 4f6a89e..97a63ca 100644
--- a/cmds/dumpsys/OWNERS
+++ b/cmds/dumpsys/OWNERS
@@ -1,5 +1,6 @@
 set noparent
 
+gavincorkery@google.com
 nandana@google.com
 jsharkey@android.com
 
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index fc745d0..d6807ff 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -9,3 +9,4 @@
 ngeoffray@google.com
 rpl@google.com
 toddke@google.com
+patb@google.com
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 9881371..059bc41 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -133,6 +133,9 @@
  * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
  * including the updates in a transaction was presented.
  *
+ * Buffers which are replaced or removed from the scene in the transaction invoking
+ * this callback may be reused after this point.
+ *
  * \param context Optional context provided by the client that is passed into
  * the callback.
  *
@@ -153,6 +156,13 @@
  * are ready to be presented. This callback will be invoked before the
  * ASurfaceTransaction_OnComplete callback.
  *
+ * This callback does not mean buffers have been released! It simply means that any new
+ * transactions applied will not overwrite the transaction for which we are receiving
+ * a callback and instead will be included in the next frame. If you are trying to avoid
+ * dropping frames (overwriting transactions), and unable to use timestamps (Which provide
+ * a more efficient solution), then this method provides a method to pace your transaction
+ * application.
+ *
  * \param context Optional context provided by the client that is passed into the callback.
  *
  * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
@@ -358,6 +368,11 @@
  * enum.
  *
  * Available since API level 29.
+ *
+ * @deprecated Use setCrop, setPosition, setBufferTransform, and setScale instead. Those functions
+ * provide well defined behavior and allows for more control by the apps. It also allows the caller
+ * to set different properties at different times, instead of having to specify all the desired
+ * properties at once.
  */
 void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
                                      ASurfaceControl* surface_control, const ARect& source,
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index ef7fd44..18b77e6 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -366,19 +366,45 @@
 
 pid_t IPCThreadState::getCallingPid() const
 {
+    checkContextIsBinderForUse(__func__);
     return mCallingPid;
 }
 
 const char* IPCThreadState::getCallingSid() const
 {
+    checkContextIsBinderForUse(__func__);
     return mCallingSid;
 }
 
 uid_t IPCThreadState::getCallingUid() const
 {
+    checkContextIsBinderForUse(__func__);
     return mCallingUid;
 }
 
+IPCThreadState::SpGuard* IPCThreadState::pushGetCallingSpGuard(SpGuard* guard) {
+    SpGuard* orig = mServingStackPointerGuard;
+    mServingStackPointerGuard = guard;
+    return orig;
+}
+
+void IPCThreadState::restoreGetCallingSpGuard(SpGuard* guard) {
+    mServingStackPointerGuard = guard;
+}
+
+void IPCThreadState::checkContextIsBinderForUse(const char* use) const {
+    if (mServingStackPointerGuard == nullptr) return;
+
+    if (!mServingStackPointer || mServingStackPointerGuard < mServingStackPointer) {
+        LOG_ALWAYS_FATAL("In context %s, %s does not make sense.",
+                         mServingStackPointerGuard->context, use);
+    }
+
+    // in the case mServingStackPointer is deeper in the stack than the guard,
+    // we must be serving a binder transaction (maybe nested). This is a binder
+    // context, so we don't abort
+}
+
 int64_t IPCThreadState::clearCallingIdentity()
 {
     // ignore mCallingSid for legacy reasons
@@ -847,15 +873,15 @@
 }
 
 IPCThreadState::IPCThreadState()
-    : mProcess(ProcessState::self()),
-      mServingStackPointer(nullptr),
-      mWorkSource(kUnsetWorkSource),
-      mPropagateWorkSource(false),
-      mIsLooper(false),
-      mStrictModePolicy(0),
-      mLastTransactionBinderFlags(0),
-      mCallRestriction(mProcess->mCallRestriction)
-{
+      : mProcess(ProcessState::self()),
+        mServingStackPointer(nullptr),
+        mServingStackPointerGuard(nullptr),
+        mWorkSource(kUnsetWorkSource),
+        mPropagateWorkSource(false),
+        mIsLooper(false),
+        mStrictModePolicy(0),
+        mLastTransactionBinderFlags(0),
+        mCallRestriction(mProcess->mCallRestriction) {
     pthread_setspecific(gTLS, this);
     clearCaller();
     mIn.setDataCapacity(256);
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index b503beb..6165911 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -123,16 +123,20 @@
     std::string regStr = (reRegister) ? "Re-registering" : "Registering";
     ALOGI("%s service %s", regStr.c_str(), name.c_str());
 
-    if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
-        ALOGE("Failed to register service %s", name.c_str());
+    if (Status status = manager->addService(name.c_str(), service, allowIsolated, dumpFlags);
+        !status.isOk()) {
+        ALOGE("Failed to register service %s (%s)", name.c_str(), status.toString8().c_str());
         return false;
     }
 
     if (!reRegister) {
-        if (!manager->registerClientCallback(name, service,
-                                             sp<android::os::IClientCallback>::fromExisting(this))
-                     .isOk()) {
-            ALOGE("Failed to add client callback for service %s", name.c_str());
+        if (Status status =
+                    manager->registerClientCallback(name, service,
+                                                    sp<android::os::IClientCallback>::fromExisting(
+                                                            this));
+            !status.isOk()) {
+            ALOGE("Failed to add client callback for service %s (%s)", name.c_str(),
+                  status.toString8().c_str());
             return false;
         }
 
@@ -171,10 +175,10 @@
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     for (auto& [name, entry] : mRegisteredServices) {
-        bool success = manager->tryUnregisterService(name, entry.service).isOk();
+        Status status = manager->tryUnregisterService(name, entry.service);
 
-        if (!success) {
-            ALOGI("Failed to unregister service %s", name.c_str());
+        if (!status.isOk()) {
+            ALOGI("Failed to unregister service %s (%s)", name.c_str(), status.toString8().c_str());
             return false;
         }
         entry.registered = false;
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 786e2db..9cc6e7f 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -22,6 +22,7 @@
 #include <thread>
 #include <vector>
 
+#include <android-base/scopeguard.h>
 #include <binder/Parcel.h>
 #include <binder/RpcServer.h>
 #include <log/log.h>
@@ -32,6 +33,7 @@
 
 namespace android {
 
+using base::ScopeGuard;
 using base::unique_fd;
 
 RpcServer::RpcServer() {}
@@ -49,8 +51,6 @@
     return setupSocketServer(UnixSocketAddress(path));
 }
 
-#ifdef __BIONIC__
-
 bool RpcServer::setupVsockServer(unsigned int port) {
     // realizing value w/ this type at compile time to avoid ubsan abort
     constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
@@ -58,8 +58,6 @@
     return setupSocketServer(VsockSocketAddress(kAnyCid, port));
 }
 
-#endif // __BIONIC__
-
 bool RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) {
     const char* kAddr = "127.0.0.1";
 
@@ -111,66 +109,51 @@
 
 void RpcServer::setRootObject(const sp<IBinder>& binder) {
     std::lock_guard<std::mutex> _l(mLock);
-    mRootObject = binder;
+    mRootObjectWeak = mRootObject = binder;
+}
+
+void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) {
+    std::lock_guard<std::mutex> _l(mLock);
+    mRootObject.clear();
+    mRootObjectWeak = binder;
 }
 
 sp<IBinder> RpcServer::getRootObject() {
     std::lock_guard<std::mutex> _l(mLock);
-    return mRootObject;
+    bool hasWeak = mRootObjectWeak.unsafe_get();
+    sp<IBinder> ret = mRootObjectWeak.promote();
+    ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr");
+    return ret;
 }
 
 void RpcServer::join() {
-    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    while (true) {
+        (void)acceptOne();
+    }
+}
 
-    std::vector<std::thread> pool;
+bool RpcServer::acceptOne() {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    LOG_ALWAYS_FATAL_IF(!hasServer(), "RpcServer must be setup to join.");
+
+    unique_fd clientFd(
+            TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC)));
+
+    if (clientFd < 0) {
+        ALOGE("Could not accept4 socket: %s", strerror(errno));
+        return false;
+    }
+    LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
+
     {
         std::lock_guard<std::mutex> _l(mLock);
-        LOG_ALWAYS_FATAL_IF(mServer.get() == -1, "RpcServer must be setup to join.");
+        std::thread thread =
+                std::thread(&RpcServer::establishConnection, this,
+                            std::move(sp<RpcServer>::fromExisting(this)), std::move(clientFd));
+        mConnectingThreads[thread.get_id()] = std::move(thread);
     }
 
-    while (true) {
-        unique_fd clientFd(
-                TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
-
-        if (clientFd < 0) {
-            ALOGE("Could not accept4 socket: %s", strerror(errno));
-            continue;
-        }
-        LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
-
-        // TODO(b/183988761): cannot trust this simple ID
-        LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
-        int32_t id;
-        if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
-            ALOGE("Could not read ID from fd %d", clientFd.get());
-            continue;
-        }
-
-        {
-            std::lock_guard<std::mutex> _l(mLock);
-
-            sp<RpcSession> session;
-            if (id == RPC_SESSION_ID_NEW) {
-                // new client!
-                LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
-                mSessionIdCounter++;
-
-                session = RpcSession::make();
-                session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
-
-                mSessions[mSessionIdCounter] = session;
-            } else {
-                auto it = mSessions.find(id);
-                if (it == mSessions.end()) {
-                    ALOGE("Cannot add thread, no record of session with ID %d", id);
-                    continue;
-                }
-                session = it->second;
-            }
-
-            session->startThread(std::move(clientFd));
-        }
-    }
+    return true;
 }
 
 std::vector<sp<RpcSession>> RpcServer::listSessions() {
@@ -183,14 +166,74 @@
     return sessions;
 }
 
-bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
-    LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
+size_t RpcServer::numUninitializedSessions() {
+    std::lock_guard<std::mutex> _l(mLock);
+    return mConnectingThreads.size();
+}
 
+void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) {
+    LOG_ALWAYS_FATAL_IF(this != server.get(), "Must pass same ownership object");
+
+    // TODO(b/183988761): cannot trust this simple ID
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    bool idValid = true;
+    int32_t id;
+    if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) {
+        ALOGE("Could not read ID from fd %d", clientFd.get());
+        idValid = false;
+    }
+
+    std::thread thisThread;
+    sp<RpcSession> session;
     {
         std::lock_guard<std::mutex> _l(mLock);
-        LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcServer can only have one server.");
+
+        auto threadId = mConnectingThreads.find(std::this_thread::get_id());
+        LOG_ALWAYS_FATAL_IF(threadId == mConnectingThreads.end(),
+                            "Must establish connection on owned thread");
+        thisThread = std::move(threadId->second);
+        ScopeGuard detachGuard = [&]() { thisThread.detach(); };
+        mConnectingThreads.erase(threadId);
+
+        if (!idValid) {
+            return;
+        }
+
+        if (id == RPC_SESSION_ID_NEW) {
+            LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs");
+            mSessionIdCounter++;
+
+            session = RpcSession::make();
+            session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter);
+
+            mSessions[mSessionIdCounter] = session;
+        } else {
+            auto it = mSessions.find(id);
+            if (it == mSessions.end()) {
+                ALOGE("Cannot add thread, no record of session with ID %d", id);
+                return;
+            }
+            session = it->second;
+        }
+
+        detachGuard.Disable();
+        session->preJoin(std::move(thisThread));
     }
 
+    // avoid strong cycle
+    server = nullptr;
+    //
+    //
+    // DO NOT ACCESS MEMBER VARIABLES BELOW
+    //
+
+    session->join(std::move(clientFd));
+}
+
+bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
+    LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
+    LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
+
     unique_fd serverFd(
             TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
     if (serverFd == -1) {
@@ -216,4 +259,39 @@
     return true;
 }
 
+void RpcServer::onSessionTerminating(const sp<RpcSession>& session) {
+    auto id = session->mId;
+    LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID");
+    LOG_RPC_DETAIL("Dropping session %d", *id);
+
+    std::lock_guard<std::mutex> _l(mLock);
+    auto it = mSessions.find(*id);
+    LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %d", *id);
+    LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %d", *id);
+    (void)mSessions.erase(it);
+}
+
+bool RpcServer::hasServer() {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    std::lock_guard<std::mutex> _l(mLock);
+    return mServer.ok();
+}
+
+unique_fd RpcServer::releaseServer() {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    std::lock_guard<std::mutex> _l(mLock);
+    return std::move(mServer);
+}
+
+bool RpcServer::setupExternalServer(base::unique_fd serverFd) {
+    LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!");
+    std::lock_guard<std::mutex> _l(mLock);
+    if (mServer.ok()) {
+        ALOGE("Each RpcServer can only have one server.");
+        return false;
+    }
+    mServer = std::move(serverFd);
+    return true;
+}
+
 } // namespace android
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 09ec20d..05fa49e 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -24,6 +24,7 @@
 #include <string_view>
 
 #include <binder/Parcel.h>
+#include <binder/RpcServer.h>
 #include <binder/Stability.h>
 #include <utils/String8.h>
 
@@ -48,7 +49,7 @@
     LOG_RPC_DETAIL("RpcSession destroyed %p", this);
 
     std::lock_guard<std::mutex> _l(mMutex);
-    LOG_ALWAYS_FATAL_IF(mServers.size() != 0,
+    LOG_ALWAYS_FATAL_IF(mServerConnections.size() != 0,
                         "Should not be able to destroy a session with servers in use.");
 }
 
@@ -60,14 +61,10 @@
     return setupSocketClient(UnixSocketAddress(path));
 }
 
-#ifdef __BIONIC__
-
 bool RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
     return setupSocketClient(VsockSocketAddress(cid, port));
 }
 
-#endif // __BIONIC__
-
 bool RpcSession::setupInetClient(const char* addr, unsigned int port) {
     auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
     if (aiStart == nullptr) return false;
@@ -87,7 +84,7 @@
         return false;
     }
 
-    addClient(std::move(serverFd));
+    addClientConnection(std::move(serverFd));
     return true;
 }
 
@@ -96,7 +93,7 @@
     return state()->getRootObject(connection.fd(), sp<RpcSession>::fromExisting(this));
 }
 
-status_t RpcSession::getMaxThreads(size_t* maxThreads) {
+status_t RpcSession::getRemoteMaxThreads(size_t* maxThreads) {
     ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT);
     return state()->getMaxThreads(connection.fd(), sp<RpcSession>::fromExisting(this), maxThreads);
 }
@@ -134,19 +131,13 @@
     return OK;
 }
 
-void RpcSession::startThread(unique_fd client) {
-    std::lock_guard<std::mutex> _l(mMutex);
-    sp<RpcSession> holdThis = sp<RpcSession>::fromExisting(this);
-    int fd = client.release();
-    auto thread = std::thread([=] {
-        holdThis->join(unique_fd(fd));
-        {
-            std::lock_guard<std::mutex> _l(holdThis->mMutex);
-            size_t erased = mThreads.erase(std::this_thread::get_id());
-            LOG_ALWAYS_FATAL_IF(erased != 0, "Could not erase thread.");
-        }
-    });
-    mThreads[thread.get_id()] = std::move(thread);
+void RpcSession::preJoin(std::thread thread) {
+    LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread");
+
+    {
+        std::lock_guard<std::mutex> _l(mMutex);
+        mThreads[thread.get_id()] = std::move(thread);
+    }
 }
 
 void RpcSession::join(unique_fd client) {
@@ -166,6 +157,30 @@
 
     LOG_ALWAYS_FATAL_IF(!removeServerConnection(connection),
                         "bad state: connection object guaranteed to be in list");
+
+    {
+        std::lock_guard<std::mutex> _l(mMutex);
+        auto it = mThreads.find(std::this_thread::get_id());
+        LOG_ALWAYS_FATAL_IF(it == mThreads.end());
+        it->second.detach();
+        mThreads.erase(it);
+    }
+}
+
+void RpcSession::terminateLocked() {
+    // TODO(b/185167543):
+    // - kindly notify other side of the connection of termination (can't be
+    // locked)
+    // - prevent new client/servers from being added
+    // - stop all threads which are currently reading/writing
+    // - terminate RpcState?
+
+    if (mTerminated) return;
+
+    sp<RpcServer> server = mForServer.promote();
+    if (server) {
+        server->onSessionTerminating(sp<RpcSession>::fromExisting(this));
+    }
 }
 
 wp<RpcServer> RpcSession::server() {
@@ -175,9 +190,9 @@
 bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
     {
         std::lock_guard<std::mutex> _l(mMutex);
-        LOG_ALWAYS_FATAL_IF(mClients.size() != 0,
+        LOG_ALWAYS_FATAL_IF(mClientConnections.size() != 0,
                             "Must only setup session once, but already has %zu clients",
-                            mClients.size());
+                            mClientConnections.size());
     }
 
     if (!setupOneSocketClient(addr, RPC_SESSION_ID_NEW)) return false;
@@ -186,7 +201,7 @@
     // instead of all at once.
     // TODO(b/186470974): first risk of blocking
     size_t numThreadsAvailable;
-    if (status_t status = getMaxThreads(&numThreadsAvailable); status != OK) {
+    if (status_t status = getRemoteMaxThreads(&numThreadsAvailable); status != OK) {
         ALOGE("Could not get max threads after initial session to %s: %s", addr.toString().c_str(),
               statusToString(status).c_str());
         return false;
@@ -200,49 +215,59 @@
 
     // we've already setup one client
     for (size_t i = 0; i + 1 < numThreadsAvailable; i++) {
-        // TODO(b/185167543): avoid race w/ accept4 not being called on server
-        for (size_t tries = 0; tries < 5; tries++) {
-            if (setupOneSocketClient(addr, mId.value())) break;
-            usleep(10000);
-        }
+        // TODO(b/185167543): shutdown existing connections?
+        if (!setupOneSocketClient(addr, mId.value())) return false;
     }
 
     return true;
 }
 
 bool RpcSession::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) {
-    unique_fd serverFd(
-            TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
-    if (serverFd == -1) {
-        int savedErrno = errno;
-        ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
-        return false;
+    for (size_t tries = 0; tries < 5; tries++) {
+        if (tries > 0) usleep(10000);
+
+        unique_fd serverFd(
+                TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+        if (serverFd == -1) {
+            int savedErrno = errno;
+            ALOGE("Could not create socket at %s: %s", addr.toString().c_str(),
+                  strerror(savedErrno));
+            return false;
+        }
+
+        if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+            if (errno == ECONNRESET) {
+                ALOGW("Connection reset on %s", addr.toString().c_str());
+                continue;
+            }
+            int savedErrno = errno;
+            ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(),
+                  strerror(savedErrno));
+            return false;
+        }
+
+        if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) {
+            int savedErrno = errno;
+            ALOGE("Could not write id to socket at %s: %s", addr.toString().c_str(),
+                  strerror(savedErrno));
+            return false;
+        }
+
+        LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+        addClientConnection(std::move(serverFd));
+        return true;
     }
 
-    if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
-        int savedErrno = errno;
-        ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
-        return false;
-    }
-
-    if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) {
-        int savedErrno = errno;
-        ALOGE("Could not write id to socket at %s: %s", addr.toString().c_str(),
-              strerror(savedErrno));
-        return false;
-    }
-
-    LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
-
-    addClient(std::move(serverFd));
-    return true;
+    ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
+    return false;
 }
 
-void RpcSession::addClient(unique_fd fd) {
+void RpcSession::addClientConnection(unique_fd fd) {
     std::lock_guard<std::mutex> _l(mMutex);
     sp<RpcConnection> session = sp<RpcConnection>::make();
     session->fd = std::move(fd);
-    mClients.push_back(session);
+    mClientConnections.push_back(session);
 }
 
 void RpcSession::setForServer(const wp<RpcServer>& server, int32_t sessionId) {
@@ -255,15 +280,19 @@
     sp<RpcConnection> session = sp<RpcConnection>::make();
     session->fd = std::move(fd);
     session->exclusiveTid = gettid();
-    mServers.push_back(session);
+    mServerConnections.push_back(session);
 
     return session;
 }
 
 bool RpcSession::removeServerConnection(const sp<RpcConnection>& connection) {
     std::lock_guard<std::mutex> _l(mMutex);
-    if (auto it = std::find(mServers.begin(), mServers.end(), connection); it != mServers.end()) {
-        mServers.erase(it);
+    if (auto it = std::find(mServerConnections.begin(), mServerConnections.end(), connection);
+        it != mServerConnections.end()) {
+        mServerConnections.erase(it);
+        if (mServerConnections.size() == 0) {
+            terminateLocked();
+        }
         return true;
     }
     return false;
@@ -283,10 +312,11 @@
         // CHECK FOR DEDICATED CLIENT SOCKET
         //
         // A server/looper should always use a dedicated session if available
-        findConnection(tid, &exclusive, &available, mSession->mClients, mSession->mClientsOffset);
+        findConnection(tid, &exclusive, &available, mSession->mClientConnections,
+                       mSession->mClientConnectionsOffset);
 
         // WARNING: this assumes a server cannot request its client to send
-        // a transaction, as mServers is excluded below.
+        // a transaction, as mServerConnections 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
@@ -296,7 +326,8 @@
         // command. So, we move to considering the second available thread
         // for subsequent calls.
         if (use == ConnectionUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) {
-            mSession->mClientsOffset = (mSession->mClientsOffset + 1) % mSession->mClients.size();
+            mSession->mClientConnectionsOffset =
+                    (mSession->mClientConnectionsOffset + 1) % mSession->mClientConnections.size();
         }
 
         // USE SERVING SOCKET (for nested transaction)
@@ -304,7 +335,7 @@
         // asynchronous calls cannot be nested
         if (use != ConnectionUse::CLIENT_ASYNC) {
             // server connections are always assigned to a thread
-            findConnection(tid, &exclusive, nullptr /*available*/, mSession->mServers,
+            findConnection(tid, &exclusive, nullptr /*available*/, mSession->mServerConnections,
                            0 /* index hint */);
         }
 
@@ -320,13 +351,13 @@
         }
 
         // in regular binder, this would usually be a deadlock :)
-        LOG_ALWAYS_FATAL_IF(mSession->mClients.size() == 0,
+        LOG_ALWAYS_FATAL_IF(mSession->mClientConnections.size() == 0,
                             "Not a client of any session. You must create a session to an "
                             "RPC server to make any non-nested (e.g. oneway or on another thread) "
                             "calls.");
 
         LOG_RPC_DETAIL("No available session (have %zu clients and %zu servers). Waiting...",
-                       mSession->mClients.size(), mSession->mServers.size());
+                       mSession->mClientConnections.size(), mSession->mServerConnections.size());
         mSession->mAvailableConnectionCv.wait(_l);
     }
     mSession->mWaitingThreads--;
diff --git a/libs/binder/RpcSocketAddress.h b/libs/binder/RpcSocketAddress.h
index c6a06cf..c7ba5d9 100644
--- a/libs/binder/RpcSocketAddress.h
+++ b/libs/binder/RpcSocketAddress.h
@@ -24,9 +24,7 @@
 #include <sys/types.h>
 #include <sys/un.h>
 
-#ifdef __BIONIC__
-#include <linux/vm_sockets.h>
-#endif
+#include "vm_sockets.h"
 
 namespace android {
 
@@ -59,8 +57,6 @@
     sockaddr_un mAddr;
 };
 
-#ifdef __BIONIC__
-
 class VsockSocketAddress : public RpcSocketAddress {
 public:
     VsockSocketAddress(unsigned int cid, unsigned int port)
@@ -80,8 +76,6 @@
     sockaddr_vm mAddr;
 };
 
-#endif // __BIONIC__
-
 class InetSocketAddress : public RpcSocketAddress {
 public:
     InetSocketAddress(const sockaddr* sockAddr, size_t size, const char* addr, unsigned int port)
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 96190dc..e5a6026 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -18,7 +18,9 @@
 
 #include "RpcState.h"
 
+#include <android-base/scopeguard.h>
 #include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
 #include <binder/RpcServer.h>
 
 #include "Debug.h"
@@ -28,6 +30,8 @@
 
 namespace android {
 
+using base::ScopeGuard;
+
 RpcState::RpcState() {}
 RpcState::~RpcState() {}
 
@@ -182,6 +186,27 @@
     }
 }
 
+RpcState::CommandData::CommandData(size_t size) : mSize(size) {
+    // The maximum size for regular binder is 1MB for all concurrent
+    // transactions. A very small proportion of transactions are even
+    // larger than a page, but we need to avoid allocating too much
+    // data on behalf of an arbitrary client, or we could risk being in
+    // a position where a single additional allocation could run out of
+    // memory.
+    //
+    // Note, this limit may not reflect the total amount of data allocated for a
+    // transaction (in some cases, additional fixed size amounts are added),
+    // though for rough consistency, we should avoid cases where this data type
+    // is used for multiple dynamic allocations for a single transaction.
+    constexpr size_t kMaxTransactionAllocation = 100 * 1000;
+    if (size == 0) return;
+    if (size > kMaxTransactionAllocation) {
+        ALOGW("Transaction requested too much data allocation %zu", size);
+        return;
+    }
+    mData.reset(new (std::nothrow) uint8_t[size]);
+}
+
 bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) {
     LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str());
 
@@ -326,7 +351,11 @@
             .asyncNumber = asyncNumber,
     };
 
-    std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+    CommandData transactionData(sizeof(RpcWireTransaction) + data.dataSize());
+    if (!transactionData.valid()) {
+        return NO_MEMORY;
+    }
+
     memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction));
     memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize());
 
@@ -379,9 +408,12 @@
         if (status != OK) return status;
     }
 
-    uint8_t* data = new uint8_t[command.bodySize];
+    CommandData data(command.bodySize);
+    if (!data.valid()) {
+        return NO_MEMORY;
+    }
 
-    if (!rpcRec(fd, "reply body", data, command.bodySize)) {
+    if (!rpcRec(fd, "reply body", data.data(), command.bodySize)) {
         return DEAD_OBJECT;
     }
 
@@ -391,9 +423,10 @@
         terminate();
         return BAD_VALUE;
     }
-    RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data);
+    RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data());
     if (rpcReply->status != OK) return rpcReply->status;
 
+    data.release();
     reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
                                nullptr, 0, cleanup_reply_data);
 
@@ -441,6 +474,18 @@
 
 status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session,
                                         const RpcWireHeader& command) {
+    IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
+    IPCThreadState::SpGuard spGuard{"processing binder RPC command"};
+    IPCThreadState::SpGuard* origGuard;
+    if (kernelBinderState != nullptr) {
+        origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard);
+    }
+    ScopeGuard guardUnguard = [&]() {
+        if (kernelBinderState != nullptr) {
+            kernelBinderState->restoreGetCallingSpGuard(origGuard);
+        }
+    };
+
     switch (command.command) {
         case RPC_COMMAND_TRANSACT:
             return processTransact(fd, session, command);
@@ -461,7 +506,10 @@
                                    const RpcWireHeader& command) {
     LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
 
-    std::vector<uint8_t> transactionData(command.bodySize);
+    CommandData transactionData(command.bodySize);
+    if (!transactionData.valid()) {
+        return NO_MEMORY;
+    }
     if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) {
         return DEAD_OBJECT;
     }
@@ -479,7 +527,7 @@
 }
 
 status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session,
-                                           std::vector<uint8_t>&& transactionData) {
+                                           CommandData transactionData) {
     if (transactionData.size() < sizeof(RpcWireTransaction)) {
         ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
               sizeof(RpcWireTransaction), transactionData.size());
@@ -500,7 +548,6 @@
         auto it = mNodeForAddress.find(addr);
         if (it == mNodeForAddress.end()) {
             ALOGE("Unknown binder address %s.", addr.toString().c_str());
-            dump();
             replyStatus = BAD_VALUE;
         } else {
             target = it->second.binder.promote();
@@ -630,7 +677,7 @@
                 // justification for const_cast (consider avoiding priority_queue):
                 // - AsyncTodo operator< doesn't depend on 'data' object
                 // - gotta go fast
-                std::vector<uint8_t> data = std::move(
+                CommandData data = std::move(
                         const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
                 it->second.asyncTodo.pop();
                 _l.unlock();
@@ -644,7 +691,10 @@
             .status = replyStatus,
     };
 
-    std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize());
+    CommandData replyData(sizeof(RpcWireReply) + reply.dataSize());
+    if (!replyData.valid()) {
+        return NO_MEMORY;
+    }
     memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply));
     memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize());
 
@@ -671,7 +721,10 @@
 status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
     LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);
 
-    std::vector<uint8_t> commandData(command.bodySize);
+    CommandData commandData(command.bodySize);
+    if (!commandData.valid()) {
+        return NO_MEMORY;
+    }
     if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) {
         return DEAD_OBJECT;
     }
@@ -690,7 +743,6 @@
     auto it = mNodeForAddress.find(addr);
     if (it == mNodeForAddress.end()) {
         ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str());
-        dump();
         return OK;
     }
 
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 3f3eb1c..31f8a22 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -21,6 +21,7 @@
 #include <binder/RpcSession.h>
 
 #include <map>
+#include <optional>
 #include <queue>
 
 namespace android {
@@ -100,6 +101,20 @@
      */
     void terminate();
 
+    // Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
+    // large allocations to avoid being requested from allocating too much data.
+    struct CommandData {
+        explicit CommandData(size_t size);
+        bool valid() { return mSize == 0 || mData != nullptr; }
+        size_t size() { return mSize; }
+        uint8_t* data() { return mData.get(); }
+        uint8_t* release() { return mData.release(); }
+
+    private:
+        std::unique_ptr<uint8_t[]> mData;
+        size_t mSize;
+    };
+
     [[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data,
                                size_t size);
     [[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
@@ -113,7 +128,7 @@
                                            const RpcWireHeader& command);
     [[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
                                                    const sp<RpcSession>& session,
-                                                   std::vector<uint8_t>&& transactionData);
+                                                   CommandData transactionData);
     [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
                                             const RpcWireHeader& command);
 
@@ -148,7 +163,7 @@
 
         // async transaction queue, _only_ for local binder
         struct AsyncTodo {
-            std::vector<uint8_t> data; // most convenient format, to move it here
+            CommandData data;
             uint64_t asyncNumber = 0;
 
             bool operator<(const AsyncTodo& o) const {
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 23a0cb0..5220b62 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -81,6 +81,32 @@
              */
             uid_t               getCallingUid() const;
 
+            /**
+             * Make it an abort to rely on getCalling* for a section of
+             * execution.
+             *
+             * Usage:
+             *     IPCThreadState::SpGuard guard { "..." };
+             *     auto* orig = pushGetCallingSpGuard(&guard);
+             *     {
+             *         // will abort if you call getCalling*, unless you are
+             *         // serving a nested binder transaction
+             *     }
+             *     restoreCallingSpGuard(orig);
+             */
+            struct SpGuard {
+                const char* context;
+            };
+            SpGuard* pushGetCallingSpGuard(SpGuard* guard);
+            void restoreGetCallingSpGuard(SpGuard* guard);
+            /**
+             * Used internally by getCalling*. Can also be used to assert that
+             * you are in a binder context (getCalling* is valid). This is
+             * intentionally not exposed as a boolean API since code should be
+             * written to know its environment.
+             */
+            void checkContextIsBinderForUse(const char* use) const;
+
             void                setStrictModePolicy(int32_t policy);
             int32_t             getStrictModePolicy() const;
 
@@ -203,6 +229,7 @@
             Parcel              mOut;
             status_t            mLastError;
             const void*         mServingStackPointer;
+            SpGuard* mServingStackPointerGuard;
             pid_t               mCallingPid;
             const char*         mCallingSid;
             uid_t               mCallingUid;
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 9659732..f3ba830 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -50,8 +50,12 @@
                               int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
      /**
       * Force the service to persist, even when it has 0 clients.
-      * If setting this flag from the server side, make sure to do so before calling registerService,
-      * or there may be a race with the default dynamic shutdown.
+      * If setting this flag from the server side, make sure to do so before calling
+      * registerService, or there may be a race with the default dynamic shutdown.
+      *
+      * This should only be used if it is every eventually set to false. If a
+      * service needs to persist but doesn't need to dynamically shut down,
+      * prefer to control it with another mechanism such as ctl.start.
       */
      void forcePersist(bool persist);
 
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index c98151d..8f0c6fd 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -22,6 +22,7 @@
 #include <utils/RefBase.h>
 
 #include <mutex>
+#include <thread>
 
 // WARNING: This is a feature which is still in development, and it is subject
 // to radical change. Any production use of this may subject your code to any
@@ -57,12 +58,10 @@
      */
     [[nodiscard]] bool setupUnixDomainServer(const char* path);
 
-#ifdef __BIONIC__
     /**
      * Creates an RPC server at the current port.
      */
     [[nodiscard]] bool setupVsockServer(unsigned int port);
-#endif // __BIONIC__
 
     /**
      * Creates an RPC server at the current port using IPv4.
@@ -75,6 +74,22 @@
      */
     [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort);
 
+    /**
+     * If setup*Server has been successful, return true. Otherwise return false.
+     */
+    [[nodiscard]] bool hasServer();
+
+    /**
+     * If hasServer(), return the server FD. Otherwise return invalid FD.
+     */
+    [[nodiscard]] base::unique_fd releaseServer();
+
+    /**
+     * Set up server using an external FD previously set up by releaseServer().
+     * Return false if there's already a server.
+     */
+    bool setupExternalServer(base::unique_fd serverFd);
+
     void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
 
     /**
@@ -91,8 +106,14 @@
     /**
      * The root object can be retrieved by any client, without any
      * authentication. TODO(b/183988761)
+     *
+     * Holds a strong reference to the root object.
      */
     void setRootObject(const sp<IBinder>& binder);
+    /**
+     * Holds a weak reference to the root object.
+     */
+    void setRootObjectWeak(const wp<IBinder>& binder);
     sp<IBinder> getRootObject();
 
     /**
@@ -103,16 +124,28 @@
     void join();
 
     /**
+     * Accept one connection on this server. You must have at least one client
+     * session before calling this.
+     */
+    [[nodiscard]] bool acceptOne();
+
+    /**
      * For debugging!
      */
     std::vector<sp<RpcSession>> listSessions();
+    size_t numUninitializedSessions();
 
     ~RpcServer();
 
+    // internal use only
+
+    void onSessionTerminating(const sp<RpcSession>& session);
+
 private:
     friend sp<RpcServer>;
     RpcServer();
 
+    void establishConnection(sp<RpcServer>&& session, base::unique_fd clientFd);
     bool setupSocketServer(const RpcSocketAddress& address);
 
     bool mAgreedExperimental = false;
@@ -121,7 +154,9 @@
     base::unique_fd mServer; // socket we are accepting sessions on
 
     std::mutex mLock; // for below
+    std::map<std::thread::id, std::thread> mConnectingThreads;
     sp<IBinder> mRootObject;
+    wp<IBinder> mRootObjectWeak;
     std::map<int32_t, sp<RpcSession>> mSessions;
     int32_t mSessionIdCounter = 0;
 };
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 3f58b2c..bcc213c 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -52,12 +52,10 @@
      */
     [[nodiscard]] bool setupUnixDomainClient(const char* path);
 
-#ifdef __BIONIC__
     /**
      * Connects to an RPC server at the CVD & port.
      */
     [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port);
-#endif // __BIONIC__
 
     /**
      * Connects to an RPC server at the given address and port.
@@ -83,7 +81,7 @@
      * Query the other side of the session for the maximum number of threads
      * it supports (maximum number of concurrent non-nested synchronous transactions)
      */
-    status_t getMaxThreads(size_t* maxThreads);
+    status_t getRemoteMaxThreads(size_t* maxThreads);
 
     [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data,
                                     Parcel* reply, uint32_t flags);
@@ -116,8 +114,11 @@
 
     status_t readId();
 
-    void startThread(base::unique_fd client);
+    // transfer ownership of thread
+    void preJoin(std::thread thread);
+    // join on thread passed to preJoin
     void join(base::unique_fd client);
+    void terminateLocked();
 
     struct RpcConnection : public RefBase {
         base::unique_fd fd;
@@ -129,7 +130,7 @@
 
     bool setupSocketClient(const RpcSocketAddress& address);
     bool setupOneSocketClient(const RpcSocketAddress& address, int32_t sessionId);
-    void addClient(base::unique_fd fd);
+    void addClientConnection(base::unique_fd fd);
     void setForServer(const wp<RpcServer>& server, int32_t sessionId);
     sp<RpcConnection> assignServerToThisThread(base::unique_fd fd);
     bool removeServerConnection(const sp<RpcConnection>& connection);
@@ -162,13 +163,13 @@
         bool mReentrant = false;
     };
 
-    // On the other side of a session, for each of mClients here, there should
-    // be one of mServers on the other side (and vice versa).
+    // On the other side of a session, for each of mClientConnections here, there should
+    // be one of mServerConnections 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 'mServers' and a thread listening on this
-    //  - the client has a single 'mClients' and makes calls to this
+    //  - the server has a single 'mServerConnections' and a thread listening on this
+    //  - the client has a single 'mClientConnections' 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.
@@ -187,15 +188,17 @@
 
     std::condition_variable mAvailableConnectionCv; // for mWaitingThreads
     size_t mWaitingThreads = 0;
-    size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction
-    std::vector<sp<RpcConnection>> mClients;
-    std::vector<sp<RpcConnection>> mServers;
+    // hint index into clients, ++ when sending an async transaction
+    size_t mClientConnectionsOffset = 0;
+    std::vector<sp<RpcConnection>> mClientConnections;
+    std::vector<sp<RpcConnection>> mServerConnections;
 
     // TODO(b/185167543): use for reverse sessions (allow client to also
     // serve calls on a session).
     // TODO(b/185167543): allow sharing between different sessions in a
-    // process? (or combine with mServers)
+    // process? (or combine with mServerConnections)
     std::map<std::thread::id, std::thread> mThreads;
+    bool mTerminated = false;
 };
 
 } // namespace android
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index a90b4aa..2a66941 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -35,7 +35,7 @@
  * \return EX_NONE on success.
  */
 __attribute__((warn_unused_result)) binder_exception_t AServiceManager_addService(
-        AIBinder* binder, const char* instance);
+        AIBinder* binder, const char* instance) __INTRODUCED_IN(29);
 
 /**
  * Gets a binder object with this specific instance name. Will return nullptr immediately if the
@@ -47,7 +47,8 @@
  *
  * \param instance identifier of the service used to lookup the service.
  */
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance)
+        __INTRODUCED_IN(29);
 
 /**
  * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on
@@ -59,7 +60,8 @@
  *
  * \param instance identifier of the service used to lookup the service.
  */
-__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance);
+__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
+        __INTRODUCED_IN(29);
 
 /**
  * Registers a lazy service with the default service manager under the 'instance' name.
@@ -135,6 +137,10 @@
 /**
  * Prevent lazy services without client from shutting down their process
  *
+ * This should only be used if it is every eventually set to false. If a
+ * service needs to persist but doesn't need to dynamically shut down,
+ * prefer to control it with another mechanism.
+ *
  * \param persist 'true' if the process should not exit.
  */
 void AServiceManager_forceLazyServicesPersist(bool persist) __INTRODUCED_IN(31);
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index c0f7c99..ec231b2 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -63,6 +63,9 @@
         "libbinder",
         "libutils",
     ],
+    static_libs: [
+        "libgmock",
+    ],
     compile_multilib: "32",
     multilib: { lib32: { suffix: "" } },
     cflags: ["-DBINDER_IPC_32BIT=1"],
@@ -101,6 +104,9 @@
         "libbinder",
         "libutils",
     ],
+    static_libs: [
+        "libgmock",
+    ],
     test_suites: ["device-tests", "vts"],
     require_root: true,
 }
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index ef4198d..41daccc 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -55,4 +55,6 @@
     oneway void sleepMsAsync(int ms);
 
     void die(boolean cleanup);
+
+    void useKernelBinderCallingId();
 }
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 5676bd1..45b2776 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <thread>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <binder/Binder.h>
@@ -41,6 +42,13 @@
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
 using namespace android;
+using testing::Not;
+
+// e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message";
+MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) {
+    *result_listener << statusToString(arg);
+    return expected == arg;
+}
 
 static ::testing::AssertionResult IsPageAligned(void *buf) {
     if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
@@ -65,6 +73,7 @@
     BINDER_LIB_TEST_REGISTER_SERVER,
     BINDER_LIB_TEST_ADD_SERVER,
     BINDER_LIB_TEST_ADD_POLL_SERVER,
+    BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION,
     BINDER_LIB_TEST_CALL_BACK,
     BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
     BINDER_LIB_TEST_DELAYED_CALL_BACK,
@@ -205,19 +214,16 @@
     protected:
         sp<IBinder> addServerEtc(int32_t *idPtr, int code)
         {
-            int ret;
             int32_t id;
             Parcel data, reply;
             sp<IBinder> binder;
 
-            ret = m_server->transact(code, data, &reply);
-            EXPECT_EQ(NO_ERROR, ret);
+            EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR));
 
             EXPECT_FALSE(binder != nullptr);
             binder = reply.readStrongBinder();
             EXPECT_TRUE(binder != nullptr);
-            ret = reply.readInt32(&id);
-            EXPECT_EQ(NO_ERROR, ret);
+            EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
             if (idPtr)
                 *idPtr = id;
             return binder;
@@ -401,29 +407,25 @@
 };
 
 TEST_F(BinderLibTest, NopTransaction) {
-    status_t ret;
     Parcel data, reply;
-    ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, NopTransactionOneway) {
-    status_t ret;
     Parcel data, reply;
-    ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY),
+                StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, NopTransactionClear) {
-    status_t ret;
     Parcel data, reply;
     // make sure it accepts the transaction flag
-    ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF),
+                StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, Freeze) {
-    status_t ret;
     Parcel data, reply, replypid;
     std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
 
@@ -442,9 +444,8 @@
         return;
     }
 
-    ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
     int32_t pid = replypid.readInt32();
-    EXPECT_EQ(NO_ERROR, ret);
     for (int i = 0; i < 10; i++) {
         EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
     }
@@ -468,42 +469,36 @@
 TEST_F(BinderLibTest, SetError) {
     int32_t testValue[] = { 0, -123, 123 };
     for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) {
-        status_t ret;
         Parcel data, reply;
         data.writeInt32(testValue[i]);
-        ret = m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply);
-        EXPECT_EQ(testValue[i], ret);
+        EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply),
+                    StatusEq(testValue[i]));
     }
 }
 
 TEST_F(BinderLibTest, GetId) {
-    status_t ret;
     int32_t id;
     Parcel data, reply;
-    ret = m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = reply.readInt32(&id);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
+    EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
     EXPECT_EQ(0, id);
 }
 
 TEST_F(BinderLibTest, PtrSize) {
-    status_t ret;
     int32_t ptrsize;
     Parcel data, reply;
     sp<IBinder> server = addServer();
     ASSERT_TRUE(server != nullptr);
-    ret = server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = reply.readInt32(&ptrsize);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
+    EXPECT_THAT(reply.readInt32(&ptrsize), StatusEq(NO_ERROR));
     RecordProperty("TestPtrSize", sizeof(void *));
     RecordProperty("ServerPtrSize", sizeof(void *));
 }
 
 TEST_F(BinderLibTest, IndirectGetId2)
 {
-    status_t ret;
     int32_t id;
     int32_t count;
     Parcel data, reply;
@@ -521,22 +516,19 @@
         datai.appendTo(&data);
     }
 
-    ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 
-    ret = reply.readInt32(&id);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
     EXPECT_EQ(0, id);
 
-    ret = reply.readInt32(&count);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR));
     EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
 
     for (size_t i = 0; i < (size_t)count; i++) {
         BinderLibTestBundle replyi(&reply);
         EXPECT_TRUE(replyi.isValid());
-        ret = replyi.readInt32(&id);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR));
         EXPECT_EQ(serverId[i], id);
         EXPECT_EQ(replyi.dataSize(), replyi.dataPosition());
     }
@@ -546,7 +538,6 @@
 
 TEST_F(BinderLibTest, IndirectGetId3)
 {
-    status_t ret;
     int32_t id;
     int32_t count;
     Parcel data, reply;
@@ -571,15 +562,13 @@
         datai.appendTo(&data);
     }
 
-    ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 
-    ret = reply.readInt32(&id);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR));
     EXPECT_EQ(0, id);
 
-    ret = reply.readInt32(&count);
-    ASSERT_EQ(NO_ERROR, ret);
+    ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR));
     EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count);
 
     for (size_t i = 0; i < (size_t)count; i++) {
@@ -587,18 +576,15 @@
 
         BinderLibTestBundle replyi(&reply);
         EXPECT_TRUE(replyi.isValid());
-        ret = replyi.readInt32(&id);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR));
         EXPECT_EQ(serverId[i], id);
 
-        ret = replyi.readInt32(&counti);
-        ASSERT_EQ(NO_ERROR, ret);
+        ASSERT_THAT(replyi.readInt32(&counti), StatusEq(NO_ERROR));
         EXPECT_EQ(1, counti);
 
         BinderLibTestBundle replyi2(&replyi);
         EXPECT_TRUE(replyi2.isValid());
-        ret = replyi2.readInt32(&id);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(replyi2.readInt32(&id), StatusEq(NO_ERROR));
         EXPECT_EQ(0, id);
         EXPECT_EQ(replyi2.dataSize(), replyi2.dataPosition());
 
@@ -610,16 +596,31 @@
 
 TEST_F(BinderLibTest, CallBack)
 {
-    status_t ret;
     Parcel data, reply;
     sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
     data.writeStrongBinder(callBack);
-    ret = m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callBack->waitEvent(5);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callBack->getResult();
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY),
+                StatusEq(NO_ERROR));
+    EXPECT_THAT(callBack->waitEvent(5), StatusEq(NO_ERROR));
+    EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
+}
+
+TEST_F(BinderLibTest, NoBinderCallContextGuard) {
+    IPCThreadState::SpGuard spGuard{"NoBinderCallContext"};
+    IPCThreadState::SpGuard *origGuard = IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
+    // yes, this test uses threads, but it's careful and uses fork in addServer
+    EXPECT_DEATH({ IPCThreadState::self()->getCallingPid(); },
+                 "In context NoBinderCallContext, getCallingPid does not make sense.");
+
+    IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
+}
+
+TEST_F(BinderLibTest, BinderCallContextGuard) {
+    sp<IBinder> binder = addServer();
+    Parcel data, reply;
+    EXPECT_THAT(binder->transact(BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION, data, &reply),
+                StatusEq(DEAD_OBJECT));
 }
 
 TEST_F(BinderLibTest, AddServer)
@@ -630,7 +631,6 @@
 
 TEST_F(BinderLibTest, DeathNotificationStrongRef)
 {
-    status_t ret;
     sp<IBinder> sbinder;
 
     sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
@@ -638,20 +638,17 @@
     {
         sp<IBinder> binder = addServer();
         ASSERT_TRUE(binder != nullptr);
-        ret = binder->linkToDeath(testDeathRecipient);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(binder->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR));
         sbinder = binder;
     }
     {
         Parcel data, reply;
-        ret = sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY);
-        EXPECT_EQ(0, ret);
+        EXPECT_THAT(sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY),
+                    StatusEq(OK));
     }
     IPCThreadState::self()->flushCommands();
-    ret = testDeathRecipient->waitEvent(5);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = sbinder->unlinkToDeath(testDeathRecipient);
-    EXPECT_EQ(DEAD_OBJECT, ret);
+    EXPECT_THAT(testDeathRecipient->waitEvent(5), StatusEq(NO_ERROR));
+    EXPECT_THAT(sbinder->unlinkToDeath(testDeathRecipient), StatusEq(DEAD_OBJECT));
 }
 
 TEST_F(BinderLibTest, DeathNotificationMultiple)
@@ -674,8 +671,9 @@
             callBack[i] = new BinderLibTestCallBack();
             data.writeStrongBinder(target);
             data.writeStrongBinder(callBack[i]);
-            ret = linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
-            EXPECT_EQ(NO_ERROR, ret);
+            EXPECT_THAT(linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data,
+                                                  &reply, TF_ONE_WAY),
+                        StatusEq(NO_ERROR));
         }
         {
             Parcel data, reply;
@@ -683,8 +681,9 @@
             passiveclient[i] = addServer();
             ASSERT_TRUE(passiveclient[i] != nullptr);
             data.writeStrongBinder(target);
-            ret = passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply, TF_ONE_WAY);
-            EXPECT_EQ(NO_ERROR, ret);
+            EXPECT_THAT(passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data,
+                                                   &reply, TF_ONE_WAY),
+                        StatusEq(NO_ERROR));
         }
     }
     {
@@ -694,10 +693,8 @@
     }
 
     for (int i = 0; i < clientcount; i++) {
-        ret = callBack[i]->waitEvent(5);
-        EXPECT_EQ(NO_ERROR, ret);
-        ret = callBack[i]->getResult();
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(callBack[i]->waitEvent(5), StatusEq(NO_ERROR));
+        EXPECT_THAT(callBack[i]->getResult(), StatusEq(NO_ERROR));
     }
 }
 
@@ -712,8 +709,7 @@
 
     sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
 
-    ret = target->linkToDeath(testDeathRecipient);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(target->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR));
 
     {
         Parcel data, reply;
@@ -750,14 +746,13 @@
         callback = new BinderLibTestCallBack();
         data.writeStrongBinder(target);
         data.writeStrongBinder(callback);
-        ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply,
+                                     TF_ONE_WAY),
+                    StatusEq(NO_ERROR));
     }
 
-    ret = callback->waitEvent(5);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callback->getResult();
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(callback->waitEvent(5), StatusEq(NO_ERROR));
+    EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, PassFile) {
@@ -773,17 +768,14 @@
         Parcel data, reply;
         uint8_t writebuf[1] = { write_value };
 
-        ret = data.writeFileDescriptor(pipefd[1], true);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(data.writeFileDescriptor(pipefd[1], true), StatusEq(NO_ERROR));
 
-        ret = data.writeInt32(sizeof(writebuf));
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(data.writeInt32(sizeof(writebuf)), StatusEq(NO_ERROR));
 
-        ret = data.write(writebuf, sizeof(writebuf));
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(data.write(writebuf, sizeof(writebuf)), StatusEq(NO_ERROR));
 
-        ret = m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply);
-        EXPECT_EQ(NO_ERROR, ret);
+        EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply),
+                    StatusEq(NO_ERROR));
     }
 
     ret = read(pipefd[0], buf, sizeof(buf));
@@ -864,11 +856,10 @@
 }
 
 TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
-    status_t ret;
     Parcel data, reply;
 
-    ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 
     const flat_binder_object *fb = reply.readObject(false);
     ASSERT_TRUE(fb != nullptr);
@@ -888,8 +879,8 @@
     wp<IBinder> keepFreedBinder;
     {
         Parcel data, reply;
-        ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply);
-        ASSERT_EQ(NO_ERROR, ret);
+        ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply),
+                    StatusEq(NO_ERROR));
         struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data());
         freedHandle = freed->handle;
         /* Add a weak ref to the freed binder so the driver does not
@@ -950,7 +941,6 @@
 }
 
 TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) {
-    status_t ret;
     Parcel data, reply;
     sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
     for (int i = 0; i < 2; i++) {
@@ -964,13 +954,12 @@
 
         datai.appendTo(&data);
     }
-    ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
+                StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, OnewayQueueing)
 {
-    status_t ret;
     Parcel data, data2;
 
     sp<IBinder> pollServer = addPollServer();
@@ -983,25 +972,21 @@
     data2.writeStrongBinder(callBack2);
     data2.writeInt32(0); // delay in us
 
-    ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY),
+                StatusEq(NO_ERROR));
 
     // The delay ensures that this second transaction will end up on the async_todo list
     // (for a single-threaded server)
-    ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY),
+                StatusEq(NO_ERROR));
 
     // The server will ensure that the two transactions are handled in the expected order;
     // If the ordering is not as expected, an error will be returned through the callbacks.
-    ret = callBack->waitEvent(2);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callBack->getResult();
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(callBack->waitEvent(2), StatusEq(NO_ERROR));
+    EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
 
-    ret = callBack2->waitEvent(2);
-    EXPECT_EQ(NO_ERROR, ret);
-    ret = callBack2->getResult();
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(callBack2->waitEvent(2), StatusEq(NO_ERROR));
+    EXPECT_THAT(callBack2->getResult(), StatusEq(NO_ERROR));
 }
 
 TEST_F(BinderLibTest, WorkSourceUnsetByDefault)
@@ -1120,8 +1105,8 @@
     ASSERT_TRUE(server != nullptr);
 
     Parcel data, reply;
-    status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply),
+                StatusEq(NO_ERROR));
 
     int policy = reply.readInt32();
     int priority = reply.readInt32();
@@ -1140,8 +1125,8 @@
     EXPECT_EQ(0, sched_setscheduler(getpid(), SCHED_RR, &param));
 
     Parcel data, reply;
-    status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply),
+                StatusEq(NO_ERROR));
 
     int policy = reply.readInt32();
     int priority = reply.readInt32();
@@ -1158,10 +1143,9 @@
     std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 };
     data.writeUint64Vector(testValue);
 
-    status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply);
-    EXPECT_EQ(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), StatusEq(NO_ERROR));
     std::vector<uint64_t> readValue;
-    ret = reply.readUint64Vector(&readValue);
+    EXPECT_THAT(reply.readUint64Vector(&readValue), StatusEq(OK));
     EXPECT_EQ(readValue, testValue);
 }
 
@@ -1186,19 +1170,18 @@
     memcpy(parcelData, &obj, sizeof(obj));
     data.setDataSize(sizeof(obj));
 
-    status_t ret = server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply);
     // Either the kernel should reject this transaction (if it's correct), but
     // if it's not, the server implementation should return an error if it
     // finds an object in the received Parcel.
-    EXPECT_NE(NO_ERROR, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply),
+                Not(StatusEq(NO_ERROR)));
 }
 
 TEST_F(BinderLibTest, GotSid) {
     sp<IBinder> server = addServer();
 
     Parcel data;
-    status_t ret = server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr);
-    EXPECT_EQ(OK, ret);
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK));
 }
 
 class BinderLibTestService : public BBinder
@@ -1298,6 +1281,18 @@
                 pthread_mutex_unlock(&m_serverWaitMutex);
                 return ret;
             }
+            case BINDER_LIB_TEST_USE_CALLING_GUARD_TRANSACTION: {
+                IPCThreadState::SpGuard spGuard{"GuardInBinderTransaction"};
+                IPCThreadState::SpGuard *origGuard =
+                        IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
+                // if the guard works, this should abort
+                (void)IPCThreadState::self()->getCallingPid();
+
+                IPCThreadState::self()->restoreGetCallingSpGuard(origGuard);
+                return NO_ERROR;
+            }
+
             case BINDER_LIB_TEST_GETPID:
                 reply->writeInt32(getpid());
                 return NO_ERROR;
@@ -1525,6 +1520,11 @@
 {
     binderLibTestServiceName += String16(binderserversuffix);
 
+    // Testing to make sure that calls that we are serving can use getCallin*
+    // even though we don't here.
+    IPCThreadState::SpGuard spGuard{"main server thread"};
+    (void)IPCThreadState::self()->pushGetCallingSpGuard(&spGuard);
+
     status_t ret;
     sp<IServiceManager> sm = defaultServiceManager();
     BinderLibTestService* testServicePtr;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 8d10727..3f94df2 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -23,6 +23,7 @@
 #include <android/binder_libbinder.h>
 #include <binder/Binder.h>
 #include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <binder/RpcServer.h>
@@ -34,14 +35,11 @@
 #include <iostream>
 #include <thread>
 
-#ifdef __BIONIC__
-#include <linux/vm_sockets.h>
-#endif //__BIONIC__
-
 #include <sys/prctl.h>
 #include <unistd.h>
 
-#include "../RpcState.h" // for debugging
+#include "../RpcState.h"   // for debugging
+#include "../vm_sockets.h" // for VMADDR_*
 
 namespace android {
 
@@ -52,6 +50,19 @@
     EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "");
 }
 
+TEST(BinderRpc, SetExternalServer) {
+    base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+    int sinkFd = sink.get();
+    auto server = RpcServer::make();
+    server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    ASSERT_FALSE(server->hasServer());
+    ASSERT_TRUE(server->setupExternalServer(std::move(sink)));
+    ASSERT_TRUE(server->hasServer());
+    base::unique_fd retrieved = server->releaseServer();
+    ASSERT_FALSE(server->hasServer());
+    ASSERT_EQ(sinkFd, retrieved.get());
+}
+
 using android::binder::Status;
 
 #define EXPECT_OK(status)                 \
@@ -181,6 +192,13 @@
             _exit(1);
         }
     }
+    Status useKernelBinderCallingId() override {
+        // this is WRONG! It does not make sense when using RPC binder, and
+        // because it is SO wrong, and so much code calls this, it should abort!
+
+        (void)IPCThreadState::self()->getCallingPid();
+        return Status::ok();
+    }
 };
 sp<IBinder> MyBinderRpcTest::mHeldBinder;
 
@@ -291,19 +309,15 @@
 
 enum class SocketType {
     UNIX,
-#ifdef __BIONIC__
     VSOCK,
-#endif // __BIONIC__
     INET,
 };
 static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
     switch (info.param) {
         case SocketType::UNIX:
             return "unix_domain_socket";
-#ifdef __BIONIC__
         case SocketType::VSOCK:
             return "vm_socket";
-#endif // __BIONIC__
         case SocketType::INET:
             return "inet_socket";
         default:
@@ -334,59 +348,53 @@
                     server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
                     server->setMaxThreads(numThreads);
 
+                    unsigned int outPort = 0;
+
                     switch (socketType) {
                         case SocketType::UNIX:
                             CHECK(server->setupUnixDomainServer(addr.c_str())) << addr;
                             break;
-#ifdef __BIONIC__
                         case SocketType::VSOCK:
                             CHECK(server->setupVsockServer(vsockPort));
                             break;
-#endif // __BIONIC__
                         case SocketType::INET: {
-                            unsigned int outPort = 0;
                             CHECK(server->setupInetServer(0, &outPort));
                             CHECK_NE(0, outPort);
-                            CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort,
-                                                            sizeof(outPort)));
                             break;
                         }
                         default:
                             LOG_ALWAYS_FATAL("Unknown socket type");
                     }
 
+                    CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort, sizeof(outPort)));
+
                     configure(server);
 
                     server->join();
                 }),
         };
 
-        unsigned int inetPort = 0;
+        // always read socket, so that we have waited for the server to start
+        unsigned int outPort = 0;
+        CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &outPort, sizeof(outPort)));
         if (socketType == SocketType::INET) {
-            CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &inetPort,
-                                           sizeof(inetPort)));
-            CHECK_NE(0, inetPort);
+            CHECK_NE(0, outPort);
         }
 
         for (size_t i = 0; i < numSessions; i++) {
             sp<RpcSession> session = RpcSession::make();
-            for (size_t tries = 0; tries < 10; tries++) {
-                usleep(10000);
-                switch (socketType) {
-                    case SocketType::UNIX:
-                        if (session->setupUnixDomainClient(addr.c_str())) goto success;
-                        break;
-#ifdef __BIONIC__
-                    case SocketType::VSOCK:
-                        if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
-                        break;
-#endif // __BIONIC__
-                    case SocketType::INET:
-                        if (session->setupInetClient("127.0.0.1", inetPort)) goto success;
-                        break;
-                    default:
-                        LOG_ALWAYS_FATAL("Unknown socket type");
-                }
+            switch (socketType) {
+                case SocketType::UNIX:
+                    if (session->setupUnixDomainClient(addr.c_str())) goto success;
+                    break;
+                case SocketType::VSOCK:
+                    if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success;
+                    break;
+                case SocketType::INET:
+                    if (session->setupInetClient("127.0.0.1", outPort)) goto success;
+                    break;
+                default:
+                    LOG_ALWAYS_FATAL("Unknown socket type");
             }
             LOG_ALWAYS_FATAL("Could not connect");
         success:
@@ -887,6 +895,19 @@
     }
 }
 
+TEST_P(BinderRpc, UseKernelBinderCallingId) {
+    auto proc = createRpcTestSocketServerProcess(1);
+
+    // we can't allocate IPCThreadState so actually the first time should
+    // succeed :(
+    EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+
+    // second time! we catch the error :)
+    EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
+
+    proc.expectInvalid = true;
+}
+
 TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
     auto proc = createRpcTestSocketServerProcess(1);
 
@@ -934,13 +955,42 @@
 INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
                         ::testing::ValuesIn({
                                 SocketType::UNIX,
+// TODO(b/185269356): working on host
 #ifdef __BIONIC__
                                 SocketType::VSOCK,
-#endif // __BIONIC__
+#endif
                                 SocketType::INET,
                         }),
                         PrintSocketType);
 
+class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool>> {};
+
+TEST_P(BinderRpcServerRootObject, WeakRootObject) {
+    using SetFn = std::function<void(RpcServer*, sp<IBinder>)>;
+    auto setRootObject = [](bool isStrong) -> SetFn {
+        return isStrong ? SetFn(&RpcServer::setRootObject) : SetFn(&RpcServer::setRootObjectWeak);
+    };
+
+    auto server = RpcServer::make();
+    auto [isStrong1, isStrong2] = GetParam();
+    auto binder1 = sp<BBinder>::make();
+    IBinder* binderRaw1 = binder1.get();
+    setRootObject(isStrong1)(server.get(), binder1);
+    EXPECT_EQ(binderRaw1, server->getRootObject());
+    binder1.clear();
+    EXPECT_EQ((isStrong1 ? binderRaw1 : nullptr), server->getRootObject());
+
+    auto binder2 = sp<BBinder>::make();
+    IBinder* binderRaw2 = binder2.get();
+    setRootObject(isStrong2)(server.get(), binder2);
+    EXPECT_EQ(binderRaw2, server->getRootObject());
+    binder2.clear();
+    EXPECT_EQ((isStrong2 ? binderRaw2 : nullptr), server->getRootObject());
+}
+
+INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject,
+                        ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
 } // namespace android
 
 int main(int argc, char** argv) {
diff --git a/libs/binder/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
similarity index 100%
rename from libs/binder/parcel_fuzzer/Android.bp
rename to libs/binder/tests/parcel_fuzzer/Android.bp
diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder.cpp
rename to libs/binder/tests/parcel_fuzzer/binder.cpp
diff --git a/libs/binder/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder.h
rename to libs/binder/tests/parcel_fuzzer/binder.h
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/binder_ndk.cpp
rename to libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
diff --git a/libs/binder/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
similarity index 97%
rename from libs/binder/parcel_fuzzer/binder_ndk.h
rename to libs/binder/tests/parcel_fuzzer/binder_ndk.h
index e69d9c1..cf24ab9 100644
--- a/libs/binder/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -23,7 +23,7 @@
 
 // libbinder_ndk doesn't export this header which breaks down its API for NDK
 // and APEX users, but we need access to it to fuzz.
-#include "../ndk/parcel_internal.h"
+#include "../../ndk/parcel_internal.h"
 
 class NdkParcelAdapter {
 public:
diff --git a/libs/binder/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/hwbinder.cpp
rename to libs/binder/tests/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/parcel_fuzzer/hwbinder.h b/libs/binder/tests/parcel_fuzzer/hwbinder.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/hwbinder.h
rename to libs/binder/tests/parcel_fuzzer/hwbinder.h
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
rename to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
rename to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/main.cpp
rename to libs/binder/tests/parcel_fuzzer/main.cpp
diff --git a/libs/binder/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/parcel_fuzzer.h
rename to libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/random_fd.cpp
rename to libs/binder/tests/parcel_fuzzer/random_fd.cpp
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/random_parcel.cpp
rename to libs/binder/tests/parcel_fuzzer/random_parcel.cpp
diff --git a/libs/binder/parcel_fuzzer/util.cpp b/libs/binder/tests/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/parcel_fuzzer/util.cpp
rename to libs/binder/tests/parcel_fuzzer/util.cpp
diff --git a/libs/binder/parcel_fuzzer/util.h b/libs/binder/tests/parcel_fuzzer/util.h
similarity index 100%
rename from libs/binder/parcel_fuzzer/util.h
rename to libs/binder/tests/parcel_fuzzer/util.h
diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp
new file mode 100644
index 0000000..1c75306
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/Android.bp
@@ -0,0 +1,40 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+    name: "binder_rpc_fuzzer",
+    host_supported: true,
+
+    fuzz_config: {
+        cc: ["smoreland@google.com"],
+    },
+
+    srcs: [
+        "main.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+
+    target: {
+        android: {
+            shared_libs: [
+                "libbinder",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libbinder",
+            ],
+        },
+    },
+}
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
new file mode 100644
index 0000000..3603ebe
--- /dev/null
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+
+#include <sys/resource.h>
+#include <sys/un.h>
+
+namespace android {
+
+static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") +
+        "/binderRpcFuzzerSocket_" + std::to_string(getpid());
+
+size_t getHardMemoryLimit() {
+    struct rlimit limit;
+    CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno;
+    return limit.rlim_max;
+}
+
+void setMemoryLimit(size_t cur, size_t max) {
+    const struct rlimit kLimit = {
+            .rlim_cur = cur,
+            .rlim_max = max,
+    };
+    CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno;
+}
+
+class SomeBinder : public BBinder {
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {
+        (void)flags;
+
+        if ((code & 1) == 0) {
+            sp<IBinder> binder;
+            (void)data.readStrongBinder(&binder);
+            if (binder != nullptr) {
+                (void)binder->pingBinder();
+            }
+        }
+        if ((code & 2) == 0) {
+            (void)data.readInt32();
+        }
+        if ((code & 4) == 0) {
+            (void)reply->writeStrongBinder(sp<BBinder>::make());
+        }
+
+        return OK;
+    }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    if (size > 50000) return 0;
+
+    unlink(kSock.c_str());
+
+    sp<RpcServer> server = RpcServer::make();
+    server->setRootObject(sp<SomeBinder>::make());
+    server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+    CHECK(server->setupUnixDomainServer(kSock.c_str()));
+
+    static constexpr size_t kMemLimit = 1llu * 1024 * 1024 * 1024;
+    size_t hardLimit = getHardMemoryLimit();
+    setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit);
+
+    std::thread serverThread([=] { (void)server->acceptOne(); });
+
+    sockaddr_un addr{
+            .sun_family = AF_UNIX,
+    };
+    CHECK_LT(kSock.size(), sizeof(addr.sun_path));
+    memcpy(&addr.sun_path, kSock.c_str(), kSock.size());
+
+    base::unique_fd clientFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+    CHECK_NE(clientFd.get(), -1);
+    CHECK_EQ(0,
+             TEMP_FAILURE_RETRY(
+                     connect(clientFd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))))
+            << strerror(errno);
+
+    serverThread.join();
+
+    // TODO(b/182938024): fuzz multiple sessions, instead of just one
+
+#if 0
+    // make fuzzer more productive locally by forcing it to create a new session
+    int32_t id = -1;
+    CHECK(base::WriteFully(clientFd, &id, sizeof(id)));
+#endif
+
+    CHECK(base::WriteFully(clientFd, data, size));
+
+    clientFd.reset();
+
+    // TODO(b/185167543): better way to force a server to shutdown
+    while (!server->listSessions().empty() && server->numUninitializedSessions()) {
+        usleep(1);
+    }
+
+    setMemoryLimit(hardLimit, hardLimit);
+
+    return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
similarity index 100%
rename from libs/binder/tests/fuzzers/Android.bp
rename to libs/binder/tests/unit_fuzzers/Android.bp
diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BinderFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BinderFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/BinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BpBinderFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/StabilityFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/StatusFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/StatusFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
similarity index 100%
rename from libs/binder/tests/fuzzers/StatusFuzzFunctions.h
rename to libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
similarity index 100%
rename from libs/binder/tests/fuzzers/TextOutputFuzz.cpp
rename to libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
similarity index 100%
rename from libs/binder/tests/fuzzers/commonFuzzHelpers.h
rename to libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h
diff --git a/libs/binder/vm_sockets.h b/libs/binder/vm_sockets.h
new file mode 100644
index 0000000..7d9732b
--- /dev/null
+++ b/libs/binder/vm_sockets.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#pragma once
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#else
+
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
+#define SO_VM_SOCKETS_BUFFER_SIZE 0
+#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
+#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
+#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
+#define SO_VM_SOCKETS_TRUSTED 5
+#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
+#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
+#define VMADDR_CID_ANY (-1U)
+#define VMADDR_PORT_ANY (-1U)
+#define VMADDR_CID_HYPERVISOR 0
+#define VMADDR_CID_LOCAL 1
+#define VMADDR_CID_HOST 2
+#define VM_SOCKETS_INVALID_VERSION (-1U)
+#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24)
+#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16)
+#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF))
+struct sockaddr_vm {
+    sa_family_t svm_family;
+    // NOLINTNEXTLINE(google-runtime-int)
+    unsigned short svm_reserved1;
+    unsigned int svm_port;
+    unsigned int svm_cid;
+    // NOLINTNEXTLINE(google-runtime-int)
+    unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) -
+                           sizeof(unsigned int) - sizeof(unsigned int)];
+};
+#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+#endif
+
+#endif
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 37fb844..08800f7 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -213,7 +213,8 @@
             // for this is the scale is calculated based on the requested size and buffer size.
             // If there's no buffer, the scale will always be 1.
             if (mLastBufferInfo.hasBuffer) {
-                setMatrix(&t, mLastBufferInfo);
+                t.setDestinationFrame(mSurfaceControl,
+                                      Rect(0, 0, newSize.getWidth(), newSize.getHeight()));
             }
             applyTransaction = true;
         }
@@ -416,8 +417,8 @@
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
     mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
 
-    setMatrix(t, mLastBufferInfo);
-    t->setCrop(mSurfaceControl, crop);
+    t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight()));
+    t->setBufferCrop(mSurfaceControl, crop);
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
     if (!bufferItem.mIsAutoTimestamp) {
@@ -543,19 +544,6 @@
     return mSize != bufferSize;
 }
 
-void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t,
-                                 const BufferInfo& bufferInfo) {
-    uint32_t bufWidth = bufferInfo.crop.getWidth();
-    uint32_t bufHeight = bufferInfo.crop.getHeight();
-
-    float sx = mSize.width / static_cast<float>(bufWidth);
-    float sy = mSize.height / static_cast<float>(bufHeight);
-
-    t->setMatrix(mSurfaceControl, sx, 0, 0, sy);
-    // Update position based on crop.
-    t->setPosition(mSurfaceControl, bufferInfo.crop.left * sx * -1, bufferInfo.crop.top * sy * -1);
-}
-
 void BLASTBufferQueue::setTransactionCompleteCallback(
         uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
     std::lock_guard _lock{mMutex};
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 267db76..e65c721 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -64,6 +64,8 @@
         fixedTransformHint(ui::Transform::ROT_INVALID),
         frameNumber(0),
         autoRefresh(false),
+        bufferCrop(Rect::INVALID_RECT),
+        destinationFrame(Rect::INVALID_RECT),
         releaseBufferListener(nullptr) {
     matrix.dsdx = matrix.dtdy = 1.0f;
     matrix.dsdy = matrix.dtdx = 0.0f;
@@ -167,6 +169,7 @@
 
     SAFE_PARCEL(output.write, stretchEffect);
     SAFE_PARCEL(output.write, bufferCrop);
+    SAFE_PARCEL(output.write, destinationFrame);
 
     return NO_ERROR;
 }
@@ -296,6 +299,7 @@
 
     SAFE_PARCEL(input.read, stretchEffect);
     SAFE_PARCEL(input.read, bufferCrop);
+    SAFE_PARCEL(input.read, destinationFrame);
 
     return NO_ERROR;
 }
@@ -543,6 +547,10 @@
         what |= eBufferCropChanged;
         bufferCrop = other.bufferCrop;
     }
+    if (other.what & eDestinationFrameChanged) {
+        what |= eDestinationFrameChanged;
+        destinationFrame = other.destinationFrame;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 11b8eba..aa93808 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1675,6 +1675,21 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDestinationFrame(
+        const sp<SurfaceControl>& sc, const Rect& destinationFrame) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eDestinationFrameChanged;
+    s->destinationFrame = destinationFrame;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 3947f22..16430b3 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -104,7 +104,7 @@
         eHasListenerCallbacksChanged = 0x20000000,
         eInputInfoChanged = 0x40000000,
         eCornerRadiusChanged = 0x80000000,
-        /* was eFrameChanged, now available 0x1'00000000, */
+        eDestinationFrameChanged = 0x1'00000000,
         eCachedBufferChanged = 0x2'00000000,
         eBackgroundColorChanged = 0x4'00000000,
         eMetadataChanged = 0x8'00000000,
@@ -228,6 +228,7 @@
     StretchEffect stretchEffect;
 
     Rect bufferCrop;
+    Rect destinationFrame;
 
     // Listens to when the buffer is safe to be released. This is used for blast
     // layers only. The callback includes a release fence as well as the graphic
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 35757be..0940e9d 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -554,6 +554,8 @@
                                       const StretchEffect& stretchEffect);
 
         Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop);
+        Transaction& setDestinationFrame(const sp<SurfaceControl>& sc,
+                                         const Rect& destinationFrame);
 
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 9400037..3c58238 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1233,8 +1233,10 @@
             }
         }
 
-        mState.maxMasteringLuminance = layer->source.buffer.maxMasteringLuminance;
-        mState.maxContentLuminance = layer->source.buffer.maxContentLuminance;
+        // Ensure luminance is at least 100 nits to avoid div-by-zero
+        const float maxLuminance = std::max(100.f, layer->source.buffer.maxLuminanceNits);
+        mState.maxMasteringLuminance = maxLuminance;
+        mState.maxContentLuminance = maxLuminance;
         mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform;
 
         const FloatRect bounds = layer->geometry.boundaries;
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index a637796..53fa622 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -60,6 +60,9 @@
     // capture of a device in landscape while the buffer is in portrait
     // orientation.
     uint32_t orientation = ui::Transform::ROT_0;
+
+    // SDR white point, -1f if unknown
+    float sdrWhitePointNits = -1.f;
 };
 
 static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 6e98352..bfb7465 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -64,8 +64,8 @@
 
     // HDR color-space setting for Y410.
     bool isY410BT2020 = false;
-    float maxMasteringLuminance = 0.0;
-    float maxContentLuminance = 0.0;
+
+    float maxLuminanceNits = 0.0;
 };
 
 // Metadata describing the layer geometry.
@@ -175,8 +175,7 @@
             lhs.textureTransform == rhs.textureTransform &&
             lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
             lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
-            lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
-            lhs.maxContentLuminance == rhs.maxContentLuminance;
+            lhs.maxLuminanceNits == rhs.maxLuminanceNits;
 }
 
 static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
@@ -239,8 +238,7 @@
     *os << "\n    .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
     *os << "\n    .isOpaque = " << settings.isOpaque;
     *os << "\n    .isY410BT2020 = " << settings.isY410BT2020;
-    *os << "\n    .maxMasteringLuminance = " << settings.maxMasteringLuminance;
-    *os << "\n    .maxContentLuminance = " << settings.maxContentLuminance;
+    *os << "\n    .maxLuminanceNits = " << settings.maxLuminanceNits;
     *os << "\n}";
 }
 
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 48d76cc..0eee564 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -108,8 +108,7 @@
             .source = PixelSource{.buffer =
                                           Buffer{
                                                   .buffer = srcTexture,
-                                                  .maxMasteringLuminance = 1000.f,
-                                                  .maxContentLuminance = 1000.f,
+                                                  .maxLuminanceNits = 1000.f,
                                           }},
     };
 
@@ -205,8 +204,7 @@
             .source = PixelSource{.buffer =
                                           Buffer{
                                                   .buffer = srcTexture,
-                                                  .maxMasteringLuminance = 1000.f,
-                                                  .maxContentLuminance = 1000.f,
+                                                  .maxLuminanceNits = 1000.f,
                                                   .textureTransform = kScaleYOnly,
                                           }},
             .sourceDataspace = kOtherDataSpace,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index c5ad64e..05bbc12 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -560,10 +560,14 @@
         const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
         bool requiresLinearEffect) {
     const auto stretchEffect = layer->stretchEffect;
+    // The given surface will be stretched by HWUI via matrix transformation
+    // which gets similar results for most surfaces
+    // Determine later on if we need to leverage the stertch shader within
+    // surface flinger
     if (stretchEffect.hasEffect()) {
         const auto targetBuffer = layer->source.buffer.buffer;
-        const auto graphicsBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
-        if (graphicsBuffer && shader) {
+        const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
+        if (graphicBuffer && shader) {
             shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
         }
     }
@@ -586,10 +590,14 @@
         } else {
             runtimeEffect = effectIter->second;
         }
+        float maxLuminance = layer->source.buffer.maxLuminanceNits;
+        // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
+        // white point
+        if (maxLuminance <= 0.f) {
+            maxLuminance = display.sdrWhitePointNits;
+        }
         return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
-                                        display.maxLuminance,
-                                        layer->source.buffer.maxMasteringLuminance,
-                                        layer->source.buffer.maxContentLuminance);
+                                        display.maxLuminance, maxLuminance);
     }
     return shader;
 }
@@ -653,33 +661,6 @@
     int mSaveCount;
 };
 
-void drawStretch(const SkRect& bounds, const StretchEffect& stretchEffect,
-                 SkCanvas* canvas, const SkPaint& paint) {
-    float top = bounds.top();
-    float left = bounds.left();
-    float bottom = bounds.bottom();
-    float right = bounds.right();
-    // Adjust the drawing bounds based on the stretch itself.
-    float stretchOffsetX =
-        round(bounds.width() * stretchEffect.getStretchWidthMultiplier());
-    float stretchOffsetY =
-        round(bounds.height() * stretchEffect.getStretchHeightMultiplier());
-    if (stretchEffect.vectorY < 0.f) {
-        top -= stretchOffsetY;
-    } else if (stretchEffect.vectorY > 0.f){
-        bottom += stretchOffsetY;
-    }
-
-    if (stretchEffect.vectorX < 0.f) {
-        left -= stretchOffsetX;
-    } else if (stretchEffect.vectorX > 0.f) {
-        right += stretchOffsetX;
-    }
-
-    auto stretchBounds = SkRect::MakeLTRB(left, top, right, bottom);
-    canvas->drawRect(stretchBounds, paint);
-}
-
 static SkRRect getBlurRRect(const BlurRegion& region) {
     const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
     const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
@@ -919,18 +900,17 @@
         // TODO(b/175915334): consider relaxing this restriction to enable more flexible
         // composition - using a well-defined invalid color is long-term less error-prone.
         if (layer->shadow.length > 0) {
-            const auto rect = layer->geometry.roundedCornersRadius > 0
-                    ? getSkRect(layer->geometry.roundedCornersCrop)
-                    : bounds.rect();
             // This would require a new parameter/flag to SkShadowUtils::DrawShadow
             LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
-            drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+            drawShadow(canvas, bounds, layer->shadow);
             continue;
         }
 
         const bool requiresLinearEffect = layer->colorTransform != mat4() ||
                 (mUseColorManagement &&
-                 needsToneMapping(layer->sourceDataspace, display.outputDataspace));
+                 needsToneMapping(layer->sourceDataspace, display.outputDataspace)) ||
+                (display.sdrWhitePointNits > 0.f &&
+                 display.sdrWhitePointNits != display.maxLuminance);
 
         // quick abort from drawing the remaining portion of the layer
         if (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
@@ -1056,17 +1036,7 @@
             paint.setAntiAlias(true);
             canvas->drawRRect(bounds, paint);
         } else {
-            auto& stretchEffect = layer->stretchEffect;
-            // TODO (njawad) temporarily disable manipulation of geometry
-            //  the layer bounds will be updated in HWUI instead of RenderEngine
-            //  in a subsequent CL
-            // Keep the method call in a dead code path to make -Werror happy
-            // with unused methods
-            if (stretchEffect.hasEffect() && /* DISABLES CODE */ (false)) {
-                drawStretch(bounds.rect(), stretchEffect, canvas, paint);
-            } else {
-                canvas->drawRect(bounds.rect(), paint);
-            }
+            canvas->drawRect(bounds.rect(), paint);
         }
         if (kFlushAfterEveryLayer) {
             ATRACE_NAME("flush surface");
@@ -1213,17 +1183,14 @@
     return mGrContext->maxRenderTargetSize();
 }
 
-void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius,
+void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                                     const ShadowSettings& settings) {
     ATRACE_CALL();
     const float casterZ = settings.length / 2.0f;
-    const auto shadowShape = cornerRadius > 0
-            ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius))
-            : SkPath::Rect(casterRect);
     const auto flags =
             settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
 
-    SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ),
+    SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
                               getSkPoint3(settings.lightPos), settings.lightRadius,
                               getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
                               flags);
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index d9caf35..1f528b7 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -98,7 +98,7 @@
     base::unique_fd flush();
     bool waitFence(base::unique_fd fenceFd);
     void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
-    void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
+    void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
                     const ShadowSettings& shadowSettings);
     // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
     // Otherwise it returns the input shader.
@@ -120,14 +120,15 @@
 
     // Identifier used or various mappings of layers to various
     // textures or shaders
-    using LayerId = uint64_t;
+    using GraphicBufferId = uint64_t;
 
     // Number of external holders of ExternalTexture references, per GraphicBuffer ID.
-    std::unordered_map<LayerId, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex);
-    // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
-    std::unordered_map<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+    std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
             GUARDED_BY(mRenderingMutex);
-    std::unordered_map<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>>
+    // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context.
+    std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+            GUARDED_BY(mRenderingMutex);
+    std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>>
             mProtectedTextureCache GUARDED_BY(mRenderingMutex);
     std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
 
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 0fbd669..9b044e1 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -127,7 +127,7 @@
         default:
             shader.append(R"(
                     float3 ScaleLuminance(float3 xyz) {
-                        return xyz * in_displayMaxLuminance;
+                        return xyz * in_inputMaxLuminance;
                     }
                 )");
             break;
@@ -448,7 +448,7 @@
 sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
-                                         float maxMasteringLuminance, float maxContentLuminance) {
+                                         float maxLuminance) {
     ATRACE_CALL();
     SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
 
@@ -467,8 +467,10 @@
     }
 
     effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
+    // If the input luminance is unknown, use display luminance (aka, no-op any luminance changes)
+    // This will be the case for eg screenshots in addition to uncalibrated displays
     effectBuilder.uniform("in_inputMaxLuminance") =
-            std::min(maxMasteringLuminance, maxContentLuminance);
+            maxLuminance > 0 ? maxLuminance : maxDisplayLuminance;
     return effectBuilder.makeShader(nullptr, false);
 }
 
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 20b8338..9e989ea 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -98,7 +98,7 @@
                                          const LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
                                          const mat4& colorTransform, float maxDisplayLuminance,
-                                         float maxMasteringLuminance, float maxContentLuminance);
+                                         float maxLuminance);
 } // namespace skia
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
index 9b62789..4ac5c40 100644
--- a/libs/renderengine/skia/filters/StretchShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -69,9 +69,8 @@
     // and the other way around.
     uniform float uInterpolationStrength;
 
-    float easeInCubic(float t, float d) {
-        float tmp = t * d;
-        return tmp * tmp * tmp;
+    float easeIn(float t, float d) {
+        return t * d;
     }
 
     float computeOverscrollStart(
@@ -84,7 +83,7 @@
     ) {
         float offsetPos = uStretchAffectedDist - inPos;
         float posBasedVariation = mix(
-                1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+                1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
         float stretchIntensity = overscroll * posBasedVariation;
         return distanceStretched - (offsetPos / (1. + stretchIntensity));
     }
@@ -100,7 +99,7 @@
     ) {
         float offsetPos = inPos - reverseStretchDist;
         float posBasedVariation = mix(
-                1. ,easeInCubic(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
+                1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
         float stretchIntensity = (-overscroll) * posBasedVariation;
         return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
     }
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index c87a836..9009ce4 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -101,6 +101,7 @@
 }
 
 void RenderEngineThreaded::primeCache() {
+    ATRACE_CALL();
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
@@ -131,6 +132,7 @@
 }
 
 void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+    ATRACE_CALL();
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
     {
@@ -146,6 +148,7 @@
 }
 
 void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+    ATRACE_CALL();
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
     {
@@ -162,6 +165,7 @@
 
 void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
                                                     bool isRenderable) {
+    ATRACE_CALL();
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
@@ -175,6 +179,7 @@
 }
 
 void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+    ATRACE_CALL();
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
@@ -247,6 +252,7 @@
                                           const bool useFramebufferCache,
                                           base::unique_fd&& bufferFence,
                                           base::unique_fd* drawFence) {
+    ATRACE_CALL();
     std::promise<status_t> resultPromise;
     std::future<status_t> resultFuture = resultPromise.get_future();
     {
@@ -264,6 +270,7 @@
 }
 
 void RenderEngineThreaded::cleanFramebufferCache() {
+    ATRACE_CALL();
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 240738d..0a49008 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -231,6 +231,10 @@
             mFlags |= SENSOR_FLAG_WAKE_UP;
         }
         break;
+    case SENSOR_TYPE_DEVICE_ORIENTATION:
+        mStringType = SENSOR_STRING_TYPE_DEVICE_ORIENTATION;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
     case SENSOR_TYPE_DYNAMIC_SENSOR_META:
         mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
         mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 446c913..8bc877f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4747,6 +4747,40 @@
     return true;
 }
 
+// Binder call
+bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) {
+    sp<IBinder> fromToken;
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+        if (toWindowHandle == nullptr) {
+            ALOGW("Could not find window associated with token=%p", destChannelToken.get());
+            return false;
+        }
+
+        const int32_t displayId = toWindowHandle->getInfo()->displayId;
+
+        auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+        if (touchStateIt == mTouchStatesByDisplay.end()) {
+            ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
+                  displayId);
+            return false;
+        }
+
+        TouchState& state = touchStateIt->second;
+        if (state.windows.size() != 1) {
+            ALOGW("Cannot transfer touch state because there are %zu windows being touched",
+                  state.windows.size());
+            return false;
+        }
+        const TouchedWindow& touchedWindow = state.windows[0];
+        fromToken = touchedWindow.windowHandle->getToken();
+    } // release lock
+
+    return transferTouchFocus(fromToken, destChannelToken);
+}
+
 void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
     if (DEBUG_FOCUS) {
         ALOGD("Resetting and dropping all events (%s).", reason);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 7ba03e8..6edc5f1 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,60 +81,60 @@
  */
 class InputDispatcher : public android::InputDispatcherInterface {
 protected:
-    virtual ~InputDispatcher();
+    ~InputDispatcher() override;
 
 public:
     explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
 
-    virtual void dump(std::string& dump) override;
-    virtual void monitor() override;
-    virtual bool waitForIdle() override;
-    virtual status_t start() override;
-    virtual status_t stop() override;
+    void dump(std::string& dump) override;
+    void monitor() override;
+    bool waitForIdle() override;
+    status_t start() override;
+    status_t stop() override;
 
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
-    virtual void notifyKey(const NotifyKeyArgs* args) override;
-    virtual void notifyMotion(const NotifyMotionArgs* args) override;
-    virtual void notifySwitch(const NotifySwitchArgs* args) override;
-    virtual void notifySensor(const NotifySensorArgs* args) override;
-    virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
-    virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
+    void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
+    void notifyKey(const NotifyKeyArgs* args) override;
+    void notifyMotion(const NotifyMotionArgs* args) override;
+    void notifySwitch(const NotifySwitchArgs* args) override;
+    void notifySensor(const NotifySensorArgs* args) override;
+    void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
+    void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+    void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
 
-    virtual android::os::InputEventInjectionResult injectInputEvent(
+    android::os::InputEventInjectionResult injectInputEvent(
             const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
             android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
             uint32_t policyFlags) override;
 
-    virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
+    std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
 
-    virtual void setInputWindows(
-            const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
-                    handlesPerDisplay) override;
-    virtual void setFocusedApplication(
+    void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+                                 handlesPerDisplay) override;
+    void setFocusedApplication(
             int32_t displayId,
             const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
-    virtual void setFocusedDisplay(int32_t displayId) override;
-    virtual void setInputDispatchMode(bool enabled, bool frozen) override;
-    virtual void setInputFilterEnabled(bool enabled) override;
-    virtual void setInTouchMode(bool inTouchMode) override;
-    virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
-    virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
+    void setFocusedDisplay(int32_t displayId) override;
+    void setInputDispatchMode(bool enabled, bool frozen) override;
+    void setInputFilterEnabled(bool enabled) override;
+    void setInTouchMode(bool inTouchMode) override;
+    void setMaximumObscuringOpacityForTouch(float opacity) override;
+    void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
 
-    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
-                                    bool isDragDrop = false) override;
+    bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+                            bool isDragDrop = false) override;
+    bool transferTouch(const sp<IBinder>& destChannelToken) override;
 
-    virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+    base::Result<std::unique_ptr<InputChannel>> createInputChannel(
             const std::string& name) override;
-    virtual void setFocusedWindow(const FocusRequest&) override;
-    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
-                                                                           bool isGestureMonitor,
-                                                                           const std::string& name,
-                                                                           int32_t pid) override;
-    virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
-    virtual status_t pilferPointers(const sp<IBinder>& token) override;
-    virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
-    virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
+    void setFocusedWindow(const FocusRequest&) override;
+    base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+                                                                   bool isGestureMonitor,
+                                                                   const std::string& name,
+                                                                   int32_t pid) override;
+    status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
+    status_t pilferPointers(const sp<IBinder>& token) override;
+    void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
+    bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
 
     std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
 
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index b601dfc..7f85e53 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -151,6 +151,14 @@
      */
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                                     bool isDragDrop) = 0;
+
+    /**
+     * Transfer touch focus to the provided channel, no matter where the current touch is.
+     *
+     * Return true on success, false if there was no on-going touch.
+     */
+    virtual bool transferTouch(const sp<IBinder>& destChannelToken) = 0;
+
     /**
      * Sets focus on the specified window.
      */
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 2d0fdf7..e91f84e 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -413,8 +413,7 @@
     }
 }
 
-void InputReader::dispatchExternalStylusState(const StylusState& state) {
-    std::scoped_lock _l(mLock);
+void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
     for (auto& devicePair : mDevices) {
         std::shared_ptr<InputDevice>& device = devicePair.second;
         device->updateExternalStylusState(state);
@@ -945,7 +944,7 @@
 }
 
 void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
-    mReader->dispatchExternalStylusState(state);
+    mReader->dispatchExternalStylusStateLocked(state);
 }
 
 InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 1405671..bc79ccf 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -203,7 +203,7 @@
 
     void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock);
     void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock);
-    void dispatchExternalStylusState(const StylusState& state);
+    void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock);
 
     // The PointerController that is shared among all the input devices that need it.
     std::weak_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index bbf51f6..855453e 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1714,7 +1714,13 @@
                          0 /*expectedFlags*/);
 }
 
-TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
+using TransferFunction =
+        std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>;
+
+class TransferTouchFixture : public InputDispatcherTest,
+                             public ::testing::WithParamInterface<TransferFunction> {};
+
+TEST_P(TransferTouchFixture, TransferTouch_OnePointer) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     // Create a couple of windows
@@ -1735,8 +1741,10 @@
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
 
-    // Transfer touch focus to the second window
-    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    // Transfer touch to the second window
+    TransferFunction f = GetParam();
+    const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+    ASSERT_TRUE(success);
     // The first window gets cancel and the second gets down
     firstWindow->consumeMotionCancel();
     secondWindow->consumeMotionDown();
@@ -1751,7 +1759,7 @@
     secondWindow->consumeMotionUp();
 }
 
-TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
+TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     PointF touchPoint = {10, 10};
@@ -1786,7 +1794,9 @@
     secondWindow->assertNoEvents();
 
     // Transfer touch focus to the second window
-    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    TransferFunction f = GetParam();
+    bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
+    ASSERT_TRUE(success);
     // The first window gets cancel and the second gets down and pointer down
     firstWindow->consumeMotionCancel();
     secondWindow->consumeMotionDown();
@@ -1813,6 +1823,21 @@
     secondWindow->consumeMotionUp();
 }
 
+// For the cases of single pointer touch and two pointers non-split touch, the api's
+// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ
+// for the case where there are multiple pointers split across several windows.
+INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture,
+                         ::testing::Values(
+                                 [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/,
+                                     sp<IBinder> destChannelToken) {
+                                     return dispatcher->transferTouch(destChannelToken);
+                                 },
+                                 [&](sp<InputDispatcher> dispatcher, sp<IBinder> from,
+                                     sp<IBinder> to) {
+                                     return dispatcher->transferTouchFocus(from, to,
+                                                                           false /*isDragAndDrop*/);
+                                 }));
+
 TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
@@ -1883,6 +1908,82 @@
     secondWindow->consumeMotionUp();
 }
 
+// Same as TransferTouchFocus_TwoPointersSplitTouch, but using 'transferTouch' api.
+// Unlike 'transferTouchFocus', calling 'transferTouch' when there are two windows receiving
+// touch is not supported, so the touch should continue on those windows and the transferred-to
+// window should get nothing.
+TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    // Create a non touch modal window that supports split touch
+    sp<FakeWindowHandle> firstWindow =
+            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+    firstWindow->setFrame(Rect(0, 0, 600, 400));
+    firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                          InputWindowInfo::Flag::SPLIT_TOUCH);
+
+    // Create a non touch modal window that supports split touch
+    sp<FakeWindowHandle> secondWindow =
+            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
+    secondWindow->setFrame(Rect(0, 400, 600, 800));
+    secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                           InputWindowInfo::Flag::SPLIT_TOUCH);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+    PointF pointInFirst = {300, 200};
+    PointF pointInSecond = {300, 600};
+
+    // Send down to the first window
+    NotifyMotionArgs firstDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {pointInFirst});
+    mDispatcher->notifyMotion(&firstDownMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Send down to the second window
+    NotifyMotionArgs secondDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&secondDownMotionArgs);
+    // The first window gets a move and the second a down
+    firstWindow->consumeMotionMove();
+    secondWindow->consumeMotionDown();
+
+    // Transfer touch focus to the second window
+    const bool transferred = mDispatcher->transferTouch(secondWindow->getToken());
+    // The 'transferTouch' call should not succeed, because there are 2 touched windows
+    ASSERT_FALSE(transferred);
+    firstWindow->assertNoEvents();
+    secondWindow->assertNoEvents();
+
+    // The rest of the dispatch should proceed as normal
+    // Send pointer up to the second window
+    NotifyMotionArgs pointerUpMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    // The first window gets MOVE and the second gets pointer up
+    firstWindow->consumeMotionMove();
+    secondWindow->consumeMotionUp();
+
+    // Send up event to the first window
+    NotifyMotionArgs upMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first window gets nothing and the second gets up
+    firstWindow->consumeMotionUp();
+    secondWindow->assertNoEvents();
+}
+
 TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml
index 5fd361c..c564b7b 100644
--- a/services/stats/android.frameworks.stats@1.0-service.xml
+++ b/services/stats/android.frameworks.stats@1.0-service.xml
@@ -1,5 +1,5 @@
 <manifest version="1.0" type="framework">
-    <hal>
+    <hal max-level="5">
         <name>android.frameworks.stats</name>
         <transport>hwbinder</transport>
         <version>1.0</version>
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 4c73b6e..cacad52 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -58,8 +58,7 @@
 
 namespace android {
 
-static constexpr float defaultMaxMasteringLuminance = 1000.0;
-static constexpr float defaultMaxContentLuminance = 1000.0;
+static constexpr float defaultMaxLuminance = 1000.0;
 
 BufferLayer::BufferLayer(const LayerCreationArgs& args)
       : Layer(args),
@@ -206,12 +205,24 @@
     layer.source.buffer.isY410BT2020 = isHdrY410();
     bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
     bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
-    layer.source.buffer.maxMasteringLuminance = hasSmpte2086
-            ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance
-            : defaultMaxMasteringLuminance;
-    layer.source.buffer.maxContentLuminance = hasCta861_3
-            ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
-            : defaultMaxContentLuminance;
+    float maxLuminance = 0.f;
+    if (hasSmpte2086 && hasCta861_3) {
+        maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
+                                mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
+    } else if (hasSmpte2086) {
+        maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
+    } else if (hasCta861_3) {
+        maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
+    } else {
+        switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+            case HAL_DATASPACE_TRANSFER_ST2084:
+            case HAL_DATASPACE_TRANSFER_HLG:
+                // Behavior-match previous releases for HDR content
+                maxLuminance = defaultMaxLuminance;
+                break;
+        }
+    }
+    layer.source.buffer.maxLuminanceNits = maxLuminance;
     layer.frameNumber = mCurrentFrameNumber;
     layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()->getId() : 0;
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 24b3599..fcf8299 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -295,12 +295,72 @@
     return true;
 }
 
+bool BufferStateLayer::setDestinationFrame(const Rect& destinationFrame) {
+    if (mCurrentState.destinationFrame == destinationFrame) return false;
+
+    mCurrentState.sequence++;
+    mCurrentState.destinationFrame = destinationFrame;
+
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+// Translate destination frame into scale and position. If a destination frame is not set, use the
+// provided scale and position
+void BufferStateLayer::updateGeometry() {
+    if (mCurrentState.destinationFrame.isEmpty()) {
+        // If destination frame is not set, use the requested transform set via
+        // BufferStateLayer::setPosition and BufferStateLayer::setMatrix.
+        mCurrentState.transform = mRequestedTransform;
+        return;
+    }
+
+    Rect destRect = mCurrentState.destinationFrame;
+    int32_t destW = destRect.width();
+    int32_t destH = destRect.height();
+    if (destRect.left < 0) {
+        destRect.left = 0;
+        destRect.right = destW;
+    }
+    if (destRect.top < 0) {
+        destRect.top = 0;
+        destRect.bottom = destH;
+    }
+
+    if (!mCurrentState.buffer) {
+        ui::Transform t;
+        t.set(destRect.left, destRect.top);
+        mCurrentState.transform = t;
+        return;
+    }
+
+    uint32_t bufferWidth = mCurrentState.buffer->getBuffer()->getWidth();
+    uint32_t bufferHeight = mCurrentState.buffer->getBuffer()->getHeight();
+    // Undo any transformations on the buffer.
+    if (mCurrentState.bufferTransform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+    uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+    if (mCurrentState.transformToDisplayInverse) {
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+
+    float sx = destW / static_cast<float>(bufferWidth);
+    float sy = destH / static_cast<float>(bufferHeight);
+    ui::Transform t;
+    t.set(sx, 0, 0, sy);
+    t.set(destRect.left, destRect.top);
+    mCurrentState.transform = t;
+    return;
+}
+
 bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix,
                                  bool allowNonRectPreservingTransforms) {
-    if (mCurrentState.transform.dsdx() == matrix.dsdx &&
-        mCurrentState.transform.dtdy() == matrix.dtdy &&
-        mCurrentState.transform.dtdx() == matrix.dtdx &&
-        mCurrentState.transform.dsdy() == matrix.dsdy) {
+    if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
+        mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
         return false;
     }
 
@@ -313,7 +373,7 @@
         return false;
     }
 
-    mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+    mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
 
     mCurrentState.sequence++;
     mCurrentState.modified = true;
@@ -323,11 +383,11 @@
 }
 
 bool BufferStateLayer::setPosition(float x, float y) {
-    if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) {
+    if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
         return false;
     }
 
-    mCurrentState.transform.set(x, y);
+    mRequestedTransform.set(x, y);
 
     mCurrentState.sequence++;
     mCurrentState.modified = true;
@@ -523,35 +583,35 @@
 
 Rect BufferStateLayer::getBufferSize(const State& s) const {
     // for buffer state layers we use the display frame size as the buffer size.
-    if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
-        return Rect(getActiveWidth(s), getActiveHeight(s));
-    }
 
     if (mBufferInfo.mBuffer == nullptr) {
         return Rect::INVALID_RECT;
     }
 
-    // if the display frame is not defined, use the parent bounds as the buffer size.
-    const auto& p = mDrawingParent.promote();
-    if (p != nullptr) {
-        Rect parentBounds = Rect(p->getBounds(Region()));
-        if (!parentBounds.isEmpty()) {
-            return parentBounds;
+    uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight();
+
+    // Undo any transformations on the buffer and return the result.
+    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (getTransformToDisplayInverse()) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
         }
     }
 
-    return Rect::INVALID_RECT;
+    return Rect(0, 0, bufWidth, bufHeight);
 }
 
 FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
-    const State& s(getDrawingState());
-    // for buffer state layers we use the display frame size as the buffer size.
-    if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) {
-        return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
+    if (mBufferInfo.mBuffer == nullptr) {
+        return parentBounds;
     }
 
-    // if the display frame is not defined, use the parent bounds as the buffer size.
-    return parentBounds;
+    return getBufferSize(getDrawingState()).toFloatRect();
 }
 
 // -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index a273230..2e48452 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -86,6 +86,8 @@
     void setAutoRefresh(bool autoRefresh) override;
 
     bool setBufferCrop(const Rect& bufferCrop) override;
+    bool setDestinationFrame(const Rect& destinationFrame) override;
+    void updateGeometry() override;
 
     // -----------------------------------------------------------------------
 
@@ -178,6 +180,10 @@
     //     - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
     std::atomic<int32_t> mPendingBufferTransactions{0};
 
+    // Contains requested position and matrix updates. This will be applied if the client does
+    // not specify a destination frame.
+    ui::Transform mRequestedTransform;
+
     // TODO(marissaw): support sticky transform for LEGACY camera mode
 
     class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 4976213..257974f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,6 +163,9 @@
     // Enables (or disables) composition on this output
     virtual void setCompositionEnabled(bool) = 0;
 
+    // Enables (or disables) layer caching on this output
+    virtual void setLayerCachingEnabled(bool) = 0;
+
     // Sets the projection state to use
     virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                                const Rect& orientedDisplaySpaceRect) = 0;
@@ -181,6 +184,9 @@
     // Sets the output color mode
     virtual void setColorProfile(const ColorProfile&) = 0;
 
+    // Sets current calibrated display brightness information
+    virtual void setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) = 0;
+
     // Outputs a string with a state dump
     virtual void dump(std::string&) const = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index eeb20fc..f10ff25 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -20,6 +20,7 @@
 #include <compositionengine/Output.h>
 #include <compositionengine/impl/ClientCompositionRequestCache.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/LayerSettings.h>
 #include <memory>
@@ -28,21 +29,18 @@
 
 namespace android::compositionengine::impl {
 
-namespace planner {
-class Planner;
-} // namespace planner
-
 // The implementation class contains the common implementation, but does not
 // actually contain the final output state.
 class Output : public virtual compositionengine::Output {
 public:
-    Output();
+    Output() = default;
     ~Output() override;
 
     // compositionengine::Output overrides
     bool isValid() const override;
     std::optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
+    void setLayerCachingEnabled(bool) override;
     void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                        const Rect& orientedDisplaySpaceRect) override;
     void setDisplaySize(const ui::Size&) override;
@@ -51,6 +49,7 @@
 
     void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
     void setColorProfile(const ColorProfile&) override;
+    void setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) override;
 
     void dump(std::string&) const override;
     void dumpPlannerInfo(const Vector<String16>& args, std::string&) const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f0ef6d6..d41c2dd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -118,6 +118,12 @@
     // The earliest time to send the present command to the HAL
     std::chrono::steady_clock::time_point earliestPresentTime;
 
+    // Current display brightness
+    float displayBrightnessNits{-1.f};
+
+    // SDR white point
+    float sdrWhitePointNits{-1.f};
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 2488c66..2ffd472 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -78,9 +78,10 @@
     void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
     void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
     void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
-                                   bool isPeekingThrough);
+                                   bool isPeekingThrough, bool skipLayer);
     void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
                                                Hwc2::IComposerClient::Composition to) const;
+    bool isClientCompositionForced(bool isPeekingThrough) const;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index b98043b..3f670a1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -125,6 +125,9 @@
 
         // Set to true when overridden info has been sent to HW composer
         bool stateOverridden = false;
+
+        // True when this layer was skipped as part of SF-side layer caching.
+        bool layerSkipped = false;
     };
 
     // The HWC state is optional, and is only set up if there is any potential
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index b09f1d1..864251f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -36,8 +36,7 @@
 
 class Flattener {
 public:
-    Flattener(Predictor& predictor, bool enableHolePunch = false)
-          : mEnableHolePunch(enableHolePunch), mPredictor(predictor) {}
+    Flattener(bool enableHolePunch = false) : mEnableHolePunch(enableHolePunch) {}
 
     void setDisplaySize(ui::Size size) { mDisplaySize = size; }
 
@@ -64,7 +63,6 @@
     void buildCachedSets(std::chrono::steady_clock::time_point now);
 
     const bool mEnableHolePunch;
-    Predictor& mPredictor;
 
     ui::Size mDisplaySize;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index c2037a8..4365b93 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -76,6 +76,8 @@
 
     std::optional<Predictor::PredictedPlan> mPredictedPlan;
     NonBufferHash mFlattenedHash = 0;
+
+    bool mPredictorEnabled = false;
 };
 
 } // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 5aa53e5..4b4d375 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,6 +36,7 @@
     MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
+    MOCK_METHOD1(setLayerCachingEnabled, void(bool));
     MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
@@ -43,6 +44,7 @@
 
     MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
     MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
+    MOCK_METHOD2(setDisplayBrightness, void(float, float));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD2(dumpPlannerInfo, void(const Vector<String16>&, std::string&));
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index b1ee3fb..7e020ee 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -46,8 +46,7 @@
             lhs.textureTransform == rhs.textureTransform &&
             lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
             lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
-            lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
-            lhs.maxContentLuminance == rhs.maxContentLuminance;
+            lhs.maxLuminanceNits == rhs.maxLuminanceNits;
 }
 
 inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs,
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 1ffb1c8..8c8331c 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -267,7 +267,7 @@
     auto& hwc = getCompositionEngine().getHwComposer();
     if (status_t result =
                 hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
-                                                &changes);
+                                                getState().earliestPresentTime, &changes);
         result != NO_ERROR) {
         ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
               strerror(-result));
@@ -367,13 +367,8 @@
         return fences;
     }
 
-    {
-        ATRACE_NAME("wait for earliest present time");
-        std::this_thread::sleep_until(getState().earliestPresentTime);
-    }
-
     auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
+    hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
 
     fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 297e687..bf36355 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -55,18 +55,6 @@
 
 namespace impl {
 
-Output::Output() {
-    const bool enableLayerCaching = [] {
-        const bool enable =
-                android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
-        return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
-    }();
-
-    if (enableLayerCaching) {
-        mPlanner = std::make_unique<planner::Planner>();
-    }
-}
-
 namespace {
 
 template <typename T>
@@ -135,6 +123,21 @@
     dirtyEntireOutput();
 }
 
+void Output::setLayerCachingEnabled(bool enabled) {
+    if (enabled == (mPlanner != nullptr)) {
+        return;
+    }
+
+    if (enabled) {
+        mPlanner = std::make_unique<planner::Planner>();
+        if (mRenderSurface) {
+            mPlanner->setDisplaySize(mRenderSurface->getSize());
+        }
+    } else {
+        mPlanner.reset();
+    }
+}
+
 void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                            const Rect& orientedDisplaySpaceRect) {
     auto& outputState = editState();
@@ -256,6 +259,18 @@
     dirtyEntireOutput();
 }
 
+void Output::setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) {
+    auto& outputState = editState();
+    if (outputState.sdrWhitePointNits == sdrWhitePointNits &&
+        outputState.displayBrightnessNits == displayBrightnessNits) {
+        // Nothing changed
+        return;
+    }
+    outputState.sdrWhitePointNits = sdrWhitePointNits;
+    outputState.displayBrightnessNits = displayBrightnessNits;
+    dirtyEntireOutput();
+}
+
 void Output::dump(std::string& out) const {
     using android::base::StringAppendF;
 
@@ -1032,8 +1047,13 @@
     clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
             ? outputState.dataspace
             : ui::Dataspace::UNKNOWN;
-    clientCompositionDisplay.maxLuminance =
-            mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+
+    // If we have a valid current display brightness use that, otherwise fall back to the
+    // display's max desired
+    clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f
+            ? outputState.displayBrightnessNits
+            : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+    clientCompositionDisplay.sdrWhitePointNits = outputState.sdrWhitePointNits;
 
     // Compute the global color transform matrix.
     if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 01da070..cd14327 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <DisplayHardware/Hal.h>
 #include <android-base/stringprintf.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/LayerFECompositionState.h>
@@ -350,12 +351,14 @@
     writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
     writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
 
-    writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough);
+    writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
+                              skipLayer);
 
     // Always set the layer color after setting the composition type.
     writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
 
     editState().hwc->stateOverridden = isOverridden;
+    editState().hwc->layerSkipped = skipLayer;
 }
 
 void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
@@ -482,7 +485,8 @@
 
     const Region& surfaceDamage = getState().overrideInfo.buffer
             ? getState().overrideInfo.damageRegion
-            : outputIndependentState.surfaceDamage;
+            : (getState().hwc->stateOverridden ? Region::INVALID_REGION
+                                               : outputIndependentState.surfaceDamage);
 
     if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::Error::NONE) {
         ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
@@ -573,17 +577,19 @@
 
 void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
                                             hal::Composition requestedCompositionType,
-                                            bool isPeekingThrough) {
+                                            bool isPeekingThrough, bool skipLayer) {
     auto& outputDependentState = editState();
 
-    // If we are forcing client composition, we need to tell the HWC
-    if (outputDependentState.forceClientComposition ||
-        (!isPeekingThrough && getLayerFE().hasRoundedCorners())) {
+    if (isClientCompositionForced(isPeekingThrough)) {
+        // If we are forcing client composition, we need to tell the HWC
         requestedCompositionType = hal::Composition::CLIENT;
     }
 
     // Set the requested composition type with the HWC whenever it changes
-    if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) {
+    // We also resend the composition type when this layer was previously skipped, to ensure that
+    // the composition type is up-to-date.
+    if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType ||
+        (outputDependentState.hwc->layerSkipped && !skipLayer)) {
         outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
 
         if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
@@ -663,12 +669,22 @@
     }
 }
 
+bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const {
+    return getState().forceClientComposition ||
+            (!isPeekingThrough && getLayerFE().hasRoundedCorners());
+}
+
 void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) {
     auto& state = editState();
     LOG_FATAL_IF(!state.hwc);
     auto& hwcState = *state.hwc;
 
-    detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+    // Only detected disallowed changes if this was not a skip layer, because the
+    // validated composition type may be arbitrary (usually DEVICE, to reflect that there were
+    // fewer GPU layers)
+    if (!hwcState.layerSkipped) {
+        detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+    }
 
     hwcState.hwcCompositionType = compositionType;
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index a63f21f..4453a99 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -20,7 +20,6 @@
 
 #include <compositionengine/impl/planner/Flattener.h>
 #include <compositionengine/impl/planner/LayerState.h>
-#include <compositionengine/impl/planner/Predictor.h>
 
 using time_point = std::chrono::steady_clock::time_point;
 using namespace std::chrono_literals;
@@ -126,9 +125,11 @@
         return left.first < right.first;
     };
 
-    const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(),
-                                                  mInitialLayerCounts.cend(), compareLayerCounts)
-                                         ->first;
+    const size_t maxLayerCount = mInitialLayerCounts.empty()
+            ? 0u
+            : std::max_element(mInitialLayerCounts.cbegin(), mInitialLayerCounts.cend(),
+                               compareLayerCounts)
+                      ->first;
 
     result.append("\n    Initial counts:\n");
     for (size_t count = 1; count < maxLayerCount; ++count) {
@@ -408,7 +409,7 @@
     }
 
     // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
-    mPredictor.getPredictedPlan({}, 0);
+    // and feedback into the predictor
 
     ++mCachedSetCreationCount;
     mCachedSetCreationCost += mNewCachedSet->getCreationCost();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 7e85dca..c26eb1e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -27,8 +27,13 @@
 namespace android::compositionengine::impl::planner {
 
 Planner::Planner()
-      : mFlattener(mPredictor,
-                   base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {}
+      : mFlattener(base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {
+    // Implicitly, layer caching must also be enabled.
+    // E.g., setprop debug.sf.enable_layer_caching 1, or
+    // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
+    mPredictorEnabled =
+            base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
+}
 
 void Planner::setDisplaySize(ui::Size size) {
     mFlattener.setDisplaySize(size);
@@ -99,19 +104,25 @@
     const bool layersWereFlattened = hash != mFlattenedHash;
     ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
 
-    mPredictedPlan =
-            mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
-                                                            : mCurrentLayers,
-                                        mFlattenedHash);
-    if (mPredictedPlan) {
-        ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
-    } else {
-        ALOGV("[%s] No prediction found\n", __func__);
+    if (mPredictorEnabled) {
+        mPredictedPlan =
+                mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
+                                                                : mCurrentLayers,
+                                            mFlattenedHash);
+        if (mPredictedPlan) {
+            ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
+        } else {
+            ALOGV("[%s] No prediction found\n", __func__);
+        }
     }
 }
 
 void Planner::reportFinalPlan(
         compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+    if (!mPredictorEnabled) {
+        return;
+    }
+
     Plan finalPlan;
     const GraphicBuffer* currentOverrideBuffer = nullptr;
     bool hasSkippedLayers = false;
@@ -185,7 +196,9 @@
                 return;
             }
 
-            mPredictor.compareLayerStacks(leftHash, rightHash, result);
+            if (mPredictorEnabled) {
+                mPredictor.compareLayerStacks(leftHash, rightHash, result);
+            }
         } else if (command == "--describe" || command == "-d") {
             if (args.size() < 3) {
                 base::StringAppendF(&result,
@@ -209,7 +222,9 @@
                 return;
             }
 
-            mPredictor.describeLayerStack(hash, result);
+            if (mPredictorEnabled) {
+                mPredictor.describeLayerStack(hash, result);
+            }
         } else if (command == "--help" || command == "-h") {
             dumpUsage(result);
         } else if (command == "--similar" || command == "-s") {
@@ -232,7 +247,9 @@
                 return;
             }
 
-            mPredictor.listSimilarStacks(*plan, result);
+            if (mPredictorEnabled) {
+                mPredictor.listSimilarStacks(*plan, result);
+            }
         } else if (command == "--layers" || command == "-l") {
             mFlattener.dumpLayers(result);
         } else {
@@ -247,7 +264,9 @@
     mFlattener.dump(result);
     result.append("\n");
 
-    mPredictor.dump(result);
+    if (mPredictorEnabled) {
+        mPredictor.dump(result);
+    }
 }
 
 void Planner::dumpUsage(std::string& result) const {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 8a83639..e63d09a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -627,7 +627,7 @@
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
     EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _))
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _))
             .WillOnce(Return(INVALID_OPERATION));
 
     mDisplay->chooseCompositionStrategy();
@@ -649,7 +649,8 @@
             .InSequence(s)
             .WillOnce(Return(false));
 
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
+    EXPECT_CALL(mHwComposer,
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
@@ -679,8 +680,9 @@
             .InSequence(s)
             .WillOnce(Return(false));
 
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
-            .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
+    EXPECT_CALL(mHwComposer,
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _))
+            .WillOnce(DoAll(SetArgPointee<3>(changes), Return(NO_ERROR)));
     EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
     EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
     EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
@@ -867,7 +869,8 @@
     sp<Fence> layer1Fence = new Fence();
     sp<Fence> layer2Fence = new Fence();
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _))
+            .Times(1);
     EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
             .WillOnce(Return(presentFence));
     EXPECT_CALL(mHwComposer,
@@ -1039,7 +1042,7 @@
 
     mDisplay->editState().isEnabled = true;
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
     EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
 
     mDisplay->postFramebuffer();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index bac894a..74d4b92 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -50,13 +50,14 @@
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
     MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId));
     MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*));
-    MOCK_METHOD3(getDeviceCompositionChanges,
-                 status_t(HalDisplayId, bool,
+    MOCK_METHOD4(getDeviceCompositionChanges,
+                 status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
                           std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
                  status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
                           ui::Dataspace));
-    MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId));
+    MOCK_METHOD2(presentAndGetReleaseFences,
+                 status_t(HalDisplayId, std::chrono::steady_clock::time_point));
     MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
     MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
     MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 3adfe40..5bd1216 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1065,12 +1065,62 @@
                               kOverrideSurfaceDamage);
     expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
     expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().hwc->stateOverridden = true;
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+                              Region::INVALID_REGION);
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().hwc->stateOverridden = true;
+    mOutputLayer.editState().hwc->layerSkipped = true;
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+                              Region::INVALID_REGION);
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
     EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    mOutputLayer.editState().forceClientComposition = true;
+    mOutputLayer.editState().hwc->stateOverridden = true;
+    mOutputLayer.editState().hwc->layerSkipped = true;
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
+                              Region::INVALID_REGION);
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
 TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
     auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make();
     OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE};
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 27980a0..11736d1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -238,6 +238,28 @@
 }
 
 /*
+ * Output::setLayerCachingEnabled()
+ */
+
+TEST_F(OutputTest, setLayerCachingEnabled_enablesCaching) {
+    const auto kSize = ui::Size(1, 1);
+    EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+    mOutput->setLayerCachingEnabled(false);
+    mOutput->setLayerCachingEnabled(true);
+
+    EXPECT_TRUE(mOutput->plannerEnabled());
+}
+
+TEST_F(OutputTest, setLayerCachingEnabled_disablesCaching) {
+    const auto kSize = ui::Size(1, 1);
+    EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize));
+    mOutput->setLayerCachingEnabled(true);
+    mOutput->setLayerCachingEnabled(false);
+
+    EXPECT_FALSE(mOutput->plannerEnabled());
+}
+
+/*
  * Output::setProjection()
  */
 
@@ -972,9 +994,7 @@
     mOutput.editState().usesDeviceComposition = true;
 
     EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
-    if (mOutput.plannerEnabled()) {
-        EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
-    }
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
     EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
 
     mOutput.prepareFrame();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 71757f6..25fab49 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -18,7 +18,6 @@
 #include <compositionengine/impl/planner/CachedSet.h>
 #include <compositionengine/impl/planner/Flattener.h>
 #include <compositionengine/impl/planner/LayerState.h>
-#include <compositionengine/impl/planner/Predictor.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/OutputLayer.h>
 #include <gtest/gtest.h>
@@ -31,7 +30,6 @@
 using impl::planner::Flattener;
 using impl::planner::LayerState;
 using impl::planner::NonBufferHash;
-using impl::planner::Predictor;
 
 using testing::_;
 using testing::ByMove;
@@ -47,7 +45,7 @@
 
 class FlattenerTest : public testing::Test {
 public:
-    FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor, true)) {}
+    FlattenerTest() : mFlattener(std::make_unique<Flattener>(true)) {}
     void SetUp() override;
 
 protected:
@@ -55,10 +53,6 @@
     void initializeFlattener(const std::vector<const LayerState*>& layers);
     void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
 
-    // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
-    // mPredictor should be mocked and checked for expectations.
-    Predictor mPredictor;
-
     // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
     renderengine::mock::RenderEngine mRenderEngine;
     std::unique_ptr<Flattener> mFlattener;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 8692ee6..0f18235 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -138,6 +138,10 @@
     getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
 }
 
+void DisplayDevice::enableLayerCaching(bool enable) {
+    getCompositionDisplay()->setLayerCachingEnabled(enable);
+}
+
 hal::PowerMode DisplayDevice::getPowerMode() const {
     return mPowerMode;
 }
@@ -268,8 +272,9 @@
     StringAppendF(&result, "+ %s\n", getDebugName().c_str());
     StringAppendF(&result, "   powerMode=%s (%d)\n", to_string(mPowerMode).c_str(),
                   static_cast<int32_t>(mPowerMode));
+    const auto activeMode = getActiveMode();
     StringAppendF(&result, "   activeMode=%s\n",
-                  mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none");
+                  activeMode ? to_string(*activeMode).c_str() : "none");
 
     result.append("   supportedModes=\n");
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 68846d3..bf249cd 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -157,6 +157,9 @@
     void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
     bool isPoweredOn() const;
 
+    // Enables layer caching on this DisplayDevice
+    void enableLayerCaching(bool enable);
+
     ui::Dataspace getCompositionDataSpace() const;
 
     /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index b73d032..dc4839e 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -471,6 +471,7 @@
 
 status_t HWComposer::getDeviceCompositionChanges(
         HalDisplayId displayId, bool frameUsesClientComposition,
+        std::chrono::steady_clock::time_point earliestPresentTime,
         std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
@@ -487,12 +488,14 @@
 
     hal::Error error = hal::Error::NONE;
 
-    // First try to skip validate altogether when there is no client
-    // composition.  When there is client composition, since we haven't
-    // rendered to the client target yet, we should not attempt to skip
-    // validate.
+    // First try to skip validate altogether when we passed the earliest time
+    // to present and there is no client. Otherwise, we may present a frame too
+    // early or in case of client composition we first need to render the
+    // client target buffer.
+    const bool canSkipValidate =
+            std::chrono::steady_clock::now() >= earliestPresentTime && !frameUsesClientComposition;
     displayData.validateWasSkipped = false;
-    if (!frameUsesClientComposition) {
+    if (canSkipValidate) {
         sp<Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
         error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
@@ -556,7 +559,8 @@
     return fence->second;
 }
 
-status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) {
+status_t HWComposer::presentAndGetReleaseFences(
+        HalDisplayId displayId, std::chrono::steady_clock::time_point earliestPresentTime) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -572,6 +576,11 @@
         return NO_ERROR;
     }
 
+    {
+        ATRACE_NAME("wait for earliest present time");
+        std::this_thread::sleep_until(earliestPresentTime);
+    }
+
     auto error = hwcDisplay->present(&displayData.lastPresentFence);
     RETURN_IF_HWC_ERROR_FOR("present", error, displayId, UNKNOWN_ERROR);
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index f532e50..5bad529 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -129,13 +129,15 @@
     // expected.
     virtual status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
+            std::chrono::steady_clock::time_point earliestPresentTime,
             std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
     virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
                                      const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
 
     // Present layers to the display and read releaseFences.
-    virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0;
+    virtual status_t presentAndGetReleaseFences(
+            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) = 0;
 
     // set power mode
     virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
@@ -268,13 +270,15 @@
 
     status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
+            std::chrono::steady_clock::time_point earliestPresentTime,
             std::optional<DeviceRequestedChanges>* outChanges) override;
 
     status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
                              const sp<GraphicBuffer>& target, ui::Dataspace) override;
 
     // Present layers to the display and read releaseFences.
-    status_t presentAndGetReleaseFences(HalDisplayId) override;
+    status_t presentAndGetReleaseFences(
+            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) override;
 
     // set power mode
     status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 21c9d74..7713749 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -129,10 +129,10 @@
     mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
     mCurrentState.metadata = args.metadata;
     mCurrentState.shadowRadius = 0.f;
-    mCurrentState.treeHasFrameRateVote = false;
     mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
     mCurrentState.frameTimelineInfo = {};
     mCurrentState.postTime = -1;
+    mCurrentState.destinationFrame.makeInvalid();
 
     if (args.flags & ISurfaceComposerClient::eNoColorFill) {
         // Set an invalid color so there is no color fill.
@@ -221,7 +221,10 @@
 }
 
 void Layer::removeFromCurrentState() {
-    mRemovedFromCurrentState = true;
+    if (!mRemovedFromCurrentState) {
+        mRemovedFromCurrentState = true;
+        mFlinger->mScheduler->deregisterLayer(this);
+    }
 
     mFlinger->markLayerPendingRemovalLocked(this);
 }
@@ -246,7 +249,10 @@
 }
 
 void Layer::addToCurrentState() {
-    mRemovedFromCurrentState = false;
+    if (mRemovedFromCurrentState) {
+        mRemovedFromCurrentState = false;
+        mFlinger->mScheduler->registerLayer(this);
+    }
 
     for (const auto& child : mCurrentChildren) {
         child->addToCurrentState();
@@ -315,55 +321,6 @@
     return reduce(mBounds, activeTransparentRegion);
 }
 
-ui::Transform Layer::getBufferScaleTransform() const {
-    // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
-    // it isFixedSize) then there may be additional scaling not accounted
-    // for in the layer transform.
-    if (!isFixedSize() || getBuffer() == nullptr) {
-        return {};
-    }
-
-    // If the layer is a buffer state layer, the active width and height
-    // could be infinite. In that case, return the effective transform.
-    const uint32_t activeWidth = getActiveWidth(getDrawingState());
-    const uint32_t activeHeight = getActiveHeight(getDrawingState());
-    if (activeWidth >= UINT32_MAX && activeHeight >= UINT32_MAX) {
-        return {};
-    }
-
-    int bufferWidth = getBuffer()->getWidth();
-    int bufferHeight = getBuffer()->getHeight();
-
-    if (getBufferTransform() & NATIVE_WINDOW_TRANSFORM_ROT_90) {
-        std::swap(bufferWidth, bufferHeight);
-    }
-
-    float sx = activeWidth / static_cast<float>(bufferWidth);
-    float sy = activeHeight / static_cast<float>(bufferHeight);
-
-    ui::Transform extraParentScaling;
-    extraParentScaling.set(sx, 0, 0, sy);
-    return extraParentScaling;
-}
-
-ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const {
-    // We need to mirror this scaling to child surfaces or we will break the contract where WM can
-    // treat child surfaces as pixels in the parent surface.
-    if (!isFixedSize() || getBuffer() == nullptr) {
-        return mEffectiveTransform;
-    }
-    return mEffectiveTransform * bufferScaleTransform;
-}
-
-FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const {
-    // We need the pre scaled layer bounds when computing child bounds to make sure the child is
-    // cropped to its parent layer after any buffer transform scaling is applied.
-    if (!isFixedSize() || getBuffer() == nullptr) {
-        return mBounds;
-    }
-    return bufferScaleTransform.inverse().transform(mBounds);
-}
-
 void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
                           float parentShadowRadius) {
     const State& s(getDrawingState());
@@ -400,11 +357,8 @@
     // don't pass it to its children.
     const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
 
-    // Add any buffer scaling to the layer's children.
-    ui::Transform bufferScaleTransform = getBufferScaleTransform();
     for (const sp<Layer>& child : mDrawingChildren) {
-        child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
-                             getTransformWithScale(bufferScaleTransform), childShadowRadius);
+        child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
     }
 }
 
@@ -858,6 +812,11 @@
     const State& s(getDrawingState());
     State& c(getCurrentState());
 
+    // Translates dest frame into scale and position updates. This helps align geometry calculations
+    // for BufferStateLayer with other layers. This should ideally happen in the client once client
+    // has the display orientation details from WM.
+    updateGeometry();
+
     if (c.width != s.width || c.height != s.height || !(c.transform == s.transform)) {
         // invalidate and recompute the visible regions if needed
         flags |= Layer::eVisibleRegion;
@@ -907,8 +866,15 @@
         // list.
         addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
     }
+    const bool frameRateVoteChanged =
+            mDrawingState.frameRateForLayerTree != stateToCommit.frameRateForLayerTree;
     mDrawingState = stateToCommit;
 
+    if (frameRateVoteChanged) {
+        mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+                                                 LayerHistory::LayerUpdateType::SetFrameRate);
+    }
+
     // Set the present state for all bufferlessSurfaceFramesTX to Presented. The
     // bufferSurfaceFrameTX will be presented in latchBuffer.
     for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -1311,8 +1277,7 @@
     };
 
     // update parents and children about the vote
-    // First traverse the tree and count how many layers has votes. In addition
-    // activate the layers in Scheduler's LayerHistory for it to check for changes
+    // First traverse the tree and count how many layers has votes.
     int layersWithVote = 0;
     traverseTree([&layersWithVote](Layer* layer) {
         const auto layerVotedWithDefaultCompatibility =
@@ -1332,20 +1297,11 @@
         }
     });
 
-    // Now update the other layers
+    // Now we can update the tree frame rate vote for each layer in the tree
+    const bool treeHasFrameRateVote = layersWithVote > 0;
     bool transactionNeeded = false;
-    traverseTree([layersWithVote, &transactionNeeded, this](Layer* layer) {
-        const bool treeHasFrameRateVote = layersWithVote > 0;
-        if (layer->mCurrentState.treeHasFrameRateVote != treeHasFrameRateVote) {
-            layer->mCurrentState.sequence++;
-            layer->mCurrentState.treeHasFrameRateVote = treeHasFrameRateVote;
-            layer->mCurrentState.modified = true;
-            layer->setTransactionFlags(eTransactionNeeded);
-            transactionNeeded = true;
-
-            mFlinger->mScheduler->recordLayerHistory(layer, systemTime(),
-                                                     LayerHistory::LayerUpdateType::SetFrameRate);
-        }
+    traverseTree([treeHasFrameRateVote, &transactionNeeded](Layer* layer) {
+        transactionNeeded = layer->updateFrameRateForLayerTree(treeHasFrameRateVote);
     });
 
     if (transactionNeeded) {
@@ -1474,32 +1430,42 @@
     return surfaceFrame;
 }
 
-Layer::FrameRate Layer::getFrameRateForLayerTree() const {
-    const auto frameRate = getDrawingState().frameRate;
+bool Layer::updateFrameRateForLayerTree(bool treeHasFrameRateVote) {
+    const auto updateCurrentState = [&](FrameRate frameRate) {
+        if (mCurrentState.frameRateForLayerTree == frameRate) {
+            return false;
+        }
+        mCurrentState.frameRateForLayerTree = frameRate;
+        mCurrentState.sequence++;
+        mCurrentState.modified = true;
+        setTransactionFlags(eTransactionNeeded);
+        return true;
+    };
+
+    const auto frameRate = mCurrentState.frameRate;
     if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) {
-        return frameRate;
+        return updateCurrentState(frameRate);
     }
 
     // This layer doesn't have a frame rate. Check if its ancestors have a vote
-    if (sp<Layer> parent = getParent(); parent) {
-        if (const auto parentFrameRate = parent->getFrameRateForLayerTree();
-            parentFrameRate.rate.isValid()) {
-            return parentFrameRate;
+    for (sp<Layer> parent = getParent(); parent; parent = parent->getParent()) {
+        if (parent->mCurrentState.frameRate.rate.isValid()) {
+            return updateCurrentState(parent->mCurrentState.frameRate);
         }
     }
 
     // This layer and its ancestors don't have a frame rate. If one of successors
     // has a vote, return a NoVote for successors to set the vote
-    if (getDrawingState().treeHasFrameRateVote) {
-        return {Fps(0.0f), FrameRateCompatibility::NoVote};
+    if (treeHasFrameRateVote) {
+        return updateCurrentState(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
     }
 
-    return frameRate;
+    return updateCurrentState(frameRate);
 }
 
-// ----------------------------------------------------------------------------
-// pageflip handling...
-// ----------------------------------------------------------------------------
+Layer::FrameRate Layer::getFrameRateForLayerTree() const {
+    return getDrawingState().frameRateForLayerTree;
+}
 
 bool Layer::isHiddenByPolicy() const {
     const State& s(mDrawingState);
@@ -1768,8 +1734,7 @@
 void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
     for (const sp<Layer>& child : mDrawingChildren) {
         child->mDrawingParent = newParent;
-        child->computeBounds(newParent->mBounds,
-                             newParent->getTransformWithScale(newParent->getBufferScaleTransform()),
+        child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
                              newParent->mEffectiveShadowRadius);
     }
 }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 284adbd..8139d8a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -239,8 +239,8 @@
 
         FrameRate frameRate;
 
-        // Indicates whether parents / children of this layer had set FrameRate
-        bool treeHasFrameRateVote;
+        // The combined frame rate of parents / children of this layer
+        FrameRate frameRateForLayerTree;
 
         // Set by window manager indicating the layer and all its children are
         // in a different orientation than the display. The hint suggests that
@@ -276,6 +276,7 @@
         StretchEffect stretchEffect;
 
         Rect bufferCrop;
+        Rect destinationFrame;
     };
 
     /*
@@ -646,16 +647,6 @@
     // Compute bounds for the layer and cache the results.
     void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
 
-    // Returns the buffer scale transform if a scaling mode is set.
-    ui::Transform getBufferScaleTransform() const;
-
-    // Get effective layer transform, taking into account all its parent transform with any
-    // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE.
-    ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const;
-
-    // Returns the bounds of the layer without any buffer scaling.
-    FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
-
     int32_t getSequence() const override { return sequence; }
 
     // For tracing.
@@ -885,8 +876,10 @@
     StretchEffect getStretchEffect() const;
 
     virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
+    virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; }
     virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
     virtual std::string getPendingBufferCounterName() { return ""; }
+    virtual void updateGeometry() {}
 
 protected:
     friend class impl::SurfaceInterceptor;
@@ -1064,6 +1057,8 @@
     // Fills in the frame and transform info for the InputWindowInfo
     void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay);
 
+    bool updateFrameRateForLayerTree(bool treeHasFrameRateVote);
+
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
     // a transform from the current layer coordinate space to display(screen) coordinate space.
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 00090d9..653aca6 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -56,16 +56,14 @@
     noWorkNeeded,
     idleTimerWaiting,
     waitForQuietFrame,
-    waitForZeroPhase,
     waitForSamplePhase,
     sample
 };
 
-constexpr auto timeForRegionSampling = 5000000ns;
-constexpr auto maxRegionSamplingSkips = 10;
 constexpr auto defaultRegionSamplingWorkDuration = 3ms;
 constexpr auto defaultRegionSamplingPeriod = 100ms;
 constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+constexpr auto maxRegionSamplingDelay = 100ms;
 // TODO: (b/127403193) duration to string conversion could probably be constexpr
 template <typename Rep, typename Per>
 inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
@@ -99,97 +97,22 @@
     }
 }
 
-struct SamplingOffsetCallback : VSyncSource::Callback {
-    SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
-                           std::chrono::nanoseconds targetSamplingWorkDuration)
-          : mRegionSamplingThread(samplingThread),
-            mTargetSamplingWorkDuration(targetSamplingWorkDuration),
-            mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
-                                                             0ns,
-                                                             /*traceVsync=*/false)) {
-        mVSyncSource->setCallback(this);
-    }
-
-    ~SamplingOffsetCallback() { stopVsyncListener(); }
-
-    SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
-    SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
-
-    void startVsyncListener() {
-        std::lock_guard lock(mMutex);
-        if (mVsyncListening) return;
-
-        mPhaseIntervalSetting = Phase::ZERO;
-        mVSyncSource->setVSyncEnabled(true);
-        mVsyncListening = true;
-    }
-
-    void stopVsyncListener() {
-        std::lock_guard lock(mMutex);
-        stopVsyncListenerLocked();
-    }
-
-private:
-    void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
-        if (!mVsyncListening) return;
-
-        mVSyncSource->setVSyncEnabled(false);
-        mVsyncListening = false;
-    }
-
-    void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
-                      nsecs_t /*deadlineTimestamp*/) final {
-        std::unique_lock<decltype(mMutex)> lock(mMutex);
-
-        if (mPhaseIntervalSetting == Phase::ZERO) {
-            ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
-            mPhaseIntervalSetting = Phase::SAMPLING;
-            mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
-            return;
-        }
-
-        if (mPhaseIntervalSetting == Phase::SAMPLING) {
-            mPhaseIntervalSetting = Phase::ZERO;
-            mVSyncSource->setDuration(0ns, 0ns);
-            stopVsyncListenerLocked();
-            lock.unlock();
-            mRegionSamplingThread.notifySamplingOffset();
-            return;
-        }
-    }
-
-    RegionSamplingThread& mRegionSamplingThread;
-    const std::chrono::nanoseconds mTargetSamplingWorkDuration;
-    mutable std::mutex mMutex;
-    enum class Phase {
-        ZERO,
-        SAMPLING
-    } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
-            = Phase::ZERO;
-    bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
-    std::unique_ptr<VSyncSource> mVSyncSource;
-};
-
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
-                                           const TimingTunables& tunables)
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables)
       : mFlinger(flinger),
-        mScheduler(scheduler),
         mTunables(tunables),
         mIdleTimer(
                 "RegSampIdle",
                 std::chrono::duration_cast<std::chrono::milliseconds>(
                         mTunables.mSamplingTimerTimeout),
                 [] {}, [this] { checkForStaleLuma(); }),
-        mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
-                                                                tunables.mSamplingDuration)),
-        lastSampleTime(0ns) {
+        mLastSampleTime(0ns) {
     mThread = std::thread([this]() { threadMain(); });
     pthread_setname_np(mThread.native_handle(), "RegionSampling");
     mIdleTimer.start();
 }
 
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
-      : RegionSamplingThread(flinger, scheduler,
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger)
+      : RegionSamplingThread(flinger,
                              TimingTunables{defaultRegionSamplingWorkDuration,
                                             defaultRegionSamplingPeriod,
                                             defaultRegionSamplingTimerTimeout}) {}
@@ -224,48 +147,46 @@
 void RegionSamplingThread::checkForStaleLuma() {
     std::lock_guard lock(mThreadControlMutex);
 
-    if (mDiscardedFrames > 0) {
-        ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
-        mDiscardedFrames = 0;
-        mPhaseCallback->startVsyncListener();
+    if (mSampleRequestTime.has_value()) {
+        ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+        mSampleRequestTime.reset();
+        mFlinger.scheduleRegionSamplingThread();
     }
 }
 
-void RegionSamplingThread::notifyNewContent() {
-    doSample();
+void RegionSamplingThread::onCompositionComplete(
+        std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
+    doSample(samplingDeadline);
 }
 
-void RegionSamplingThread::notifySamplingOffset() {
-    doSample();
-}
-
-void RegionSamplingThread::doSample() {
+void RegionSamplingThread::doSample(
+        std::optional<std::chrono::steady_clock::time_point> samplingDeadline) {
     std::lock_guard lock(mThreadControlMutex);
-    auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
-    if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+    const auto now = std::chrono::steady_clock::now();
+    if (mLastSampleTime + mTunables.mSamplingPeriod > now) {
+        // content changed, but we sampled not too long ago, so we need to sample some time in the
+        // future.
         ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
-        if (mDiscardedFrames == 0) mDiscardedFrames++;
+        mSampleRequestTime = now;
         return;
     }
-    if (mDiscardedFrames < maxRegionSamplingSkips) {
+    if (!mSampleRequestTime.has_value() || now - *mSampleRequestTime < maxRegionSamplingDelay) {
         // If there is relatively little time left for surfaceflinger
         // until the next vsync deadline, defer this sampling work
         // to a later frame, when hopefully there will be more time.
-        const DisplayStatInfo stats = mScheduler.getDisplayStatInfo(systemTime());
-        if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
+        if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) {
             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
-            mDiscardedFrames++;
+            mSampleRequestTime = mSampleRequestTime.value_or(now);
             return;
         }
     }
 
     ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
 
-    mDiscardedFrames = 0;
-    lastSampleTime = now;
+    mSampleRequestTime.reset();
+    mLastSampleTime = now;
 
     mIdleTimer.reset();
-    mPhaseCallback->stopVsyncListener();
 
     mSampleRequested = true;
     mCondition.notify_one();
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 86632db..2231853 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -63,9 +63,8 @@
     struct EnvironmentTimingTunables : TimingTunables {
         EnvironmentTimingTunables();
     };
-    explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
-                                  const TimingTunables& tunables);
-    explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+    explicit RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables);
+    explicit RegionSamplingThread(SurfaceFlinger& flinger);
 
     ~RegionSamplingThread();
 
@@ -76,12 +75,11 @@
     // Remove the listener to stop receiving median luma notifications.
     void removeListener(const sp<IRegionSamplingListener>& listener);
 
-    // Notifies sampling engine that new content is available. This will trigger a sampling
-    // pass at some point in the future.
-    void notifyNewContent();
-
-    // Notifies the sampling engine that it has a good timing window in which to sample.
-    void notifySamplingOffset();
+    // Notifies sampling engine that composition is done and new content is
+    // available, and the deadline for the sampling work on the main thread to
+    // be completed without eating the budget of another frame.
+    void onCompositionComplete(
+            std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
 
 private:
     struct Descriptor {
@@ -99,7 +97,7 @@
             const sp<GraphicBuffer>& buffer, const Point& leftTop,
             const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation);
 
-    void doSample();
+    void doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline);
     void binderDied(const wp<IBinder>& who) override;
     void checkForStaleLuma();
 
@@ -107,20 +105,18 @@
     void threadMain();
 
     SurfaceFlinger& mFlinger;
-    Scheduler& mScheduler;
     const TimingTunables mTunables;
     scheduler::OneShotTimer mIdleTimer;
 
-    std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
-
     std::thread mThread;
 
     std::mutex mThreadControlMutex;
     std::condition_variable_any mCondition;
     bool mRunning GUARDED_BY(mThreadControlMutex) = true;
     bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false;
-    uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0;
-    std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex);
+    std::optional<std::chrono::steady_clock::time_point> mSampleRequestTime
+            GUARDED_BY(mThreadControlMutex);
+    std::chrono::steady_clock::time_point mLastSampleTime GUARDED_BY(mThreadControlMutex);
 
     std::mutex mSamplingMutex;
     std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex);
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index ce5c31a..50b38c9 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -60,8 +60,7 @@
                 mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                         .readyDuration = mReadyDuration.count(),
                                         .earliestVsync = mLastCallTime.count()});
-        LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
-                            "Error scheduling callback: rc %X", scheduleResult);
+        LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
     }
 
     void stop() {
@@ -100,8 +99,7 @@
                     mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                             .readyDuration = mReadyDuration.count(),
                                             .earliestVsync = vsyncTime});
-            LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
-                                "Error rescheduling callback: rc %X", scheduleResult);
+            LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
         }
     }
 
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index f4bc2a1..0563795 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -84,8 +84,11 @@
 LayerHistory::~LayerHistory() = default;
 
 void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
-    auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
     std::lock_guard lock(mLock);
+    for (const auto& info : mLayerInfos) {
+        LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str());
+    }
+    auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
@@ -94,7 +97,7 @@
 
     const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
                                  [layer](const auto& pair) { return pair.first == layer; });
-    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+    LOG_ALWAYS_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
 
     const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
     if (i < mActiveLayersEnd) {
@@ -111,7 +114,11 @@
 
     const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
                                  [layer](const auto& pair) { return pair.first == layer; });
-    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+    if (it == mLayerInfos.end()) {
+        // Offscreen layer
+        ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
+        return;
+    }
 
     const auto& info = it->second;
     const auto layerProps = LayerInfo::LayerProps{
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 7ff0ddf..4d51125 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -23,7 +23,6 @@
 #include <utils/threads.h>
 
 #include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
 
 #include "EventThread.h"
 #include "FrameTimeline.h"
@@ -33,27 +32,32 @@
 namespace android::impl {
 
 void MessageQueue::Handler::dispatchRefresh() {
-    if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+    if ((mEventMask.fetch_or(eventMaskRefresh) & eventMaskRefresh) == 0) {
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
     }
 }
 
 void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
-    if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+    if ((mEventMask.fetch_or(eventMaskInvalidate) & eventMaskInvalidate) == 0) {
         mVsyncId = vsyncId;
         mExpectedVSyncTime = expectedVSyncTimestamp;
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
     }
 }
 
+bool MessageQueue::Handler::invalidatePending() {
+    constexpr auto pendingMask = eventMaskInvalidate | eventMaskRefresh;
+    return (mEventMask.load() & pendingMask) != 0;
+}
+
 void MessageQueue::Handler::handleMessage(const Message& message) {
     switch (message.what) {
         case INVALIDATE:
-            android_atomic_and(~eventMaskInvalidate, &mEventMask);
+            mEventMask.fetch_and(~eventMaskInvalidate);
             mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
             break;
         case REFRESH:
-            android_atomic_and(~eventMaskRefresh, &mEventMask);
+            mEventMask.fetch_and(~eventMaskRefresh);
             mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
             break;
     }
@@ -106,7 +110,7 @@
     {
         std::lock_guard lock(mVsync.mutex);
         mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
-        mVsync.mScheduled = false;
+        mVsync.scheduled = false;
     }
     mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
                                          {targetWakeupTime, readyTime, vsyncTime}),
@@ -131,9 +135,10 @@
     ATRACE_CALL();
     std::lock_guard lock(mVsync.mutex);
     mVsync.workDuration = workDuration;
-    if (mVsync.mScheduled) {
-        mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
-                                       mVsync.lastCallbackTime.count()});
+    if (mVsync.scheduled) {
+        mVsync.expectedWakeupTime = mVsync.registration->schedule(
+                {mVsync.workDuration.get().count(),
+                 /*readyDuration=*/0, mVsync.lastCallbackTime.count()});
     }
 }
 
@@ -176,10 +181,11 @@
     }
 
     std::lock_guard lock(mVsync.mutex);
-    mVsync.mScheduled = true;
-    mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
-                                   .readyDuration = 0,
-                                   .earliestVsync = mVsync.lastCallbackTime.count()});
+    mVsync.scheduled = true;
+    mVsync.expectedWakeupTime =
+            mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+                                           .readyDuration = 0,
+                                           .earliestVsync = mVsync.lastCallbackTime.count()});
 }
 
 void MessageQueue::refresh() {
@@ -200,4 +206,19 @@
     }
 }
 
+std::optional<std::chrono::steady_clock::time_point> MessageQueue::nextExpectedInvalidate() {
+    if (mHandler->invalidatePending()) {
+        return std::chrono::steady_clock::now();
+    }
+
+    std::lock_guard lock(mVsync.mutex);
+    if (mVsync.scheduled) {
+        LOG_ALWAYS_FATAL_IF(!mVsync.expectedWakeupTime.has_value(), "callback was never scheduled");
+        const auto expectedWakeupTime = std::chrono::nanoseconds(*mVsync.expectedWakeupTime);
+        return std::optional<std::chrono::steady_clock::time_point>(expectedWakeupTime);
+    }
+
+    return std::nullopt;
+}
+
 } // namespace android::impl
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 2934af0..58ce9b9 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -72,6 +72,7 @@
     virtual void postMessage(sp<MessageHandler>&&) = 0;
     virtual void invalidate() = 0;
     virtual void refresh() = 0;
+    virtual std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() = 0;
 };
 
 // ---------------------------------------------------------------------------
@@ -81,9 +82,13 @@
 class MessageQueue : public android::MessageQueue {
 protected:
     class Handler : public MessageHandler {
-        enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
+        enum : uint32_t {
+            eventMaskInvalidate = 0x1,
+            eventMaskRefresh = 0x2,
+            eventMaskTransaction = 0x4
+        };
         MessageQueue& mQueue;
-        int32_t mEventMask;
+        std::atomic<uint32_t> mEventMask;
         std::atomic<int64_t> mVsyncId;
         std::atomic<nsecs_t> mExpectedVSyncTime;
 
@@ -92,6 +97,7 @@
         void handleMessage(const Message& message) override;
         virtual void dispatchRefresh();
         virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
+        virtual bool invalidatePending();
     };
 
     friend class Handler;
@@ -107,7 +113,8 @@
         TracedOrdinal<std::chrono::nanoseconds> workDuration
                 GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
         std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
-        bool mScheduled GUARDED_BY(mutex) = false;
+        bool scheduled GUARDED_BY(mutex) = false;
+        std::optional<nsecs_t> expectedWakeupTime GUARDED_BY(mutex);
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
@@ -141,6 +148,8 @@
 
     // sends REFRESH message at next VSYNC
     void refresh() override;
+
+    std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() override;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 9d71103..b52706f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,8 +16,10 @@
 
 #pragma once
 
+#include <utils/Log.h>
 #include <utils/Timers.h>
 #include <functional>
+#include <optional>
 #include <string>
 
 #include "StrongTyping.h"
@@ -26,7 +28,8 @@
 class TimeKeeper;
 class VSyncTracker;
 
-enum class ScheduleResult { Scheduled, CannotSchedule, Error };
+using ScheduleResult = std::optional<nsecs_t>;
+
 enum class CancelResult { Cancelled, TooLate, Error };
 
 /*
@@ -121,11 +124,8 @@
      *
      * \param [in] token           The callback to schedule.
      * \param [in] scheduleTiming  The timing information for this schedule call
-     * \return                     A ScheduleResult::Scheduled if callback was scheduled.
-     *                             A ScheduleResult::CannotSchedule
-     *                             if (workDuration + readyDuration - earliestVsync) is in the past,
-     * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if
-     * there was another error.
+     * \return                     The expected callback time if a callback was scheduled.
+     *                             std::nullopt if the callback is not registered.
      */
     virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index ca6ea27..28be962 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -26,6 +26,20 @@
 namespace android::scheduler {
 using base::StringAppendF;
 
+namespace {
+nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
+                                const VSyncDispatch::ScheduleTiming& timing) {
+    return nextVsyncTime - timing.readyDuration - timing.workDuration;
+}
+
+nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
+                                const VSyncDispatch::ScheduleTiming& timing) {
+    const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
+            std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+    return getExpectedCallbackTime(nextVsyncTime, timing);
+}
+} // namespace
+
 VSyncDispatch::~VSyncDispatch() = default;
 VSyncTracker::~VSyncTracker() = default;
 TimeKeeper::~TimeKeeper() = default;
@@ -74,7 +88,7 @@
     bool const wouldSkipAVsyncTarget =
             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
     if (wouldSkipAVsyncTarget) {
-        return ScheduleResult::Scheduled;
+        return getExpectedCallbackTime(nextVsyncTime, timing);
     }
 
     bool const alreadyDispatchedForVsync = mLastDispatchTime &&
@@ -89,7 +103,7 @@
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mScheduleTiming = timing;
     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
-    return ScheduleResult::Scheduled;
+    return getExpectedCallbackTime(nextVsyncTime, timing);
 }
 
 void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
@@ -317,7 +331,7 @@
 
 ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                  ScheduleTiming scheduleTiming) {
-    auto result = ScheduleResult::Error;
+    ScheduleResult result;
     {
         std::lock_guard lock(mMutex);
 
@@ -333,11 +347,11 @@
         auto const rearmImminent = now > mIntendedWakeupTime;
         if (CC_UNLIKELY(rearmImminent)) {
             callback->addPendingWorkloadUpdate(scheduleTiming);
-            return ScheduleResult::Scheduled;
+            return getExpectedCallbackTime(mTracker, now, scheduleTiming);
         }
 
         result = callback->schedule(scheduleTiming, mTracker, now);
-        if (result == ScheduleResult::CannotSchedule) {
+        if (!result.has_value()) {
             return result;
         }
 
@@ -416,7 +430,7 @@
 
 ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
     if (!mValidToken) {
-        return ScheduleResult::Error;
+        return std::nullopt;
     }
     return mDispatch.get().schedule(mToken, scheduleTiming);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index beda834..a5b7107 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -287,6 +287,8 @@
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
 const String16 sDump("android.permission.DUMP");
+const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
+
 const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
 
 // ---------------------------------------------------------------------------
@@ -306,6 +308,7 @@
 Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 bool SurfaceFlinger::useFrameRateApi;
+bool SurfaceFlinger::enableSdrDimming;
 
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
     switch(displayColorSetting) {
@@ -381,6 +384,12 @@
     mColorSpaceAgnosticDataspace =
             static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
 
+    mLayerCachingEnabled = [] {
+        const bool enable =
+                android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
+        return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
+    }();
+
     useContextPriority = use_context_priority(true);
 
     using Values = SurfaceFlingerProperties::primary_display_orientation_values;
@@ -471,6 +480,9 @@
     base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
 
     mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+
+    // Debug property overrides ro. property
+    enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
 }
 
 SurfaceFlinger::~SurfaceFlinger() = default;
@@ -1480,8 +1492,13 @@
     }
 
     return ftl::chain(schedule([=]() MAIN_THREAD {
-               if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
-                   return getHwComposer().setDisplayBrightness(*displayId,
+               if (const auto display = getDisplayDeviceLocked(displayToken)) {
+                   if (enableSdrDimming) {
+                       display->getCompositionDisplay()
+                               ->setDisplayBrightness(brightness.sdrWhitePointNits,
+                                                      brightness.displayBrightnessNits);
+                   }
+                   return getHwComposer().setDisplayBrightness(display->getPhysicalId(),
                                                                brightness.displayBrightness);
                } else {
                    ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
@@ -1703,11 +1720,11 @@
 }
 
 SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
-    // We are storing the last 2 present fences. If sf's phase offset is to be
-    // woken up before the actual vsync but targeting the next vsync, we need to check
-    // fence N-2
-    return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0]
-                                                          : mPreviousPresentFences[1];
+    const auto now = systemTime();
+    const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod;
+    const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod;
+    return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0]
+                                             : mPreviousPresentFences[1];
 }
 
 bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
@@ -1903,6 +1920,7 @@
         mRefreshPending = true;
         onMessageRefresh();
     }
+    notifyRegionSamplingThread();
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
@@ -2190,20 +2208,10 @@
         mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
             if (layer->isVisible() &&
                 compositionDisplay->belongsInOutput(layer->getCompositionEngineLayerFE())) {
-                bool isHdr = false;
-                switch (layer->getDataSpace()) {
-                    case ui::Dataspace::BT2020:
-                    case ui::Dataspace::BT2020_HLG:
-                    case ui::Dataspace::BT2020_PQ:
-                    case ui::Dataspace::BT2020_ITU:
-                    case ui::Dataspace::BT2020_ITU_HLG:
-                    case ui::Dataspace::BT2020_ITU_PQ:
-                        isHdr = true;
-                        break;
-                    default:
-                        isHdr = false;
-                        break;
-                }
+                const Dataspace transfer =
+                        static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
+                const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
+                                    transfer == Dataspace::TRANSFER_HLG);
 
                 if (isHdr) {
                     info.numberOfHdrLayers++;
@@ -2305,10 +2313,6 @@
         }
     }
 
-    if (mLumaSampling && mRegionSamplingThread) {
-        mRegionSamplingThread->notifyNewContent();
-    }
-
     // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
     // side-effect of getTotalSize(), so we check that again here
     if (ATRACE_ENABLED()) {
@@ -2642,6 +2646,7 @@
     builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
     builder.setName(state.displayName);
     const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+    compositionDisplay->setLayerCachingEnabled(mLayerCachingEnabled);
 
     sp<compositionengine::DisplaySurface> displaySurface;
     sp<IGraphicBufferProducer> producer;
@@ -2816,16 +2821,19 @@
 }
 
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
-    /*
-     * Traversal of the children
-     * (perform the transaction for each of them if needed)
-     */
+    // Commit display transactions
+    const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
+    if (displayTransactionNeeded) {
+        processDisplayChangesLocked();
+        processDisplayHotplugEventsLocked();
+    }
 
-    if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
-        mForceTraversal = false;
+    // Commit layer transactions. This needs to happen after display transactions are
+    // committed because some geometry logic relies on display orientation.
+    if ((transactionFlags & eTraversalNeeded) || mForceTraversal || displayTransactionNeeded) {
         mCurrentState.traverse([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-            if (!trFlags) return;
+            if (!trFlags && !displayTransactionNeeded) return;
 
             const uint32_t flags = layer->doTransaction(0);
             if (flags & Layer::eVisibleRegion)
@@ -2837,15 +2845,7 @@
         });
     }
 
-    /*
-     * Perform display own transactions if needed
-     */
-
-    if (transactionFlags & eDisplayTransactionNeeded) {
-        processDisplayChangesLocked();
-        processDisplayHotplugEventsLocked();
-    }
-
+    // Update transform hint
     if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
         // The transform hint might have changed for some layers
         // (either because a display has changed, or because a layer
@@ -3065,8 +3065,7 @@
                            configs.late.sfWorkDuration);
 
     mRegionSamplingThread =
-            new RegionSamplingThread(*this, *mScheduler,
-                                     RegionSamplingThread::EnvironmentTimingTunables());
+            new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
     mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
     // Dispatch a mode change request for the primary display on scheduler
     // initialization, so that the EventThreads always contain a reference to a
@@ -4039,6 +4038,11 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eDestinationFrameChanged) {
+        if (layer->setDestinationFrame(s.destinationFrame)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
     // the same transaction, then we have to make sure we reparent the children first so we do not
@@ -5187,9 +5191,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1038 are currently used for backdoors. The code
+    // Numbers from 1000 to 1040 are currently used for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1039) {
+    if (code >= 1000 && code <= 1040) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5586,6 +5590,36 @@
                 mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
                 return NO_ERROR;
             }
+            // Toggle caching feature
+            // First argument is an int32 - nonzero enables caching and zero disables caching
+            // Second argument is an optional uint64 - if present, then limits enabling/disabling
+            // caching to a particular physical display
+            case 1040: {
+                n = data.readInt32();
+                std::optional<PhysicalDisplayId> inputId = std::nullopt;
+                if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) {
+                    const auto token =
+                            getPhysicalDisplayToken(static_cast<PhysicalDisplayId>(inputDisplayId));
+                    if (!token) {
+                        ALOGE("No display with id: %" PRIu64, inputDisplayId);
+                        return NAME_NOT_FOUND;
+                    }
+
+                    inputId = std::make_optional<PhysicalDisplayId>(inputDisplayId);
+                }
+                {
+                    Mutex::Autolock lock(mStateLock);
+                    mLayerCachingEnabled = n != 0;
+                    for (const auto& [_, display] : mDisplays) {
+                        if (!inputId || *inputId == display->getPhysicalId()) {
+                            display->enableLayerCaching(mLayerCachingEnabled);
+                        }
+                    }
+                }
+                invalidateHwcGeometry();
+                repaintEverything();
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -5681,6 +5715,14 @@
     }
 }
 
+static bool hasCaptureBlackoutContentPermission() {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
+            PermissionCache::checkPermission(sCaptureBlackoutContent, pid, uid);
+}
+
 static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) {
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
@@ -5851,6 +5893,10 @@
     Rect layerStackSpaceRect;
     ui::Dataspace dataspace;
     bool captureSecureLayers;
+
+    // Call this before holding mStateLock to avoid any deadlocking.
+    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
+
     {
         Mutex::Autolock lock(mStateLock);
 
@@ -5860,9 +5906,8 @@
             return NAME_NOT_FOUND;
         }
 
-        const int uid = IPCThreadState::self()->getCallingUid();
-        const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
-        if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
+        if (!canCaptureBlackoutContent &&
+            parent->getCurrentState().flags & layer_state_t::eLayerSecure) {
             ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
             return PERMISSION_DENIED;
         }
@@ -6012,8 +6057,7 @@
         return BAD_VALUE;
     }
 
-    const int uid = IPCThreadState::self()->getCallingUid();
-    const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
+    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
 
     static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
         if (mRefreshPending) {
@@ -6033,8 +6077,9 @@
 
         status_t result = NO_ERROR;
         renderArea->render([&] {
-            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem,
-                                            regionSampling, grayscale, captureResults);
+            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer,
+                                            canCaptureBlackoutContent, regionSampling, grayscale,
+                                            captureResults);
         });
 
         captureResults.result = result;
@@ -6046,8 +6091,9 @@
 
 status_t SurfaceFlinger::renderScreenImplLocked(
         const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
-        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem,
-        bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) {
+        const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+        bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
+        ScreenCaptureResults& captureResults) {
     ATRACE_CALL();
 
     traverseLayers([&](Layer* layer) {
@@ -6060,7 +6106,7 @@
     // We allow the system server to take screenshots of secure layers for
     // use in situations like the Screen-rotation animation and place
     // the impetus on WindowManager to not persist them.
-    if (captureResults.capturedSecureLayers && !forSystem) {
+    if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
         ALOGW("FB is protected: PERMISSION_DENIED");
         return PERMISSION_DENIED;
     }
@@ -6400,13 +6446,17 @@
 
 void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
     mNumLayers++;
-    mScheduler->registerLayer(layer);
+    if (!layer->isRemovedFromCurrentState()) {
+        mScheduler->registerLayer(layer);
+    }
 }
 
 void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
-    mScheduler->deregisterLayer(layer);
     mNumLayers--;
     removeFromOffscreenLayers(layer);
+    if (!layer->isRemovedFromCurrentState()) {
+        mScheduler->deregisterLayer(layer);
+    }
 }
 
 // WARNING: ONLY CALL THIS FROM LAYER DTOR
@@ -6715,6 +6765,19 @@
 
     return layer;
 }
+
+void SurfaceFlinger::scheduleRegionSamplingThread() {
+    static_cast<void>(schedule([&] { notifyRegionSamplingThread(); }));
+}
+
+void SurfaceFlinger::notifyRegionSamplingThread() {
+    if (!mLumaSampling || !mRegionSamplingThread) {
+        return;
+    }
+
+    mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate());
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4bbdd48..cb8d312 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -271,6 +271,10 @@
 
     static constexpr SkipInitializationTag SkipInitialization;
 
+    // Whether or not SDR layers should be dimmed to the desired SDR white point instead of
+    // being treated as native display brightness
+    static bool enableSdrDimming;
+
     // must be called before clients can connect
     void init() ANDROID_API;
 
@@ -911,8 +915,8 @@
                                  const sp<IScreenCaptureListener>&);
     status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
                                     const std::shared_ptr<renderengine::ExternalTexture>&,
-                                    bool forSystem, bool regionSampling, bool grayscale,
-                                    ScreenCaptureResults&);
+                                    bool canCaptureBlackoutContent, bool regionSampling,
+                                    bool grayscale, ScreenCaptureResults&);
 
     sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
     sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock);
@@ -1236,6 +1240,7 @@
     int mDebugRegion = 0;
     bool mDebugDisableHWC = false;
     bool mDebugDisableTransformHint = false;
+    bool mLayerCachingEnabled = false;
     volatile nsecs_t mDebugInTransaction = 0;
     bool mForceFullDamage = false;
     bool mPropagateBackpressureClientComposition = false;
@@ -1428,6 +1433,9 @@
             REQUIRES(mStateLock);
 
     std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint;
+
+    void scheduleRegionSamplingThread();
+    void notifyRegionSamplingThread();
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index b3dca78..4a69c8f 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -321,6 +321,10 @@
     return defaultValue;
 }
 
+bool enable_sdr_dimming(bool defaultValue) {
+    return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue);
+}
+
 int32_t display_update_imminent_timeout_ms(int32_t defaultValue) {
     auto temp = SurfaceFlingerProperties::display_update_imminent_timeout_ms();
     if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index b19d216..039d316 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -102,6 +102,8 @@
 
 bool enable_layer_caching(bool defaultValue);
 
+bool enable_sdr_dimming(bool defaultValue);
+
 } // namespace sysprop
 } // namespace android
 #endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 10d58a6..d6a0787 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -351,12 +351,16 @@
             std::max(mTimeStats.displayEventConnectionsCountLegacy, count);
 }
 
+static int32_t toMs(nsecs_t nanos) {
+    int64_t millis =
+            std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds(nanos))
+                    .count();
+    millis = std::clamp(millis, int64_t(INT32_MIN), int64_t(INT32_MAX));
+    return static_cast<int32_t>(millis);
+}
+
 static int32_t msBetween(nsecs_t start, nsecs_t end) {
-    int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
-                            std::chrono::nanoseconds(end - start))
-                            .count();
-    delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
-    return static_cast<int32_t>(delta);
+    return toMs(end - start);
 }
 
 void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) {
@@ -829,10 +833,9 @@
         // TimeStats Histograms only retain positive values, so we don't need to check if these
         // deadlines were really missed if we know that the frame had jank, since deadlines
         // that were met will be dropped.
-        timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta));
-        timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter));
-        timeStatsLayer.deltas["appDeadlineDeltas"].insert(
-                static_cast<int32_t>(info.appDeadlineDelta));
+        timelineStats.displayDeadlineDeltas.insert(toMs(info.displayDeadlineDelta));
+        timelineStats.displayPresentDeltas.insert(toMs(info.displayPresentJitter));
+        timeStatsLayer.deltas["appDeadlineDeltas"].insert(toMs(info.appDeadlineDelta));
     }
 }
 
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index ee5542d..78f8a2f 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -463,3 +463,12 @@
     access: Readonly
     prop_name: "ro.surface_flinger.enable_layer_caching"
 }
+
+# Enables SDR layer dimming
+prop {
+    api_name: "enable_sdr_dimming"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.enable_sdr_dimming"
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 47e14f6..9c567d6 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -53,6 +53,10 @@
     prop_name: "ro.surface_flinger.protected_contents"
   }
   prop {
+    api_name: "enable_sdr_dimming"
+    prop_name: "ro.surface_flinger.enable_sdr_dimming"
+  }
+  prop {
     api_name: "force_hwc_copy_for_virtual_displays"
     prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
   }
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index 4753362..34c9182 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -19,6 +19,7 @@
 #pragma clang diagnostic ignored "-Wconversion"
 
 #include <gui/BufferItemConsumer.h>
+#include <private/android_filesystem_config.h>
 #include "TransactionTestHarnesses.h"
 
 namespace android {
@@ -170,7 +171,11 @@
     args.displayToken = mDisplay;
 
     ScreenCaptureResults captureResults;
-    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+    {
+        // Ensure the UID is not root because root has all permissions
+        UIDFaker f(AID_APP_START);
+        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
+    }
 
     Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
     ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 2e9c10c..6912fcf 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -84,7 +84,11 @@
 
     Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
 
-    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+    {
+        // Ensure the UID is not root because root has all permissions
+        UIDFaker f(AID_APP_START);
+        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+    }
 
     UIDFaker f(AID_SYSTEM);
 
@@ -528,7 +532,7 @@
     ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
 }
 
-TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
+TEST_F(ScreenCaptureTest, CaptureSecureLayer) {
     sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
                                               ISurfaceComposerClient::eFXSurfaceBufferState);
     sp<SurfaceControl> secureLayer =
@@ -552,8 +556,12 @@
     args.childrenOnly = false;
     ScreenCaptureResults captureResults;
 
-    // Call from outside system with secure layers will result in permission denied
-    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+    {
+        // Ensure the UID is not root because root has all permissions
+        UIDFaker f(AID_APP_START);
+        // Call from outside system with secure layers will result in permission denied
+        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
+    }
 
     UIDFaker f(AID_SYSTEM);
 
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 54f4c7c..a9ad249 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -66,7 +66,7 @@
             ALOGD("schedule: %zu", token.value());
             if (mCallbacks.count(token) == 0) {
                 ALOGD("schedule: callback %zu not registered", token.value());
-                return scheduler::ScheduleResult::Error;
+                return scheduler::ScheduleResult{};
             }
 
             auto& callback = mCallbacks.at(token);
@@ -75,7 +75,7 @@
             callback.targetWakeupTime =
                     timing.earliestVsync - timing.workDuration - timing.readyDuration;
             ALOGD("schedule: callback %zu scheduled", token.value());
-            return scheduler::ScheduleResult::Scheduled;
+            return scheduler::ScheduleResult{callback.targetWakeupTime};
         });
 
         ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 8208b3f..dbd51fe 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -104,8 +104,12 @@
     const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
 }
 
 TEST_F(MessageQueueTest, invalidateTwice) {
@@ -114,11 +118,15 @@
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_EQ(4567, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
 }
 
 TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
@@ -127,8 +135,10 @@
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+    EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
+    EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
 
     const auto startTime = 100;
     const auto endTime = startTime + mDuration.count();
@@ -141,12 +151,14 @@
     EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
     EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
 
+    EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+
     const auto timingAfterCallback =
             scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
                                                      .readyDuration = 0,
                                                      .earliestVsync = presentTime};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 }
 
@@ -158,7 +170,7 @@
                                                      .readyDuration = 0,
                                                      .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index ff53a7b..3e4e130 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -1011,6 +1011,9 @@
     constexpr size_t MISSED_FRAMES = 4;
     constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
     constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
+    constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
+    constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
+    constexpr nsecs_t APP_DEADLINE_DELTA = 3'000'000;
 
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -1036,24 +1039,35 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+                                      JankType::SurfaceFlingerCpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+                                      JankType::SurfaceFlingerGpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::DisplayHAL, 1, 2, 3});
+                                      JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::AppDeadlineMissed, 1, 2, 3});
+                                      JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerScheduling, 1, 2, 3});
+                                      JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::PredictionError, 1, 2, 3});
+                                      JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
-                                      3});
+                                      JankType::AppDeadlineMissed | JankType::BufferStuffing,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::BufferStuffing, 1, 2, 3});
+                                      JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::None, 1, 2, 3});
+                                      JankType::None, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
 
     std::string pulledData;
     EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
@@ -1137,6 +1151,10 @@
 TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
     constexpr size_t LATE_ACQUIRE_FRAMES = 2;
     constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
+    constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000;
+    constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000;
+    constexpr nsecs_t APP_DEADLINE_DELTA_2MS = 2'000'000;
+    constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000;
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
@@ -1155,22 +1173,32 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
 
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+                                      JankType::SurfaceFlingerCpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+                                      JankType::SurfaceFlingerGpuDeadlineMissed,
+                                      DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+                                      APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::DisplayHAL, 1, 2, 3});
+                                      JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::AppDeadlineMissed, 1, 2, 3});
+                                      JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::SurfaceFlingerScheduling, 1, 2, 2});
+                                      JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::PredictionError, 1, 2, 2});
+                                      JankType::PredictionError, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2,
-                                      2});
+                                      JankType::AppDeadlineMissed | JankType::BufferStuffing,
+                                      DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS,
+                                      APP_DEADLINE_DELTA_2MS});
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                      JankType::None, 1, 2, 3});
+                                      JankType::None, DISPLAY_DEADLINE_DELTA,
+                                      DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
 
     std::string pulledData;
     EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index b64cce9..d59d64b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -232,11 +232,12 @@
         VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
                                           mVsyncMoveThreshold};
         CountingCallback cb(mDispatch);
-        EXPECT_EQ(mDispatch.schedule(cb,
-                                     {.workDuration = 100,
-                                      .readyDuration = 0,
-                                      .earliestVsync = 1000}),
-                  ScheduleResult::Scheduled);
+        const auto result = mDispatch.schedule(cb,
+                                               {.workDuration = 100,
+                                                .readyDuration = 0,
+                                                .earliestVsync = 1000});
+        EXPECT_TRUE(result.has_value());
+        EXPECT_EQ(900, *result);
     }
 }
 
@@ -245,11 +246,13 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = intended}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = 100,
+                                            .readyDuration = 0,
+                                            .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
+
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -277,11 +280,12 @@
     EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = workDuration,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = workDuration,
+                                            .readyDuration = 0,
+                                            .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -289,11 +293,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
 }
 
@@ -302,11 +306,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
     mMockClock.advanceBy(950);
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
 }
@@ -316,11 +320,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -337,11 +341,11 @@
 
     PausingCallback cb(mDispatch, 50ms);
     cb.stashResource(resource);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -535,21 +539,25 @@
     std::optional<nsecs_t> lastTarget;
     tmp = mDispatch.registerCallback(
             [&](auto timestamp, auto, auto) {
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp - mVsyncMoveThreshold}),
-                          ScheduleResult::Scheduled);
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp}),
-                          ScheduleResult::Scheduled);
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp + mVsyncMoveThreshold}),
-                          ScheduleResult::Scheduled);
+                auto result =
+                        mDispatch.schedule(tmp,
+                                           {.workDuration = 400,
+                                            .readyDuration = 0,
+                                            .earliestVsync = timestamp - mVsyncMoveThreshold});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                result = mDispatch.schedule(tmp,
+                                            {.workDuration = 400,
+                                             .readyDuration = 0,
+                                             .earliestVsync = timestamp});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                result = mDispatch.schedule(tmp,
+                                            {.workDuration = 400,
+                                             .readyDuration = 0,
+                                             .earliestVsync = timestamp + mVsyncMoveThreshold});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
                 lastTarget = timestamp;
             },
             "oo");
@@ -627,36 +635,41 @@
 
 TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
     VSyncDispatch::CallbackToken token(100);
-    EXPECT_THAT(mDispatch.schedule(token,
-                                   {.workDuration = 100,
-                                    .readyDuration = 0,
-                                    .earliestVsync = 1000}),
-                Eq(ScheduleResult::Error));
+    EXPECT_FALSE(mDispatch
+                         .schedule(token,
+                                   {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+                         .has_value());
     EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
 }
 
 // b/1450138150
 TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
     EXPECT_CALL(mMockClock, alarmAt(_, 500));
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     mMockClock.advanceBy(400);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1200, *result);
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
 }
@@ -667,24 +680,30 @@
             .WillOnce(Return(1000))
             .WillOnce(Return(1002));
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     mMockClock.advanceBy(400);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(602, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -692,26 +711,32 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1100, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
     EXPECT_CALL(mMockClock, alarmAt(_, 600));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     advanceToNextCallback();
 }
@@ -754,16 +779,19 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -779,16 +807,19 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
     CountingCallback cb(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1630, *result);
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -802,12 +833,15 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -828,12 +862,15 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -861,12 +898,15 @@
             .InSequence(seq)
             .WillOnce(Return(1000));
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(610, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(700);
@@ -886,11 +926,12 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 70,
-                                  .readyDuration = 30,
-                                  .earliestVsync = intended}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = 70,
+                                            .readyDuration = 30,
+                                            .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -922,9 +963,9 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -944,9 +985,9 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
-                               mStubTracker, now),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+                               mStubTracker, now)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(9500));
@@ -967,9 +1008,9 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -1002,9 +1043,9 @@
     entry.update(mStubTracker, 0);
     EXPECT_FALSE(entry.wakeupTime());
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(wakeup, Eq(900));
@@ -1018,9 +1059,9 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     entry.update(mStubTracker, 0);
 
     auto const wakeup = entry.wakeupTime();
@@ -1031,26 +1072,26 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     entry.executing(); // 1000 is executing
     // had 1000 not been executing, this could have been scheduled for time 800.
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 }
@@ -1071,32 +1112,32 @@
             .InSequence(seq)
             .WillOnce(Return(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 
     entry.executing(); // 1000 is executing
 
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
@@ -1128,9 +1169,9 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 453c93a..0e7b320 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -39,6 +39,7 @@
                  void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                       std::chrono::nanoseconds));
     MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
+    MOCK_METHOD0(nextExpectedInvalidate, std::optional<std::chrono::steady_clock::time_point>());
 };
 
 } // namespace android::mock