Merge "Add TFLite model for motion prediction."
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index cc038ae..695faf8 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -718,7 +718,8 @@
     if (service.guaranteeClient) {
         // we have no record of this client
         if (!service.hasClients && !hasClients) {
-            sendClientCallbackNotifications(serviceName, true);
+            sendClientCallbackNotifications(serviceName, true,
+                                            "service is guaranteed to be in use");
         }
 
         // guarantee is temporary
@@ -729,34 +730,41 @@
     if (isCalledOnInterval) {
         if (hasClients && !service.hasClients) {
             // client was retrieved in some other way
-            sendClientCallbackNotifications(serviceName, true);
+            sendClientCallbackNotifications(serviceName, true, "we now have a record of a client");
         }
 
         // there are no more clients, but the callback has not been called yet
         if (!hasClients && service.hasClients) {
-            sendClientCallbackNotifications(serviceName, false);
+            sendClientCallbackNotifications(serviceName, false,
+                                            "we now have no record of a client");
         }
     }
 
     return count;
 }
 
-void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
+void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName,
+                                                     bool hasClients, const char* context) {
     auto serviceIt = mNameToService.find(serviceName);
     if (serviceIt == mNameToService.end()) {
-        ALOGW("sendClientCallbackNotifications could not find service %s", serviceName.c_str());
+        ALOGW("sendClientCallbackNotifications could not find service %s when %s",
+              serviceName.c_str(), context);
         return;
     }
     Service& service = serviceIt->second;
 
-    CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
-        << " so we can't tell clients again that we have client: " << hasClients;
+    CHECK(hasClients != service.hasClients)
+            << "Record shows: " << service.hasClients
+            << " so we can't tell clients again that we have client: " << hasClients
+            << " when: " << context;
 
-    ALOGI("Notifying %s they have clients: %d", serviceName.c_str(), hasClients);
+    ALOGI("Notifying %s they %s have clients when %s", serviceName.c_str(),
+          hasClients ? "do" : "don't", context);
 
     auto ccIt = mNameToClientCallback.find(serviceName);
     CHECK(ccIt != mNameToClientCallback.end())
-        << "sendClientCallbackNotifications could not find callbacks for service ";
+            << "sendClientCallbackNotifications could not find callbacks for service when "
+            << context;
 
     for (const auto& callback : ccIt->second) {
         callback->onClients(service.binder, hasClients);
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index b24c11c..f9d4f8f 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -92,8 +92,9 @@
                         ServiceCallbackMap::iterator* it,
                         bool* found);
     ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval);
-     // Also updates mHasClients (of what the last callback was)
-    void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
+    // Also updates mHasClients (of what the last callback was)
+    void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients,
+                                         const char* context);
     // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
     // this updates the iterator to the next location
     void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 4a5bd5e..b494f89 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -37,6 +37,7 @@
 
 #include <android/api-level.h>
 #include <stdint.h>
+#include <unistd.h>
 
 __BEGIN_DECLS
 
@@ -173,7 +174,7 @@
  */
 int APerformanceHint_setThreads(
         APerformanceHintSession* session,
-        const int32_t* threadIds,
+        const pid_t* threadIds,
         size_t size) __INTRODUCED_IN(__ANDROID_API_U__);
 
 __END_DECLS
diff --git a/include/input/Input.h b/include/input/Input.h
index e281675..7573282 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -529,7 +529,7 @@
     inline nsecs_t getEventTime() const { return mEventTime; }
 
     static const char* getLabel(int32_t keyCode);
-    static int32_t getKeyCodeFromLabel(const char* label);
+    static std::optional<int> getKeyCodeFromLabel(const char* label);
 
     void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
                     std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
@@ -550,6 +550,8 @@
     nsecs_t mEventTime;
 };
 
+std::ostream& operator<<(std::ostream& out, const KeyEvent& event);
+
 /*
  * Motion events.
  */
@@ -840,7 +842,7 @@
     }
 
     static const char* getLabel(int32_t axis);
-    static int32_t getAxisFromLabel(const char* label);
+    static std::optional<int> getAxisFromLabel(const char* label);
 
     static std::string actionToString(int32_t action);
 
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index b4374ac..4668fce 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -35,22 +35,22 @@
 
 class InputEventLookup {
 public:
-    static int lookupValueByLabel(const std::unordered_map<std::string, int>& map,
-                                  const char* literal);
+    static std::optional<int> lookupValueByLabel(const std::unordered_map<std::string, int>& map,
+                                                 const char* literal);
 
     static const char* lookupLabelByValue(const std::vector<InputEventLabel>& vec, int value);
 
-    static int32_t getKeyCodeByLabel(const char* label);
+    static std::optional<int> getKeyCodeByLabel(const char* label);
 
     static const char* getLabelByKeyCode(int32_t keyCode);
 
-    static uint32_t getKeyFlagByLabel(const char* label);
+    static std::optional<int> getKeyFlagByLabel(const char* label);
 
-    static int32_t getAxisByLabel(const char* label);
+    static std::optional<int> getAxisByLabel(const char* label);
 
     static const char* getAxisLabel(int32_t axisId);
 
-    static int32_t getLedByLabel(const char* label);
+    static std::optional<int> getLedByLabel(const char* label);
 
 private:
     static const std::unordered_map<std::string, int> KEYCODES;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 808b1ec..7617136 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -74,6 +74,9 @@
     name: "libbinder_common_defaults",
     host_supported: true,
 
+    // for vndbinder and binderRpcTest
+    vendor_available: true,
+
     srcs: [
         "Binder.cpp",
         "BpBinder.cpp",
@@ -197,6 +200,7 @@
 
 cc_library_headers {
     name: "trusty_mock_headers",
+    vendor_available: true,
     host_supported: true,
 
     export_include_dirs: [
@@ -211,6 +215,8 @@
 
 cc_defaults {
     name: "trusty_mock_defaults",
+    vendor_available: true,
+    host_supported: true,
 
     header_libs: [
         "trusty_mock_headers",
@@ -295,8 +301,6 @@
 
     version_script: "libbinder.map",
 
-    // for vndbinder
-    vendor_available: true,
     vndk: {
         enabled: true,
     },
@@ -455,6 +459,7 @@
 cc_library_static {
     name: "libbinder_tls_static",
     defaults: ["libbinder_tls_defaults"],
+    vendor_available: true,
     visibility: [
         ":__subpackages__",
     ],
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 44ff62b..0aca163 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -375,6 +375,10 @@
     return (mDataSize > mDataPos ? mDataSize : mDataPos);
 }
 
+size_t Parcel::dataBufferSize() const {
+    return mDataSize;
+}
+
 size_t Parcel::dataAvail() const
 {
     size_t result = dataSize() - dataPosition();
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 5406205..2e70304 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -123,8 +123,8 @@
                        static_cast<int32_t>(timestamp.tv_nsec),
                        0};
 
-    t.mData.mInterfaceName = String8(interfaceName);
-    if (interfaceName.size() != t.mData.mInterfaceName.bytes()) {
+    t.mData.mInterfaceName = std::string(String8(interfaceName).string());
+    if (interfaceName.size() != t.mData.mInterfaceName.size()) {
         LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
                       "utf-8: "
                    << interfaceName;
@@ -231,7 +231,8 @@
                 break;
             }
             case INTERFACE_NAME_CHUNK: {
-                t.mData.mInterfaceName.setTo(reinterpret_cast<char*>(payloadMap), chunk.dataSize);
+                t.mData.mInterfaceName =
+                        std::string(reinterpret_cast<char*>(payloadMap), chunk.dataSize);
                 break;
             }
             case DATA_PARCEL_CHUNK: {
@@ -308,7 +309,7 @@
     }
     if (NO_ERROR !=
         writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t),
-                   reinterpret_cast<const uint8_t*>(mData.mInterfaceName.string()))) {
+                   reinterpret_cast<const uint8_t*>(mData.mInterfaceName.c_str()))) {
         LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get();
         return UNKNOWN_ERROR;
     }
@@ -328,7 +329,7 @@
     return NO_ERROR;
 }
 
-const android::String8& RecordedTransaction::getInterfaceName() const {
+const std::string& RecordedTransaction::getInterfaceName() const {
     return mData.mInterfaceName;
 }
 
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 180c67c..04cb61f 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -99,5 +99,19 @@
     {
       "name": "binderLibTest"
     }
+  ],
+  "kernel-presubmit": [
+    {
+      "name": "binderDriverInterfaceTest"
+    },
+    {
+      "name": "binderLibTest"
+    },
+    {
+      "name": "binderSafeInterfaceTest"
+    },
+    {
+      "name": "memunreachable_binder_test"
+    }
   ]
 }
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index f730acb..162cd40 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -75,6 +75,7 @@
     size_t              dataAvail() const;
     size_t              dataPosition() const;
     size_t              dataCapacity() const;
+    size_t dataBufferSize() const;
 
     status_t            setDataSize(size_t size);
 
diff --git a/libs/binder/include/binder/RecordedTransaction.h b/libs/binder/include/binder/RecordedTransaction.h
index 4966330..eb765fe 100644
--- a/libs/binder/include/binder/RecordedTransaction.h
+++ b/libs/binder/include/binder/RecordedTransaction.h
@@ -41,7 +41,7 @@
 
     [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const;
 
-    const String8& getInterfaceName() const;
+    const std::string& getInterfaceName() const;
     uint32_t getCode() const;
     uint32_t getFlags() const;
     int32_t getReturnedStatus() const;
@@ -73,7 +73,7 @@
 
     struct MovableData { // movable
         TransactionHeader mHeader;
-        String8 mInterfaceName;
+        std::string mInterfaceName;
     };
     MovableData mData;
     Parcel mSent;
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 9b0d222..b0c7f6d 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -139,30 +139,6 @@
 };
 
 template <typename _T>
-class ToEmptyString {
-    template <typename _U>
-    static std::enable_if_t<false
-#ifdef HAS_NDK_INTERFACE
-                                    || std::is_base_of_v<::ndk::ICInterface, _U>
-#if __ANDROID_API__ >= 31
-                                    || std::is_same_v<::ndk::AParcelableHolder, _U>
-#endif
-#endif  // HAS_NDK_INTERFACE
-#ifdef HAS_CPP_INTERFACE
-                                    || std::is_base_of_v<IInterface, _U> ||
-                                    std::is_same_v<IBinder, _U>
-#endif
-                            ,
-                            std::true_type>
-    _test(int);
-    template <typename _U>
-    static std::false_type _test(...);
-
-   public:
-    enum { value = decltype(_test<_T>(0))::value };
-};
-
-template <typename _T>
 struct TypeDependentFalse {
     enum { value = false };
 };
@@ -171,9 +147,7 @@
 
 template <typename _T>
 std::string ToString(const _T& t) {
-    if constexpr (details::ToEmptyString<_T>::value) {
-        return "<unimplemented>";
-    } else if constexpr (std::is_same_v<bool, _T>) {
+    if constexpr (std::is_same_v<bool, _T>) {
         return t ? "true" : "false";
     } else if constexpr (std::is_same_v<char16_t, _T>) {
         // TODO(b/244494451): codecvt is deprecated in C++17 -- suppress the
@@ -193,6 +167,24 @@
         return ss.str();
     } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
         return "fd:" + std::to_string(t.get());
+    } else if constexpr (std::is_base_of_v<::ndk::ICInterface, _T>) {
+        // TODO(b/266248339): this format is to make it easy to handle resolv_integration_test
+        // freezing the output format. We would like to print more info.
+        return "<interface>";
+#if __ANDROID_API__ >= 31
+    } else if constexpr (std::is_same_v<::ndk::AParcelableHolder, _T>) {
+        return "AParcelableHolder";
+#endif
+#endif  // HAS_NDK_INTERFACE
+#ifdef HAS_CPP_INTERFACE
+    } else if constexpr (std::is_base_of_v<IInterface, _T>) {
+        std::stringstream ss;
+        ss << "interface:" << std::hex << &t;
+        return ss.str();
+    } else if constexpr (std::is_same_v<IBinder, _T>) {
+        std::stringstream ss;
+        ss << "binder:" << std::hex << &t;
+        return ss.str();
 #endif
 #ifdef HAS_STRING16
     } else if constexpr (std::is_same_v<String16, _T>) {
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 94f72d9..b5a2e2f 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -695,7 +695,10 @@
     if (parcel->get()->objectsCount()) {
         return STATUS_INVALID_OPERATION;
     }
-    int32_t dataSize = AParcel_getDataSize(parcel);
+    // b/264739302 - getDataSize will return dataPos if it is greater than dataSize
+    // which will cause crashes in memcpy at later point. Instead compare with
+    // actual length of internal buffer
+    int32_t dataSize = parcel->get()->dataBufferSize();
     if (len > static_cast<size_t>(dataSize) || start > static_cast<size_t>(dataSize) - len) {
         return STATUS_BAD_VALUE;
     }
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index bab4e73..458dff3 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -138,6 +138,7 @@
 
 aidl_interface {
     name: "binderRpcTestIface",
+    vendor_available: true,
     host_supported: true,
     unstable: true,
     srcs: [
@@ -158,6 +159,7 @@
 
 cc_library_static {
     name: "libbinder_tls_test_utils",
+    vendor_available: true,
     host_supported: true,
     target: {
         darwin: {
@@ -211,6 +213,7 @@
     defaults: [
         "binderRpcTest_common_defaults",
     ],
+    vendor_available: true,
     gtest: false,
     auto_gen_config: false,
     srcs: [
@@ -221,10 +224,18 @@
 
 cc_defaults {
     name: "binderRpcTest_defaults",
+    vendor_available: true,
     target: {
         android: {
             test_suites: ["vts"],
         },
+
+        vendor: {
+            shared_libs: [
+                "libbinder_trusty",
+                "libtrusty",
+            ],
+        },
     },
     defaults: [
         "binderRpcTest_common_defaults",
@@ -267,6 +278,7 @@
     name: "binderRpcTest_static_defaults",
 
     shared_libs: [
+        "liblog",
         "libutils",
         // libcrypto_static is not visible to this module
         "libcrypto",
@@ -274,7 +286,6 @@
     static_libs: [
         "libbase",
         "libcutils",
-        "liblog",
         "libssl",
     ],
 
@@ -360,6 +371,30 @@
 }
 
 cc_test {
+    name: "binderRpcTest_on_trusty_mock",
+    defaults: [
+        "trusty_mock_defaults",
+    ],
+
+    srcs: [
+        "binderRpcUniversalTests.cpp",
+        "binderRpcTestCommon.cpp",
+        "binderRpcTestTrusty.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder_on_trusty_mock",
+        "libbase",
+        "libutils",
+        "libcutils",
+    ],
+
+    static_libs: [
+        "binderRpcTestIface-cpp",
+    ],
+}
+
+cc_test {
     name: "binderRpcTest",
     defaults: [
         "binderRpcTest_defaults",
@@ -371,6 +406,7 @@
     required: [
         "libbinder_on_trusty_mock",
         "binderRpcTestService_on_trusty_mock",
+        "binderRpcTest_on_trusty_mock",
     ],
 }
 
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index f7498c4..955c650 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1406,9 +1406,11 @@
     ASSERT_TRUE(server != nullptr);
     int32_t delay = 1000; // ms
     data.writeInt32(delay);
+    // b/266537959 - must take before taking lock, since countdown is started in the remote
+    // process there.
+    size_t epochMsBefore = epochMillis();
     EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK, data, &reply), NO_ERROR);
     std::vector<std::thread> ts;
-    size_t epochMsBefore = epochMillis();
     for (size_t i = 0; i < kKernelThreads + 1; i++) {
         ts.push_back(std::thread([&] {
             Parcel local_reply;
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
index 2f5c8c6..30172cc 100644
--- a/libs/binder/tests/binderRecordedTransactionTest.cpp
+++ b/libs/binder/tests/binderRecordedTransactionTest.cpp
@@ -45,7 +45,7 @@
 
     auto retrievedTransaction = RecordedTransaction::fromFile(fd);
 
-    EXPECT_EQ(retrievedTransaction->getInterfaceName(), android::String8(interfaceName));
+    EXPECT_EQ(retrievedTransaction->getInterfaceName(), android::String8(interfaceName).c_str());
     EXPECT_EQ(retrievedTransaction->getCode(), 1);
     EXPECT_EQ(retrievedTransaction->getFlags(), 42);
     EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 36c8d8c..84c93dd 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -28,6 +28,11 @@
 #include <sys/prctl.h>
 #include <sys/socket.h>
 
+#ifdef __ANDROID_VENDOR__
+#include <binder/RpcTransportTipcAndroid.h>
+#include <trusty/tipc.h>
+#endif // __ANDROID_VENDOR__
+
 #include "binderRpcTestCommon.h"
 #include "binderRpcTestFixture.h"
 
@@ -45,6 +50,10 @@
 constexpr bool kEnableSharedLibs = true;
 #endif
 
+#ifdef __ANDROID_VENDOR__
+constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
+#endif
+
 static std::string WaitStatusToString(int wstatus) {
     if (WIFEXITED(wstatus)) {
         return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
@@ -270,6 +279,11 @@
 
     auto ret = std::make_unique<LinuxProcessSession>(
             Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) {
+                if (socketType == SocketType::TIPC) {
+                    // Trusty has a single persistent service
+                    return;
+                }
+
                 auto writeFd = std::to_string(writeEnd.get());
                 auto readFd = std::to_string(readEnd.get());
                 execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
@@ -288,31 +302,47 @@
         serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
                 static_cast<int32_t>(mode));
     }
-    writeToFd(ret->host.writeEnd(), serverConfig);
+    if (socketType != SocketType::TIPC) {
+        writeToFd(ret->host.writeEnd(), serverConfig);
+    }
 
     std::vector<sp<RpcSession>> sessions;
     auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
     for (size_t i = 0; i < options.numSessions; i++) {
-        sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
+        std::unique_ptr<RpcTransportCtxFactory> factory;
+        if (socketType == SocketType::TIPC) {
+#ifdef __ANDROID_VENDOR__
+            factory = RpcTransportCtxFactoryTipcAndroid::make();
+#else
+            LOG_ALWAYS_FATAL("TIPC socket type only supported on vendor");
+#endif
+        } else {
+            factory = newFactory(rpcSecurity, certVerifier);
+        }
+        sessions.emplace_back(RpcSession::make(std::move(factory)));
     }
 
-    auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd());
-    BinderRpcTestClientInfo clientInfo;
-    for (const auto& session : sessions) {
-        auto& parcelableCert = clientInfo.certs.emplace_back();
-        parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
-    }
-    writeToFd(ret->host.writeEnd(), clientInfo);
+    BinderRpcTestServerInfo serverInfo;
+    if (socketType != SocketType::TIPC) {
+        serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd());
+        BinderRpcTestClientInfo clientInfo;
+        for (const auto& session : sessions) {
+            auto& parcelableCert = clientInfo.certs.emplace_back();
+            parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
+        }
+        writeToFd(ret->host.writeEnd(), clientInfo);
 
-    CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
-    if (socketType == SocketType::INET) {
-        CHECK_NE(0, serverInfo.port);
-    }
+        CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
+        if (socketType == SocketType::INET) {
+            CHECK_NE(0, serverInfo.port);
+        }
 
-    if (rpcSecurity == RpcSecurity::TLS) {
-        const auto& serverCert = serverInfo.cert.data;
-        CHECK_EQ(OK,
-                 certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert));
+        if (rpcSecurity == RpcSecurity::TLS) {
+            const auto& serverCert = serverInfo.cert.data;
+            CHECK_EQ(OK,
+                     certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+                                                             serverCert));
+        }
     }
 
     status_t status;
@@ -343,6 +373,19 @@
             case SocketType::INET:
                 status = session->setupInetClient("127.0.0.1", serverInfo.port);
                 break;
+            case SocketType::TIPC:
+                status = session->setupPreconnectedClient({}, [=]() {
+#ifdef __ANDROID_VENDOR__
+                    auto port = trustyIpcPort(serverVersion);
+                    int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
+                    return tipcFd >= 0 ? android::base::unique_fd(tipcFd)
+                                       : android::base::unique_fd();
+#else
+                    LOG_ALWAYS_FATAL("Tried to connect to Trusty outside of vendor");
+                    return android::base::unique_fd();
+#endif
+                });
+                break;
             default:
                 LOG_ALWAYS_FATAL("Unknown socket type");
         }
@@ -684,6 +727,10 @@
 }
 
 TEST_P(BinderRpc, DeathRecipientFailsWithoutIncoming) {
+    if (socketType() == SocketType::TIPC) {
+        // This should work, but Trusty takes too long to restart the service
+        GTEST_SKIP() << "Service death test not supported on Trusty";
+    }
     class MyDeathRec : public IBinder::DeathRecipient {
     public:
         void binderDied(const wp<IBinder>& /* who */) override {}
@@ -725,6 +772,11 @@
 }
 
 TEST_P(BinderRpc, Die) {
+    if (socketType() == SocketType::TIPC) {
+        // This should work, but Trusty takes too long to restart the service
+        GTEST_SKIP() << "Service death test not supported on Trusty";
+    }
+
     for (bool doDeathCleanup : {true, false}) {
         auto proc = createRpcTestSocketServerProcess({});
 
@@ -777,6 +829,10 @@
 }
 
 TEST_P(BinderRpc, FileDescriptorTransportRejectNone) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
             .serverSupportedFileDescriptorTransportModes =
@@ -793,6 +849,10 @@
 }
 
 TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
@@ -809,6 +869,10 @@
 }
 
 TEST_P(BinderRpc, FileDescriptorTransportOptionalUnix) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
             .serverSupportedFileDescriptorTransportModes =
@@ -822,6 +886,10 @@
 }
 
 TEST_P(BinderRpc, ReceiveFile) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
@@ -842,6 +910,10 @@
 }
 
 TEST_P(BinderRpc, SendFiles) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
@@ -914,6 +986,10 @@
 }
 
 TEST_P(BinderRpc, AppendInvalidFd) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
+
     auto proc = createRpcTestSocketServerProcess({
             .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
             .serverSupportedFileDescriptorTransportModes =
@@ -940,6 +1016,7 @@
     ASSERT_EQ(-1, pRaw.readFileDescriptor());
 }
 
+#ifndef __ANDROID_VENDOR__ // No AIBinder_fromPlatformBinder on vendor
 TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
     if constexpr (!kEnableSharedLibs) {
         GTEST_SKIP() << "Test disabled because Binder was built as a static library";
@@ -971,6 +1048,7 @@
     ASSERT_TRUE(status.isOk()) << status.getDescription();
     ASSERT_EQ("aoeuaoeu", out);
 }
+#endif // __ANDROID_VENDOR__
 
 ssize_t countFds() {
     DIR* dir = opendir("/proc/self/fd/");
@@ -986,6 +1064,9 @@
     if (serverSingleThreaded()) {
         GTEST_SKIP() << "This test requires multiple threads";
     }
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+    }
 
     ssize_t beforeFds = countFds();
     ASSERT_GE(beforeFds, 0);
@@ -1100,6 +1181,21 @@
     return ret;
 }
 
+static std::vector<SocketType> testTipcSocketTypes() {
+#ifdef __ANDROID_VENDOR__
+    auto port = trustyIpcPort(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+    int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
+    if (tipcFd >= 0) {
+        close(tipcFd);
+        return {SocketType::TIPC};
+    }
+#endif // __ANDROID_VENDOR__
+
+    // TIPC is not supported on this device, most likely
+    // because /dev/trusty-ipc-dev0 is missing
+    return {};
+}
+
 INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
                         ::testing::Combine(::testing::ValuesIn(testSocketTypes()),
                                            ::testing::ValuesIn(RpcSecurityValues()),
@@ -1109,6 +1205,14 @@
                                            ::testing::Values(false, true)),
                         BinderRpc::PrintParamInfo);
 
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
+                        ::testing::Combine(::testing::ValuesIn(testTipcSocketTypes()),
+                                           ::testing::Values(RpcSecurity::RAW),
+                                           ::testing::ValuesIn(testVersions()),
+                                           ::testing::ValuesIn(testVersions()),
+                                           ::testing::Values(true), ::testing::Values(true)),
+                        BinderRpc::PrintParamInfo);
+
 class BinderRpcServerRootObject
       : public ::testing::TestWithParam<std::tuple<bool, bool, RpcSecurity>> {};
 
@@ -1360,7 +1464,10 @@
                               addr, port);
                         return base::unique_fd{};
                     };
-                }
+                } break;
+                case SocketType::TIPC: {
+                    LOG_ALWAYS_FATAL("RpcTransportTest should not be enabled for TIPC");
+                } break;
             }
             mFd = rpcServer->releaseServer();
             if (!mFd.fd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 262d7e4..a467ee3 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -90,6 +90,7 @@
     UNIX_RAW,
     VSOCK,
     INET,
+    TIPC,
 };
 
 static inline std::string PrintToString(SocketType socketType) {
@@ -106,6 +107,8 @@
             return "vm_socket";
         case SocketType::INET:
             return "inet_socket";
+        case SocketType::TIPC:
+            return "trusty_ipc";
         default:
             LOG_ALWAYS_FATAL("Unknown socket type");
             return "";
diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
index 5a78782..c99d68a 100644
--- a/libs/binder/tests/binderRpcTestFixture.h
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -106,6 +106,10 @@
 
     // Whether the test params support sending FDs in parcels.
     bool supportsFdTransport() const {
+        if (socketType() == SocketType::TIPC) {
+            // Trusty does not support file descriptors yet
+            return false;
+        }
         return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
                 (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
                  socketType() == SocketType::UNIX_BOOTSTRAP ||
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
new file mode 100644
index 0000000..b3bb5eb
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#define LOG_TAG "binderRpcTest"
+
+#include <android-base/stringprintf.h>
+#include <binder/RpcTransportTipcTrusty.h>
+#include <trusty-gtest.h>
+#include <trusty_ipc.h>
+
+#include "binderRpcTestFixture.h"
+
+namespace android {
+
+// Destructors need to be defined, even if pure virtual
+ProcessSession::~ProcessSession() {}
+
+class TrustyProcessSession : public ProcessSession {
+public:
+    ~TrustyProcessSession() override {}
+
+    void setCustomExitStatusCheck(std::function<void(int wstatus)> /*f*/) override {
+        LOG_ALWAYS_FATAL("setCustomExitStatusCheck() not supported");
+    }
+
+    void terminate() override { LOG_ALWAYS_FATAL("terminate() not supported"); }
+};
+
+std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+    auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
+    auto ret = PrintToString(type) + "_clientV" + std::to_string(clientVersion) + "_serverV" +
+            std::to_string(serverVersion);
+    if (singleThreaded) {
+        ret += "_single_threaded";
+    }
+    if (noKernel) {
+        ret += "_no_kernel";
+    }
+    return ret;
+}
+
+// This creates a new process serving an interface on a certain number of
+// threads.
+std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
+        const BinderRpcOptions& options) {
+    LOG_ALWAYS_FATAL_IF(options.numIncomingConnections != 0,
+                        "Non-zero incoming connections %zu on Trusty",
+                        options.numIncomingConnections);
+
+    uint32_t clientVersion = std::get<2>(GetParam());
+    uint32_t serverVersion = std::get<3>(GetParam());
+
+    auto ret = std::make_unique<TrustyProcessSession>();
+
+    status_t status;
+    for (size_t i = 0; i < options.numSessions; i++) {
+        auto factory = android::RpcTransportCtxFactoryTipcTrusty::make();
+        auto session = android::RpcSession::make(std::move(factory));
+
+        EXPECT_TRUE(session->setProtocolVersion(clientVersion));
+        session->setMaxOutgoingThreads(options.numOutgoingConnections);
+        session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
+
+        status = session->setupPreconnectedClient({}, [&]() {
+            auto port = trustyIpcPort(serverVersion);
+            int rc = connect(port.c_str(), IPC_CONNECT_WAIT_FOR_PORT);
+            LOG_ALWAYS_FATAL_IF(rc < 0, "Failed to connect to service: %d", rc);
+            return base::unique_fd(rc);
+        });
+        if (options.allowConnectFailure && status != OK) {
+            ret->sessions.clear();
+            break;
+        }
+        LOG_ALWAYS_FATAL_IF(status != OK, "Failed to connect to service: %s",
+                            statusToString(status).c_str());
+        ret->sessions.push_back({session, session->getRootObject()});
+    }
+
+    return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
+                        ::testing::Combine(::testing::Values(SocketType::TIPC),
+                                           ::testing::Values(RpcSecurity::RAW),
+                                           ::testing::ValuesIn(testVersions()),
+                                           ::testing::ValuesIn(testVersions()),
+                                           ::testing::Values(false), ::testing::Values(true)),
+                        BinderRpc::PrintParamInfo);
+
+} // namespace android
+
+PORT_GTEST(BinderRpcTest, "com.android.trusty.binderRpcTest");
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 9cd8a82..11a22b0 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -113,6 +113,10 @@
 }
 
 TEST_P(BinderRpc, AppendSeparateFormats) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "Trusty does not support multiple server processes";
+    }
+
     auto proc1 = createRpcTestSocketServerProcess({});
     auto proc2 = createRpcTestSocketServerProcess({});
 
@@ -155,7 +159,9 @@
 
 TEST_P(BinderRpc, SendAndGetResultBackBig) {
     auto proc = createRpcTestSocketServerProcess({});
-    std::string single = std::string(1024, 'a');
+    // Trusty has a limit of 4096 bytes for the entire RPC Binder message
+    size_t singleLen = socketType() == SocketType::TIPC ? 512 : 4096;
+    std::string single = std::string(singleLen, 'a');
     std::string doubled;
     EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
     EXPECT_EQ(single + single, doubled);
@@ -259,6 +265,10 @@
 // aren't supported.
 
 TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
+    if (socketType() == SocketType::TIPC) {
+        GTEST_SKIP() << "Trusty does not support multiple server processes";
+    }
+
     auto proc1 = createRpcTestSocketServerProcess({});
     auto proc2 = createRpcTestSocketServerProcess({});
 
@@ -319,12 +329,16 @@
 }
 
 TEST_P(BinderRpc, NestedTransactions) {
+    auto fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX;
+    if (socketType() == SocketType::TIPC) {
+        // TIPC does not support file descriptors yet
+        fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE;
+    }
     auto proc = createRpcTestSocketServerProcess({
             // Enable FD support because it uses more stack space and so represents
             // something closer to a worst case scenario.
-            .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
-            .serverSupportedFileDescriptorTransportModes =
-                    {RpcSession::FileDescriptorTransportMode::UNIX},
+            .clientFileDescriptorTransportMode = fileDescriptorTransportMode,
+            .serverSupportedFileDescriptorTransportModes = {fileDescriptorTransportMode},
     });
 
     auto nastyNester = sp<MyBinderRpcTestDefault>::make();
@@ -372,11 +386,11 @@
     EXPECT_EQ(b, weak.promote());
 }
 
-#define expectSessions(expected, iface)                   \
+#define EXPECT_SESSIONS(expected, iface)                  \
     do {                                                  \
         int session;                                      \
         EXPECT_OK((iface)->getNumOpenSessions(&session)); \
-        EXPECT_EQ(expected, session);                     \
+        EXPECT_EQ(static_cast<int>(expected), session);   \
     } while (false)
 
 TEST_P(BinderRpc, SingleSession) {
@@ -388,9 +402,9 @@
     EXPECT_OK(session->getName(&out));
     EXPECT_EQ("aoeu", out);
 
-    expectSessions(1, proc.rootIface);
+    EXPECT_SESSIONS(1, proc.rootIface);
     session = nullptr;
-    expectSessions(0, proc.rootIface);
+    EXPECT_SESSIONS(0, proc.rootIface);
 }
 
 TEST_P(BinderRpc, ManySessions) {
@@ -399,24 +413,24 @@
     std::vector<sp<IBinderRpcSession>> sessions;
 
     for (size_t i = 0; i < 15; i++) {
-        expectSessions(i, proc.rootIface);
+        EXPECT_SESSIONS(i, proc.rootIface);
         sp<IBinderRpcSession> session;
         EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
         sessions.push_back(session);
     }
-    expectSessions(sessions.size(), proc.rootIface);
+    EXPECT_SESSIONS(sessions.size(), proc.rootIface);
     for (size_t i = 0; i < sessions.size(); i++) {
         std::string out;
         EXPECT_OK(sessions.at(i)->getName(&out));
         EXPECT_EQ(std::to_string(i), out);
     }
-    expectSessions(sessions.size(), proc.rootIface);
+    EXPECT_SESSIONS(sessions.size(), proc.rootIface);
 
     while (!sessions.empty()) {
         sessions.pop_back();
-        expectSessions(sessions.size(), proc.rootIface);
+        EXPECT_SESSIONS(sessions.size(), proc.rootIface);
     }
-    expectSessions(0, proc.rootIface);
+    EXPECT_SESSIONS(0, proc.rootIface);
 }
 
 TEST_P(BinderRpc, OnewayCallDoesNotWait) {
@@ -469,7 +483,7 @@
                     cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
                 }
 
-                EXPECT_EQ(cb->mValues.size(), 1)
+                EXPECT_EQ(cb->mValues.size(), 1UL)
                         << "callIsOneway: " << callIsOneway
                         << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
                 if (cb->mValues.empty()) continue;
diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json
new file mode 100644
index 0000000..d8b080f
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/manifest.json
@@ -0,0 +1,6 @@
+{
+    "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b",
+    "app_name": "binderRpcTest",
+    "min_heap": 163840,
+    "min_stack": 16384
+}
diff --git a/libs/binder/trusty/binderRpcTest/rules.mk b/libs/binder/trusty/binderRpcTest/rules.mk
new file mode 100644
index 0000000..ae39492
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/rules.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2022 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_TESTS_DIR := frameworks/native/libs/binder/tests
+
+MODULE := $(LOCAL_DIR)
+
+MANIFEST := $(LOCAL_DIR)/manifest.json
+
+MODULE_SRCS += \
+	$(LIBBINDER_TESTS_DIR)/binderRpcUniversalTests.cpp \
+	$(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
+	$(LIBBINDER_TESTS_DIR)/binderRpcTestTrusty.cpp \
+
+MODULE_LIBRARY_DEPS += \
+	$(LOCAL_DIR)/aidl \
+	frameworks/native/libs/binder/trusty \
+	frameworks/native/libs/binder/trusty/ndk \
+	trusty/user/base/lib/googletest \
+	trusty/user/base/lib/libstdc++-trusty \
+
+include make/trusted_app.mk
diff --git a/libs/binder/trusty/build-config-usertests b/libs/binder/trusty/build-config-usertests
new file mode 100644
index 0000000..d0a1fbc
--- /dev/null
+++ b/libs/binder/trusty/build-config-usertests
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 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.
+
+# This file lists userspace tests
+
+[
+    porttest("com.android.trusty.binderRpcTest"),
+]
diff --git a/libs/binder/trusty/include_mock/trusty-gtest.h b/libs/binder/trusty/include_mock/trusty-gtest.h
new file mode 100644
index 0000000..046b403
--- /dev/null
+++ b/libs/binder/trusty/include_mock/trusty-gtest.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#define PORT_GTEST(suite, port) \
+    int main(void) {            \
+        return 0;               \
+    }
diff --git a/libs/binder/trusty/include_mock/trusty_ipc.h b/libs/binder/trusty/include_mock/trusty_ipc.h
index 43ab84a..db044c2 100644
--- a/libs/binder/trusty/include_mock/trusty_ipc.h
+++ b/libs/binder/trusty/include_mock/trusty_ipc.h
@@ -27,6 +27,8 @@
 #define IPC_PORT_ALLOW_TA_CONNECT 0x1
 #define IPC_PORT_ALLOW_NS_CONNECT 0x2
 
+#define IPC_CONNECT_WAIT_FOR_PORT 0x1
+
 #define IPC_HANDLE_POLL_HUP 0x1
 #define IPC_HANDLE_POLL_MSG 0x2
 #define IPC_HANDLE_POLL_SEND_UNBLOCKED 0x4
diff --git a/libs/binder/trusty/usertests-inc.mk b/libs/binder/trusty/usertests-inc.mk
index 2f5a7f4..1300121 100644
--- a/libs/binder/trusty/usertests-inc.mk
+++ b/libs/binder/trusty/usertests-inc.mk
@@ -14,4 +14,6 @@
 #
 
 TRUSTY_USER_TESTS += \
+	frameworks/native/libs/binder/trusty/binderRpcTest \
 	frameworks/native/libs/binder/trusty/binderRpcTest/service \
+
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 797d6ae..60603ba 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -587,9 +587,23 @@
         t->setDesiredPresentTime(bufferItem.mTimestamp);
     }
 
-    if (!mNextFrameTimelineInfoQueue.empty()) {
-        t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
-        mNextFrameTimelineInfoQueue.pop();
+    // Drop stale frame timeline infos
+    while (!mPendingFrameTimelines.empty() &&
+           mPendingFrameTimelines.front().first < bufferItem.mFrameNumber) {
+        ATRACE_FORMAT_INSTANT("dropping stale frameNumber: %" PRIu64 " vsyncId: %" PRId64,
+                              mPendingFrameTimelines.front().first,
+                              mPendingFrameTimelines.front().second.vsyncId);
+        mPendingFrameTimelines.pop();
+    }
+
+    if (!mPendingFrameTimelines.empty() &&
+        mPendingFrameTimelines.front().first == bufferItem.mFrameNumber) {
+        ATRACE_FORMAT_INSTANT("Transaction::setFrameTimelineInfo frameNumber: %" PRIu64
+                              " vsyncId: %" PRId64,
+                              bufferItem.mFrameNumber,
+                              mPendingFrameTimelines.front().second.vsyncId);
+        t->setFrameTimelineInfo(mPendingFrameTimelines.front().second);
+        mPendingFrameTimelines.pop();
     }
 
     {
@@ -653,6 +667,7 @@
     {
         std::unique_lock _lock{mMutex};
         BBQ_TRACE();
+
         const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
         BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
 
@@ -847,12 +862,13 @@
         return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
     }
 
-    status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+    status_t setFrameTimelineInfo(uint64_t frameNumber,
+                                  const FrameTimelineInfo& frameTimelineInfo) override {
         std::unique_lock _lock{mMutex};
         if (mDestroyed) {
             return DEAD_OBJECT;
         }
-        return mBbq->setFrameTimelineInfo(frameTimelineInfo);
+        return mBbq->setFrameTimelineInfo(frameNumber, frameTimelineInfo);
     }
 
     void destroy() override {
@@ -874,9 +890,12 @@
     return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply();
 }
 
-status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+status_t BLASTBufferQueue::setFrameTimelineInfo(uint64_t frameNumber,
+                                                const FrameTimelineInfo& frameTimelineInfo) {
+    ATRACE_FORMAT("%s(%s) frameNumber: %" PRIu64 " vsyncId: %" PRId64, __func__, mName.c_str(),
+                  frameNumber, frameTimelineInfo.vsyncId);
     std::unique_lock _lock{mMutex};
-    mNextFrameTimelineInfoQueue.push(frameTimelineInfo);
+    mPendingFrameTimelines.push({frameNumber, frameTimelineInfo});
     return OK;
 }
 
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a77ca04..cefb9a7 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -63,7 +63,8 @@
                                  Vector<ComposerState>& state, const Vector<DisplayState>& displays,
                                  uint32_t flags, const sp<IBinder>& applyToken,
                                  const InputWindowCommands& commands, int64_t desiredPresentTime,
-                                 bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
+                                 bool isAutoTimestamp,
+                                 const std::vector<client_cache_t>& uncacheBuffers,
                                  bool hasListenerCallbacks,
                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
                                  uint64_t transactionId) override {
@@ -87,8 +88,11 @@
         SAFE_PARCEL(commands.write, data);
         SAFE_PARCEL(data.writeInt64, desiredPresentTime);
         SAFE_PARCEL(data.writeBool, isAutoTimestamp);
-        SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
-        SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+        SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(uncacheBuffers.size()));
+        for (const client_cache_t& uncacheBuffer : uncacheBuffers) {
+            SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
+            SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+        }
         SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
 
         SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
@@ -158,11 +162,14 @@
             SAFE_PARCEL(data.readInt64, &desiredPresentTime);
             SAFE_PARCEL(data.readBool, &isAutoTimestamp);
 
-            client_cache_t uncachedBuffer;
+            SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
+            std::vector<client_cache_t> uncacheBuffers(count);
             sp<IBinder> tmpBinder;
-            SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
-            uncachedBuffer.token = tmpBinder;
-            SAFE_PARCEL(data.readUint64, &uncachedBuffer.id);
+            for (size_t i = 0; i < count; i++) {
+                SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
+                uncacheBuffers[i].token = tmpBinder;
+                SAFE_PARCEL(data.readUint64, &uncacheBuffers[i].id);
+            }
 
             bool hasListenerCallbacks = false;
             SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
@@ -182,7 +189,7 @@
 
             return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
                                        inputWindowCommands, desiredPresentTime, isAutoTimestamp,
-                                       uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
+                                       uncacheBuffers, hasListenerCallbacks, listenerCallbacks,
                                        transactionId);
         }
         default: {
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2b25b61..985c549 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -33,7 +33,8 @@
     ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
     ON_RELEASE_BUFFER,
     ON_TRANSACTION_QUEUE_STALLED,
-    LAST = ON_TRANSACTION_QUEUE_STALLED,
+    ON_TRUSTED_PRESENTATION_CHANGED,
+    LAST = ON_TRUSTED_PRESENTATION_CHANGED,
 };
 
 } // Anonymous namespace
@@ -302,6 +303,11 @@
                                  onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED,
                                                              reason);
     }
+
+    void onTrustedPresentationChanged(int id, bool inTrustedPresentationState) override {
+        callRemoteAsync<decltype(&ITransactionCompletedListener::onTrustedPresentationChanged)>(
+                Tag::ON_TRUSTED_PRESENTATION_CHANGED, id, inTrustedPresentationState);
+    }
 };
 
 // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -325,6 +331,9 @@
         case Tag::ON_TRANSACTION_QUEUE_STALLED:
             return callLocalAsync(data, reply,
                                   &ITransactionCompletedListener::onTransactionQueueStalled);
+        case Tag::ON_TRUSTED_PRESENTATION_CHANGED:
+            return callLocalAsync(data, reply,
+                                  &ITransactionCompletedListener::onTrustedPresentationChanged);
     }
 }
 
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 59b62fe..8372363 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -185,6 +185,10 @@
     if (hasBufferData) {
         SAFE_PARCEL(output.writeParcelable, *bufferData);
     }
+    SAFE_PARCEL(output.writeParcelable, trustedPresentationThresholds);
+    SAFE_PARCEL(output.writeParcelable, trustedPresentationListener);
+    SAFE_PARCEL(output.writeFloat, currentSdrHdrRatio);
+    SAFE_PARCEL(output.writeFloat, desiredSdrHdrRatio);
     return NO_ERROR;
 }
 
@@ -315,6 +319,15 @@
     } else {
         bufferData = nullptr;
     }
+
+    SAFE_PARCEL(input.readParcelable, &trustedPresentationThresholds);
+    SAFE_PARCEL(input.readParcelable, &trustedPresentationListener);
+
+    SAFE_PARCEL(input.readFloat, &tmpFloat);
+    currentSdrHdrRatio = tmpFloat;
+    SAFE_PARCEL(input.readFloat, &tmpFloat);
+    desiredSdrHdrRatio = tmpFloat;
+
     return NO_ERROR;
 }
 
@@ -553,10 +566,20 @@
         what |= eBufferChanged;
         bufferData = other.bufferData;
     }
+    if (other.what & eTrustedPresentationInfoChanged) {
+        what |= eTrustedPresentationInfoChanged;
+        trustedPresentationListener = other.trustedPresentationListener;
+        trustedPresentationThresholds = other.trustedPresentationThresholds;
+    }
     if (other.what & eDataspaceChanged) {
         what |= eDataspaceChanged;
         dataspace = other.dataspace;
     }
+    if (other.what & eExtendedRangeBrightnessChanged) {
+        what |= eExtendedRangeBrightnessChanged;
+        desiredSdrHdrRatio = other.desiredSdrHdrRatio;
+        currentSdrHdrRatio = other.currentSdrHdrRatio;
+    }
     if (other.what & eHdrMetadataChanged) {
         what |= eHdrMetadataChanged;
         hdrMetadata = other.hdrMetadata;
@@ -703,6 +726,8 @@
     CHECK_DIFF(diff, eCropChanged, other, crop);
     if (other.what & eBufferChanged) diff |= eBufferChanged;
     CHECK_DIFF(diff, eDataspaceChanged, other, dataspace);
+    CHECK_DIFF2(diff, eExtendedRangeBrightnessChanged, other, currentSdrHdrRatio,
+                desiredSdrHdrRatio);
     CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
     if (other.what & eSurfaceDamageRegionChanged &&
         (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
@@ -998,4 +1023,20 @@
     return NO_ERROR;
 }
 
+status_t TrustedPresentationListener::writeToParcel(Parcel* parcel) const {
+    SAFE_PARCEL(parcel->writeStrongBinder, callbackInterface);
+    SAFE_PARCEL(parcel->writeInt32, callbackId);
+    return NO_ERROR;
+}
+
+status_t TrustedPresentationListener::readFromParcel(const Parcel* parcel) {
+    sp<IBinder> tmpBinder = nullptr;
+    SAFE_PARCEL(parcel->readNullableStrongBinder, &tmpBinder);
+    if (tmpBinder) {
+        callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+    }
+    SAFE_PARCEL(parcel->readInt32, &callbackId);
+    return NO_ERROR;
+}
+
 }; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index edb18a8..b18bf5b 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1863,6 +1863,7 @@
 
 int Surface::dispatchSetFrameTimelineInfo(va_list args) {
     ATRACE_CALL();
+    auto frameNumber = static_cast<uint64_t>(va_arg(args, uint64_t));
     auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
     auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
     auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
@@ -1872,7 +1873,7 @@
     ftlInfo.vsyncId = frameTimelineVsyncId;
     ftlInfo.inputEventId = inputEventId;
     ftlInfo.startTimeNanos = startTimeNanos;
-    return setFrameTimelineInfo(ftlInfo);
+    return setFrameTimelineInfo(frameNumber, ftlInfo);
 }
 
 bool Surface::transformToDisplayInverse() const {
@@ -2641,7 +2642,8 @@
     return NO_ERROR;
 }
 
-status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& /*frameTimelineInfo*/) {
+status_t Surface::setFrameTimelineInfo(uint64_t /*frameNumber*/,
+                                       const FrameTimelineInfo& /*frameTimelineInfo*/) {
     // ISurfaceComposer no longer supports setFrameTimelineInfo
     return BAD_VALUE;
 }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 92125ea..cf9828b 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -24,6 +24,7 @@
 #include <android/gui/DisplayState.h>
 #include <android/gui/ISurfaceComposerClient.h>
 #include <android/gui/IWindowInfosListener.h>
+#include <android/gui/TrustedPresentationThresholds.h>
 #include <android/os/IInputConstants.h>
 #include <gui/TraceUtils.h>
 #include <utils/Errors.h>
@@ -63,6 +64,7 @@
 using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
 using gui::FocusRequest;
 using gui::IRegionSamplingListener;
+using gui::TrustedPresentationThresholds;
 using gui::WindowInfo;
 using gui::WindowInfoHandle;
 using gui::WindowInfosListener;
@@ -518,6 +520,45 @@
     }
 }
 
+SurfaceComposerClient::PresentationCallbackRAII::PresentationCallbackRAII(
+        TransactionCompletedListener* tcl, int id) {
+    mTcl = tcl;
+    mId = id;
+}
+
+SurfaceComposerClient::PresentationCallbackRAII::~PresentationCallbackRAII() {
+    mTcl->clearTrustedPresentationCallback(mId);
+}
+
+sp<SurfaceComposerClient::PresentationCallbackRAII>
+TransactionCompletedListener::addTrustedPresentationCallback(TrustedPresentationCallback tpc,
+                                                             int id, void* context) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    mTrustedPresentationCallbacks[id] =
+            std::tuple<TrustedPresentationCallback, void*>(tpc, context);
+    return new SurfaceComposerClient::PresentationCallbackRAII(this, id);
+}
+
+void TransactionCompletedListener::clearTrustedPresentationCallback(int id) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    mTrustedPresentationCallbacks.erase(id);
+}
+
+void TransactionCompletedListener::onTrustedPresentationChanged(int id,
+                                                                bool presentedWithinThresholds) {
+    TrustedPresentationCallback tpc;
+    void* context;
+    {
+        std::scoped_lock<std::mutex> lock(mMutex);
+        auto it = mTrustedPresentationCallbacks.find(id);
+        if (it == mTrustedPresentationCallbacks.end()) {
+            return;
+        }
+        std::tie(tpc, context) = it->second;
+    }
+    tpc(context, presentedWithinThresholds);
+}
+
 // ---------------------------------------------------------------------------
 
 void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -565,11 +606,13 @@
         return NO_ERROR;
     }
 
-    uint64_t cache(const sp<GraphicBuffer>& buffer) {
+    uint64_t cache(const sp<GraphicBuffer>& buffer,
+                   std::optional<client_cache_t>& outUncacheBuffer) {
         std::lock_guard<std::mutex> lock(mMutex);
 
         if (mBuffers.size() >= BUFFER_CACHE_MAX_SIZE) {
-            evictLeastRecentlyUsedBuffer();
+            outUncacheBuffer = findLeastRecentlyUsedBuffer();
+            mBuffers.erase(outUncacheBuffer->id);
         }
 
         buffer->addDeathCallback(removeDeadBufferCallback, nullptr);
@@ -580,16 +623,13 @@
 
     void uncache(uint64_t cacheId) {
         std::lock_guard<std::mutex> lock(mMutex);
-        uncacheLocked(cacheId);
-    }
-
-    void uncacheLocked(uint64_t cacheId) REQUIRES(mMutex) {
-        mBuffers.erase(cacheId);
-        SurfaceComposerClient::doUncacheBufferTransaction(cacheId);
+        if (mBuffers.erase(cacheId)) {
+            SurfaceComposerClient::doUncacheBufferTransaction(cacheId);
+        }
     }
 
 private:
-    void evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
+    client_cache_t findLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
         auto itr = mBuffers.begin();
         uint64_t minCounter = itr->second;
         auto minBuffer = itr;
@@ -603,7 +643,8 @@
             }
             itr++;
         }
-        uncacheLocked(minBuffer->first);
+
+        return {.token = getToken(), .id = minBuffer->first};
     }
 
     uint64_t getCounter() REQUIRES(mMutex) {
@@ -741,6 +782,18 @@
     InputWindowCommands inputWindowCommands;
     inputWindowCommands.read(*parcel);
 
+    count = static_cast<size_t>(parcel->readUint32());
+    if (count > parcel->dataSize()) {
+        return BAD_VALUE;
+    }
+    std::vector<client_cache_t> uncacheBuffers(count);
+    for (size_t i = 0; i < count; i++) {
+        sp<IBinder> tmpBinder;
+        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+        uncacheBuffers[i].token = tmpBinder;
+        SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id);
+    }
+
     // Parsing was successful. Update the object.
     mId = transactionId;
     mTransactionNestCount = transactionNestCount;
@@ -755,6 +808,7 @@
     mComposerStates = composerStates;
     mInputWindowCommands = inputWindowCommands;
     mApplyToken = applyToken;
+    mUncacheBuffers = std::move(uncacheBuffers);
     return NO_ERROR;
 }
 
@@ -806,6 +860,13 @@
     }
 
     mInputWindowCommands.write(*parcel);
+
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+    for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+        SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+        SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+    }
+
     return NO_ERROR;
 }
 
@@ -873,6 +934,10 @@
         }
     }
 
+    for (const auto& cacheId : other.mUncacheBuffers) {
+        mUncacheBuffers.push_back(cacheId);
+    }
+
     mInputWindowCommands.merge(other.mInputWindowCommands);
 
     mMayContainBuffer |= other.mMayContainBuffer;
@@ -891,6 +956,7 @@
     mDisplayStates.clear();
     mListenerCallbacks.clear();
     mInputWindowCommands.clear();
+    mUncacheBuffers.clear();
     mMayContainBuffer = false;
     mTransactionNestCount = 0;
     mAnimation = false;
@@ -913,10 +979,10 @@
     uncacheBuffer.token = BufferCache::getInstance().getToken();
     uncacheBuffer.id = cacheId;
     Vector<ComposerState> composerStates;
-    status_t status =
-            sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
-                                    ISurfaceComposer::eOneWay, Transaction::getDefaultApplyToken(),
-                                    {}, systemTime(), true, uncacheBuffer, false, {}, generateId());
+    status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
+                                              ISurfaceComposer::eOneWay,
+                                              Transaction::getDefaultApplyToken(), {}, systemTime(),
+                                              true, {uncacheBuffer}, false, {}, generateId());
     if (status != NO_ERROR) {
         ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
                         strerror(-status));
@@ -954,7 +1020,11 @@
             s->bufferData->buffer = nullptr;
         } else {
             // Cache-miss. Include the buffer and send the new cacheId.
-            cacheId = BufferCache::getInstance().cache(s->bufferData->buffer);
+            std::optional<client_cache_t> uncacheBuffer;
+            cacheId = BufferCache::getInstance().cache(s->bufferData->buffer, uncacheBuffer);
+            if (uncacheBuffer) {
+                mUncacheBuffers.push_back(*uncacheBuffer);
+            }
         }
         s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
         s->bufferData->cachedBuffer.token = BufferCache::getInstance().getToken();
@@ -1087,8 +1157,7 @@
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
                             mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
-                            {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
-                            hasListenerCallbacks, listenerCallbacks, mId);
+                            mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId);
     mId = generateId();
 
     // Clear the current states and flags
@@ -1604,6 +1673,21 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setExtendedRangeBrightness(
+        const sp<SurfaceControl>& sc, float currentBufferRatio, float desiredRatio) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eExtendedRangeBrightnessChanged;
+    s->currentSdrHdrRatio = currentBufferRatio;
+    s->desiredSdrHdrRatio = desiredRatio;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata(
         const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) {
     layer_state_t* s = getLayerState(sc);
@@ -2098,6 +2182,45 @@
     t.startTimeNanos = 0;
 }
 
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setTrustedPresentationCallback(
+        const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
+        const TrustedPresentationThresholds& thresholds, void* context,
+        sp<SurfaceComposerClient::PresentationCallbackRAII>& outCallbackRef) {
+    auto listener = TransactionCompletedListener::getInstance();
+    outCallbackRef = listener->addTrustedPresentationCallback(cb, sc->getLayerId(), context);
+
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eTrustedPresentationInfoChanged;
+    s->trustedPresentationThresholds = thresholds;
+    s->trustedPresentationListener.callbackInterface = TransactionCompletedListener::getIInstance();
+    s->trustedPresentationListener.callbackId = sc->getLayerId();
+
+    return *this;
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::clearTrustedPresentationCallback(const sp<SurfaceControl>& sc) {
+    auto listener = TransactionCompletedListener::getInstance();
+    listener->clearTrustedPresentationCallback(sc->getLayerId());
+
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eTrustedPresentationInfoChanged;
+    s->trustedPresentationThresholds = TrustedPresentationThresholds();
+    s->trustedPresentationListener.callbackInterface = nullptr;
+    s->trustedPresentationListener.callbackId = -1;
+
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 SurfaceComposerClient::SurfaceComposerClient() : mStatus(NO_INIT) {}
diff --git a/libs/gui/aidl/android/gui/IHdrConversionConstants.aidl b/libs/gui/aidl/android/gui/IHdrConversionConstants.aidl
new file mode 100644
index 0000000..7697f29
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IHdrConversionConstants.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+interface IHdrConversionConstants
+{
+    /** HDR Conversion Mode when there is no conversion being done */
+    const int HdrConversionModePassthrough = 1;
+
+    /** HDR Conversion Mode when HDR conversion is decided by the system or implementation */
+    const int HdrConversionModeAuto = 2;
+
+    /** HDR Conversion Mode when the output HDR types is selected by the user or framework */
+    const int HdrConversionModeForce = 3;
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl
index 1af5746..5fb1a83 100644
--- a/libs/gui/aidl/android/gui/OverlayProperties.aidl
+++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl
@@ -20,7 +20,9 @@
 parcelable OverlayProperties {
     parcelable SupportedBufferCombinations {
         int[] pixelFormats;
-        int[] dataspaces;
+        int[] standards;
+        int[] transfers;
+        int[] ranges;
     }
     SupportedBufferCombinations[] combinations;
 
diff --git a/libs/gui/aidl/android/gui/TrustedPresentationThresholds.aidl b/libs/gui/aidl/android/gui/TrustedPresentationThresholds.aidl
new file mode 100644
index 0000000..1eea5b4
--- /dev/null
+++ b/libs/gui/aidl/android/gui/TrustedPresentationThresholds.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+parcelable TrustedPresentationThresholds {
+    float minAlpha = -1.0f;
+    float minFractionRendered = -1.0f;
+
+    int stabilityRequirementMs = 0;
+}
diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
index 761f08f..17f4c63 100644
--- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
@@ -130,7 +130,7 @@
     queue->setFrameRate(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeIntegral<int8_t>(),
                         mFdp.ConsumeBool() /*shouldBeSeamless*/);
     FrameTimelineInfo info;
-    queue->setFrameTimelineInfo(info);
+    queue->setFrameTimelineInfo(mFdp.ConsumeIntegral<uint64_t>(), info);
 
     ManageResourceHandle handle(&mFdp);
     queue->setSidebandStream(handle.getStream());
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 001d8e5..c93ab86 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -106,7 +106,7 @@
     void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
 
     status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
-    status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+    status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
 
     void setSidebandStream(const sp<NativeHandle>& stream);
 
@@ -231,7 +231,7 @@
     std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
             mPendingTransactions GUARDED_BY(mMutex);
 
-    std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
+    std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex);
 
     // Tracks the last acquired frame number
     uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
index ec884cf..c826c17 100644
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -24,6 +24,7 @@
 #include <binder/Parcelable.h>
 #include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
+#include <ui/Rect.h>
 
 namespace android::gui {
 
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 045cc2a..ae56f9f 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -113,8 +113,9 @@
             const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
             const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
             const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-            bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-            const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
+            bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
+            bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+            uint64_t transactionId) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 453e8f3..d593f56 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -196,6 +196,8 @@
                                  uint32_t currentMaxAcquiredBufferCount) = 0;
 
     virtual void onTransactionQueueStalled(const String8& name) = 0;
+
+    virtual void onTrustedPresentationChanged(int id, bool inTrustedPresentationState) = 0;
 };
 
 class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index ecde47f..b8bee72 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 
 #include <android/gui/IWindowInfosReportedListener.h>
+#include <android/gui/TrustedPresentationThresholds.h>
 #include <android/native_window.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ITransactionCompletedListener.h>
@@ -56,6 +57,8 @@
 using gui::ISurfaceComposerClient;
 using gui::LayerMetadata;
 
+using gui::TrustedPresentationThresholds;
+
 struct client_cache_t {
     wp<IBinder> token = nullptr;
     uint64_t id;
@@ -65,6 +68,19 @@
     bool isValid() const { return token != nullptr; }
 };
 
+class TrustedPresentationListener : public Parcelable {
+public:
+    sp<ITransactionCompletedListener> callbackInterface;
+    int callbackId = -1;
+
+    void invoke(bool presentedWithinThresholds) {
+        callbackInterface->onTrustedPresentationChanged(callbackId, presentedWithinThresholds);
+    }
+
+    status_t writeToParcel(Parcel* parcel) const;
+    status_t readFromParcel(const Parcel* parcel);
+};
+
 class BufferData : public Parcelable {
 public:
     virtual ~BufferData() = default;
@@ -148,7 +164,7 @@
     enum {
         ePositionChanged = 0x00000001,
         eLayerChanged = 0x00000002,
-        /* unused = 0x00000004, */
+        eTrustedPresentationInfoChanged = 0x00000004,
         eAlphaChanged = 0x00000008,
         eMatrixChanged = 0x00000010,
         eTransparentRegionChanged = 0x00000020,
@@ -193,7 +209,8 @@
         eAutoRefreshChanged = 0x1000'00000000,
         eStretchChanged = 0x2000'00000000,
         eTrustedOverlayChanged = 0x4000'00000000,
-        eDropInputModeChanged = 0x8000'00000000
+        eDropInputModeChanged = 0x8000'00000000,
+        eExtendedRangeBrightnessChanged = 0x10000'00000000
     };
 
     layer_state_t();
@@ -224,7 +241,8 @@
             layer_state_t::eBufferTransformChanged | layer_state_t::eDataspaceChanged |
             layer_state_t::eSidebandStreamChanged | layer_state_t::eSurfaceDamageRegionChanged |
             layer_state_t::eTransformToDisplayInverseChanged |
-            layer_state_t::eTransparentRegionChanged;
+            layer_state_t::eTransparentRegionChanged |
+            layer_state_t::eExtendedRangeBrightnessChanged;
 
     // Content updates.
     static constexpr uint64_t CONTENT_CHANGES = layer_state_t::BUFFER_CHANGES |
@@ -232,9 +250,9 @@
             layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBackgroundColorChanged |
             layer_state_t::eBlurRegionsChanged | layer_state_t::eColorChanged |
             layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged |
-            layer_state_t::eCornerRadiusChanged | layer_state_t::eHdrMetadataChanged |
-            layer_state_t::eRenderBorderChanged | layer_state_t::eShadowRadiusChanged |
-            layer_state_t::eStretchChanged;
+            layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
+            layer_state_t::eHdrMetadataChanged | layer_state_t::eRenderBorderChanged |
+            layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged;
 
     // Changes which invalidates the layer's visible region in CE.
     static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -245,7 +263,17 @@
             layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged |
             layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
             layer_state_t::eFlagsChanged | layer_state_t::eLayerStackChanged |
-            layer_state_t::eTrustedOverlayChanged;
+            layer_state_t::eTrustedOverlayChanged | layer_state_t::eFrameRateChanged |
+            layer_state_t::eFixedTransformHintChanged;
+
+    // Changes affecting data sent to input.
+    static constexpr uint64_t INPUT_CHANGES = layer_state_t::GEOMETRY_CHANGES |
+            layer_state_t::HIERARCHY_CHANGES | layer_state_t::eInputInfoChanged |
+            layer_state_t::eDropInputModeChanged | layer_state_t::eTrustedOverlayChanged;
+
+    // Changes that affect the visible region on a display.
+    static constexpr uint64_t VISIBLE_REGION_CHANGES =
+            layer_state_t::GEOMETRY_CHANGES | layer_state_t::HIERARCHY_CHANGES;
 
     bool hasValidBuffer() const;
     void sanitize(int32_t permissions);
@@ -359,6 +387,11 @@
     gui::DropInputMode dropInputMode;
 
     bool dimmingEnabled;
+    float currentSdrHdrRatio = 1.f;
+    float desiredSdrHdrRatio = 1.f;
+
+    TrustedPresentationThresholds trustedPresentationThresholds;
+    TrustedPresentationListener trustedPresentationListener;
 };
 
 class ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index b9ccdc9..39a59e4 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -213,7 +213,7 @@
 
     virtual status_t setFrameRate(float frameRate, int8_t compatibility,
                                   int8_t changeFrameRateStrategy);
-    virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+    virtual status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
 
 protected:
     virtual ~Surface();
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2458a40..c5f59c8 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -58,6 +58,7 @@
 class IGraphicBufferProducer;
 class ITunnelModeEnabledListener;
 class Region;
+class TransactionCompletedListener;
 
 using gui::DisplayCaptureArgs;
 using gui::IRegionSamplingListener;
@@ -106,6 +107,8 @@
                            const sp<Fence>& /*presentFence*/,
                            const SurfaceStats& /*stats*/)>;
 
+using TrustedPresentationCallback = std::function<void(void*, bool)>;
+
 // ---------------------------------------------------------------------------
 
 class ReleaseCallbackThread {
@@ -390,6 +393,13 @@
         std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
     };
 
+    struct PresentationCallbackRAII : public RefBase {
+        sp<TransactionCompletedListener> mTcl;
+        int mId;
+        PresentationCallbackRAII(TransactionCompletedListener* tcl, int id);
+        virtual ~PresentationCallbackRAII();
+    };
+
     class Transaction : public Parcelable {
     private:
         static sp<IBinder> sApplyToken;
@@ -402,6 +412,7 @@
         SortedVector<DisplayState> mDisplayStates;
         std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
                 mListenerCallbacks;
+        std::vector<client_cache_t> mUncacheBuffers;
 
         uint64_t mId;
 
@@ -548,6 +559,8 @@
         Transaction& setBufferHasBarrier(const sp<SurfaceControl>& sc,
                                          uint64_t barrierFrameNumber);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
+        Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc,
+                                                float currentBufferRatio, float desiredRatio);
         Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
         Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
                                             const Region& surfaceDamageRegion);
@@ -569,6 +582,59 @@
         Transaction& addTransactionCommittedCallback(
                 TransactionCompletedCallbackTakesContext callback, void* callbackContext);
 
+        /**
+         * Set a callback to receive feedback about the presentation of a layer.
+         * When the layer is presented according to the passed in Thresholds,
+         * it is said to "enter the state", and receives the callback with true.
+         * When the conditions fall out of thresholds, it is then said to leave the
+         * state.
+         *
+         * There are a few simple thresholds:
+         *    minAlpha: Lower bound on computed alpha
+         *    minFractionRendered: Lower bounds on fraction of pixels that
+         *    were rendered.
+         *    stabilityThresholdMs: A time that alpha and fraction rendered
+         *    must remain within bounds before we can "enter the state"
+         *
+         * The fraction of pixels rendered is a computation based on scale, crop
+         * and occlusion. The calculation may be somewhat counterintuitive, so we
+         * can work through an example. Imagine we have a layer with a 100x100 buffer
+         * which is occluded by (10x100) pixels on the left, and cropped by (100x10) pixels
+         * on the top. Furthermore imagine this layer is scaled by 0.9 in both dimensions.
+         * (c=crop,o=occluded,b=both,x=none
+         *      b c c c
+         *      o x x x
+         *      o x x x
+         *      o x x x
+         *
+         * We first start by computing fr=xscale*yscale=0.9*0.9=0.81, indicating
+         * that "81%" of the pixels were rendered. This corresponds to what was 100
+         * pixels being displayed in 81 pixels. This is somewhat of an abuse of
+         * language, as the information of merged pixels isn't totally lost, but
+         * we err on the conservative side.
+         *
+         * We then repeat a similar process for the crop and covered regions and
+         * accumulate the results: fr = fr * (fractionNotCropped) * (fractionNotCovered)
+         * So for this example we would get 0.9*0.9*0.9*0.9=0.65...
+         *
+         * Notice that this is not completely accurate, as we have double counted
+         * the region marked as b. However we only wanted a "lower bound" and so it
+         * is ok to err in this direction. Selection of the threshold will ultimately
+         * be somewhat arbitrary, and so there are some somewhat arbitrary decisions in
+         * this API as well.
+         *
+         * The caller must keep "PresentationCallbackRAII" alive, or the callback
+         * in SurfaceComposerClient will be unregistered.
+         */
+        Transaction& setTrustedPresentationCallback(const sp<SurfaceControl>& sc,
+                                                    TrustedPresentationCallback callback,
+                                                    const TrustedPresentationThresholds& thresholds,
+                                                    void* context,
+                                                    sp<PresentationCallbackRAII>& outCallbackOwner);
+
+        // Clear local memory in SCC
+        Transaction& clearTrustedPresentationCallback(const sp<SurfaceControl>& sc);
+
         // ONLY FOR BLAST ADAPTER
         Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
 
@@ -795,6 +861,9 @@
     std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
     std::unordered_map<void*, std::function<void(const std::string&)>> mQueueStallListeners;
 
+    std::unordered_map<int, std::tuple<TrustedPresentationCallback, void*>>
+            mTrustedPresentationCallbacks;
+
 public:
     static sp<TransactionCompletedListener> getInstance();
     static sp<ITransactionCompletedListener> getIInstance();
@@ -814,6 +883,10 @@
     void addQueueStallListener(std::function<void(const std::string&)> stallListener, void* id);
     void removeQueueStallListener(void *id);
 
+    sp<SurfaceComposerClient::PresentationCallbackRAII> addTrustedPresentationCallback(
+            TrustedPresentationCallback tpc, int id, void* context);
+    void clearTrustedPresentationCallback(int id);
+
     /*
      * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
      * surface. Jank classifications arrive as part of the transaction callbacks about previous
@@ -844,6 +917,8 @@
 
     void onTransactionQueueStalled(const String8& reason) override;
 
+    void onTrustedPresentationChanged(int id, bool presentedWithinThresholds) override;
+
 private:
     ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
     static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3014804..32d60cd 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -701,7 +701,7 @@
                                  const sp<IBinder>& /*applyToken*/,
                                  const InputWindowCommands& /*inputWindowCommands*/,
                                  int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
-                                 const client_cache_t& /*cachedBuffer*/,
+                                 const std::vector<client_cache_t>& /*cachedBuffer*/,
                                  bool /*hasListenerCallbacks*/,
                                  const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
                                  uint64_t /*transactionId*/) override {
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c796439..133b260 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -299,7 +299,7 @@
     return InputEventLookup::getLabelByKeyCode(keyCode);
 }
 
-int32_t KeyEvent::getKeyCodeFromLabel(const char* label) {
+std::optional<int> KeyEvent::getKeyCodeFromLabel(const char* label) {
     return InputEventLookup::getKeyCodeByLabel(label);
 }
 
@@ -343,6 +343,28 @@
     return "UNKNOWN";
 }
 
+std::ostream& operator<<(std::ostream& out, const KeyEvent& event) {
+    out << "KeyEvent { action=" << KeyEvent::actionToString(event.getAction());
+
+    out << ", keycode=" << event.getKeyCode() << "(" << KeyEvent::getLabel(event.getKeyCode())
+        << ")";
+
+    if (event.getMetaState() != 0) {
+        out << ", metaState=" << event.getMetaState();
+    }
+
+    out << ", eventTime=" << event.getEventTime();
+    out << ", downTime=" << event.getDownTime();
+    out << ", flags=" << std::hex << event.getFlags() << std::dec;
+    out << ", repeatCount=" << event.getRepeatCount();
+    out << ", deviceId=" << event.getDeviceId();
+    out << ", source=" << inputEventSourceToString(event.getSource());
+    out << ", displayId=" << event.getDisplayId();
+    out << ", eventId=" << event.getId();
+    out << "}";
+    return out;
+}
+
 // --- PointerCoords ---
 
 float PointerCoords::getAxisValue(int32_t axis) const {
@@ -869,7 +891,7 @@
     return InputEventLookup::getAxisLabel(axis);
 }
 
-int32_t MotionEvent::getAxisFromLabel(const char* label) {
+std::optional<int> MotionEvent::getAxisFromLabel(const char* label) {
     return InputEventLookup::getAxisByLabel(label);
 }
 
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 7159e27..d97c1bb 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -438,11 +438,11 @@
 
 const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE};
 
-int InputEventLookup::lookupValueByLabel(const std::unordered_map<std::string, int>& map,
-                                         const char* literal) {
+std::optional<int> InputEventLookup::lookupValueByLabel(
+        const std::unordered_map<std::string, int>& map, const char* literal) {
     std::string str(literal);
     auto it = map.find(str);
-    return it != map.end() ? it->second : 0;
+    return it != map.end() ? std::make_optional(it->second) : std::nullopt;
 }
 
 const char* InputEventLookup::lookupLabelByValue(const std::vector<InputEventLabel>& vec,
@@ -453,8 +453,8 @@
     return nullptr;
 }
 
-int32_t InputEventLookup::getKeyCodeByLabel(const char* label) {
-    return int32_t(lookupValueByLabel(KEYCODES, label));
+std::optional<int> InputEventLookup::getKeyCodeByLabel(const char* label) {
+    return lookupValueByLabel(KEYCODES, label);
 }
 
 const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) {
@@ -464,20 +464,20 @@
     return nullptr;
 }
 
-uint32_t InputEventLookup::getKeyFlagByLabel(const char* label) {
-    return uint32_t(lookupValueByLabel(FLAGS, label));
+std::optional<int> InputEventLookup::getKeyFlagByLabel(const char* label) {
+    return lookupValueByLabel(FLAGS, label);
 }
 
-int32_t InputEventLookup::getAxisByLabel(const char* label) {
-    return int32_t(lookupValueByLabel(AXES, label));
+std::optional<int> InputEventLookup::getAxisByLabel(const char* label) {
+    return lookupValueByLabel(AXES, label);
 }
 
 const char* InputEventLookup::getAxisLabel(int32_t axisId) {
     return lookupLabelByValue(AXES_NAMES, axisId);
 }
 
-int32_t InputEventLookup::getLedByLabel(const char* label) {
-    return int32_t(lookupValueByLabel(LEDS, label));
+std::optional<int> InputEventLookup::getLedByLabel(const char* label) {
+    return lookupValueByLabel(LEDS, label);
 }
 
 } // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 6bfac40..737bd15 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -999,7 +999,7 @@
 
     mTokenizer->skipDelimiters(WHITESPACE);
     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+    std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
     if (!keyCode) {
         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
                 keyCodeToken.string());
@@ -1010,19 +1010,19 @@
     ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
             mapUsage ? "usage" : "scan code", code, keyCode);
 #endif
-    map.insert_or_assign(code, keyCode);
+    map.insert_or_assign(code, *keyCode);
     return NO_ERROR;
 }
 
 status_t KeyCharacterMap::Parser::parseKey() {
     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+    std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
     if (!keyCode) {
         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
                 keyCodeToken.string());
         return BAD_VALUE;
     }
-    if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
+    if (mMap->mKeys.indexOfKey(*keyCode) >= 0) {
         ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
                 keyCodeToken.string());
         return BAD_VALUE;
@@ -1036,11 +1036,9 @@
         return BAD_VALUE;
     }
 
-#if DEBUG_PARSER
-    ALOGD("Parsed beginning of key: keyCode=%d.", keyCode);
-#endif
-    mKeyCode = keyCode;
-    mMap->mKeys.add(keyCode, new Key());
+    ALOGD_IF(DEBUG_PARSER, "Parsed beginning of key: keyCode=%d.", *keyCode);
+    mKeyCode = *keyCode;
+    mMap->mKeys.add(*keyCode, new Key());
     mState = STATE_KEY;
     return NO_ERROR;
 }
@@ -1136,7 +1134,7 @@
             } else if (token == "fallback") {
                 mTokenizer->skipDelimiters(WHITESPACE);
                 token = mTokenizer->nextToken(WHITESPACE);
-                int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
+                std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
                 if (!keyCode) {
                     ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
                             mTokenizer->getLocation().string(),
@@ -1148,12 +1146,12 @@
                             mTokenizer->getLocation().string());
                     return BAD_VALUE;
                 }
-                behavior.fallbackKeyCode = keyCode;
+                behavior.fallbackKeyCode = *keyCode;
                 haveFallback = true;
             } else if (token == "replace") {
                 mTokenizer->skipDelimiters(WHITESPACE);
                 token = mTokenizer->nextToken(WHITESPACE);
-                int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
+                std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
                 if (!keyCode) {
                     ALOGE("%s: Invalid key code label for replace, got '%s'.",
                             mTokenizer->getLocation().string(),
@@ -1170,7 +1168,7 @@
                             mTokenizer->getLocation().string());
                     return BAD_VALUE;
                 }
-                behavior.replacementKeyCode = keyCode;
+                behavior.replacementKeyCode = *keyCode;
                 haveReplacement = true;
 
             } else {
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 7371033..a2649f6 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "KeyLayoutMap"
 
+#include <android-base/logging.h>
 #include <android/keycodes.h>
 #include <ftl/enum.h>
 #include <input/InputEventLabels.h>
@@ -54,6 +55,21 @@
 namespace android {
 namespace {
 
+std::optional<int> parseInt(const char* str) {
+    char* end;
+    errno = 0;
+    const int value = strtol(str, &end, 0);
+    if (end == str) {
+        LOG(ERROR) << "Could not parse " << str;
+        return {};
+    }
+    if (errno == ERANGE) {
+        LOG(ERROR) << "Out of bounds: " << str;
+        return {};
+    }
+    return value;
+}
+
 constexpr const char* WHITESPACE = " \t\r";
 
 template <InputDeviceSensorType S>
@@ -336,16 +352,15 @@
         codeToken = mTokenizer->nextToken(WHITESPACE);
     }
 
-    char* end;
-    int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
-    if (*end) {
+    std::optional<int> code = parseInt(codeToken.string());
+    if (!code) {
         ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
                 mapUsage ? "usage" : "scan code", codeToken.string());
         return BAD_VALUE;
     }
     std::unordered_map<int32_t, Key>& map =
             mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
-    if (map.find(code) != map.end()) {
+    if (map.find(*code) != map.end()) {
         ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
                 mapUsage ? "usage" : "scan code", codeToken.string());
         return BAD_VALUE;
@@ -353,7 +368,7 @@
 
     mTokenizer->skipDelimiters(WHITESPACE);
     String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+    std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
     if (!keyCode) {
         ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
                 keyCodeToken.string());
@@ -366,40 +381,39 @@
         if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
 
         String8 flagToken = mTokenizer->nextToken(WHITESPACE);
-        uint32_t flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
+        std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
         if (!flag) {
             ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
                     flagToken.string());
             return BAD_VALUE;
         }
-        if (flags & flag) {
+        if (flags & *flag) {
             ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
                     flagToken.string());
             return BAD_VALUE;
         }
-        flags |= flag;
+        flags |= *flag;
     }
 
     ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
-             mapUsage ? "usage" : "scan code", code, keyCode, flags);
+             mapUsage ? "usage" : "scan code", *code, *keyCode, flags);
 
     Key key;
-    key.keyCode = keyCode;
+    key.keyCode = *keyCode;
     key.flags = flags;
-    map.insert({code, key});
+    map.insert({*code, key});
     return NO_ERROR;
 }
 
 status_t KeyLayoutMap::Parser::parseAxis() {
     String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
-    char* end;
-    int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
-    if (*end) {
+    std::optional<int> scanCode = parseInt(scanCodeToken.string());
+    if (!scanCode) {
         ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
                 scanCodeToken.string());
         return BAD_VALUE;
     }
-    if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) {
+    if (mMap->mAxes.find(*scanCode) != mMap->mAxes.end()) {
         ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
                 scanCodeToken.string());
         return BAD_VALUE;
@@ -414,48 +428,53 @@
 
         mTokenizer->skipDelimiters(WHITESPACE);
         String8 axisToken = mTokenizer->nextToken(WHITESPACE);
-        axisInfo.axis = InputEventLookup::getAxisByLabel(axisToken.string());
-        if (axisInfo.axis < 0) {
+        std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.string());
+        if (!axis) {
             ALOGE("%s: Expected inverted axis label, got '%s'.",
                     mTokenizer->getLocation().string(), axisToken.string());
             return BAD_VALUE;
         }
+        axisInfo.axis = *axis;
     } else if (token == "split") {
         axisInfo.mode = AxisInfo::MODE_SPLIT;
 
         mTokenizer->skipDelimiters(WHITESPACE);
         String8 splitToken = mTokenizer->nextToken(WHITESPACE);
-        axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0));
-        if (*end) {
+        std::optional<int> splitValue = parseInt(splitToken.string());
+        if (!splitValue) {
             ALOGE("%s: Expected split value, got '%s'.",
                     mTokenizer->getLocation().string(), splitToken.string());
             return BAD_VALUE;
         }
+        axisInfo.splitValue = *splitValue;
 
         mTokenizer->skipDelimiters(WHITESPACE);
         String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
-        axisInfo.axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
-        if (axisInfo.axis < 0) {
+        std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
+        if (!axis) {
             ALOGE("%s: Expected low axis label, got '%s'.",
                     mTokenizer->getLocation().string(), lowAxisToken.string());
             return BAD_VALUE;
         }
+        axisInfo.axis = *axis;
 
         mTokenizer->skipDelimiters(WHITESPACE);
         String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
-        axisInfo.highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
-        if (axisInfo.highAxis < 0) {
+        std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
+        if (!highAxis) {
             ALOGE("%s: Expected high axis label, got '%s'.",
                     mTokenizer->getLocation().string(), highAxisToken.string());
             return BAD_VALUE;
         }
+        axisInfo.highAxis = *highAxis;
     } else {
-        axisInfo.axis = InputEventLookup::getAxisByLabel(token.string());
-        if (axisInfo.axis < 0) {
+        std::optional<int> axis = InputEventLookup::getAxisByLabel(token.string());
+        if (!axis) {
             ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
                     mTokenizer->getLocation().string(), token.string());
             return BAD_VALUE;
         }
+        axisInfo.axis = *axis;
     }
 
     for (;;) {
@@ -467,12 +486,13 @@
         if (keywordToken == "flat") {
             mTokenizer->skipDelimiters(WHITESPACE);
             String8 flatToken = mTokenizer->nextToken(WHITESPACE);
-            axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0));
-            if (*end) {
+            std::optional<int> flatOverride = parseInt(flatToken.string());
+            if (!flatOverride) {
                 ALOGE("%s: Expected flat value, got '%s'.",
                         mTokenizer->getLocation().string(), flatToken.string());
                 return BAD_VALUE;
             }
+            axisInfo.flatOverride = *flatOverride;
         } else {
             ALOGE("%s: Expected keyword 'flat', got '%s'.",
                     mTokenizer->getLocation().string(), keywordToken.string());
@@ -483,9 +503,9 @@
     ALOGD_IF(DEBUG_PARSER,
              "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
              "splitValue=%d, flatOverride=%d.",
-             scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue,
+             *scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue,
              axisInfo.flatOverride);
-    mMap->mAxes.insert({scanCode, axisInfo});
+    mMap->mAxes.insert({*scanCode, axisInfo});
     return NO_ERROR;
 }
 
@@ -497,9 +517,8 @@
         mTokenizer->skipDelimiters(WHITESPACE);
         codeToken = mTokenizer->nextToken(WHITESPACE);
     }
-    char* end;
-    int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
-    if (*end) {
+    std::optional<int> code = parseInt(codeToken.string());
+    if (!code) {
         ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(),
                 mapUsage ? "usage" : "scan code", codeToken.string());
         return BAD_VALUE;
@@ -507,7 +526,7 @@
 
     std::unordered_map<int32_t, Led>& map =
             mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
-    if (map.find(code) != map.end()) {
+    if (map.find(*code) != map.end()) {
         ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(),
                 mapUsage ? "usage" : "scan code", codeToken.string());
         return BAD_VALUE;
@@ -515,19 +534,19 @@
 
     mTokenizer->skipDelimiters(WHITESPACE);
     String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
-    if (ledCode < 0) {
+    std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
+    if (!ledCode) {
         ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
                 ledCodeToken.string());
         return BAD_VALUE;
     }
 
     ALOGD_IF(DEBUG_PARSER, "Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code",
-             code, ledCode);
+             *code, *ledCode);
 
     Led led;
-    led.ledCode = ledCode;
-    map.insert({code, led});
+    led.ledCode = *ledCode;
+    map.insert({*code, led});
     return NO_ERROR;
 }
 
@@ -565,16 +584,15 @@
 // sensor 0x05 GYROSCOPE Z
 status_t KeyLayoutMap::Parser::parseSensor() {
     String8 codeToken = mTokenizer->nextToken(WHITESPACE);
-    char* end;
-    int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
-    if (*end) {
+    std::optional<int> code = parseInt(codeToken.string());
+    if (!code) {
         ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(),
               "abs code", codeToken.string());
         return BAD_VALUE;
     }
 
     std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode;
-    if (map.find(code) != map.end()) {
+    if (map.find(*code) != map.end()) {
         ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(),
               "abs code", codeToken.string());
         return BAD_VALUE;
@@ -599,13 +617,13 @@
     }
     int32_t sensorDataIndex = indexOpt.value();
 
-    ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
+    ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", *code,
              ftl::enum_string(sensorType).c_str(), sensorDataIndex);
 
     Sensor sensor;
     sensor.sensorType = sensorType;
     sensor.sensorDataIndex = sensorDataIndex;
-    map.emplace(code, sensor);
+    map.emplace(*code, sensor);
     return NO_ERROR;
 }
 
diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl
index 3bc7068..e80c2a5 100644
--- a/libs/input/android/os/InputEventInjectionResult.aidl
+++ b/libs/input/android/os/InputEventInjectionResult.aidl
@@ -37,4 +37,7 @@
 
     /* Injection failed due to a timeout. */
     TIMED_OUT = 3,
+
+    ftl_first=PENDING,
+    ftl_last=TIMED_OUT,
 }
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index 2344463..ee961f0 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -133,6 +133,20 @@
     ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
 }
 
+TEST_F(InputDeviceKeyMapTest, keyCharacteMapBadAxisLabel) {
+    std::string klPath = base::GetExecutableDirectory() + "/data/bad_axis_label.kl";
+
+    base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+    ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
+}
+
+TEST_F(InputDeviceKeyMapTest, keyCharacteMapBadLedLabel) {
+    std::string klPath = base::GetExecutableDirectory() + "/data/bad_led_label.kl";
+
+    base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+    ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
+}
+
 TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
 #if !defined(__ANDROID__)
     GTEST_SKIP() << "Can't check kernel configs on host";
diff --git a/libs/input/tests/data/bad_axis_label.kl b/libs/input/tests/data/bad_axis_label.kl
new file mode 100644
index 0000000..6897380
--- /dev/null
+++ b/libs/input/tests/data/bad_axis_label.kl
@@ -0,0 +1,17 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This KL should not be loaded because the axis label is not valid
+
+axis 0 DEFINITELY_NOT_AXIS_LABEL
diff --git a/libs/input/tests/data/bad_led_label.kl b/libs/input/tests/data/bad_led_label.kl
new file mode 100644
index 0000000..293c0d2
--- /dev/null
+++ b/libs/input/tests/data/bad_led_label.kl
@@ -0,0 +1,17 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This KL should not be loaded because the led label is invalid
+
+led 0 ABSOLUTELY_NOT_LED_LABEL
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
index d0de48f..419b63d 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
@@ -47,7 +47,7 @@
      */
     void* getDecompressedImagePtr();
     /*
-     * Returns the decompressed raw image buffer size. This mgit ethod must be called only after
+     * Returns the decompressed raw image buffer size. This method must be called only after
      * calling decompressImage().
      */
     size_t getDecompressedImageSize();
@@ -92,10 +92,6 @@
                                       size_t* pWidth, size_t* pHeight,
                                       std::vector<uint8_t>* iccData,
                                       std::vector<uint8_t>* exifData);
-    /*
-     * Extracts EXIF package and updates the EXIF position / length without decoding the image.
-     */
-    bool extractEXIF(const void* image, int length);
 
 private:
     bool decode(const void* image, int length, bool decodeToRGBA);
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index ae15d24..696be1b 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -321,13 +321,17 @@
                                 jr_compressed_ptr dest);
 
     /*
-     * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
-     * and the compressed recovery map as input, and update the XMP metadata with the end of JPEG
-     * marker, and append the compressed gian map after the JPEG.
+     * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
+     * the compressed recovery map and optionally the exif package as inputs, and generate the XMP
+     * metadata, and finally append everything in the order of:
+     *     SOI, APP2(EXIF) (if EXIF is from outside), APP2(XMP), primary image, recovery map
+     * Note that EXIF package is only available for encoding API-0 and API-1. For encoding API-2 and
+     * API-3 this parameter is null, but the primary image in JPEG/R may still have EXIF as long as
+     * the input JPEG has EXIF.
      *
      * @param compressed_jpeg_image compressed 8-bit JPEG image
      * @param compress_recovery_map compressed recover map
-     * @param exif EXIF package
+     * @param (nullable) exif EXIF package
      * @param metadata JPEG/R metadata to encode in XMP of the jpeg
      * @param dest compressed JPEGR image
      * @return NO_ERROR if calculation succeeds, error code if error occurs.
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
index 8b2672f..c36a363 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -45,7 +45,6 @@
  * @return status of succeed or error code.
  */
 status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position);
-status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position);
 
 
 /*
@@ -105,83 +104,6 @@
  * @return XMP metadata in type of string
  */
 std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
-
-/*
- * Add J R entry to existing exif, or create a new one with J R entry if it's null.
- * EXIF syntax / change:
- * ori:
- * FF E1 - APP1
- * 01 FC - size of APP1 (to be calculated)
- * -----------------------------------------------------
- * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
- * 49 49 2A 00 - TIFF Header
- * 08 00 00 00 - offset to the IFD (image file directory)
- * 06 00 - 6 entries
- * 00 01 - Width Tag
- * 03 00 - 'Short' type
- * 01 00 00 00 - 1 component
- * 00 05 00 00 - image with 0x500
- *--------------------------------------------------------------------------
- * new:
- * FF E1 - APP1
- * 02 08 - new size, equals to old size + EXIF_J_R_ENTRY_LENGTH (12)
- *-----------------------------------------------------
- * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
- * 49 49 2A 00 - TIFF Header
- * 08 00 00 00 - offset to the IFD (image file directory)
- * 07 00 - +1 entry
- * 4A 52   Custom ('J''R') Tag
- * 07 00 - Unknown type
- * 01 00 00 00 - 1 component
- * 00 00 00 00 - empty data
- * 00 01 - Width Tag
- * 03 00 - 'Short' type
- * 01 00 00 00 - 1 component
- * 00 05 00 00 - image with 0x500
- */
-status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest);
-
-/*
- * Modify offsets in EXIF in place.
- *
- * Each tag has the following structure:
- *
- * 00 01 - Tag
- * 03 00 - data format
- * 01 00 00 00 - number of components
- * 00 05 00 00 - value
- *
- * The value means offset if
- * (1) num_of_components * bytes_per_component > 4 bytes, or
- * (2) tag == 0x8769 (ExifOffset).
- * In both cases, the method will add EXIF_J_R_ENTRY_LENGTH (12) to the offsets.
- */
-void updateExifOffsets(jr_exif_ptr exif, int pos, bool use_big_endian);
-void updateExifOffsets(jr_exif_ptr exif, int pos, int num_entry, bool use_big_endian);
-
-/*
- * Read data from the target position and target length in bytes;
- */
-int readValue(uint8_t* data, int pos, int length, bool use_big_endian);
-
-/*
- * Returns the length of data format in bytes
- *
- *  ----------------------------------------------------------------------------------------------
- *  |       value       |         1       |        2        |        3         |       4         |
- *  |       format      |  unsigned byte  |  ascii strings  |  unsigned short  |  unsigned long  |
- *  |  bytes/component  |         1       |        1        |        2         |       4         |
- *  ----------------------------------------------------------------------------------------------
- *  |       value       |         5       |        6        |        7         |       8         |
- *  |       format      |unsigned rational|   signed byte   |    undefined     |  signed short   |
- *  |  bytes/component  |         8       |        1        |        1         |       2         |
- *  ----------------------------------------------------------------------------------------------
- *  |       value       |         9       |        10       |        11        |       12        |
- *  |       format      |   signed long   | signed rational |   single float   |  double float   |
- *  |  bytes/component  |         4       |        8        |        4         |       8         |
- *  ----------------------------------------------------------------------------------------------
- */
-int findFormatLengthInBytes(int data_format);
 }
 
 #endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp
index 6fbc6b0..1bf609a 100644
--- a/libs/jpegrecoverymap/jpegdecoder.cpp
+++ b/libs/jpegrecoverymap/jpegdecoder.cpp
@@ -248,60 +248,6 @@
     return true;
 }
 
-// TODO (Fyodor/Dichen): merge this method with getCompressedImageParameters() since they have
-// similar functionality. Yet Dichen is not familiar with who's calling
-// getCompressedImageParameters(), looks like it's used by some pending CLs.
-bool JpegDecoder::extractEXIF(const void* image, int length) {
-    jpeg_decompress_struct cinfo;
-    jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
-    jpegrerror_mgr myerr;
-
-    cinfo.err = jpeg_std_error(&myerr.pub);
-    myerr.pub.error_exit = jpegrerror_exit;
-
-    if (setjmp(myerr.setjmp_buffer)) {
-        jpeg_destroy_decompress(&cinfo);
-        return false;
-    }
-    jpeg_create_decompress(&cinfo);
-
-    jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
-    jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
-    jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
-
-    cinfo.src = &mgr;
-    jpeg_read_header(&cinfo, TRUE);
-
-    bool exifAppears = false;
-    size_t pos = 2;  // position after SOI
-    for (jpeg_marker_struct* marker = cinfo.marker_list;
-         marker && !exifAppears;
-         marker = marker->next) {
-
-        pos += 4;
-        pos += marker->original_length;
-
-        if (marker->marker != kAPP1Marker) {
-            continue;
-        }
-
-        const unsigned int len = marker->data_length;
-        if (!exifAppears &&
-            len > kExifIdCode.size() &&
-            !strncmp(reinterpret_cast<const char*>(marker->data),
-                     kExifIdCode.c_str(),
-                     kExifIdCode.size())) {
-            mEXIFBuffer.resize(len, 0);
-            memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
-            exifAppears = true;
-            mExifPos = pos - marker->original_length;
-        }
-    }
-
-    jpeg_destroy_decompress(&cinfo);
-    return true;
-}
-
 bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
         bool isSingleChannel) {
     if (isSingleChannel) {
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index 22289de..30aa846 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -175,18 +175,7 @@
   jpeg.data = jpeg_encoder.getCompressedImagePtr();
   jpeg.length = jpeg_encoder.getCompressedImageSize();
 
-  jpegr_exif_struct new_exif;
-  if (exif == nullptr || exif->data == nullptr) {
-      new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
-  } else {
-      new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
-  }
-  new_exif.data = new uint8_t[new_exif.length];
-  std::unique_ptr<uint8_t[]> new_exif_data;
-  new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
-  JPEGR_CHECK(updateExif(exif, &new_exif));
-
-  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
+  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, exif, &metadata, dest));
 
   return NO_ERROR;
 }
@@ -250,19 +239,7 @@
   jpeg.data = jpeg_encoder.getCompressedImagePtr();
   jpeg.length = jpeg_encoder.getCompressedImageSize();
 
-  jpegr_exif_struct new_exif;
-  if (exif == nullptr || exif->data == nullptr) {
-      new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
-  } else {
-      new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
-  }
-
-  new_exif.data = new uint8_t[new_exif.length];
-  std::unique_ptr<uint8_t[]> new_exif_data;
-  new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
-  JPEGR_CHECK(updateExif(exif, &new_exif));
-
-  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
+  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, exif, &metadata, dest));
 
   return NO_ERROR;
 }
@@ -311,47 +288,7 @@
   compressed_map.data = compressed_map_data.get();
   JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
 
-  // Extract EXIF from JPEG without decoding.
-  JpegDecoder jpeg_decoder;
-  if (!jpeg_decoder.extractEXIF(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
-    return ERROR_JPEGR_DECODE_ERROR;
-  }
-
-  // Update exif.
-  jpegr_exif_struct exif;
-  exif.data = nullptr;
-  exif.length = 0;
-  jpegr_compressed_struct new_jpeg_image;
-  new_jpeg_image.data = nullptr;
-  new_jpeg_image.length = 0;
-  if (jpeg_decoder.getEXIFPos() != 0) {
-    copyJpegWithoutExif(&new_jpeg_image,
-                        compressed_jpeg_image,
-                        jpeg_decoder.getEXIFPos(),
-                        jpeg_decoder.getEXIFSize());
-    exif.data = jpeg_decoder.getEXIFPtr();
-    exif.length = jpeg_decoder.getEXIFSize();
-  }
-
-  jpegr_exif_struct new_exif;
-  if (exif.data == nullptr) {
-      new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
-  } else {
-      new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
-  }
-
-  new_exif.data = new uint8_t[new_exif.length];
-  std::unique_ptr<uint8_t[]> new_exif_data;
-  new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
-  JPEGR_CHECK(updateExif(&exif, &new_exif));
-
-  JPEGR_CHECK(appendRecoveryMap(
-          new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
-          &compressed_map, &new_exif, &metadata, dest));
-
-  if (new_jpeg_image.data != nullptr) {
-    free(new_jpeg_image.data);
-  }
+  JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, nullptr, &metadata, dest));
 
   return NO_ERROR;
 }
@@ -384,33 +321,6 @@
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
   uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
 
-  // Update exif.
-  jpegr_exif_struct exif;
-  exif.data = nullptr;
-  exif.length = 0;
-  jpegr_compressed_struct new_jpeg_image;
-  new_jpeg_image.data = nullptr;
-  new_jpeg_image.length = 0;
-  if (jpeg_decoder.getEXIFPos() != 0) {
-    copyJpegWithoutExif(&new_jpeg_image,
-                        compressed_jpeg_image,
-                        jpeg_decoder.getEXIFPos(),
-                        jpeg_decoder.getEXIFSize());
-    exif.data = jpeg_decoder.getEXIFPtr();
-    exif.length = jpeg_decoder.getEXIFSize();
-  }
-
-  jpegr_exif_struct new_exif;
-  if (exif.data == nullptr) {
-      new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
-  } else {
-      new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
-  }
-  new_exif.data = new uint8_t[new_exif.length];
-  std::unique_ptr<uint8_t[]> new_exif_data;
-  new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
-  JPEGR_CHECK(updateExif(&exif, &new_exif));
-
   if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
    || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
     return ERROR_JPEGR_RESOLUTION_MISMATCH;
@@ -435,13 +345,7 @@
   compressed_map.data = compressed_map_data.get();
   JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
 
-  JPEGR_CHECK(appendRecoveryMap(
-          new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
-          &compressed_map, &new_exif, &metadata, dest));
-
-  if (new_jpeg_image.data != nullptr) {
-    free(new_jpeg_image.data);
-  }
+  JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, nullptr, &metadata, dest));
 
   return NO_ERROR;
 }
@@ -967,15 +871,20 @@
 
 // JPEG/R structure:
 // SOI (ff d8)
+//
+// (Optional, only if EXIF package is from outside)
 // APP1 (ff e1)
 // 2 bytes of length (2 + length of exif package)
 // EXIF package (this includes the first two bytes representing the package length)
-// APP1 (ff e1)
+//
+// (Required, XMP package) APP1 (ff e1)
 // 2 bytes of length (2 + 29 + length of xmp package)
 // name space ("http://ns.adobe.com/xap/1.0/\0")
 // xmp
-// primary image (without the first two bytes (SOI) and without EXIF, may have other packages)
-// secondary image (the recovery map)
+//
+// (Required) primary image (without the first two bytes (SOI), may have other packages)
+//
+// (Required) secondary image (the recovery map)
 //
 // Metadata versions we are using:
 // ECMA TR-98 for JFIF marker
@@ -989,7 +898,6 @@
                                         jr_compressed_ptr dest) {
   if (compressed_jpeg_image == nullptr
    || compressed_recovery_map == nullptr
-   || exif == nullptr
    || metadata == nullptr
    || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -1002,7 +910,7 @@
   JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
 
   // Write EXIF
-  {
+  if (exif != nullptr) {
     const int length = 2 + exif->length;
     const uint8_t lengthH = ((length >> 8) & 0xff);
     const uint8_t lengthL = (length & 0xff);
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp
index d5ad9a5..1617b8b 100644
--- a/libs/jpegrecoverymap/recoverymaputils.cpp
+++ b/libs/jpegrecoverymap/recoverymaputils.cpp
@@ -22,8 +22,6 @@
 #include <image_io/xml/xml_handler.h>
 #include <image_io/xml/xml_rule.h>
 
-#include <utils/Log.h>
-
 using namespace photos_editing_formats::image_io;
 using namespace std;
 
@@ -55,12 +53,6 @@
   return NO_ERROR;
 }
 
-status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position) {
-  memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
-  position += length;
-  return NO_ERROR;
-}
-
 // Extremely simple XML Handler - just searches for interesting elements
 class XMPXmlHandler : public XmlHandler {
 public:
@@ -344,185 +336,4 @@
   return ss.str();
 }
 
-/*
- * Helper function
- * Add J R entry to existing exif, or create a new one with J R entry if it's null.
- */
-status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest) {
-  if (exif == nullptr || exif->data == nullptr) {
-    uint8_t data[PSEUDO_EXIF_PACKAGE_LENGTH] = {
-        0x45, 0x78, 0x69, 0x66, 0x00, 0x00,
-        0x49, 0x49, 0x2A, 0x00,
-        0x08, 0x00, 0x00, 0x00,
-        0x01, 0x00,
-        0x4A, 0x52,
-        0x07, 0x00,
-        0x01, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00};
-    int pos = 0;
-    Write(dest, data, PSEUDO_EXIF_PACKAGE_LENGTH, pos);
-    return NO_ERROR;
-  }
-
-  int num_entry = 0;
-  uint8_t num_entry_low = 0;
-  uint8_t num_entry_high = 0;
-  bool use_big_endian = false;
-  if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4949) {
-      num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[14];
-      num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[15];
-  } else if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4d4d) {
-      use_big_endian = true;
-      num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[14];
-      num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[15];
-  } else {
-      return ERROR_JPEGR_METADATA_ERROR;
-  }
-  num_entry = (num_entry_high << 8) | num_entry_low;
-  num_entry += 1;
-  num_entry_low = num_entry & 0xff;
-  num_entry_high = (num_entry >> 8) & 0xff;
-
-  int pos = 0;
-  Write(dest, (uint8_t*)exif->data, 14, pos);
-
-  if (use_big_endian) {
-    Write(dest, &num_entry_high, 1, pos);
-    Write(dest, &num_entry_low, 1, pos);
-    uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
-          0x4A, 0x52,
-          0x00, 0x07,
-          0x00, 0x00, 0x00, 0x01,
-          0x00, 0x00, 0x00, 0x00};
-    Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
-  } else {
-    Write(dest, &num_entry_low, 1, pos);
-    Write(dest, &num_entry_high, 1, pos);
-    uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
-          0x4A, 0x52,
-          0x07, 0x00,
-          0x01, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x00};
-    Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
-  }
-
-  Write(dest, (uint8_t*)exif->data + 16, exif->length - 16, pos);
-
-  updateExifOffsets(dest,
-                    28, // start from the second tag, skip the "JR" tag
-                    num_entry - 1,
-                    use_big_endian);
-
-  return NO_ERROR;
-}
-
-/*
- * Helper function
- * Modify offsets in EXIF in place.
- */
-void updateExifOffsets(jr_exif_ptr exif, int pos, bool use_big_endian) {
-  int num_entry = readValue(reinterpret_cast<uint8_t*>(exif->data), pos, 2, use_big_endian);
-  updateExifOffsets(exif, pos + 2, num_entry, use_big_endian);
-}
-
-void updateExifOffsets(jr_exif_ptr exif, int pos, int num_entry, bool use_big_endian) {
-  for (int i = 0; i < num_entry; pos += EXIF_J_R_ENTRY_LENGTH, i++) {
-    int tag = readValue(reinterpret_cast<uint8_t*>(exif->data), pos, 2, use_big_endian);
-    bool need_to_update_offset = false;
-    if (tag == 0x8769) {
-      need_to_update_offset = true;
-      int sub_ifd_offset =
-              readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 8, 4, use_big_endian)
-              + 6  // "Exif\0\0";
-              + EXIF_J_R_ENTRY_LENGTH;
-      updateExifOffsets(exif, sub_ifd_offset, use_big_endian);
-    } else {
-      int data_format =
-              readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 2, 2, use_big_endian);
-      int num_of_components =
-              readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 4, 4, use_big_endian);
-      int data_length = findFormatLengthInBytes(data_format) * num_of_components;
-      if (data_length > 4) {
-        need_to_update_offset = true;
-      }
-    }
-
-    if (!need_to_update_offset) {
-      continue;
-    }
-
-    int offset = readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 8, 4, use_big_endian);
-
-    offset += EXIF_J_R_ENTRY_LENGTH;
-
-    if (use_big_endian) {
-      reinterpret_cast<uint8_t*>(exif->data)[pos + 11] = offset & 0xff;
-      reinterpret_cast<uint8_t*>(exif->data)[pos + 10] = (offset >> 8) & 0xff;
-      reinterpret_cast<uint8_t*>(exif->data)[pos + 9] = (offset >> 16) & 0xff;
-      reinterpret_cast<uint8_t*>(exif->data)[pos + 8] = (offset >> 24) & 0xff;
-    } else {
-      reinterpret_cast<uint8_t*>(exif->data)[pos + 8] = offset & 0xff;
-      reinterpret_cast<uint8_t*>(exif->data)[pos + 9] = (offset >> 8) & 0xff;
-      reinterpret_cast<uint8_t*>(exif->data)[pos + 10] = (offset >> 16) & 0xff;
-      reinterpret_cast<uint8_t*>(exif->data)[pos + 11] = (offset >> 24) & 0xff;
-    }
-  }
-}
-
-/*
- * Read data from the target position and target length in bytes;
- */
-int readValue(uint8_t* data, int pos, int length, bool use_big_endian) {
-  if (length == 2) {
-    if (use_big_endian) {
-      return (data[pos] << 8) | data[pos + 1];
-    } else {
-      return (data[pos + 1] << 8) | data[pos];
-    }
-  } else if (length == 4) {
-    if (use_big_endian) {
-      return (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
-    } else {
-      return (data[pos + 3] << 24) | (data[pos + 2] << 16) | (data[pos + 1] << 8) | data[pos];
-    }
-  } else {
-    // Not support for now.
-    ALOGE("Error in readValue(): pos=%d, length=%d", pos, length);
-    return -1;
-  }
-}
-
-/*
- * Helper function
- * Returns the length of data format in bytes
- */
-int findFormatLengthInBytes(int data_format) {
-  switch (data_format) {
-    case 1:  // unsigned byte
-    case 2:  // ascii strings
-    case 6:  // signed byte
-    case 7:  // undefined
-      return 1;
-
-    case 3:  // unsigned short
-    case 8:  // signed short
-      return 2;
-
-    case 4:  // unsigned long
-    case 9:  // signed long
-    case 11:  // single float
-      return 4;
-
-    case 5:  // unsigned rational
-    case 10:  // signed rational
-    case 12:  // double float
-      return 8;
-
-    default:
-      // should not hit here
-      ALOGE("Error in findFormatLengthInBytes(): data_format=%d", data_format);
-      return -1;
-  }
-}
-
-} // namespace android::recoverymap
\ No newline at end of file
+} // namespace android::recoverymap
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index c7745e6..6c54635 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1067,11 +1067,12 @@
 }
 
 static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
+                                                        uint64_t frameNumber,
                                                         int64_t frameTimelineVsyncId,
                                                         int32_t inputEventId,
                                                         int64_t startTimeNanos) {
-    return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId,
-                           inputEventId, startTimeNanos);
+    return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
+                           frameTimelineVsyncId, inputEventId, startTimeNanos);
 }
 
 // ------------------------------------------------------------------------------------------------
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index 178bc29..a2e4115 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -34,6 +34,9 @@
     BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY
     BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER
     BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS
+    BATTERY_PROP_CHARGING_POLICY = 7, // equals BATTERY_PROPERTY_CHARGING_POLICY
+    BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE
+    BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE
 };
 
 struct BatteryProperties {
diff --git a/services/batteryservice/include/batteryservice/BatteryServiceConstants.h b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
index 8a90a12..2d7072d 100644
--- a/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
+++ b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
@@ -1,7 +1,5 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
-
-#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
-#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#ifndef AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
+#define AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
 
 #ifdef __cplusplus
 extern "C" {
@@ -15,6 +13,8 @@
     BATTERY_STATUS_FULL = 5,
 };
 
+// must be kept in sync with definitions in
+// hardware/interfaces/health/aidl/android/hardware/health/BatteryHealth.aidl
 enum {
     BATTERY_HEALTH_UNKNOWN = 1,
     BATTERY_HEALTH_GOOD = 2,
@@ -23,10 +23,23 @@
     BATTERY_HEALTH_OVER_VOLTAGE = 5,
     BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6,
     BATTERY_HEALTH_COLD = 7,
+    BATTERY_HEALTH_FAIR = 8,
+    BATTERY_HEALTH_NOT_AVAILABLE = 11,
+    BATTERY_HEALTH_INCONSISTENT = 12,
+};
+
+// must be kept in sync with definitions in
+// hardware/interfaces/health/aidl/android/hardware/health/BatteryChargingState.aidl
+enum {
+    BATTERY_STATUS_NORMAL = 1,
+    BATTERY_STATUS_TOO_COLD = 2,
+    BATTERY_STATUS_TOO_HOT = 3,
+    BATTERY_STATUS_LONG_LIFE = 4,
+    BATTERY_STATUS_ADAPTIVE = 5,
 };
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif  // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#endif  // AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index a864cf8..2450235 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -73,6 +73,9 @@
     if (changes & CHANGE_ENABLED_STATE) {
         result += "ENABLED_STATE | ";
     }
+    if (changes & CHANGE_TOUCHPAD_SETTINGS) {
+        result += "TOUCHPAD_SETTINGS | ";
+    }
     if (changes & CHANGE_MUST_REOPEN) {
         result += "MUST_REOPEN | ";
     }
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 3d7242e..cacf30b 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -61,6 +61,7 @@
       "name": "FrameworksCoreTests",
       "options": [
         {
+          "include-filter": "android.hardware.input",
           "include-filter": "android.view.VerifiedKeyEventTest",
           "include-filter": "android.view.VerifiedMotionEventTest"
         }
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 7bbfb95..ce7c882 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -331,4 +331,28 @@
     return seq;
 }
 
+std::ostream& operator<<(std::ostream& out, const DispatchEntry& entry) {
+    out << "DispatchEntry{resolvedAction=";
+    switch (entry.eventEntry->type) {
+        case EventEntry::Type::KEY: {
+            out << KeyEvent::actionToString(entry.resolvedAction);
+            break;
+        }
+        case EventEntry::Type::MOTION: {
+            out << MotionEvent::actionToString(entry.resolvedAction);
+            break;
+        }
+        default: {
+            out << "<invalid, not a key or a motion>";
+            break;
+        }
+    }
+    std::string transform;
+    entry.transform.dump(transform, "transform");
+    out << ", resolvedFlags=" << entry.resolvedFlags
+        << ", targetFlags=" << entry.targetFlags.string() << ", transform=" << transform
+        << "} original =" << entry.eventEntry->getDescription();
+    return out;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 3799814..8dc2a2a 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -254,6 +254,8 @@
     static uint32_t nextSeq();
 };
 
+std::ostream& operator<<(std::ostream& out, const DispatchEntry& entry);
+
 VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry);
 VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry,
                                                        const ui::Transform& rawTransform);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 37a451b..dc9f02a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -20,6 +20,7 @@
 #define LOG_NDEBUG 1
 
 #include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android/os/IInputConstants.h>
@@ -616,6 +617,12 @@
     return out;
 }
 
+template <typename T>
+std::vector<T>& operator+=(std::vector<T>& left, const std::vector<T>& right) {
+    left.insert(left.end(), right.begin(), right.end());
+    return left;
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -1018,8 +1025,7 @@
         const auto [x, y] = resolveTouchedPosition(motionEntry);
         const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
 
-        sp<WindowInfoHandle> touchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus);
+        auto [touchedWindowHandle, _] = findTouchedWindowAtLocked(displayId, x, y, isStylus);
         if (touchedWindowHandle != nullptr &&
             touchedWindowHandle->getApplicationToken() !=
                     mAwaitedFocusedApplication->getApplicationToken()) {
@@ -1142,15 +1148,11 @@
     }
 }
 
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
-                                                                int32_t y, TouchState* touchState,
-                                                                bool isStylus,
-                                                                bool addOutsideTargets,
-                                                                bool ignoreDragWindow) const {
-    if (addOutsideTargets && touchState == nullptr) {
-        LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
-    }
+std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>>
+InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus,
+                                           bool ignoreDragWindow) const {
     // Traverse windows from front to back to find touched window.
+    std::vector<InputTarget> outsideTargets;
     const auto& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
         if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
@@ -1159,16 +1161,16 @@
 
         const WindowInfo& info = *windowHandle->getInfo();
         if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
-            return windowHandle;
+            return {windowHandle, outsideTargets};
         }
 
-        if (addOutsideTargets &&
-            info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
-            touchState->addOrUpdateWindow(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
-                                          BitSet32(0));
+        if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
+            addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
+                                  BitSet32(0), /*firstDownTimeInTarget=*/std::nullopt,
+                                  outsideTargets);
         }
     }
-    return nullptr;
+    return {nullptr, {}};
 }
 
 std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
@@ -1756,16 +1758,11 @@
             pilferPointersLocked(mDragState->dragWindow->getToken());
         }
 
-        std::vector<TouchedWindow> touchedWindows =
+        inputTargets =
                 findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
                                                /*byref*/ injectionResult);
-        for (const TouchedWindow& touchedWindow : touchedWindows) {
-            LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED,
-                                "Shouldn't be adding window if the injection didn't succeed.");
-            addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
-                                  touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
-                                  inputTargets);
-        }
+        LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED &&
+                            !inputTargets.empty());
     } else {
         // Non touch event.  (eg. trackball)
         sp<WindowInfoHandle> focusedWindow =
@@ -2137,12 +2134,12 @@
     return true;
 }
 
-std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
+std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
         nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
         InputEventInjectionResult& outInjectionResult) {
     ATRACE_CALL();
 
-    std::vector<TouchedWindow> touchedWindows;
+    std::vector<InputTarget> targets;
     // For security reasons, we defer updating the touch state until we are sure that
     // event injection will be allowed.
     const int32_t displayId = entry.displayId;
@@ -2169,8 +2166,12 @@
     const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
                                 maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
                                 maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
-    const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
-                             maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
+    // A DOWN could be generated from POINTER_DOWN if the initial pointers did not land into any
+    // touchable windows.
+    const bool wasDown = oldState != nullptr && oldState->isDown();
+    const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
+            (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
+    const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction;
     const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
 
     if (newGesture) {
@@ -2181,7 +2182,7 @@
                   displayId);
             // TODO: test multiple simultaneous input streams.
             outInjectionResult = InputEventInjectionResult::FAILED;
-            return touchedWindows; // wrong device
+            return {}; // wrong device
         }
         tempTouchState.clearWindowsWithoutPointers();
         tempTouchState.deviceId = entry.deviceId;
@@ -2193,7 +2194,7 @@
               displayId);
         // TODO: test multiple simultaneous input streams.
         outInjectionResult = InputEventInjectionResult::FAILED;
-        return touchedWindows; // wrong device
+        return {}; // wrong device
     }
 
     if (isHoverAction) {
@@ -2206,12 +2207,15 @@
         /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
         const auto [x, y] = resolveTouchedPosition(entry);
         const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-        const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
+        // Outside targets should be added upon first dispatched DOWN event. That means, this should
+        // be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
         const bool isStylus = isPointerFromStylus(entry, pointerIndex);
-        sp<WindowInfoHandle> newTouchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus,
-                                          isDown /*addOutsideTargets*/);
+        auto [newTouchedWindowHandle, outsideTargets] =
+                findTouchedWindowAtLocked(displayId, x, y, isStylus);
 
+        if (isDown) {
+            targets += outsideTargets;
+        }
         // Handle the case where we did not find a window.
         if (newTouchedWindowHandle == nullptr) {
             ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
@@ -2225,7 +2229,7 @@
             ALOGW("Dropping injected touch event: %s", (*err).c_str());
             outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
             newTouchedWindowHandle = nullptr;
-            goto Failed;
+            return {};
         }
 
         // Figure out whether splitting will be allowed for this window.
@@ -2256,7 +2260,7 @@
             ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
                   x, y, displayId);
             outInjectionResult = InputEventInjectionResult::FAILED;
-            goto Failed;
+            return {};
         }
 
         for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
@@ -2347,7 +2351,7 @@
                      "dropped the pointer down event in display %" PRId32 ": %s",
                      displayId, entry.getDescription().c_str());
             outInjectionResult = InputEventInjectionResult::FAILED;
-            goto Failed;
+            return {};
         }
 
         addDragEventLocked(entry);
@@ -2359,15 +2363,13 @@
             const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
             sp<WindowInfoHandle> oldTouchedWindowHandle =
                     tempTouchState.getFirstForegroundWindowHandle();
-            sp<WindowInfoHandle> newTouchedWindowHandle =
-                    findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus);
+            auto [newTouchedWindowHandle, _] = findTouchedWindowAtLocked(displayId, x, y, isStylus);
 
             // Verify targeted injection.
             if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
                 ALOGW("Dropping injected event: %s", (*err).c_str());
                 outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
-                newTouchedWindowHandle = nullptr;
-                goto Failed;
+                return {};
             }
 
             // Drop touch events if requested by input feature
@@ -2384,9 +2386,15 @@
                           newTouchedWindowHandle->getName().c_str(), displayId);
                 }
                 // Make a slippery exit from the old window.
-                tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
-                                                 InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
-                                                 BitSet32(0));
+                BitSet32 pointerIds;
+                const int32_t pointerId = entry.pointerProperties[0].id;
+                pointerIds.markBit(pointerId);
+
+                const TouchedWindow& touchedWindow =
+                        tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
+                addWindowTargetLocked(oldTouchedWindowHandle,
+                                      InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds,
+                                      touchedWindow.firstDownTimeInTarget, targets);
 
                 // Make a slippery entrance into the new window.
                 if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2407,14 +2415,13 @@
                     targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
                 }
 
-                BitSet32 pointerIds;
-                pointerIds.markBit(entry.pointerProperties[0].id);
                 tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds,
                                                  entry.eventTime);
 
                 // Check if the wallpaper window should deliver the corresponding event.
                 slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
-                                   tempTouchState, pointerIds);
+                                   tempTouchState, pointerId, targets);
+                tempTouchState.removeTouchedPointerFromWindow(pointerId, oldTouchedWindowHandle);
             }
         }
 
@@ -2437,7 +2444,11 @@
     {
         std::vector<TouchedWindow> hoveringWindows =
                 getHoveringWindowsLocked(oldState, tempTouchState, entry);
-        touchedWindows.insert(touchedWindows.end(), hoveringWindows.begin(), hoveringWindows.end());
+        for (const TouchedWindow& touchedWindow : hoveringWindows) {
+            addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+                                  touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
+                                  targets);
+        }
     }
     // Ensure that we have at least one foreground window or at least one window that cannot be a
     // foreground target. If we only have windows that are not receiving foreground touches (e.g. we
@@ -2452,7 +2463,7 @@
         ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
               displayId, entry.getDescription().c_str());
         outInjectionResult = InputEventInjectionResult::FAILED;
-        goto Failed;
+        return {};
     }
 
     // Ensure that all touched windows are valid for injection.
@@ -2472,34 +2483,35 @@
                   "%d:%s",
                   *entry.injectionState->targetUid, errs.c_str());
             outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
-            goto Failed;
+            return {};
         }
     }
 
-    // Check whether windows listening for outside touches are owned by the same UID. If it is
-    // set the policy flag that we will not reveal coordinate information to this window.
+    // Check whether windows listening for outside touches are owned by the same UID. If the owner
+    // has a different UID, then we will not reveal coordinate information to this window.
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<WindowInfoHandle> foregroundWindowHandle =
                 tempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle) {
             const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
-            for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
-                if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
-                    sp<WindowInfoHandle> windowInfoHandle = touchedWindow.windowHandle;
-                    if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) {
-                        tempTouchState.addOrUpdateWindow(windowInfoHandle,
-                                                         InputTarget::Flags::ZERO_COORDS,
-                                                         BitSet32(0));
+            for (InputTarget& target : targets) {
+                if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
+                    sp<WindowInfoHandle> targetWindow =
+                            getWindowHandleLocked(target.inputChannel->getConnectionToken());
+                    if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
+                        target.flags |= InputTarget::Flags::ZERO_COORDS;
                     }
                 }
             }
         }
     }
 
-    // Success!  Output targets for everything except hovers.
-    if (!isHoverAction) {
-        touchedWindows.insert(touchedWindows.end(), tempTouchState.windows.begin(),
-                              tempTouchState.windows.end());
+    // Success!  Output targets from the touch state.
+    tempTouchState.clearWindowsWithoutPointers();
+    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
+        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+                              touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
+                              targets);
     }
 
     outInjectionResult = InputEventInjectionResult::SUCCEEDED;
@@ -2507,7 +2519,6 @@
     // in the next iteration.
     tempTouchState.filterNonAsIsTouchWindows();
 
-Failed:
     // Update final pieces of touch state if the injector had permission.
     if (switchedDevice) {
         if (DEBUG_FOCUS) {
@@ -2571,7 +2582,7 @@
         mTouchStatesByDisplay.erase(displayId);
     }
 
-    return touchedWindows;
+    return targets;
 }
 
 void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
@@ -2579,9 +2590,8 @@
     // have an explicit reason to support it.
     constexpr bool isStylus = false;
 
-    const sp<WindowInfoHandle> dropWindow =
-            findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, isStylus,
-                                      false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
+    auto [dropWindow, _] =
+            findTouchedWindowAtLocked(displayId, x, y, isStylus, true /*ignoreDragWindow*/);
     if (dropWindow) {
         vec2 local = dropWindow->getInfo()->transform.transform(x, y);
         sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
@@ -2637,10 +2647,8 @@
             // until we have an explicit reason to support it.
             constexpr bool isStylus = false;
 
-            const sp<WindowInfoHandle> hoverWindowHandle =
-                    findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
-                                              isStylus, false /*addOutsideTargets*/,
-                                              true /*ignoreDragWindow*/);
+            auto [hoverWindowHandle, _] = findTouchedWindowAtLocked(entry.displayId, x, y, isStylus,
+                                                                    true /*ignoreDragWindow*/);
             // enqueue drag exit if needed.
             if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
                 !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -3382,6 +3390,10 @@
             case EventEntry::Type::KEY: {
                 const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
                 std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
+                if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                    LOG(DEBUG) << "Publishing " << *dispatchEntry << " to "
+                               << connection->getInputChannelName();
+                }
 
                 // Publish the key event.
                 status = connection->inputPublisher
@@ -3397,6 +3409,10 @@
             }
 
             case EventEntry::Type::MOTION: {
+                if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+                    LOG(DEBUG) << "Publishing " << *dispatchEntry << " to "
+                               << connection->getInputChannelName();
+                }
                 status = publishMotionEvent(*connection, *dispatchEntry);
                 break;
             }
@@ -4448,6 +4464,9 @@
 
     bool needWake = false;
     while (!injectedEntries.empty()) {
+        if (DEBUG_INJECTION) {
+            LOG(DEBUG) << "Injecting " << injectedEntries.front()->getDescription();
+        }
         needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
         injectedEntries.pop();
     }
@@ -4510,7 +4529,8 @@
     } // release lock
 
     if (DEBUG_INJECTION) {
-        ALOGD("injectInputEvent - Finished with result %d.", injectionResult);
+        LOG(DEBUG) << "injectInputEvent - Finished with result "
+                   << ftl::enum_string(injectionResult);
     }
 
     return injectionResult;
@@ -4554,7 +4574,8 @@
     InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
         if (DEBUG_INJECTION) {
-            ALOGD("Setting input event injection result to %d.", injectionResult);
+            LOG(DEBUG) << "Setting input event injection result to "
+                       << ftl::enum_string(injectionResult);
         }
 
         if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
@@ -6521,7 +6542,10 @@
 void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
                                          const sp<WindowInfoHandle>& oldWindowHandle,
                                          const sp<WindowInfoHandle>& newWindowHandle,
-                                         TouchState& state, const BitSet32& pointerIds) {
+                                         TouchState& state, int32_t pointerId,
+                                         std::vector<InputTarget>& targets) {
+    BitSet32 pointerIds;
+    pointerIds.markBit(pointerId);
     const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
             gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
     const bool newHasWallpaper = targetFlags.test(InputTarget::Flags::FOREGROUND) &&
@@ -6536,8 +6560,12 @@
     }
 
     if (oldWallpaper != nullptr) {
-        state.addOrUpdateWindow(oldWallpaper, InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
-                                BitSet32(0));
+        const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
+        addWindowTargetLocked(oldWallpaper,
+                              oldTouchedWindow.targetFlags |
+                                      InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
+                              pointerIds, oldTouchedWindow.firstDownTimeInTarget, targets);
+        state.removeTouchedPointerFromWindow(pointerId, oldWallpaper);
     }
 
     if (newWallpaper != nullptr) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 91ca2db..81f8de8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -237,9 +237,9 @@
     // to transfer focus to a new application.
     std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
 
-    sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
-            int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
-            bool addOutsideTargets = false, bool ignoreDragWindow = false) const REQUIRES(mLock);
+    std::pair<sp<android::gui::WindowInfoHandle>, std::vector<InputTarget>>
+    findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus = false,
+                              bool ignoreDragWindow = false) const REQUIRES(mLock);
 
     std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
             int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
@@ -543,7 +543,7 @@
     sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
             nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
             android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
-    std::vector<TouchedWindow> findTouchedWindowTargetsLocked(
+    std::vector<InputTarget> findTouchedWindowTargetsLocked(
             nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
             android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
     std::vector<Monitor> selectResponsiveMonitorsLocked(
@@ -697,7 +697,8 @@
     void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
                             const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
                             const sp<android::gui::WindowInfoHandle>& newWindowHandle,
-                            TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock);
+                            TouchState& state, int32_t pointerId, std::vector<InputTarget>& targets)
+            REQUIRES(mLock);
     void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
                                 ftl::Flags<InputTarget::Flags> newTargetFlags,
                                 const sp<android::gui::WindowInfoHandle> fromWindowHandle,
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index f120fc9..ad37d02 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -37,6 +37,16 @@
     }
 }
 
+void TouchState::removeTouchedPointerFromWindow(
+        int32_t pointerId, const sp<android::gui::WindowInfoHandle>& windowHandle) {
+    for (TouchedWindow& touchedWindow : windows) {
+        if (touchedWindow.windowHandle == windowHandle) {
+            touchedWindow.pointerIds.clearBit(pointerId);
+            return;
+        }
+    }
+}
+
 void TouchState::clearHoveringPointers() {
     for (TouchedWindow& touchedWindow : windows) {
         touchedWindow.clearHoveringPointers();
@@ -70,7 +80,6 @@
             return;
         }
     }
-
     TouchedWindow touchedWindow;
     touchedWindow.windowHandle = windowHandle;
     touchedWindow.targetFlags = targetFlags;
@@ -175,6 +184,13 @@
     return nullptr;
 }
 
+const TouchedWindow& TouchState::getTouchedWindow(const sp<WindowInfoHandle>& windowHandle) const {
+    auto it = std::find_if(windows.begin(), windows.end(),
+                           [&](const TouchedWindow& w) { return w.windowHandle == windowHandle; });
+    LOG_ALWAYS_FATAL_IF(it == windows.end(), "Could not find %s", windowHandle->getName().c_str());
+    return *it;
+}
+
 bool TouchState::isDown() const {
     return std::any_of(windows.begin(), windows.end(),
                        [](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); });
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index b75e6ef..4025db8 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -43,6 +43,8 @@
     void clearWindowsWithoutPointers();
 
     void removeTouchedPointer(int32_t pointerId);
+    void removeTouchedPointerFromWindow(int32_t pointerId,
+                                        const sp<android::gui::WindowInfoHandle>& windowHandle);
     void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
                            ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
                            std::optional<nsecs_t> eventTime = std::nullopt);
@@ -62,6 +64,8 @@
     sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
     bool isSlippery() const;
     sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
+    const TouchedWindow& getTouchedWindow(
+            const sp<android::gui::WindowInfoHandle>& windowHandle) const;
     // Whether any of the windows are currently being touched
     bool isDown() const;
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index d55ab28..2173117 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -161,7 +161,7 @@
 struct InputReaderConfiguration {
     // Describes changes that have occurred.
     enum {
-        // The pointer speed changed.
+        // The mouse pointer speed changed.
         CHANGE_POINTER_SPEED = 1 << 0,
 
         // The pointer gesture control changed.
@@ -200,6 +200,9 @@
         // The stylus button reporting configurations has changed.
         CHANGE_STYLUS_BUTTON_REPORTING = 1 << 12,
 
+        // The touchpad settings changed.
+        CHANGE_TOUCHPAD_SETTINGS = 1 << 13,
+
         // All devices must be reopened.
         CHANGE_MUST_REOPEN = 1 << 31,
     };
@@ -309,6 +312,20 @@
     // The latest request to enable or disable Pointer Capture.
     PointerCaptureRequest pointerCaptureRequest;
 
+    // The touchpad pointer speed, as a number from -7 (slowest) to 7 (fastest).
+    int32_t touchpadPointerSpeed;
+
+    // True to invert the touchpad scrolling direction, so that moving two fingers downwards on the
+    // touchpad scrolls the content upwards.
+    bool touchpadNaturalScrollingEnabled;
+
+    // True to enable tap-to-click on touchpads.
+    bool touchpadTapToClickEnabled;
+
+    // True to enable a zone on the right-hand side of touchpads where clicks will be turned into
+    // context (a.k.a. "right") clicks.
+    bool touchpadRightClickZoneEnabled;
+
     // The set of currently disabled input devices.
     std::set<int32_t> disabledDevices;
 
@@ -337,6 +354,10 @@
             pointerGestureZoomSpeedRatio(0.3f),
             showTouches(false),
             pointerCaptureRequest(),
+            touchpadPointerSpeed(0),
+            touchpadNaturalScrollingEnabled(true),
+            touchpadTapToClickEnabled(true),
+            touchpadRightClickZoneEnabled(false),
             stylusButtonMotionEventsEnabled(true) {}
 
     static std::string changesToString(uint32_t changes);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 9bd50f7..c598c0a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -209,9 +209,15 @@
 
     // Touchscreens and touchpad devices.
     static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
-            sysprop::InputProperties::enable_touchpad_gestures_library().value_or(false);
+            sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
+    // TODO(b/246587538): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) and DualSense
+    // (ce6) touchpads, or at least load this setting from the IDC file.
+    const InputDeviceIdentifier identifier = contextPtr->getDeviceIdentifier();
+    const bool isSonyGamepadTouchpad = identifier.vendor == 0x054c &&
+            (identifier.product == 0x05c4 || identifier.product == 0x09cc ||
+             identifier.product == 0x0ce6);
     if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
-        classes.test(InputDeviceClass::TOUCH_MT)) {
+        classes.test(InputDeviceClass::TOUCH_MT) && !isSonyGamepadTouchpad) {
         mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
     } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
         mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ddddca2..0c57628 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -884,8 +884,6 @@
         mDeviceMode = DeviceMode::POINTER;
         if (hasStylus()) {
             mSource |= AINPUT_SOURCE_STYLUS;
-        } else {
-            mSource |= AINPUT_SOURCE_TOUCHPAD;
         }
     } else if (isTouchScreen()) {
         mSource = AINPUT_SOURCE_TOUCHSCREEN;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index b6313a1..9f32311 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -146,6 +146,18 @@
         }
         mGestureConverter.setOrientation(orientation);
     }
+    if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS)) {
+        // TODO(b/265798483): load an Android-specific acceleration curve instead of mapping to one
+        // of five ChromeOS curves.
+        const int pointerSensitivity = (config->touchpadPointerSpeed + 7) / 3 + 1;
+        mPropertyProvider.getProperty("Pointer Sensitivity").setIntValues({pointerSensitivity});
+        mPropertyProvider.getProperty("Invert Scrolling")
+                .setBoolValues({config->touchpadNaturalScrollingEnabled});
+        mPropertyProvider.getProperty("Tap Enable")
+                .setBoolValues({config->touchpadTapToClickEnabled});
+        mPropertyProvider.getProperty("Button Right Click Zone Enable")
+                .setBoolValues({config->touchpadRightClickZoneEnabled});
+    }
     return {};
 }
 
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 561b1f8..d636d44 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -219,11 +219,11 @@
     float deltaY = gesture.details.scroll.dy;
     rotateDelta(mOrientation, &deltaX, &deltaY);
 
-    coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) - deltaX);
-    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) - deltaY);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) + deltaX);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + deltaY);
     // TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET.
-    coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, gesture.details.scroll.dx);
-    coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, gesture.details.scroll.dy);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, -gesture.details.scroll.dx);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
     out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
                                  mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
                                  mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 36a39bb..9c624ba 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -244,7 +244,7 @@
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
     GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
 
-    Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10);
+    Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
     std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
     ASSERT_EQ(2u, args.size());
 
@@ -261,7 +261,7 @@
                       WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
                       WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
 
-    Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5);
+    Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
     ASSERT_EQ(1u, args.size());
     ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
@@ -289,7 +289,7 @@
     GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
     converter.setOrientation(ui::ROTATION_90);
 
-    Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10);
+    Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
     std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
     ASSERT_EQ(2u, args.size());
 
@@ -306,7 +306,7 @@
                       WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
                       WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
 
-    Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5);
+    Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
     ASSERT_EQ(1u, args.size());
     ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
@@ -332,10 +332,10 @@
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
     GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
 
-    Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10);
+    Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
     std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
 
-    Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5);
+    Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
     args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
 
     Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 95d35f4..feda191 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -5036,7 +5036,7 @@
     prepareAxes(POSITION);
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
@@ -8865,8 +8865,8 @@
     prepareAxes(POSITION);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    // Check source is a touchpad that would obtain the PointerController.
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+    // Check source is mouse that would obtain the PointerController.
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 
     NotifyMotionArgs motionArgs;
     processPosition(mapper, 100, 100);
@@ -9936,11 +9936,11 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 
-    // A non captured touchpad should have a mouse and touchpad source.
+    // non captured touchpad should be a mouse source
     mFakePolicy->setPointerCapture(false);
     configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) {
@@ -9999,10 +9999,10 @@
     mFakePolicy->setPointerCapture(false);
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    // An uncaptured touchpad should be a pointer device, with additional touchpad source.
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+    // uncaptured touchpad should be a pointer device
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 
-    // A captured touchpad should just have a touchpad source.
+    // captured touchpad should be a touchpad device
     mFakePolicy->setPointerCapture(true);
     configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index 9b03344..b7aecdf 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -185,12 +185,8 @@
 }
 
 ::android::SensorManager& SensorManagerAidl::getInternalManager() {
-    std::lock_guard<std::mutex> lock(mInternalManagerMutex);
-    if (mInternalManager == nullptr) {
-        mInternalManager = &::android::SensorManager::getInstanceForPackage(
-                String16(ISensorManager::descriptor));
-    }
-    return *mInternalManager;
+    return ::android::SensorManager::getInstanceForPackage(
+            String16(ISensorManager::descriptor));
 }
 
 /* One global looper for all event queues created from this SensorManager. */
diff --git a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
index c77ee88..83496f6 100644
--- a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
+++ b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
@@ -57,8 +57,6 @@
     ::android::SensorManager& getInternalManager();
     sp<Looper> getLooper();
 
-    std::mutex mInternalManagerMutex;
-    ::android::SensorManager* mInternalManager = nullptr; // does not own
     sp<Looper> mLooper;
 
     volatile bool mStopThread;
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
index 9380600..f04712c 100644
--- a/services/sensorservice/hidl/SensorManager.cpp
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -193,12 +193,8 @@
 }
 
 ::android::SensorManager& SensorManager::getInternalManager() {
-    std::lock_guard<std::mutex> lock(mInternalManagerMutex);
-    if (mInternalManager == nullptr) {
-        mInternalManager = &::android::SensorManager::getInstanceForPackage(
-                String16(ISensorManager::descriptor));
-    }
-    return *mInternalManager;
+    return ::android::SensorManager::getInstanceForPackage(
+            String16(ISensorManager::descriptor));
 }
 
 Return<void> SensorManager::createEventQueue(
diff --git a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
index 8d7a05b..1b085ac 100644
--- a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
+++ b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
@@ -58,8 +58,6 @@
     ::android::SensorManager& getInternalManager();
     sp<Looper> getLooper();
 
-    std::mutex mInternalManagerMutex;
-    ::android::SensorManager* mInternalManager = nullptr; // does not own
     sp<Looper> mLooper;
 
     volatile bool mStopThread;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index ad98e93..5bb2497 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -209,6 +209,9 @@
     // The dimming flag
     bool dimmingEnabled{true};
 
+    float currentSdrHdrRatio = 1.f;
+    float desiredSdrHdrRatio = 1.f;
+
     virtual ~LayerFECompositionState();
 
     // Debugging
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index a405c4d..531b659 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -121,6 +121,10 @@
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "hdr metadata types", hdrMetadata.validTypes);
     dumpVal(out, "dimming enabled", dimmingEnabled);
+    if (currentSdrHdrRatio > 1.01f || desiredSdrHdrRatio > 1.01f) {
+        dumpVal(out, "current sdr/hdr ratio", currentSdrHdrRatio);
+        dumpVal(out, "desired sdr/hdr ratio", desiredSdrHdrRatio);
+    }
     dumpVal(out, "colorTransform", colorTransform);
 
     out.append("\n");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 60a2c83..6b69ce7 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -340,10 +340,17 @@
         state.dimmingRatio = 1.f;
         state.whitePointNits = getOutput().getState().displayBrightnessNits;
     } else {
-        state.dimmingRatio = std::clamp(getOutput().getState().sdrWhitePointNits /
-                                                getOutput().getState().displayBrightnessNits,
-                                        0.f, 1.f);
-        state.whitePointNits = getOutput().getState().sdrWhitePointNits;
+        float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
+        // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
+        // range that we may need to re-adjust to the current display conditions
+        if ((state.dataspace & HAL_DATASPACE_RANGE_MASK) == HAL_DATASPACE_RANGE_EXTENDED &&
+            layerFEState->currentSdrHdrRatio > 1.01f) {
+            layerBrightnessNits *= layerFEState->currentSdrHdrRatio;
+        }
+        state.dimmingRatio =
+                std::clamp(layerBrightnessNits / getOutput().getState().displayBrightnessNits, 0.f,
+                           1.f);
+        state.whitePointNits = layerBrightnessNits;
     }
 
     // These are evaluated every frame as they can potentially change at any
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 2b6a519..c61f7d8 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -166,7 +166,8 @@
             .receivesInput = receivesInput(),
             .isSecure = isSecure(),
             .isPrimary = isPrimary(),
-            .rotationFlags = ui::Transform::toRotationFlags(mOrientation)};
+            .rotationFlags = ui::Transform::toRotationFlags(mOrientation),
+            .transformHint = getTransformHint()};
 }
 
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 27a099c..925f111 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -1095,6 +1095,12 @@
 }
 
 void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const {
+    if (mSurfaceFrames.empty()) {
+        // We don't want to trace display frames without any surface frames updates as this cannot
+        // be janky
+        return;
+    }
+
     if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
         // DisplayFrame should not have an invalid token.
         ALOGE("Cannot trace DisplayFrame with invalid token");
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
index 0c7b24a..6b9d7a2 100644
--- a/services/surfaceflinger/FrontEnd/DisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <sstream>
+
 #include <gui/DisplayInfo.h>
 
 namespace android::surfaceflinger::frontend {
@@ -29,6 +31,16 @@
     // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
     bool isPrimary;
     ui::Transform::RotationFlags rotationFlags;
+    ui::Transform::RotationFlags transformHint;
+    std::string getDebugString() const {
+        std::stringstream debug;
+        debug << "DisplayInfo {displayId=" << info.displayId << " lw=" << info.logicalWidth
+              << " lh=" << info.logicalHeight << " transform={" << transform.dsdx() << " ,"
+              << transform.dsdy() << " ," << transform.dtdx() << " ," << transform.dtdy()
+              << "} isSecure=" << isSecure << " isPrimary=" << isPrimary
+              << " rotationFlags=" << rotationFlags << " transformHint=" << transformHint << "}";
+        return debug.str();
+    }
 };
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 7b5a157..9d2aaab 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -18,10 +18,12 @@
 
 #include <binder/Binder.h>
 #include <gui/LayerMetadata.h>
+#include <ui/LayerStack.h>
 #include <utils/StrongPointer.h>
 #include <cstdint>
 #include <limits>
 #include <optional>
+
 constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max();
 
 namespace android {
@@ -51,6 +53,7 @@
     bool addToRoot = true;
     wp<IBinder> parentHandle = nullptr;
     wp<IBinder> mirrorLayerHandle = nullptr;
+    ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
 };
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 514a642..a4fac1c 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -28,8 +28,8 @@
                         const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) {
     auto lhsLayer = lhs.first->getLayer();
     auto rhsLayer = rhs.first->getLayer();
-    if (lhsLayer->layerStack != rhsLayer->layerStack) {
-        return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
+    if (lhsLayer->layerStack.id != rhsLayer->layerStack.id) {
+        return lhsLayer->layerStack.id > rhsLayer->layerStack.id;
     }
     if (lhsLayer->z != rhsLayer->z) {
         return lhsLayer->z < rhsLayer->z;
@@ -75,11 +75,11 @@
     for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
         auto& [child, childVariant] = *it;
         if (traverseThisLayer && child->getLayer()->z >= 0) {
+            traverseThisLayer = false;
             bool breakTraversal = !visitor(*this, traversalPath);
             if (breakTraversal) {
                 return;
             }
-            traverseThisLayer = false;
         }
         if (childVariant == LayerHierarchy::Variant::Detached) {
             continue;
@@ -252,8 +252,8 @@
     attachToParent(hierarchy);
     attachToRelativeParent(hierarchy);
 
-    if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
-        LayerHierarchy* mirror = getHierarchyFromId(layer->mirrorId);
+    for (uint32_t mirrorId : layer->mirrorIds) {
+        LayerHierarchy* mirror = getHierarchyFromId(mirrorId);
         hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
     }
 }
@@ -292,14 +292,14 @@
     auto it = hierarchy->mChildren.begin();
     while (it != hierarchy->mChildren.end()) {
         if (it->second == LayerHierarchy::Variant::Mirror) {
-            hierarchy->mChildren.erase(it);
-            break;
+            it = hierarchy->mChildren.erase(it);
+        } else {
+            it++;
         }
-        it++;
     }
 
-    if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
-        hierarchy->addChild(getHierarchyFromId(layer->mirrorId), LayerHierarchy::Variant::Mirror);
+    for (uint32_t mirrorId : layer->mirrorIds) {
+        hierarchy->addChild(getHierarchyFromId(mirrorId), LayerHierarchy::Variant::Mirror);
     }
 }
 
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 8cdc240..ca8d301 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -41,11 +41,13 @@
 // states.
 class LayerHierarchy {
 public:
-    enum Variant {
+    enum Variant : uint32_t {
         Attached,
         Detached,
         Relative,
         Mirror,
+        ftl_first = Attached,
+        ftl_last = Mirror,
     };
     // Represents a unique path to a node.
     struct TraversalPath {
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 5514c06..547a852 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -44,9 +44,28 @@
 
         layer.parentId = linkLayer(layer.parentId, layer.id);
         layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
-        layer.mirrorId = linkLayer(layer.mirrorId, layer.id);
+        if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+            // if this layer is mirroring a display, then walk though all the existing root layers
+            // for the layer stack and add them as children to be mirrored.
+            mDisplayMirroringLayers.emplace_back(layer.id);
+            for (auto& rootLayer : mLayers) {
+                if (rootLayer->isRoot() && rootLayer->layerStack == layer.layerStackToMirror) {
+                    layer.mirrorIds.emplace_back(rootLayer->id);
+                    linkLayer(rootLayer->id, layer.id);
+                }
+            }
+        } else {
+            // Check if we are mirroring a single layer, and if so add it to the list of children
+            // to be mirrored.
+            layer.layerIdToMirror = linkLayer(layer.layerIdToMirror, layer.id);
+            if (layer.layerIdToMirror != UNASSIGNED_LAYER_ID) {
+                layer.mirrorIds.emplace_back(layer.layerIdToMirror);
+            }
+        }
         layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
-
+        if (layer.isRoot()) {
+            updateDisplayMirrorLayers(layer);
+        }
         mLayers.emplace_back(std::move(newLayer));
     }
 }
@@ -85,7 +104,14 @@
 
         layer.parentId = unlinkLayer(layer.parentId, layer.id);
         layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
-        layer.mirrorId = unlinkLayer(layer.mirrorId, layer.id);
+        if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+            layer.mirrorIds = unlinkLayers(layer.mirrorIds, layer.id);
+            swapErase(mDisplayMirroringLayers, layer.id);
+        } else {
+            layer.layerIdToMirror = unlinkLayer(layer.layerIdToMirror, layer.id);
+            layer.mirrorIds.clear();
+        }
+
         layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id);
 
         auto& references = it->second.references;
@@ -106,8 +132,8 @@
             if (linkedLayer->relativeParentId == layer.id) {
                 linkedLayer->relativeParentId = UNASSIGNED_LAYER_ID;
             }
-            if (linkedLayer->mirrorId == layer.id) {
-                linkedLayer->mirrorId = UNASSIGNED_LAYER_ID;
+            if (swapErase(linkedLayer->mirrorIds, layer.id)) {
+                linkedLayer->changes |= RequestedLayerState::Changes::Mirror;
             }
             if (linkedLayer->touchCropId == layer.id) {
                 linkedLayer->touchCropId = UNASSIGNED_LAYER_ID;
@@ -200,6 +226,12 @@
             if (oldParentId != layer->parentId) {
                 unlinkLayer(oldParentId, layer->id);
                 layer->parentId = linkLayer(layer->parentId, layer->id);
+                if (oldParentId == UNASSIGNED_LAYER_ID) {
+                    updateDisplayMirrorLayers(*layer);
+                }
+            }
+            if (layer->what & layer_state_t::eLayerStackChanged && layer->isRoot()) {
+                updateDisplayMirrorLayers(*layer);
             }
             if (oldRelativeParentId != layer->relativeParentId) {
                 unlinkLayer(oldRelativeParentId, layer->id);
@@ -308,6 +340,14 @@
     return UNASSIGNED_LAYER_ID;
 }
 
+std::vector<uint32_t> LayerLifecycleManager::unlinkLayers(const std::vector<uint32_t>& layerIds,
+                                                          uint32_t linkedLayer) {
+    for (uint32_t layerId : layerIds) {
+        unlinkLayer(layerId, linkedLayer);
+    }
+    return {};
+}
+
 std::string LayerLifecycleManager::References::getDebugString() const {
     std::string debugInfo = owner.name + "[" + std::to_string(owner.id) + "] refs:";
     std::for_each(references.begin(), references.end(),
@@ -329,4 +369,30 @@
     mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
 }
 
+// Some layers mirror the entire display stack. Since we don't have a single root layer per display
+// we have to track all these layers and update what they mirror when the list of root layers
+// on a display changes. This function walks through the list of display mirroring layers
+// and updates its list of layers that its mirroring. This function should be called when a new
+// root layer is added, removed or moved to another display.
+void LayerLifecycleManager::updateDisplayMirrorLayers(RequestedLayerState& rootLayer) {
+    for (uint32_t mirrorLayerId : mDisplayMirroringLayers) {
+        RequestedLayerState* mirrorLayer = getLayerFromId(mirrorLayerId);
+        bool canBeMirrored =
+                rootLayer.isRoot() && rootLayer.layerStack == mirrorLayer->layerStackToMirror;
+        bool currentlyMirrored =
+                std::find(mirrorLayer->mirrorIds.begin(), mirrorLayer->mirrorIds.end(),
+                          rootLayer.id) != mirrorLayer->mirrorIds.end();
+
+        if (canBeMirrored && !currentlyMirrored) {
+            mirrorLayer->mirrorIds.emplace_back(rootLayer.id);
+            linkLayer(rootLayer.id, mirrorLayer->id);
+            mirrorLayer->changes |= RequestedLayerState::Changes::Mirror;
+        } else if (!canBeMirrored && currentlyMirrored) {
+            swapErase(mirrorLayer->mirrorIds, rootLayer.id);
+            unlinkLayer(rootLayer.id, mirrorLayer->id);
+            mirrorLayer->changes |= RequestedLayerState::Changes::Mirror;
+        }
+    }
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index 63a7afc..25d27ee 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -80,6 +80,9 @@
     std::vector<uint32_t>* getLinkedLayersFromId(uint32_t);
     uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink);
     uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer);
+    std::vector<uint32_t> unlinkLayers(const std::vector<uint32_t>& layerIds, uint32_t linkedLayer);
+
+    void updateDisplayMirrorLayers(RequestedLayerState& rootLayer);
 
     struct References {
         // Lifetime tied to mLayers
@@ -90,6 +93,8 @@
     std::unordered_map<uint32_t, References> mIdToLayer;
     // Listeners are invoked once changes are committed.
     std::vector<std::shared_ptr<ILifecycleListener>> mListeners;
+    // Layers that mirror a display stack (see updateDisplayMirrorLayers)
+    std::vector<uint32_t> mDisplayMirroringLayers;
 
     // Aggregation of changes since last commit.
     ftl::Flags<RequestedLayerState::Changes> mGlobalChanges;
diff --git a/services/surfaceflinger/FrontEnd/LayerLog.h b/services/surfaceflinger/FrontEnd/LayerLog.h
new file mode 100644
index 0000000..47e1e30
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLog.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// Uncomment to trace layer updates for a single layer
+// #define LOG_LAYER 1
+
+#ifdef LOG_LAYER
+#define LLOGV(LAYER_ID, x, ...) \
+    ALOGV_IF(((LAYER_ID) == LOG_LAYER), "[%d] %s " x, LOG_LAYER, __func__, ##__VA_ARGS__);
+#else
+#define LLOGV(LAYER_ID, x, ...) ALOGV("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
+#endif
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index d483a99..4e69565 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -61,7 +61,7 @@
 }
 
 bool LayerSnapshot::hasBufferOrSidebandStream() const {
-    return ((sidebandStream != nullptr) || (buffer != nullptr));
+    return ((sidebandStream != nullptr) || (externalTexture != nullptr));
 }
 
 bool LayerSnapshot::drawShadows() const {
@@ -99,7 +99,7 @@
 
     // If the buffer has no alpha channel, then we are opaque
     if (hasBufferOrSidebandStream() &&
-        isOpaqueFormat(buffer ? buffer->getPixelFormat() : PIXEL_FORMAT_NONE)) {
+        isOpaqueFormat(externalTexture ? externalTexture->getPixelFormat() : PIXEL_FORMAT_NONE)) {
         return true;
     }
 
@@ -108,14 +108,14 @@
 }
 
 bool LayerSnapshot::isHiddenByPolicy() const {
-    if (CC_UNLIKELY(invalidTransform)) {
-        ALOGW("Hide layer %s because it has invalid transformation.", name.c_str());
-        return true;
-    }
-    return isHiddenByPolicyFromParent || isHiddenByPolicyFromRelativeParent;
+    return invalidTransform || isHiddenByPolicyFromParent || isHiddenByPolicyFromRelativeParent;
 }
 
 bool LayerSnapshot::getIsVisible() const {
+    if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) {
+        return false;
+    }
+
     if (!hasSomethingToDraw()) {
         return false;
     }
@@ -128,19 +128,23 @@
 }
 
 std::string LayerSnapshot::getIsVisibleReason() const {
-    if (!hasSomethingToDraw()) {
-        return "!hasSomethingToDraw";
-    }
+    // not visible
+    if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
+    if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
+    if (invalidTransform) return "invalidTransform";
+    if (isHiddenByPolicyFromParent) return "hidden by parent or layer flag";
+    if (isHiddenByPolicyFromRelativeParent) return "hidden by relative parent";
+    if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur";
 
-    if (isHiddenByPolicy()) {
-        return "isHiddenByPolicy";
-    }
-
-    if (color.a > 0.0f || hasBlur()) {
-        return "";
-    }
-
-    return "alpha = 0 and !hasBlur";
+    // visible
+    std::stringstream reason;
+    if (sidebandStream != nullptr) reason << " sidebandStream";
+    if (externalTexture != nullptr) reason << " buffer";
+    if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}";
+    if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
+    if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
+    if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size();
+    return reason.str();
 }
 
 bool LayerSnapshot::canReceiveInput() const {
@@ -152,11 +156,16 @@
     return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet);
 }
 
+bool LayerSnapshot::hasInputInfo() const {
+    return inputInfo.token != nullptr ||
+            inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
 std::string LayerSnapshot::getDebugString() const {
-    return "Snapshot(" + base::StringPrintf("%p", this) + "){" + path.toString() + name +
-            " isHidden=" + std::to_string(isHiddenByPolicyFromParent) +
-            " isHiddenRelative=" + std::to_string(isHiddenByPolicyFromRelativeParent) +
-            " isVisible=" + std::to_string(isVisible) + " " + getIsVisibleReason() + "}";
+    std::stringstream debug;
+    debug << "Snapshot{" << path.toString() << name << " isVisible=" << isVisible << " {"
+          << getIsVisibleReason() << "} changes=" << changes.string() << "}";
+    return debug.str();
 }
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index d14bd3a..159410f 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -20,6 +20,7 @@
 #include <renderengine/LayerSettings.h>
 #include "LayerHierarchy.h"
 #include "RequestedLayerState.h"
+#include "Scheduler/LayerInfo.h"
 #include "android-base/stringprintf.h"
 
 namespace android::surfaceflinger::frontend {
@@ -39,6 +40,10 @@
     }
 };
 
+struct ChildState {
+    bool hasValidFrameRate = false;
+};
+
 // LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition
 // Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
 // passed to Render Engine are created using properties stored on this struct.
@@ -59,6 +64,7 @@
     bool layerOpaqueFlagSet;
     RoundedCornerState roundedCorner;
     FloatRect transformedBounds;
+    Rect transformedBoundsWithoutTransparentRegion;
     renderengine::ShadowSettings shadowSettings;
     bool premultipliedAlpha;
     bool isHdrY410;
@@ -75,6 +81,11 @@
     ui::Transform localTransform;
     gui::DropInputMode dropInputMode;
     bool isTrustedOverlay;
+    gui::GameMode gameMode;
+    scheduler::LayerInfo::FrameRate frameRate;
+    ui::Transform::RotationFlags fixedTransformHint;
+    bool handleSkipScreenshotFlag = false;
+    ChildState childState;
 
     static bool isOpaqueFormat(PixelFormat format);
     static bool isTransformValid(const ui::Transform& t);
@@ -91,6 +102,7 @@
     bool isHiddenByPolicy() const;
     std::string getDebugString() const;
     std::string getIsVisibleReason() const;
+    bool hasInputInfo() const;
 };
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index bff12d7..d0ffe61 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -24,6 +24,8 @@
 #include <numeric>
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/Hal.h"
+#include "LayerLog.h"
+#include "TimeStats/TimeStats.h"
 #include "ftl/small_map.h"
 
 namespace android::surfaceflinger::frontend {
@@ -250,6 +252,64 @@
     return blendMode;
 }
 
+void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
+                         bool forceFullDamage, Region& outSurfaceDamageRegion) {
+    if (!hasReadyFrame) {
+        outSurfaceDamageRegion.clear();
+        return;
+    }
+    if (forceFullDamage) {
+        outSurfaceDamageRegion = Region::INVALID_REGION;
+    } else {
+        outSurfaceDamageRegion = requested.surfaceDamageRegion;
+    }
+}
+
+void updateVisibility(LayerSnapshot& snapshot) {
+    snapshot.isVisible = snapshot.getIsVisible();
+
+    // TODO(b/238781169) we are ignoring this compat for now, since we will have
+    // to remove any optimization based on visibility.
+
+    // For compatibility reasons we let layers which can receive input
+    // receive input before they have actually submitted a buffer. Because
+    // of this we use canReceiveInput instead of isVisible to check the
+    // policy-visibility, ignoring the buffer state. However for layers with
+    // hasInputInfo()==false we can use the real visibility state.
+    // We are just using these layers for occlusion detection in
+    // InputDispatcher, and obviously if they aren't visible they can't occlude
+    // anything.
+    const bool visible =
+            (snapshot.inputInfo.token != nullptr) ? snapshot.canReceiveInput() : snapshot.isVisible;
+    snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+}
+
+bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
+    if (requested.potentialCursor) {
+        return false;
+    }
+
+    if (snapshot.inputInfo.token != nullptr) {
+        return true;
+    }
+
+    if (snapshot.hasBufferOrSidebandStream()) {
+        return true;
+    }
+
+    return requested.windowInfoHandle &&
+            requested.windowInfoHandle->getInfo()->inputConfig.test(
+                    gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
+void clearChanges(LayerSnapshot& snapshot) {
+    snapshot.changes.clear();
+    snapshot.contentDirty = false;
+    snapshot.hasReadyFrame = false;
+    snapshot.sidebandStreamHasFrame = false;
+    snapshot.surfaceDamage.clear();
+}
+
 } // namespace
 
 LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() {
@@ -274,6 +334,9 @@
     snapshot.inputInfo.touchOcclusionMode = gui::TouchOcclusionMode::BLOCK_UNTRUSTED;
     snapshot.dropInputMode = gui::DropInputMode::NONE;
     snapshot.isTrustedOverlay = false;
+    snapshot.gameMode = gui::GameMode::Unsupported;
+    snapshot.frameRate = {};
+    snapshot.fixedTransformHint = ui::Transform::ROT_INVALID;
     return snapshot;
 }
 
@@ -285,16 +348,15 @@
 }
 
 bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) {
-    if (args.forceUpdate) {
-        // force update requested, so skip the fast path
+    if (args.forceUpdate || args.displayChanges) {
+        // force update requested, or we have display changes, so skip the fast path
         return false;
     }
 
     if (args.layerLifecycleManager.getGlobalChanges().get() == 0) {
         // there are no changes, so just clear the change flags from before.
         for (auto& snapshot : mSnapshots) {
-            snapshot->changes.clear();
-            snapshot->contentDirty = false;
+            clearChanges(*snapshot);
         }
         return true;
     }
@@ -320,14 +382,14 @@
     // Walk through the snapshots, clearing previous change flags and updating the snapshots
     // if needed.
     for (auto& snapshot : mSnapshots) {
-        snapshot->changes.clear();
-        snapshot->contentDirty = false;
+        clearChanges(*snapshot);
         auto it = layersWithChanges.find(snapshot->path.id);
         if (it != layersWithChanges.end()) {
             ALOGV("%s fast path snapshot changes = %s", __func__,
                   mRootSnapshot.changes.string().c_str());
             LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
-            updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root);
+            updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root,
+                           /*newSnapshot=*/false);
         }
     }
     return true;
@@ -335,7 +397,6 @@
 
 void LayerSnapshotBuilder::updateSnapshots(const Args& args) {
     ATRACE_NAME("UpdateSnapshots");
-    ALOGV("%s updateSnapshots force = %s", __func__, std::to_string(args.forceUpdate).c_str());
     if (args.forceUpdate || args.displayChanges) {
         mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays);
     }
@@ -352,7 +413,7 @@
     }
 
     sortSnapshotsByZ(args);
-    mRootSnapshot.changes.clear();
+    clearChanges(mRootSnapshot);
 
     // Destroy unreachable snapshots
     if (args.layerLifecycleManager.getDestroyedLayers().empty()) {
@@ -372,6 +433,7 @@
         }
 
         mIdToSnapshot.erase(traversalPath);
+        mSnapshots.back()->globalZ = it->get()->globalZ;
         std::iter_swap(it, mSnapshots.end() - 1);
         mSnapshots.erase(mSnapshots.end() - 1);
     }
@@ -384,12 +446,15 @@
     updateSnapshots(args);
 }
 
-void LayerSnapshotBuilder::updateSnapshotsInHierarchy(const Args& args,
-                                                      const LayerHierarchy& hierarchy,
-                                                      LayerHierarchy::TraversalPath& traversalPath,
-                                                      const LayerSnapshot& parentSnapshot) {
+const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy(
+        const Args& args, const LayerHierarchy& hierarchy,
+        LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot) {
     const RequestedLayerState* layer = hierarchy.getLayer();
-    LayerSnapshot* snapshot = getOrCreateSnapshot(traversalPath, *layer);
+    LayerSnapshot* snapshot = getSnapshot(traversalPath);
+    const bool newSnapshot = snapshot == nullptr;
+    if (newSnapshot) {
+        snapshot = createSnapshot(traversalPath, *layer);
+    }
     if (traversalPath.isRelative()) {
         bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative;
         updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args);
@@ -397,23 +462,18 @@
         if (traversalPath.isAttached()) {
             resetRelativeState(*snapshot);
         }
-        updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath);
-    }
-
-    // If layer is hidden by policy we can avoid update its children. If the visibility
-    // changed this update, then we still need to set the visibility on all the children.
-    if (snapshot->isHiddenByPolicy() &&
-        (!snapshot->changes.any(RequestedLayerState::Changes::Visibility |
-                                RequestedLayerState::Changes::Hierarchy))) {
-        return;
+        updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath, newSnapshot);
     }
 
     for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
         LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath,
                                                                 childHierarchy->getLayer()->id,
                                                                 variant);
-        updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
+        const LayerSnapshot& childSnapshot =
+                updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
+        updateChildState(*snapshot, childSnapshot, args);
     }
+    return *snapshot;
 }
 
 LayerSnapshot* LayerSnapshotBuilder::getSnapshot(uint32_t layerId) const {
@@ -429,22 +489,17 @@
     return it == mIdToSnapshot.end() ? nullptr : it->second;
 }
 
-LayerSnapshot* LayerSnapshotBuilder::getOrCreateSnapshot(const LayerHierarchy::TraversalPath& id,
-                                                         const RequestedLayerState& layer) {
-    auto snapshot = getSnapshot(id);
-    if (snapshot) {
-        return snapshot;
-    }
-
+LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& id,
+                                                    const RequestedLayerState& layer) {
     mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, id));
-    snapshot = mSnapshots.back().get();
+    LayerSnapshot* snapshot = mSnapshots.back().get();
     snapshot->globalZ = static_cast<size_t>(mSnapshots.size()) - 1;
     mIdToSnapshot[id] = snapshot;
     return snapshot;
 }
 
 void LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) {
-    if (!args.forceUpdate &&
+    if (!mResortSnapshots && !args.forceUpdate &&
         !args.layerLifecycleManager.getGlobalChanges().any(
                 RequestedLayerState::Changes::Hierarchy |
                 RequestedLayerState::Changes::Visibility)) {
@@ -453,6 +508,8 @@
         return;
     }
 
+    mResortSnapshots = false;
+
     size_t globalZ = 0;
     args.root.traverseInZOrder(
             [this, &globalZ](const LayerHierarchy&,
@@ -467,7 +524,8 @@
                     return false;
                 }
 
-                if (snapshot->isVisible) {
+                if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
+                    updateVisibility(*snapshot);
                     size_t oldZ = snapshot->globalZ;
                     size_t newZ = globalZ++;
                     snapshot->globalZ = newZ;
@@ -475,16 +533,17 @@
                         return true;
                     }
                     mSnapshots[newZ]->globalZ = oldZ;
+                    LLOGV(snapshot->sequence, "Made visible z=%zu -> %zu %s", oldZ, newZ,
+                          snapshot->getDebugString().c_str());
                     std::iter_swap(mSnapshots.begin() + static_cast<ssize_t>(oldZ),
                                    mSnapshots.begin() + static_cast<ssize_t>(newZ));
                 }
-
                 return true;
             });
-
+    mNumInterestingSnapshots = (int)globalZ;
     while (globalZ < mSnapshots.size()) {
         mSnapshots[globalZ]->globalZ = globalZ;
-        mSnapshots[globalZ]->isVisible = false;
+        updateVisibility(*mSnapshots[globalZ]);
         globalZ++;
     }
 }
@@ -493,7 +552,8 @@
                                                const LayerSnapshot& parentSnapshot,
                                                bool parentIsRelative, const Args& args) {
     if (parentIsRelative) {
-        snapshot.isHiddenByPolicyFromRelativeParent = parentSnapshot.isHiddenByPolicyFromParent;
+        snapshot.isHiddenByPolicyFromRelativeParent =
+                parentSnapshot.isHiddenByPolicyFromParent || parentSnapshot.invalidTransform;
         if (args.includeMetadata) {
             snapshot.relativeLayerMetadata = parentSnapshot.layerMetadata;
         }
@@ -507,6 +567,38 @@
     snapshot.isVisible = snapshot.getIsVisible();
 }
 
+void LayerSnapshotBuilder::updateChildState(LayerSnapshot& snapshot,
+                                            const LayerSnapshot& childSnapshot, const Args& args) {
+    if (snapshot.childState.hasValidFrameRate) {
+        return;
+    }
+    if (args.forceUpdate || childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) {
+        // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes
+        // for the same reason we are allowing touch boost for those layers. See
+        // RefreshRateSelector::rankFrameRates for details.
+        using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+        const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.rate.isValid() &&
+                childSnapshot.frameRate.type == FrameRateCompatibility::Default;
+        const auto layerVotedWithNoVote =
+                childSnapshot.frameRate.type == FrameRateCompatibility::NoVote;
+        const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.rate.isValid() &&
+                childSnapshot.frameRate.type == FrameRateCompatibility::Exact;
+
+        snapshot.childState.hasValidFrameRate |= layerVotedWithDefaultCompatibility ||
+                layerVotedWithNoVote || layerVotedWithExactCompatibility;
+
+        // If we don't have a valid frame rate, but the children do, we set this
+        // layer as NoVote to allow the children to control the refresh rate
+        if (!snapshot.frameRate.rate.isValid() &&
+            snapshot.frameRate.type != FrameRateCompatibility::NoVote &&
+            snapshot.childState.hasValidFrameRate) {
+            snapshot.frameRate =
+                    scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+            snapshot.changes |= childSnapshot.changes & RequestedLayerState::Changes::FrameRate;
+        }
+    }
+}
+
 void LayerSnapshotBuilder::resetRelativeState(LayerSnapshot& snapshot) {
     snapshot.isHiddenByPolicyFromRelativeParent = false;
     snapshot.relativeLayerMetadata.mMap.clear();
@@ -523,27 +615,71 @@
 void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
                                           const RequestedLayerState& requested,
                                           const LayerSnapshot& parentSnapshot,
-                                          const LayerHierarchy::TraversalPath& path) {
+                                          const LayerHierarchy::TraversalPath& path,
+                                          bool newSnapshot) {
     // Always update flags and visibility
     ftl::Flags<RequestedLayerState::Changes> parentChanges = parentSnapshot.changes &
             (RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
              RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
              RequestedLayerState::Changes::AffectsChildren);
     snapshot.changes = parentChanges | requested.changes;
-    snapshot.isHiddenByPolicyFromParent =
-            parentSnapshot.isHiddenByPolicyFromParent || requested.isHiddenByPolicy();
+    snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
+            parentSnapshot.invalidTransform || requested.isHiddenByPolicy();
     snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
-    if (snapshot.isHiddenByPolicyFromParent) {
-        snapshot.isVisible = false;
-        return;
-    }
+    // TODO(b/238781169) scope down the changes to only buffer updates.
+    snapshot.hasReadyFrame =
+            (snapshot.contentDirty || requested.autoRefresh) && (requested.externalTexture);
+    // TODO(b/238781169) how is this used? ag/15523870
+    snapshot.sidebandStreamHasFrame = false;
+    updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage,
+                        snapshot.surfaceDamage);
 
+    const bool forceUpdate = newSnapshot || args.forceUpdate ||
+            snapshot.changes.any(RequestedLayerState::Changes::Visibility |
+                                 RequestedLayerState::Changes::Created);
     uint32_t displayRotationFlags =
             getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack);
 
-    const bool forceUpdate = args.forceUpdate ||
-            snapshot.changes.any(RequestedLayerState::Changes::Visibility |
-                                 RequestedLayerState::Changes::Created);
+    // always update the buffer regardless of visibility
+    if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES) {
+        snapshot.acquireFence =
+                (requested.externalTexture &&
+                 requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
+                ? requested.bufferData->acquireFence
+                : Fence::NO_FENCE;
+        snapshot.buffer =
+                requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
+        snapshot.bufferSize = requested.getBufferSize(displayRotationFlags);
+        snapshot.geomBufferSize = snapshot.bufferSize;
+        snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
+        snapshot.dataspace = requested.dataspace;
+        snapshot.externalTexture = requested.externalTexture;
+        snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
+        snapshot.geomBufferTransform = requested.bufferTransform;
+        snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
+        snapshot.geomContentCrop = requested.getBufferCrop();
+        snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream();
+        snapshot.hasProtectedContent = requested.externalTexture &&
+                requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
+        snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
+                requested.api == NATIVE_WINDOW_API_MEDIA &&
+                requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
+        snapshot.sidebandStream = requested.sidebandStream;
+        snapshot.transparentRegionHint = requested.transparentRegion;
+        snapshot.color.rgb = requested.getColor().rgb;
+        snapshot.currentSdrHdrRatio = requested.currentSdrHdrRatio;
+        snapshot.desiredSdrHdrRatio = requested.desiredSdrHdrRatio;
+    }
+
+    if (snapshot.isHiddenByPolicyFromParent && !newSnapshot) {
+        if (forceUpdate ||
+            snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
+                                 RequestedLayerState::Changes::Geometry |
+                                 RequestedLayerState::Changes::Input)) {
+            updateInput(snapshot, requested, parentSnapshot, path, args);
+        }
+        return;
+    }
 
     if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
         // If root layer, use the layer stack otherwise get the parent's layer stack.
@@ -567,6 +703,21 @@
             snapshot.colorTransform = requested.colorTransform;
             snapshot.colorTransformIsIdentity = !requested.hasColorTransform;
         }
+        snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
+                ? requested.gameMode
+                : parentSnapshot.gameMode;
+        snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
+                              (requested.requestedFrameRate.type ==
+                               scheduler::LayerInfo::FrameRateCompatibility::NoVote))
+                ? requested.requestedFrameRate
+                : parentSnapshot.frameRate;
+        snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
+                ? requested.fixedTransformHint
+                : parentSnapshot.fixedTransformHint;
+        // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
+        // marked as skip capture
+        snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
+                (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
     }
 
     if (forceUpdate || requested.changes.get() != 0) {
@@ -576,35 +727,11 @@
                 (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
     }
 
-    if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES) {
-        snapshot.acquireFence =
-                (requested.bufferData) ? requested.bufferData->acquireFence : Fence::NO_FENCE;
-        snapshot.buffer =
-                requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
-        snapshot.bufferSize = requested.getBufferSize(displayRotationFlags);
-        snapshot.geomBufferSize = snapshot.bufferSize;
-        snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
-        snapshot.dataspace = requested.dataspace;
-        snapshot.externalTexture = requested.externalTexture;
-        snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
-        snapshot.geomBufferTransform = requested.bufferTransform;
-        snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
-        snapshot.geomContentCrop = requested.getBufferCrop();
-        snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream();
-        snapshot.hasProtectedContent = requested.externalTexture &&
-                requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
-        snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
-                requested.api == NATIVE_WINDOW_API_MEDIA &&
-                requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
-        snapshot.sidebandStream = requested.sidebandStream;
-        snapshot.surfaceDamage = requested.surfaceDamageRegion;
-        snapshot.transparentRegionHint = requested.transparentRegion;
-    }
-
     if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) {
         snapshot.color.rgb = requested.getColor().rgb;
         snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
-        snapshot.backgroundBlurRadius = static_cast<int>(requested.backgroundBlurRadius);
+        snapshot.backgroundBlurRadius =
+                args.supportsBlur ? static_cast<int>(requested.backgroundBlurRadius) : 0;
         snapshot.blurRegions = requested.blurRegions;
         snapshot.hdrMetadata = requested.hdrMetadata;
     }
@@ -620,12 +747,7 @@
         snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
                              RequestedLayerState::Changes::Geometry |
                              RequestedLayerState::Changes::Input)) {
-        static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
-        const std::optional<frontend::DisplayInfo> displayInfo =
-                args.displays.get(snapshot.outputFilter.layerStack);
-        bool noValidDisplay = !displayInfo.has_value();
-        updateInput(snapshot, requested, parentSnapshot, displayInfo.value_or(sDefaultInfo),
-                    noValidDisplay, path);
+        updateInput(snapshot, requested, parentSnapshot, path, args);
     }
 
     // computed snapshot properties
@@ -636,12 +758,14 @@
     }
     snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
             requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
-    snapshot.isVisible = snapshot.getIsVisible();
     snapshot.isOpaque = snapshot.isContentOpaque() && !snapshot.roundedCorner.hasRoundedCorners() &&
             snapshot.color.a == 1.f;
     snapshot.blendMode = getBlendMode(snapshot, requested);
-
-    ALOGV("%supdated [%d]%s changes parent:%s global:%s local:%s requested:%s %s from parent %s",
+    // TODO(b/238781169) pass this from flinger
+    // snapshot.fps;
+    // snapshot.metadata;
+    LLOGV(snapshot.sequence,
+          "%supdated [%d]%s changes parent:%s global:%s local:%s requested:%s %s from parent %s",
           args.forceUpdate ? "Force " : "", requested.id, requested.name.c_str(),
           parentSnapshot.changes.string().c_str(), snapshot.changes.string().c_str(),
           requested.changes.string().c_str(), std::to_string(requested.what).c_str(),
@@ -694,12 +818,35 @@
     snapshot.localTransform = requested.getTransform(displayRotationFlags);
     snapshot.localTransformInverse = snapshot.localTransform.inverse();
     snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform;
+    const bool transformWasInvalid = snapshot.invalidTransform;
     snapshot.invalidTransform = !LayerSnapshot::isTransformValid(snapshot.geomLayerTransform);
     if (snapshot.invalidTransform) {
-        ALOGW("Resetting transform for %s because it has an invalid transformation.",
-              requested.getDebugStringShort().c_str());
+        auto& t = snapshot.geomLayerTransform;
+        auto& requestedT = requested.requestedTransform;
+        std::string transformDebug =
+                base::StringPrintf(" transform={%f,%f,%f,%f}  requestedTransform={%f,%f,%f,%f}",
+                                   t.dsdx(), t.dsdy(), t.dtdx(), t.dtdy(), requestedT.dsdx(),
+                                   requestedT.dsdy(), requestedT.dtdx(), requestedT.dtdy());
+        std::string bufferDebug;
+        if (requested.externalTexture) {
+            auto unRotBuffer = requested.getUnrotatedBufferSize(displayRotationFlags);
+            auto& destFrame = requested.destinationFrame;
+            bufferDebug = base::StringPrintf(" buffer={%d,%d}  displayRot=%d"
+                                             " destFrame={%d,%d,%d,%d} unRotBuffer={%d,%d}",
+                                             requested.externalTexture->getWidth(),
+                                             requested.externalTexture->getHeight(),
+                                             displayRotationFlags, destFrame.left, destFrame.top,
+                                             destFrame.right, destFrame.bottom,
+                                             unRotBuffer.getHeight(), unRotBuffer.getWidth());
+        }
+        ALOGW("Resetting transform for %s because it is invalid.%s%s",
+              snapshot.getDebugString().c_str(), transformDebug.c_str(), bufferDebug.c_str());
         snapshot.geomLayerTransform.reset();
     }
+    if (transformWasInvalid != snapshot.invalidTransform) {
+        // If transform is invalid, the layer will be hidden.
+        mResortSnapshots = true;
+    }
     snapshot.geomInverseLayerTransform = snapshot.geomLayerTransform.inverse();
 
     FloatRect parentBounds = parentSnapshot.geomLayerBounds;
@@ -711,12 +858,19 @@
     }
     snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(parentBounds);
     snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
+    const Rect geomLayerBoundsWithoutTransparentRegion =
+            RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+                                        requested.transparentRegion);
+    snapshot.transformedBoundsWithoutTransparentRegion =
+            snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion);
     snapshot.parentTransform = parentSnapshot.geomLayerTransform;
 
     // Subtract the transparent region and snap to the bounds
-    Rect bounds =
+    const Rect bounds =
             RequestedLayerState::reduce(snapshot.croppedBufferSize, requested.transparentRegion);
-    snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
+    if (requested.potentialCursor) {
+        snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
+    }
 
     // TODO(b/238781169) use dest vs src
     snapshot.bufferNeedsFiltering = snapshot.externalTexture &&
@@ -749,15 +903,28 @@
 void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot,
                                        const RequestedLayerState& requested,
                                        const LayerSnapshot& parentSnapshot,
-                                       const frontend::DisplayInfo& displayInfo,
-                                       bool noValidDisplay,
-                                       const LayerHierarchy::TraversalPath& path) {
+                                       const LayerHierarchy::TraversalPath& path,
+                                       const Args& args) {
+    if (requested.windowInfoHandle) {
+        snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
+    } else {
+        snapshot.inputInfo = {};
+    }
     snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
-    if (!requested.hasInputInfo()) {
-        snapshot.inputInfo.inputConfig = gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL;
+
+    if (!needsInputInfo(snapshot, requested)) {
         return;
     }
 
+    static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
+    const std::optional<frontend::DisplayInfo> displayInfoOpt =
+            args.displays.get(snapshot.outputFilter.layerStack);
+    bool noValidDisplay = !displayInfoOpt.has_value();
+    auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
+
+    if (!requested.windowInfoHandle) {
+        snapshot.inputInfo.inputConfig = gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL;
+    }
     fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
 
     if (noValidDisplay) {
@@ -766,17 +933,6 @@
         snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_TOUCHABLE;
     }
 
-    // For compatibility reasons we let layers which can receive input
-    // receive input before they have actually submitted a buffer. Because
-    // of this we use canReceiveInput instead of isVisible to check the
-    // policy-visibility, ignoring the buffer state. However for layers with
-    // hasInputInfo()==false we can use the real visibility state.
-    // We are just using these layers for occlusion detection in
-    // InputDispatcher, and obviously if they aren't visible they can't occlude
-    // anything.
-    const bool visible = requested.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
-    snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
-
     snapshot.inputInfo.alpha = snapshot.color.a;
     snapshot.inputInfo.touchOcclusionMode = parentSnapshot.inputInfo.touchOcclusionMode;
     if (requested.dropInputMode == gui::DropInputMode::ALL ||
@@ -830,4 +986,28 @@
     return mSnapshots;
 }
 
+void LayerSnapshotBuilder::forEachVisibleSnapshot(const ConstVisitor& visitor) const {
+    for (int i = 0; i < mNumInterestingSnapshots; i++) {
+        LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
+        if (!snapshot.isVisible) continue;
+        visitor(snapshot);
+    }
+}
+
+void LayerSnapshotBuilder::forEachVisibleSnapshot(const Visitor& visitor) {
+    for (int i = 0; i < mNumInterestingSnapshots; i++) {
+        std::unique_ptr<LayerSnapshot>& snapshot = mSnapshots.at((size_t)i);
+        if (!snapshot->isVisible) continue;
+        visitor(snapshot);
+    }
+}
+
+void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) const {
+    for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) {
+        LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
+        if (!snapshot.hasInputInfo()) continue;
+        visitor(snapshot);
+    }
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 33b250c..abb7e66 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -44,6 +44,8 @@
         // Set to true if there were display changes since last update.
         bool displayChanges = false;
         const renderengine::ShadowSettings& globalShadowSettings;
+        bool supportsBlur = true;
+        bool forceFullDamage = false;
     };
     LayerSnapshotBuilder();
 
@@ -56,10 +58,22 @@
     // change flags.
     void update(const Args&);
     std::vector<std::unique_ptr<LayerSnapshot>>& getSnapshots();
+    LayerSnapshot* getSnapshot(uint32_t layerId) const;
+
+    typedef std::function<void(const LayerSnapshot& snapshot)> ConstVisitor;
+
+    // Visit each visible snapshot in z-order
+    void forEachVisibleSnapshot(const ConstVisitor& visitor) const;
+
+    typedef std::function<void(std::unique_ptr<LayerSnapshot>& snapshot)> Visitor;
+    // Visit each visible snapshot in z-order and move the snapshot if needed
+    void forEachVisibleSnapshot(const Visitor& visitor);
+
+    // Visit each snapshot interesting to input reverse z-order
+    void forEachInputSnapshot(const ConstVisitor& visitor) const;
 
 private:
     friend class LayerSnapshotTest;
-    LayerSnapshot* getSnapshot(uint32_t layerId) const;
     LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath& id) const;
     static LayerSnapshot getRootSnapshot();
 
@@ -69,28 +83,29 @@
 
     void updateSnapshots(const Args& args);
 
-    void updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
-                                    LayerHierarchy::TraversalPath& traversalPath,
-                                    const LayerSnapshot& parentSnapshot);
-    void updateSnapshot(LayerSnapshot& snapshot, const Args& args, const RequestedLayerState&,
-                        const LayerSnapshot& parentSnapshot,
-                        const LayerHierarchy::TraversalPath& path);
+    const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
+                                                    LayerHierarchy::TraversalPath& traversalPath,
+                                                    const LayerSnapshot& parentSnapshot);
+    void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
+                        const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&,
+                        bool newSnapshot);
     static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
                                     bool parentIsRelative, const Args& args);
     static void resetRelativeState(LayerSnapshot& snapshot);
     static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
                                     const LayerSnapshot& parentSnapshot);
-    static void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
-                                  const LayerSnapshot& parentSnapshot,
-                                  uint32_t displayRotationFlags);
+    void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
+                           const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
     static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
                               const renderengine::ShadowSettings& globalShadowSettings);
     void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
-                     const LayerSnapshot& parentSnapshot, const frontend::DisplayInfo& displayInfo,
-                     bool noValidDisplay, const LayerHierarchy::TraversalPath& path);
+                     const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
+                     const Args& args);
     void sortSnapshotsByZ(const Args& args);
-    LayerSnapshot* getOrCreateSnapshot(const LayerHierarchy::TraversalPath& id,
-                                       const RequestedLayerState& layer);
+    LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id,
+                                  const RequestedLayerState& layer);
+    void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
+                          const Args& args);
 
     struct TraversalPathHash {
         std::size_t operator()(const LayerHierarchy::TraversalPath& key) const {
@@ -105,6 +120,8 @@
             mIdToSnapshot;
     std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots;
     LayerSnapshot mRootSnapshot;
+    bool mResortSnapshots = false;
+    int mNumInterestingSnapshots = 0;
 };
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index dcc16e8..d63b126 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -14,16 +14,18 @@
  * limitations under the License.
  */
 
-#include "FrontEnd/LayerCreationArgs.h"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #undef LOG_TAG
 #define LOG_TAG "RequestedLayerState"
 
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 #include <sys/types.h>
 
 #include "Layer.h"
+#include "LayerCreationArgs.h"
 #include "LayerHandle.h"
+#include "LayerLog.h"
 #include "RequestedLayerState.h"
 
 namespace android::surfaceflinger::frontend {
@@ -43,11 +45,21 @@
     return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
 }
 
+std::string layerIdsToString(const std::vector<uint32_t>& layerIds) {
+    std::stringstream stream;
+    stream << "{";
+    for (auto layerId : layerIds) {
+        stream << layerId << ",";
+    }
+    stream << "}";
+    return stream.str();
+}
+
 } // namespace
 
 RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args)
       : id(args.sequence),
-        name(args.name),
+        name(args.name + "#" + std::to_string(args.sequence)),
         canBeRoot(args.addToRoot),
         layerCreationFlags(args.flags),
         textureName(args.textureName),
@@ -59,8 +71,14 @@
     changes |= RequestedLayerState::Changes::Metadata;
     handleAlive = true;
     parentId = LayerHandle::getLayerId(args.parentHandle.promote());
-    mirrorId = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
-    if (mirrorId != UNASSIGNED_LAYER_ID) {
+    if (args.parentHandle != nullptr) {
+        canBeRoot = false;
+    }
+    layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
+    if (layerIdToMirror != UNASSIGNED_LAYER_ID) {
+        changes |= RequestedLayerState::Changes::Mirror;
+    } else if (args.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+        layerStackToMirror = args.layerStackToMirror;
         changes |= RequestedLayerState::Changes::Mirror;
     }
 
@@ -83,6 +101,7 @@
     } else {
         color.rgb = {0.0_hf, 0.0_hf, 0.0_hf};
     }
+    LLOGV(layerId, "Created %s flags=%d", getDebugString().c_str(), flags);
     color.a = 1.0f;
 
     crop.makeInvalid();
@@ -90,6 +109,8 @@
     layerStack = ui::DEFAULT_LAYER_STACK;
     transformToDisplayInverse = false;
     dataspace = ui::Dataspace::UNKNOWN;
+    desiredSdrHdrRatio = 1.f;
+    currentSdrHdrRatio = 1.f;
     dataspaceRequested = false;
     hdrMetadata.validTypes = 0;
     surfaceDamageRegion = Region::INVALID_REGION;
@@ -114,11 +135,14 @@
     defaultFrameRateCompatibility =
             static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
     dataspace = ui::Dataspace::V0_SRGB;
+    gameMode = gui::GameMode::Unsupported;
+    requestedFrameRate = {};
 }
 
 void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
-    bool oldFlags = flags;
-    Rect oldBufferSize = getBufferSize(0);
+    const uint32_t oldFlags = flags;
+    const half oldAlpha = color.a;
+    const bool hadBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
     const layer_state_t& clientState = resolvedComposerState.state;
 
     uint64_t clientChanges = what | layer_state_t::diff(clientState);
@@ -127,14 +151,28 @@
 
     if (clientState.what & layer_state_t::eFlagsChanged) {
         if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) {
-            changes |= RequestedLayerState::Changes::Visibility;
+            changes |= RequestedLayerState::Changes::Visibility |
+                    RequestedLayerState::Changes::VisibleRegion;
         }
         if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) {
             changes |= RequestedLayerState::Changes::Geometry;
         }
     }
-    if (clientState.what & layer_state_t::eBufferChanged && oldBufferSize != getBufferSize(0)) {
-        changes |= RequestedLayerState::Changes::Geometry;
+    if (clientState.what &
+        (layer_state_t::eBufferChanged | layer_state_t::eSidebandStreamChanged)) {
+        const bool hasBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
+        if (hadBufferOrSideStream != hasBufferOrSideStream) {
+            changes |= RequestedLayerState::Changes::Geometry |
+                    RequestedLayerState::Changes::VisibleRegion |
+                    RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input |
+                    RequestedLayerState::Changes::Buffer;
+        }
+    }
+    if (what & (layer_state_t::eAlphaChanged)) {
+        if (oldAlpha == 0 || color.a == 0) {
+            changes |= RequestedLayerState::Changes::Visibility |
+                    RequestedLayerState::Changes::VisibleRegion;
+        }
     }
     if (clientChanges & layer_state_t::HIERARCHY_CHANGES)
         changes |= RequestedLayerState::Changes::Hierarchy;
@@ -144,7 +182,10 @@
         changes |= RequestedLayerState::Changes::Geometry;
     if (clientChanges & layer_state_t::AFFECTS_CHILDREN)
         changes |= RequestedLayerState::Changes::AffectsChildren;
-
+    if (clientChanges & layer_state_t::INPUT_CHANGES)
+        changes |= RequestedLayerState::Changes::Input;
+    if (clientChanges & layer_state_t::VISIBLE_REGION_CHANGES)
+        changes |= RequestedLayerState::Changes::VisibleRegion;
     if (clientState.what & layer_state_t::eColorTransformChanged) {
         static const mat4 identityMatrix = mat4();
         hasColorTransform = colorTransform != identityMatrix;
@@ -183,7 +224,6 @@
         wp<IBinder>& touchableRegionCropHandle =
                 windowInfoHandle->editInfo()->touchableRegionCropHandle;
         touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote());
-        changes |= RequestedLayerState::Changes::Input;
         touchableRegionCropHandle.clear();
     }
     if (clientState.what & layer_state_t::eStretchChanged) {
@@ -205,6 +245,27 @@
     if (clientState.what & layer_state_t::eMatrixChanged) {
         requestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
     }
+    if (clientState.what & layer_state_t::eMetadataChanged) {
+        const int32_t requestedGameMode =
+                clientState.metadata.getInt32(gui::METADATA_GAME_MODE, -1);
+        if (requestedGameMode != -1) {
+            // The transaction will be received on the Task layer and needs to be applied to all
+            // child layers.
+            if (static_cast<int32_t>(gameMode) != requestedGameMode) {
+                gameMode = static_cast<gui::GameMode>(requestedGameMode);
+                changes |= RequestedLayerState::Changes::AffectsChildren;
+            }
+        }
+    }
+    if (clientState.what & layer_state_t::eFrameRateChanged) {
+        const auto compatibility =
+                Layer::FrameRate::convertCompatibility(clientState.frameRateCompatibility);
+        const auto strategy = Layer::FrameRate::convertChangeFrameRateStrategy(
+                clientState.changeFrameRateStrategy);
+        requestedFrameRate =
+                Layer::FrameRate(Fps::fromValue(clientState.frameRate), compatibility, strategy);
+        changes |= RequestedLayerState::Changes::FrameRate;
+    }
 }
 
 ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const {
@@ -261,7 +322,7 @@
     return "[" + std::to_string(id) + "]" + name + ",parent=" + layerIdToString(parentId) +
             ",relativeParent=" + layerIdToString(relativeParentId) +
             ",isRelativeOf=" + std::to_string(isRelativeOf) +
-            ",mirrorId=" + layerIdToString(mirrorId) +
+            ",mirrorIds=" + layerIdsToString(mirrorIds) +
             ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
 }
 
@@ -368,7 +429,8 @@
 // If the relative parentid is unassigned, the layer will be considered relative but won't be
 // reachable.
 bool RequestedLayerState::hasValidRelativeParent() const {
-    return isRelativeOf && parentId != relativeParentId;
+    return isRelativeOf &&
+            (parentId != relativeParentId || relativeParentId == UNASSIGNED_LAYER_ID);
 }
 
 bool RequestedLayerState::hasInputInfo() const {
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 95240d0..6317b95 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -20,6 +20,7 @@
 #include <ftl/flags.h>
 #include <gui/LayerState.h>
 #include <renderengine/ExternalTexture.h>
+#include "Scheduler/LayerInfo.h"
 
 #include "LayerCreationArgs.h"
 #include "TransactionState.h"
@@ -48,6 +49,9 @@
         Metadata = 1u << 10,
         Visibility = 1u << 11,
         AffectsChildren = 1u << 12,
+        FrameRate = 1u << 13,
+        VisibleRegion = 1u << 14,
+        Buffer = 1u << 15,
     };
     static Rect reduce(const Rect& win, const Region& exclude);
     RequestedLayerState(const LayerCreationArgs&);
@@ -91,13 +95,17 @@
     ui::Transform requestedTransform;
     std::shared_ptr<FenceTime> acquireFenceTime;
     std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+    gui::GameMode gameMode;
+    scheduler::LayerInfo::FrameRate requestedFrameRate;
+    ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+    uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
 
     // book keeping states
     bool handleAlive = true;
     bool isRelativeOf = false;
     uint32_t parentId = UNASSIGNED_LAYER_ID;
     uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
-    uint32_t mirrorId = UNASSIGNED_LAYER_ID;
+    std::vector<uint32_t> mirrorIds{};
     uint32_t touchCropId = UNASSIGNED_LAYER_ID;
     uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
     ftl::Flags<RequestedLayerState::Changes> changes;
diff --git a/services/surfaceflinger/FrontEnd/SwapErase.h b/services/surfaceflinger/FrontEnd/SwapErase.h
index f672f99..0061c53 100644
--- a/services/surfaceflinger/FrontEnd/SwapErase.h
+++ b/services/surfaceflinger/FrontEnd/SwapErase.h
@@ -23,12 +23,15 @@
 // remove an element from a vector that avoids relocating all the elements after the one
 // that is erased.
 template <typename T>
-void swapErase(std::vector<T>& vec, const T& value) {
+bool swapErase(std::vector<T>& vec, const T& value) {
+    bool found = false;
     auto it = std::find(vec.begin(), vec.end(), value);
     if (it != vec.end()) {
         std::iter_swap(it, vec.end() - 1);
         vec.erase(vec.end() - 1);
+        found = true;
     }
+    return found;
 }
 
 // Similar to swapErase(std::vector<T>& vec, const T& value) but erases the first element
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 4ada2b6..9b70c16 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -33,6 +33,17 @@
         int32_t maxW = 0;
         int32_t maxH = 0;
         int32_t flags = 0;
+        // Counter-intuitively a value of "1" means "as much as you can give me" due to "1" being
+        // the default value for all layers, so any HDR layer with a value of 1.f means no
+        // reduced maximum has been requested
+        // TODO: Should the max desired ratio have a better meaning for HLG/PQ so this can be
+        // eliminated? If we assume an SDR white point of even just 100 nits for those content
+        // then HLG could have a meaningful max ratio of 10.f and PQ of 100.f instead of needing
+        // to treat 1.f as "uncapped"
+        // With peak display brightnesses exceeding 1,000 nits currently, HLG's request could
+        // actually be satisfied in some ambient conditions such that limiting that max for that
+        // content in theory makes sense
+        float maxDesiredSdrHdrRatio = 0.f;
 
         bool operator==(const HdrLayerInfo& other) const {
             return numberOfHdrLayers == other.numberOfHdrLayers && maxW == other.maxW &&
@@ -40,6 +51,20 @@
         }
 
         bool operator!=(const HdrLayerInfo& other) const { return !(*this == other); }
+
+        void mergeDesiredRatio(float update) {
+            if (maxDesiredSdrHdrRatio == 0.f) {
+                // If nothing is set, take the incoming value
+                maxDesiredSdrHdrRatio = update;
+            } else if (update == 1.f) {
+                // If the request is to "go to max", then take it regardless
+                maxDesiredSdrHdrRatio = 1.f;
+            } else if (maxDesiredSdrHdrRatio != 1.f) {
+                // If we're not currently asked to "go to max", then take the max
+                // of the incoming requests
+                maxDesiredSdrHdrRatio = std::max(maxDesiredSdrHdrRatio, update);
+            }
+        }
     };
 
     HdrLayerInfoReporter() = default;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0179d62..7a4b337 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -196,7 +196,7 @@
         mDrawingState.color.b = -1.0_hf;
     }
 
-    mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod());
+    mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod().ns());
 
     mOwnerUid = args.ownerUid;
     mOwnerPid = args.ownerPid;
@@ -252,6 +252,10 @@
     if (mHadClonedChild) {
         mFlinger->mNumClones--;
     }
+    if (hasTrustedPresentationListener()) {
+        mFlinger->mNumTrustedPresentationListeners--;
+        updateTrustedPresentationState(nullptr, -1 /* time_in_ms */, true /* leaveState*/);
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -281,6 +285,7 @@
         mRemovedFromDrawingState = true;
         mFlinger->mScheduler->deregisterLayer(this);
     }
+    updateTrustedPresentationState(nullptr, -1 /* time_in_ms */, true /* leaveState*/);
 
     mFlinger->markLayerPendingRemovalLocked(sp<Layer>::fromExisting(this));
 }
@@ -378,6 +383,92 @@
     return reduce(mBounds, activeTransparentRegion);
 }
 
+// No early returns.
+void Layer::updateTrustedPresentationState(const DisplayDevice* display, int64_t time_in_ms,
+                                           bool leaveState) {
+    if (!hasTrustedPresentationListener()) {
+        return;
+    }
+    const bool lastState = mLastComputedTrustedPresentationState;
+    mLastComputedTrustedPresentationState = false;
+
+    if (!leaveState) {
+        const auto outputLayer = findOutputLayerForDisplay(display);
+        if (outputLayer != nullptr) {
+            mLastComputedTrustedPresentationState =
+                    computeTrustedPresentationState(mBounds, mSourceBounds,
+                                                    outputLayer->getState().coveredRegion,
+                                                    mScreenBounds, getCompositionState()->alpha,
+                                                    getCompositionState()->geomLayerTransform,
+                                                    mTrustedPresentationThresholds);
+        }
+    }
+    const bool newState = mLastComputedTrustedPresentationState;
+    if (lastState && !newState) {
+        // We were in the trusted presentation state, but now we left it,
+        // emit the callback if needed
+        if (mLastReportedTrustedPresentationState) {
+            mLastReportedTrustedPresentationState = false;
+            mTrustedPresentationListener.invoke(false);
+        }
+        // Reset the timer
+        mEnteredTrustedPresentationStateTime = -1;
+    } else if (!lastState && newState) {
+        // We were not in the trusted presentation state, but we entered it, begin the timer
+        // and make sure this gets called at least once more!
+        mEnteredTrustedPresentationStateTime = time_in_ms;
+        mFlinger->forceFutureUpdate(mTrustedPresentationThresholds.stabilityRequirementMs * 1.5);
+    }
+
+    // Has the timer elapsed, but we are still in the state? Emit a callback if needed
+    if (!mLastReportedTrustedPresentationState && newState &&
+        (time_in_ms - mEnteredTrustedPresentationStateTime >
+         mTrustedPresentationThresholds.stabilityRequirementMs)) {
+        mLastReportedTrustedPresentationState = true;
+        mTrustedPresentationListener.invoke(true);
+    }
+}
+
+/**
+ * See SurfaceComposerClient.h: setTrustedPresentationCallback for discussion
+ * of how the parameters and thresholds are interpreted. The general spirit is
+ * to produce an upper bound on the amount of the buffer which was presented.
+ */
+bool Layer::computeTrustedPresentationState(const FloatRect& bounds, const FloatRect& sourceBounds,
+                                            const Region& coveredRegion,
+                                            const FloatRect& screenBounds, float alpha,
+                                            const ui::Transform& effectiveTransform,
+                                            const TrustedPresentationThresholds& thresholds) {
+    if (alpha < thresholds.minAlpha) {
+        return false;
+    }
+    if (sourceBounds.getWidth() == 0 || sourceBounds.getHeight() == 0) {
+        return false;
+    }
+    if (screenBounds.getWidth() == 0 || screenBounds.getHeight() == 0) {
+        return false;
+    }
+
+    const float sx = effectiveTransform.dsdx();
+    const float sy = effectiveTransform.dsdy();
+    float fractionRendered = std::min(sx * sy, 1.0f);
+
+    float boundsOverSourceW = bounds.getWidth() / (float)sourceBounds.getWidth();
+    float boundsOverSourceH = bounds.getHeight() / (float)sourceBounds.getHeight();
+    fractionRendered *= boundsOverSourceW * boundsOverSourceH;
+
+    Rect coveredBounds = coveredRegion.bounds();
+    fractionRendered *= (1 -
+                         ((coveredBounds.width() / (float)screenBounds.getWidth()) *
+                          coveredBounds.height() / (float)screenBounds.getHeight()));
+
+    if (fractionRendered < thresholds.minFractionRendered) {
+        return false;
+    }
+
+    return true;
+}
+
 void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
                           float parentShadowRadius) {
     const State& s(getDrawingState());
@@ -475,7 +566,8 @@
     snapshot->geomLayerTransform = getTransform();
     snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse();
     snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState);
-    snapshot->localTransformInverse = getActiveTransform(drawingState).inverse();
+    snapshot->localTransform = getActiveTransform(drawingState);
+    snapshot->localTransformInverse = snapshot->localTransform.inverse();
     snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
     snapshot->alpha = alpha;
     snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
@@ -530,6 +622,8 @@
     snapshot->surfaceDamage = surfaceDamageRegion;
     snapshot->hasProtectedContent = isProtected();
     snapshot->dimmingEnabled = isDimmingEnabled();
+    snapshot->currentSdrHdrRatio = getCurrentSdrHdrRatio();
+    snapshot->desiredSdrHdrRatio = getDesiredSdrHdrRatio();
 
     const bool usesRoundedCorners = hasRoundedCorners();
 
@@ -2595,6 +2689,8 @@
     mDrawingState = from->mDrawingState;
     // Skip callback info since they are not applicable for cloned layers.
     mDrawingState.releaseBufferListener = nullptr;
+    // TODO (b/238781169) currently broken for mirror layers because we do not
+    // track release fences for mirror layers composed on other displays
     mDrawingState.callbackHandles = {};
 }
 
@@ -2662,18 +2758,18 @@
 
 void Layer::onSurfaceFrameCreated(
         const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
-    if (!hasBufferOrSidebandStreamInDrawing()) {
-        return;
-    }
-
     while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
         // Too many SurfaceFrames pending classification. The front of the deque is probably not
         // tracked by FrameTimeline and will never be presented. This will only result in a memory
         // leak.
-        ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
-              mName.c_str());
-        std::string miniDump = mPendingJankClassifications.front()->miniDump();
-        ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
+        if (hasBufferOrSidebandStreamInDrawing()) {
+            // Only log for layers with a buffer, since we expect the jank data to be drained for
+            // these, while there may be no jank listeners for bufferless layers.
+            ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
+                  mName.c_str());
+            std::string miniDump = mPendingJankClassifications.front()->miniDump();
+            ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
+        }
         mPendingJankClassifications.pop_front();
     }
     mPendingJankClassifications.emplace_back(surfaceFrame);
@@ -2947,6 +3043,17 @@
     return true;
 }
 
+bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio) {
+    if (mDrawingState.currentSdrHdrRatio == currentBufferRatio &&
+        mDrawingState.desiredSdrHdrRatio == desiredRatio)
+        return false;
+    mDrawingState.currentSdrHdrRatio = currentBufferRatio;
+    mDrawingState.desiredSdrHdrRatio = desiredRatio;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
     if (mDrawingState.hdrMetadata == hdrMetadata) return false;
     mDrawingState.hdrMetadata = hdrMetadata;
@@ -3178,7 +3285,11 @@
     auto lastDataspace = mBufferInfo.mDataspace;
     mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
     if (lastDataspace != mBufferInfo.mDataspace) {
-        mFlinger->mSomeDataspaceChanged = true;
+        mFlinger->mHdrLayerInfoChanged = true;
+    }
+    if (mBufferInfo.mDesiredSdrHdrRatio != mDrawingState.desiredSdrHdrRatio) {
+        mBufferInfo.mDesiredSdrHdrRatio = mDrawingState.desiredSdrHdrRatio;
+        mFlinger->mHdrLayerInfoChanged = true;
     }
     mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
     mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
@@ -3469,6 +3580,14 @@
         }
     }
 
+    if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) {
+        if (mDrawingState.currentSdrHdrRatio != s.currentSdrHdrRatio ||
+            mDrawingState.desiredSdrHdrRatio != s.desiredSdrHdrRatio) {
+            ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
+            return false;
+        }
+    }
+
     ALOGV("%s: true", __func__);
     return true;
 }
@@ -3969,6 +4088,19 @@
     return *this;
 }
 
+void Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+                                       TrustedPresentationListener const& listener) {
+    bool hadTrustedPresentationListener = hasTrustedPresentationListener();
+    mTrustedPresentationListener = listener;
+    mTrustedPresentationThresholds = thresholds;
+    bool haveTrustedPresentationListener = hasTrustedPresentationListener();
+    if (!hadTrustedPresentationListener && haveTrustedPresentationListener) {
+        mFlinger->mNumTrustedPresentationListeners++;
+    } else if (hadTrustedPresentationListener && !haveTrustedPresentationListener) {
+        mFlinger->mNumTrustedPresentationListeners--;
+    }
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8281140..3384e4a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -223,6 +223,8 @@
         gui::DropInputMode dropInputMode;
         bool autoRefresh = false;
         bool dimmingEnabled = true;
+        float currentSdrHdrRatio = 1.f;
+        float desiredSdrHdrRatio = 1.f;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -289,7 +291,9 @@
     virtual mat4 getColorTransform() const;
     virtual bool hasColorTransform() const;
     virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
-    virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; };
+    virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }
+    float getDesiredSdrHdrRatio() const { return getDrawingState().desiredSdrHdrRatio; }
+    float getCurrentSdrHdrRatio() const { return getDrawingState().currentSdrHdrRatio; }
 
     bool setTransform(uint32_t /*transform*/);
     bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
@@ -298,6 +302,7 @@
                    nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
                    std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
     bool setDataspace(ui::Dataspace /*dataspace*/);
+    bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
     bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
     bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
     bool setApi(int32_t /*api*/);
@@ -499,6 +504,7 @@
         uint64_t mFrameNumber;
 
         bool mFrameLatencyNeeded{false};
+        float mDesiredSdrHdrRatio = 1.f;
     };
 
     BufferInfo mBufferInfo;
@@ -527,6 +533,19 @@
 
     uint32_t getTransactionFlags() const { return mTransactionFlags; }
 
+    static bool computeTrustedPresentationState(const FloatRect& bounds,
+                                                const FloatRect& sourceBounds,
+                                                const Region& coveredRegion,
+                                                const FloatRect& screenBounds, float,
+                                                const ui::Transform&,
+                                                const TrustedPresentationThresholds&);
+    void updateTrustedPresentationState(const DisplayDevice* display, int64_t time_in_ms,
+                                        bool leaveState);
+
+    inline bool hasTrustedPresentationListener() {
+        return mTrustedPresentationListener.callbackInterface != nullptr;
+    }
+
     // Sets the masked bits.
     void setTransactionFlags(uint32_t mask);
 
@@ -728,6 +747,9 @@
     std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
             const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
 
+    void setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+                                    TrustedPresentationListener const& listener);
+
     // Creates a new handle each time, so we only expect
     // this to be called once.
     sp<IBinder> getHandle();
@@ -885,6 +907,12 @@
     // These are only accessed by the main thread or the tracing thread.
     State mDrawingState;
 
+    TrustedPresentationThresholds mTrustedPresentationThresholds;
+    TrustedPresentationListener mTrustedPresentationListener;
+    bool mLastComputedTrustedPresentationState = false;
+    bool mLastReportedTrustedPresentationState = false;
+    int64_t mEnteredTrustedPresentationStateTime = -1;
+
     uint32_t mTransactionFlags{0};
     // Updated in doTransaction, used to track the last sequence number we
     // committed. Currently this is really only used for updating visible
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 6e64e0a..839500f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -344,12 +344,13 @@
                                                      renderengine::impl::ExternalTexture::Usage::
                                                              WRITEABLE);
     }
+    auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
 
     constexpr bool kRegionSampling = true;
     constexpr bool kGrayscale = false;
 
     if (const auto fenceResult =
-                mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+                mFlinger.captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, buffer,
                                              kRegionSampling, kGrayscale, nullptr)
                         .get();
         fenceResult.ok()) {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index b62b15c..e8c891e 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -37,7 +37,6 @@
 namespace android {
 
 class Layer;
-class Scheduler;
 class SurfaceFlinger;
 struct SamplingOffsetCallback;
 
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 387364c..3c20e3b 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -34,6 +34,21 @@
             mRotationFlags(rotation),
             mLayerStackSpaceRect(layerStackRect) {}
 
+    static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
+            std::function<void(const LayerVector::Visitor&)> traverseLayers) {
+        return [traverseLayers = std::move(traverseLayers)]() {
+            std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+            traverseLayers([&](Layer* layer) {
+                // Layer::prepareClientComposition uses the layer's snapshot to populate the
+                // resulting LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings
+                // are generated with the layer's current buffer and geometry.
+                layer->updateSnapshot(true /* updateGeometry */);
+                layers.emplace_back(layer, layer->copyCompositionEngineLayerFE());
+            });
+            return layers;
+        };
+    }
+
     virtual ~RenderArea() = default;
 
     // Invoke drawLayers to render layers into the render area.
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index e827c12..9b04497 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -132,6 +132,10 @@
     mLooper->sendMessage(handler, Message());
 }
 
+void MessageQueue::postMessageDelayed(sp<MessageHandler>&& handler, nsecs_t uptimeDelay) {
+    mLooper->sendMessageDelayed(uptimeDelay, handler, Message());
+}
+
 void MessageQueue::scheduleConfigure() {
     struct ConfigureHandler : MessageHandler {
         explicit ConfigureHandler(ICompositor& compositor) : compositor(compositor) {}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 71f8645..ad0ea72 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -79,6 +79,7 @@
     virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
     virtual void waitMessage() = 0;
     virtual void postMessage(sp<MessageHandler>&&) = 0;
+    virtual void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) = 0;
     virtual void scheduleConfigure() = 0;
     virtual void scheduleFrame() = 0;
 
@@ -144,6 +145,7 @@
 
     void waitMessage() override;
     void postMessage(sp<MessageHandler>&&) override;
+    void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) override;
 
     void scheduleConfigure() override;
     void scheduleFrame() override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 30821d8..1d27cfc 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -932,14 +932,22 @@
     const char* const whence = __func__;
     std::deque<ScoredFrameRate> ranking;
     const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
+        using fps_approx_ops::operator<;
         const auto& modePtr = frameRateMode.modePtr;
         if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
             return;
         }
 
+        const bool ascending = (refreshRateOrder == RefreshRateOrder::Ascending);
+        if (ascending && frameRateMode.fps < getMinRefreshRateByPolicyLocked()->getFps()) {
+            // TODO(b/266481656): Once this bug is fixed, we can remove this workaround and actually
+            //  use a lower frame rate when we want Ascending frame rates.
+            return;
+        }
+
         float score = calculateDistanceScoreFromMax(frameRateMode.fps);
-        const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
-        if (inverseScore) {
+
+        if (ascending) {
             score = 1.0f / score;
         }
         if (preferredDisplayModeOpt) {
@@ -951,6 +959,7 @@
             constexpr float kNonPreferredModePenalty = 0.95f;
             score *= kNonPreferredModePenalty;
         }
+
         ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
               to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score);
         ranking.emplace_back(ScoredFrameRate{frameRateMode, score});
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index bc465ce..1fc1519 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -60,8 +60,12 @@
 
 namespace android::scheduler {
 
-Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
-      : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
+Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features,
+                     sp<VsyncModulator> modulatorPtr)
+      : impl::MessageQueue(compositor),
+        mFeatures(features),
+        mVsyncModulator(std::move(modulatorPtr)),
+        mSchedulerCallback(callback) {}
 
 Scheduler::~Scheduler() {
     // MessageQueue depends on VsyncSchedule, so first destroy it.
@@ -186,17 +190,19 @@
     };
 }
 
-ConnectionHandle Scheduler::createConnection(const char* connectionName,
-                                             frametimeline::TokenManager* tokenManager,
-                                             std::chrono::nanoseconds workDuration,
-                                             std::chrono::nanoseconds readyDuration) {
-    auto throttleVsync = makeThrottleVsyncCallback();
-    auto getVsyncPeriod = makeGetVsyncPeriodFunction();
-    auto eventThread =
-            std::make_unique<impl::EventThread>(connectionName, *mVsyncSchedule, tokenManager,
-                                                std::move(throttleVsync), std::move(getVsyncPeriod),
-                                                workDuration, readyDuration);
-    return createConnection(std::move(eventThread));
+ConnectionHandle Scheduler::createEventThread(Cycle cycle,
+                                              frametimeline::TokenManager* tokenManager,
+                                              std::chrono::nanoseconds workDuration,
+                                              std::chrono::nanoseconds readyDuration) {
+    auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
+                                                           *mVsyncSchedule, tokenManager,
+                                                           makeThrottleVsyncCallback(),
+                                                           makeGetVsyncPeriodFunction(),
+                                                           workDuration, readyDuration);
+
+    auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle;
+    handle = createConnection(std::move(eventThread));
+    return handle;
 }
 
 ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -356,6 +362,20 @@
     thread->setDuration(workDuration, readyDuration);
 }
 
+void Scheduler::setVsyncConfigSet(const VsyncConfigSet& configs, Period vsyncPeriod) {
+    setVsyncConfig(mVsyncModulator->setVsyncConfigSet(configs), vsyncPeriod);
+}
+
+void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) {
+    setDuration(mAppConnectionHandle,
+                /* workDuration */ config.appWorkDuration,
+                /* readyDuration */ config.sfWorkDuration);
+    setDuration(mSfConnectionHandle,
+                /* workDuration */ vsyncPeriod,
+                /* readyDuration */ config.sfWorkDuration);
+    setDuration(config.sfWorkDuration);
+}
+
 void Scheduler::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 20221d1..ef7d0cf 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,6 +36,7 @@
 #include <ftl/optional.h>
 #include <scheduler/Features.h>
 #include <scheduler/Time.h>
+#include <scheduler/VsyncConfig.h>
 #include <ui/DisplayId.h>
 
 #include "Display/DisplayMap.h"
@@ -47,6 +48,7 @@
 #include "OneShotTimer.h"
 #include "RefreshRateSelector.h"
 #include "Utils/Dumper.h"
+#include "VsyncModulator.h"
 #include "VsyncSchedule.h"
 
 namespace android::scheduler {
@@ -104,7 +106,7 @@
     using Impl = android::impl::MessageQueue;
 
 public:
-    Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
+    Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>);
     virtual ~Scheduler();
 
     void startTimers();
@@ -139,9 +141,21 @@
         return std::move(future);
     }
 
-    ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
-                                      std::chrono::nanoseconds workDuration,
-                                      std::chrono::nanoseconds readyDuration);
+    template <typename F, typename T = std::invoke_result_t<F>>
+    [[nodiscard]] std::future<T> scheduleDelayed(F&& f, nsecs_t uptimeDelay) {
+        auto [task, future] = makeTask(std::move(f));
+        postMessageDelayed(std::move(task), uptimeDelay);
+        return std::move(future);
+    }
+
+    enum class Cycle {
+        Render,       // Surface rendering.
+        LastComposite // Ahead of display compositing by one refresh period.
+    };
+
+    ConnectionHandle createEventThread(Cycle, frametimeline::TokenManager*,
+                                       std::chrono::nanoseconds workDuration,
+                                       std::chrono::nanoseconds readyDuration);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(
             ConnectionHandle, EventRegistrationFlags eventRegistration = {});
@@ -161,6 +175,18 @@
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
+    const VsyncModulator& vsyncModulator() const { return *mVsyncModulator; }
+
+    template <typename... Args,
+              typename Handler = std::optional<VsyncConfig> (VsyncModulator::*)(Args...)>
+    void modulateVsync(Handler handler, Args... args) {
+        if (const auto config = (*mVsyncModulator.*handler)(args...)) {
+            setVsyncConfig(*config, getLeaderVsyncPeriod());
+        }
+    }
+
+    void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod);
+
     // Sets the render rate for the scheduler to run at.
     void setRenderRate(Fps);
 
@@ -232,8 +258,8 @@
     // Retrieves the overridden refresh rate for a given uid.
     std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
 
-    nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
-        return leaderSelectorPtr()->getActiveMode().fps.getPeriodNsecs();
+    Period getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
+        return leaderSelectorPtr()->getActiveMode().fps.getPeriod();
     }
 
     // Returns the framerate of the layer with the given sequence ID
@@ -263,6 +289,7 @@
     void displayPowerTimerCallback(TimerState);
 
     void setVsyncPeriod(nsecs_t period);
+    void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
 
     // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
     // `mLeaderDisplayId` is never `std::nullopt`.
@@ -323,6 +350,9 @@
     mutable std::mutex mConnectionsLock;
     std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
 
+    ConnectionHandle mAppConnectionHandle;
+    ConnectionHandle mSfConnectionHandle;
+
     mutable std::mutex mHWVsyncLock;
     bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
     bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
@@ -332,6 +362,9 @@
     const FeatureFlags mFeatures;
     std::optional<VsyncSchedule> mVsyncSchedule;
 
+    // Shifts the VSYNC phase during certain transactions and refresh rate changes.
+    const sp<VsyncModulator> mVsyncModulator;
+
     // Used to choose refresh rate if content detection is enabled.
     LayerHistory mLayerHistory;
 
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index ff31651..6ae10f3 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -42,12 +42,12 @@
 
 VsyncConfiguration::VsyncConfiguration(Fps currentFps) : mRefreshRateFps(currentFps) {}
 
-PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
+VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
     std::lock_guard lock(mLock);
     return getConfigsForRefreshRateLocked(fps);
 }
 
-PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
+VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
     if (const auto offsets = mOffsetsCache.get(fps)) {
         return offsets->get();
     }
@@ -134,7 +134,7 @@
         mThresholdForNextVsync(thresholdForNextVsync),
         mHwcMinWorkDuration(hwcMinWorkDuration) {}
 
-PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
     if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
         return getHighFpsOffsets(vsyncDuration);
     } else {
@@ -158,7 +158,7 @@
 }
 } // namespace
 
-PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
     const auto earlySfOffset =
             mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
 
@@ -196,7 +196,7 @@
     };
 }
 
-PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
     const auto earlySfOffset =
             mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
             ? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
@@ -286,7 +286,7 @@
 }
 } // namespace
 
-WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
     const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
         return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
                               : std::chrono::nanoseconds(duration);
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index 02ebd70..a24e43f 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -20,12 +20,12 @@
 #include <optional>
 #include <string>
 
+#include <android-base/thread_annotations.h>
 #include <ftl/small_map.h>
 #include <utils/Timers.h>
 
 #include <scheduler/Fps.h>
-
-#include "VsyncModulator.h"
+#include <scheduler/VsyncConfig.h>
 
 namespace android::scheduler {
 
@@ -37,8 +37,6 @@
  */
 class VsyncConfiguration {
 public:
-    using VsyncConfigSet = VsyncModulator::VsyncConfigSet;
-
     virtual ~VsyncConfiguration() = default;
     virtual VsyncConfigSet getCurrentConfigs() const = 0;
     virtual VsyncConfigSet getConfigsForRefreshRate(Fps fps) const = 0;
@@ -85,7 +83,7 @@
     void dump(std::string& result) const override;
 
 protected:
-    virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
+    virtual VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
 
     VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
 
@@ -115,7 +113,7 @@
                  nsecs_t hwcMinWorkDuration);
 
 private:
-    VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+    VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
 
     VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const;
     VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const;
@@ -154,7 +152,7 @@
                  nsecs_t hwcMinWorkDuration);
 
 private:
-    VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+    VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
 
     const nsecs_t mSfDuration;
     const nsecs_t mAppDuration;
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index 138d8d6..c9af4c2 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -40,7 +40,7 @@
         mNow(now),
         mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
 
-VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
+VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
     std::lock_guard<std::mutex> lock(mMutex);
     mVsyncConfigSet = config;
     return updateVsyncConfigLocked();
@@ -129,7 +129,7 @@
     return updateVsyncConfig();
 }
 
-VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const {
+VsyncConfig VsyncModulator::getVsyncConfig() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mVsyncConfig;
 }
@@ -147,7 +147,7 @@
     }
 }
 
-const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+const VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
     switch (getNextVsyncConfigType()) {
         case VsyncConfigType::Early:
             return mVsyncConfigSet.early;
@@ -158,12 +158,12 @@
     }
 }
 
-VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() {
+VsyncConfig VsyncModulator::updateVsyncConfig() {
     std::lock_guard<std::mutex> lock(mMutex);
     return updateVsyncConfigLocked();
 }
 
-VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
+VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
     const VsyncConfig& offsets = getNextVsyncConfig();
     mVsyncConfig = offsets;
 
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index 537cae1..dc4dafd 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -25,19 +25,13 @@
 #include <binder/IBinder.h>
 #include <utils/Timers.h>
 
+#include <scheduler/TransactionSchedule.h>
+#include <scheduler/VsyncConfig.h>
+
 #include "../WpHash.h"
 
 namespace android::scheduler {
 
-// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
-// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
-// fixed number of frames, respectively.
-enum class TransactionSchedule {
-    Late,  // Default.
-    EarlyStart,
-    EarlyEnd
-};
-
 // Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
 class VsyncModulator : public IBinder::DeathRecipient {
 public:
@@ -51,39 +45,8 @@
     // This may keep early offsets for an extra frame, but avoids a race with transaction commit.
     static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
 
-    // Phase offsets and work durations for SF and app deadlines from VSYNC.
-    struct VsyncConfig {
-        nsecs_t sfOffset;
-        nsecs_t appOffset;
-        std::chrono::nanoseconds sfWorkDuration;
-        std::chrono::nanoseconds appWorkDuration;
-
-        bool operator==(const VsyncConfig& other) const {
-            return sfOffset == other.sfOffset && appOffset == other.appOffset &&
-                    sfWorkDuration == other.sfWorkDuration &&
-                    appWorkDuration == other.appWorkDuration;
-        }
-
-        bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
-    };
-
     using VsyncConfigOpt = std::optional<VsyncConfig>;
 
-    struct VsyncConfigSet {
-        VsyncConfig early;    // Used for early transactions, and during refresh rate change.
-        VsyncConfig earlyGpu; // Used during GPU composition.
-        VsyncConfig late;     // Default.
-        std::chrono::nanoseconds hwcMinWorkDuration; // Used for calculating the
-                                                     // earliest present time
-
-        bool operator==(const VsyncConfigSet& other) const {
-            return early == other.early && earlyGpu == other.earlyGpu && late == other.late &&
-                    hwcMinWorkDuration == other.hwcMinWorkDuration;
-        }
-
-        bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
-    };
-
     using Clock = std::chrono::steady_clock;
     using TimePoint = Clock::time_point;
     using Now = TimePoint (*)();
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 5522ff8..d6329e2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -23,7 +23,7 @@
 #include <type_traits>
 
 #include <android-base/stringprintf.h>
-#include <utils/Timers.h>
+#include <scheduler/Time.h>
 
 namespace android {
 
@@ -52,6 +52,7 @@
     constexpr float getValue() const { return mFrequency; }
     int getIntValue() const { return static_cast<int>(std::round(mFrequency)); }
 
+    constexpr Period getPeriod() const { return Period::fromNs(mPeriod); }
     constexpr nsecs_t getPeriodNsecs() const { return mPeriod; }
 
 private:
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h b/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h
new file mode 100644
index 0000000..6fc44dd
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::scheduler {
+
+// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
+// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
+// fixed number of frames, respectively.
+enum class TransactionSchedule {
+    Late, // Default.
+    EarlyStart,
+    EarlyEnd
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
new file mode 100644
index 0000000..3b1985f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+#include <utils/Timers.h>
+
+namespace android::scheduler {
+
+// Phase offsets and work durations for SF and app deadlines from VSYNC.
+struct VsyncConfig {
+    nsecs_t sfOffset;
+    nsecs_t appOffset;
+    std::chrono::nanoseconds sfWorkDuration;
+    std::chrono::nanoseconds appWorkDuration;
+
+    bool operator==(const VsyncConfig& other) const {
+        return sfOffset == other.sfOffset && appOffset == other.appOffset &&
+                sfWorkDuration == other.sfWorkDuration && appWorkDuration == other.appWorkDuration;
+    }
+
+    bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+};
+
+struct VsyncConfigSet {
+    VsyncConfig early;    // Used for early transactions, and during refresh rate change.
+    VsyncConfig earlyGpu; // Used during GPU composition.
+    VsyncConfig late;     // Default.
+    std::chrono::nanoseconds hwcMinWorkDuration; // Used for calculating the earliest present time.
+
+    bool operator==(const VsyncConfigSet& other) const {
+        return early == other.early && earlyGpu == other.earlyGpu && late == other.late &&
+                hwcMinWorkDuration == other.hwcMinWorkDuration;
+    }
+
+    bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 40de4d6..7d0dc93 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -130,6 +130,7 @@
 #include "FrameTracer/FrameTracer.h"
 #include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerSnapshot.h"
 #include "HdrLayerInfoReporter.h"
 #include "Layer.h"
 #include "LayerProtoHelper.h"
@@ -142,6 +143,7 @@
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncModulator.h"
 #include "ScreenCaptureOutput.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerProperties.h"
@@ -189,6 +191,7 @@
 using gui::LayerMetadata;
 using gui::WindowInfo;
 using gui::aidl_utils::binderStatusFromStatusT;
+using scheduler::VsyncModulator;
 using ui::Dataspace;
 using ui::DisplayPrimaries;
 using ui::RenderIntent;
@@ -1163,7 +1166,7 @@
             mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps());
             // As we called to set period, we will call to onRefreshRateChangeCompleted once
             // VsyncController model is locked.
-            modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
+            mScheduler->modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
 
             updatePhaseConfiguration(mode.fps);
             mScheduler->setModeChangePending(true);
@@ -1488,14 +1491,26 @@
         std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(),
                        std::back_inserter(pixelFormats),
                        [](const auto& val) { return static_cast<int32_t>(val); });
-        std::vector<int32_t> dataspaces;
-        dataspaces.reserve(combination.dataspaces.size());
-        std::transform(combination.dataspaces.cbegin(), combination.dataspaces.cend(),
-                       std::back_inserter(dataspaces),
+        std::vector<int32_t> standards;
+        standards.reserve(combination.standards.size());
+        std::transform(combination.standards.cbegin(), combination.standards.cend(),
+                       std::back_inserter(standards),
+                       [](const auto& val) { return static_cast<int32_t>(val); });
+        std::vector<int32_t> transfers;
+        transfers.reserve(combination.transfers.size());
+        std::transform(combination.transfers.cbegin(), combination.transfers.cend(),
+                       std::back_inserter(transfers),
+                       [](const auto& val) { return static_cast<int32_t>(val); });
+        std::vector<int32_t> ranges;
+        ranges.reserve(combination.ranges.size());
+        std::transform(combination.ranges.cbegin(), combination.ranges.cend(),
+                       std::back_inserter(ranges),
                        [](const auto& val) { return static_cast<int32_t>(val); });
         gui::OverlayProperties::SupportedBufferCombinations outCombination;
         outCombination.pixelFormats = std::move(pixelFormats);
-        outCombination.dataspaces = std::move(dataspaces);
+        outCombination.standards = std::move(standards);
+        outCombination.transfers = std::move(transfers);
+        outCombination.ranges = std::move(ranges);
         outProperties->combinations.emplace_back(outCombination);
     }
     outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces;
@@ -2027,7 +2042,7 @@
     bool periodFlushed = false;
     mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
     if (periodFlushed) {
-        modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
+        mScheduler->modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
     }
 }
 
@@ -2105,7 +2120,7 @@
     const auto& schedule = mScheduler->getVsyncSchedule();
 
     const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(frameTime);
-    if (mVsyncModulator->getVsyncConfig().sfOffset > 0) {
+    if (mScheduler->vsyncModulator().getVsyncConfig().sfOffset > 0) {
         return vsyncDeadline;
     }
 
@@ -2219,17 +2234,19 @@
     mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
             activeDisplay->getPowerMode() == hal::PowerMode::ON;
     if (mPowerHintSessionEnabled) {
-        const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
-        const Period vsyncPeriod = Period::fromNs(display->getActiveMode().fps.getPeriodNsecs());
         mPowerAdvisor->setCommitStart(frameTime);
         mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
 
         // Frame delay is how long we should have minus how long we actually have.
-        const Duration idealSfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration;
+        const Duration idealSfWorkDuration =
+                mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration;
         const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime);
 
         mPowerAdvisor->setFrameDelay(frameDelay);
         mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
+
+        const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+        const Period vsyncPeriod = display->getActiveMode().fps.getPeriod();
         mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
 
         // Send early hint here to make sure there's not another frame pending
@@ -2254,9 +2271,17 @@
 
         bool needsTraversal = false;
         if (clearTransactionFlags(eTransactionFlushNeeded)) {
+            // Locking:
+            // 1. to prevent onHandleDestroyed from being called while the state lock is held,
+            // we must keep a copy of the transactions (specifically the composer
+            // states) around outside the scope of the lock.
+            // 2. Transactions and created layers do not share a lock. To prevent applying
+            // transactions with layers still in the createdLayer queue, flush the transactions
+            // before committing the created layers.
+            std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
             needsTraversal |= commitMirrorDisplays(vsyncId);
             needsTraversal |= commitCreatedLayers(vsyncId);
-            needsTraversal |= flushTransactionQueues(vsyncId);
+            needsTraversal |= applyTransactions(transactions, vsyncId);
         }
 
         const bool shouldCommit =
@@ -2432,7 +2457,7 @@
         scheduleComposite(FrameHint::kNone);
     }
 
-    postComposition();
+    postComposition(presentTime);
 
     const bool prevFrameHadClientComposition = mHadClientComposition;
 
@@ -2456,7 +2481,7 @@
 
     // TODO: b/160583065 Enable skip validation when SF caches all client composition layers
     const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition;
-    modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
+    mScheduler->modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
 
     mLayersWithQueuedFrames.clear();
     if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
@@ -2464,7 +2489,7 @@
         mLayerTracing.notify(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
     }
 
-    mVisibleRegionsWereDirtyThisFrame = mVisibleRegionsDirty; // Cache value for use in post-comp
+    if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
     mVisibleRegionsDirty = false;
 
     if (mCompositionEngine->needsAnotherUpdate()) {
@@ -2491,22 +2516,33 @@
     mLayersPendingRefresh.clear();
 }
 
-bool SurfaceFlinger::isHdrLayer(Layer* layer) const {
-    // Treat all layers as non-HDR if:
-    // 1. They do not have a valid HDR dataspace. Currently we treat those as PQ or HLG. and
-    // 2. The layer is allowed to be dimmed. WindowManager may disable dimming in order to
-    // keep animations invoking SDR screenshots of HDR layers seamless. Treat such tagged
-    // layers as HDR so that DisplayManagerService does not try to change the screen brightness
-    if (!isHdrDataspace(layer->getDataSpace()) && layer->isDimmingEnabled()) {
-        return false;
-    }
+bool SurfaceFlinger::isHdrLayer(const frontend::LayerSnapshot& snapshot) const {
+    // Even though the camera layer may be using an HDR transfer function or otherwise be "HDR"
+    // the device may need to avoid boosting the brightness as a result of these layers to
+    // reduce power consumption during camera recording
     if (mIgnoreHdrCameraLayers) {
-        auto buffer = layer->getBuffer();
-        if (buffer && (buffer->getUsage() & GRALLOC_USAGE_HW_CAMERA_WRITE) != 0) {
+        if (snapshot.externalTexture &&
+            (snapshot.externalTexture->getUsage() & GRALLOC_USAGE_HW_CAMERA_WRITE) != 0) {
             return false;
         }
     }
-    return true;
+    if (isHdrDataspace(snapshot.dataspace)) {
+        return true;
+    }
+    // If the layer is not allowed to be dimmed, treat it as HDR. WindowManager may disable
+    // dimming in order to keep animations invoking SDR screenshots of HDR layers seamless.
+    // Treat such tagged layers as HDR so that DisplayManagerService does not try to change
+    // the screen brightness
+    if (!snapshot.dimmingEnabled) {
+        return true;
+    }
+    // RANGE_EXTENDED layers may identify themselves as being "HDR" via a desired sdr/hdr ratio
+    if ((snapshot.dataspace & (int32_t)Dataspace::RANGE_MASK) ==
+                (int32_t)Dataspace::RANGE_EXTENDED &&
+        snapshot.desiredSdrHdrRatio > 1.01f) {
+        return true;
+    }
+    return false;
 }
 
 ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId,
@@ -2546,7 +2582,7 @@
     return ui::ROTATION_0;
 }
 
-void SurfaceFlinger::postComposition() {
+void SurfaceFlinger::postComposition(nsecs_t callTime) {
     ATRACE_CALL();
     ALOGV(__func__);
 
@@ -2625,18 +2661,20 @@
         mAddingHDRLayerInfoListener = false;
     }
 
-    if (haveNewListeners || mSomeDataspaceChanged || mVisibleRegionsWereDirtyThisFrame) {
+    if (haveNewListeners || mHdrLayerInfoChanged) {
         for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
             HdrLayerInfoReporter::HdrLayerInfo info;
             int32_t maxArea = 0;
             mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
                 const auto layerFe = layer->getCompositionEngineLayerFE();
-                if (layer->isVisible() &&
-                    compositionDisplay->includesLayer(layer->getOutputFilter())) {
-                    if (isHdrLayer(layer)) {
+                const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
+                if (snapshot.isVisible &&
+                    compositionDisplay->includesLayer(snapshot.outputFilter)) {
+                    if (isHdrLayer(snapshot)) {
                         const auto* outputLayer =
                             compositionDisplay->getOutputLayerForLayer(layerFe);
                         if (outputLayer) {
+                            info.mergeDesiredRatio(snapshot.desiredSdrHdrRatio);
                             info.numberOfHdrLayers++;
                             const auto displayFrame = outputLayer->getState().displayFrame;
                             const int32_t area = displayFrame.width() * displayFrame.height();
@@ -2653,8 +2691,7 @@
         }
     }
 
-    mSomeDataspaceChanged = false;
-    mVisibleRegionsWereDirtyThisFrame = false;
+    mHdrLayerInfoChanged = false;
 
     mTransactionCallbackInvoker.addPresentFence(std::move(presentFence));
     mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
@@ -2712,6 +2749,17 @@
         }
     }
 
+    if (mNumTrustedPresentationListeners > 0) {
+        // We avoid any reverse traversal upwards so this shouldn't be too expensive
+        mDrawingState.traverse([&](Layer* layer) {
+            if (!layer->hasTrustedPresentationListener()) {
+                return;
+            }
+            layer->updateTrustedPresentationState(display, nanoseconds_to_milliseconds(callTime),
+                                                  false);
+        });
+    }
+
     // 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()) {
@@ -2768,7 +2816,7 @@
     // so we can call commitTransactionsLocked unconditionally.
     // We clear the flags with mStateLock held to guarantee that
     // mCurrentState won't change until the transaction is committed.
-    modulateVsync(&VsyncModulator::onTransactionCommit);
+    mScheduler->modulateVsync(&VsyncModulator::onTransactionCommit);
     commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
 
     mDebugInTransaction = 0;
@@ -3569,19 +3617,18 @@
 }
 
 void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
+    using namespace scheduler;
+
     LOG_ALWAYS_FATAL_IF(mScheduler);
 
     const auto activeMode = display->refreshRateSelector().getActiveMode();
     const Fps activeRefreshRate = activeMode.fps;
     mRefreshRateStats =
-            std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate,
-                                                          hal::PowerMode::OFF);
+            std::make_unique<RefreshRateStats>(*mTimeStats, activeRefreshRate, hal::PowerMode::OFF);
 
     mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate);
-    mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
 
-    using Feature = scheduler::Feature;
-    scheduler::FeatureFlags features;
+    FeatureFlags features;
 
     if (sysprop::use_content_detection_for_refresh_rate(false)) {
         features |= Feature::kContentDetection;
@@ -3597,9 +3644,11 @@
         features |= Feature::kKernelIdleTimer;
     }
 
-    mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
-                                                        static_cast<ISchedulerCallback&>(*this),
-                                                        features);
+    auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
+
+    mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
+                                             static_cast<ISchedulerCallback&>(*this), features,
+                                             std::move(modulatorPtr));
     mScheduler->createVsyncSchedule(features);
     mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
 
@@ -3607,15 +3656,17 @@
     mScheduler->startTimers();
 
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
-    const nsecs_t vsyncPeriod = activeRefreshRate.getPeriodNsecs();
+
     mAppConnectionHandle =
-            mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
-                                         /*workDuration=*/configs.late.appWorkDuration,
-                                         /*readyDuration=*/configs.late.sfWorkDuration);
+            mScheduler->createEventThread(Scheduler::Cycle::Render,
+                                          mFrameTimeline->getTokenManager(),
+                                          /* workDuration */ configs.late.appWorkDuration,
+                                          /* readyDuration */ configs.late.sfWorkDuration);
     mSfConnectionHandle =
-            mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
-                                         /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
-                                         /*readyDuration=*/configs.late.sfWorkDuration);
+            mScheduler->createEventThread(Scheduler::Cycle::LastComposite,
+                                          mFrameTimeline->getTokenManager(),
+                                          /* workDuration */ activeRefreshRate.getPeriod(),
+                                          /* readyDuration */ configs.late.sfWorkDuration);
 
     mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
                           *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
@@ -3626,21 +3677,10 @@
     mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
 }
 
-void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
+void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) {
     mVsyncConfiguration->setRefreshRateFps(refreshRate);
-    setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
-                   refreshRate.getPeriodNsecs());
-}
-
-void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
-                                    nsecs_t vsyncPeriod) {
-    mScheduler->setDuration(mAppConnectionHandle,
-                            /*workDuration=*/config.appWorkDuration,
-                            /*readyDuration=*/config.sfWorkDuration);
-    mScheduler->setDuration(mSfConnectionHandle,
-                            /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
-                            /*readyDuration=*/config.sfWorkDuration);
-    mScheduler->setDuration(config.sfWorkDuration);
+    mScheduler->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs(),
+                                  refreshRate.getPeriod());
 }
 
 void SurfaceFlinger::doCommitTransactions() {
@@ -3839,10 +3879,14 @@
 
 void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
                                          const sp<IBinder>& applyToken, FrameHint frameHint) {
-    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
+    mScheduler->modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
 
     if (const bool scheduled = mTransactionFlags.fetch_or(mask) & mask; !scheduled) {
         scheduleCommit(frameHint);
+    } else if (frameHint == FrameHint::kActive) {
+        // Even if the next frame is already scheduled, we should reset the idle timer
+        // as a new activity just happened.
+        mScheduler->resetIdleTimer();
     }
 }
 
@@ -3952,19 +3996,20 @@
             std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
 }
 
+// For tests only
 bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
-    // to prevent onHandleDestroyed from being called while the lock is held,
-    // we must keep a copy of the transactions (specifically the composer
-    // states) around outside the scope of the lock
     std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
-    {
-        Mutex::Autolock _l(mStateLock);
-        return applyTransactions(transactions, vsyncId);
-    }
+    return applyTransactions(transactions, vsyncId);
 }
 
 bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
                                        VsyncId vsyncId) {
+    Mutex::Autolock _l(mStateLock);
+    return applyTransactionsLocked(transactions, vsyncId);
+}
+
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions,
+                                             VsyncId vsyncId) {
     bool needsTraversal = false;
     // Now apply all transactions.
     for (auto& transaction : transactions) {
@@ -3973,7 +4018,7 @@
                                       transaction.displays, transaction.flags,
                                       transaction.inputWindowCommands,
                                       transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                      transaction.buffer, transaction.postTime,
+                                      std::move(transaction.uncacheBufferIds), transaction.postTime,
                                       transaction.permissions, transaction.hasListenerCallbacks,
                                       transaction.listenerCallbacks, transaction.originPid,
                                       transaction.originUid, transaction.id);
@@ -3999,7 +4044,7 @@
     const auto predictedPresentTime = TimePoint::fromNs(prediction->presentTime);
 
     // The duration for which SF can delay a frame if it is considered early based on the
-    // VsyncModulator::VsyncConfig::appWorkDuration.
+    // VsyncConfig::appWorkDuration.
     if (constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
         std::chrono::abs(predictedPresentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold) {
         return false;
@@ -4040,7 +4085,7 @@
         // We don't want to latch unsignaled if are in early / client composition
         // as it leads to jank due to RenderEngine waiting for unsignaled buffer
         // or window animations being slow.
-        const auto isDefaultVsyncConfig = mVsyncModulator->isVsyncConfigDefault();
+        const auto isDefaultVsyncConfig = mScheduler->vsyncModulator().isVsyncConfigDefault();
         if (!isDefaultVsyncConfig) {
             ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; !isDefaultVsyncConfig)",
                   __func__);
@@ -4061,8 +4106,9 @@
         const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
         const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-        const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
+        bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+        uint64_t transactionId) {
     ATRACE_CALL();
 
     uint32_t permissions =
@@ -4096,6 +4142,15 @@
     const int originPid = ipc->getCallingPid();
     const int originUid = ipc->getCallingUid();
 
+    std::vector<uint64_t> uncacheBufferIds;
+    uncacheBufferIds.reserve(uncacheBuffers.size());
+    for (const auto& uncacheBuffer : uncacheBuffers) {
+        sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
+        if (buffer != nullptr) {
+            uncacheBufferIds.push_back(buffer->getId());
+        }
+    }
+
     std::vector<ResolvedComposerState> resolvedStates;
     resolvedStates.reserve(states.size());
     for (auto& state : states) {
@@ -4113,14 +4168,22 @@
         }
     }
 
-    TransactionState state{frameTimelineInfo,  resolvedStates,
-                           displays,           flags,
-                           applyToken,         inputWindowCommands,
-                           desiredPresentTime, isAutoTimestamp,
-                           uncacheBuffer,      postTime,
-                           permissions,        hasListenerCallbacks,
-                           listenerCallbacks,  originPid,
-                           originUid,          transactionId};
+    TransactionState state{frameTimelineInfo,
+                           resolvedStates,
+                           displays,
+                           flags,
+                           applyToken,
+                           inputWindowCommands,
+                           desiredPresentTime,
+                           isAutoTimestamp,
+                           std::move(uncacheBufferIds),
+                           postTime,
+                           permissions,
+                           hasListenerCallbacks,
+                           listenerCallbacks,
+                           originPid,
+                           originUid,
+                           transactionId};
 
     if (mTransactionTracing) {
         mTransactionTracing->addQueuedTransaction(state);
@@ -4144,7 +4207,7 @@
                                            Vector<DisplayState>& displays, uint32_t flags,
                                            const InputWindowCommands& inputWindowCommands,
                                            const int64_t desiredPresentTime, bool isAutoTimestamp,
-                                           const client_cache_t& uncacheBuffer,
+                                           const std::vector<uint64_t>& uncacheBufferIds,
                                            const int64_t postTime, uint32_t permissions,
                                            bool hasListenerCallbacks,
                                            const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -4185,11 +4248,8 @@
         ALOGE("Only privileged callers are allowed to send input commands.");
     }
 
-    if (uncacheBuffer.isValid()) {
-        sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
-        if (buffer != nullptr) {
-            mBufferIdsToUncache.push_back(buffer->getId());
-        }
+    for (uint64_t uncacheBufferId : uncacheBufferIds) {
+        mBufferIdsToUncache.push_back(uncacheBufferId);
     }
 
     // If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -4450,9 +4510,6 @@
     if (what & layer_state_t::eDataspaceChanged) {
         if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eHdrMetadataChanged) {
-        if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
-    }
     if (what & layer_state_t::eSurfaceDamageRegionChanged) {
         if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded;
     }
@@ -4526,6 +4583,14 @@
     if (what & layer_state_t::eDimmingEnabledChanged) {
         if (layer->setDimmingEnabled(s.dimmingEnabled)) flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eExtendedRangeBrightnessChanged) {
+        if (layer->setExtendedRangeBrightness(s.currentSdrHdrRatio, s.desiredSdrHdrRatio)) {
+            flags |= eTraversalNeeded;
+        }
+    }
+    if (what & layer_state_t::eHdrMetadataChanged) {
+        if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eTrustedOverlayChanged) {
         if (layer->setTrustedOverlay(s.isTrustedOverlay)) {
             flags |= eTraversalNeeded;
@@ -4587,6 +4652,11 @@
         layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
     }
 
+    if (what & layer_state_t::eTrustedPresentationInfoChanged) {
+        layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
+                                          s.trustedPresentationListener);
+    }
+
     if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
     // Do not put anything that updates layer state or modifies flags after
     // setTransactionCompletedListener
@@ -6350,8 +6420,9 @@
     auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
         traverseLayersInLayerStack(layerStack, args.uid, visitor);
     };
+    auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
 
-    auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+    auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
                                       args.pixelFormat, args.allowProtected, args.grayscale,
                                       captureListener);
     return fenceStatus(future.get());
@@ -6386,6 +6457,7 @@
     auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
         traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
     };
+    auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
 
     if (captureListener == nullptr) {
         ALOGE("capture screen must provide a capture listener callback");
@@ -6395,7 +6467,7 @@
     constexpr bool kAllowProtected = false;
     constexpr bool kGrayscale = false;
 
-    auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+    auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size,
                                       ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale,
                                       captureListener);
     return fenceStatus(future.get());
@@ -6413,7 +6485,7 @@
     ui::Size reqSize;
     sp<Layer> parent;
     Rect crop(args.sourceCrop);
-    std::unordered_set<sp<Layer>, SpHash<Layer>> excludeLayers;
+    std::unordered_set<uint32_t> excludeLayerIds;
     ui::Dataspace dataspace;
 
     // Call this before holding mStateLock to avoid any deadlocking.
@@ -6453,9 +6525,9 @@
         reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
 
         for (const auto& handle : args.excludeHandles) {
-            sp<Layer> excludeLayer = LayerHandle::getLayer(handle);
-            if (excludeLayer != nullptr) {
-                excludeLayers.emplace(excludeLayer);
+            uint32_t excludeLayer = LayerHandle::getLayerId(handle);
+            if (excludeLayer != UNASSIGNED_LAYER_ID) {
+                excludeLayerIds.emplace(excludeLayer);
             } else {
                 ALOGW("Invalid layer handle passed as excludeLayer to captureLayers");
                 return NAME_NOT_FOUND;
@@ -6484,7 +6556,7 @@
                                                  args.captureSecureLayers);
     });
 
-    auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
+    auto traverseLayers = [parent, args, excludeLayerIds](const LayerVector::Visitor& visitor) {
         parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
             if (!layer->isVisible()) {
                 return;
@@ -6496,7 +6568,7 @@
 
             auto p = sp<Layer>::fromExisting(layer);
             while (p != nullptr) {
-                if (excludeLayers.count(p) != 0) {
+                if (excludeLayerIds.count(p->sequence) != 0) {
                     return;
                 }
                 p = p->getParent();
@@ -6505,20 +6577,21 @@
             visitor(layer);
         });
     };
+    auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
 
     if (captureListener == nullptr) {
         ALOGE("capture screen must provide a capture listener callback");
         return BAD_VALUE;
     }
 
-    auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+    auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
                                       args.pixelFormat, args.allowProtected, args.grayscale,
                                       captureListener);
     return fenceStatus(future.get());
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
-        RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+        RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
         ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale,
         const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
@@ -6537,15 +6610,18 @@
     const bool supportsProtected = getRenderEngine().supportsProtectedContent();
     bool hasProtectedLayer = false;
     if (allowProtected && supportsProtected) {
-        auto future = mScheduler->schedule([=]() {
-            bool protectedLayerFound = false;
-            traverseLayers([&](Layer* layer) {
-                protectedLayerFound =
-                        protectedLayerFound || (layer->isVisible() && layer->isProtected());
-            });
-            return protectedLayerFound;
-        });
-        hasProtectedLayer = future.get();
+        hasProtectedLayer = mScheduler
+                                    ->schedule([=]() {
+                                        bool protectedLayerFound = false;
+                                        auto layers = getLayerSnapshots();
+                                        for (auto& [layer, layerFe] : layers) {
+                                            protectedLayerFound |=
+                                                    (layerFe->mSnapshot->isVisible &&
+                                                     layerFe->mSnapshot->hasProtectedContent);
+                                        }
+                                        return protectedLayerFound;
+                                    })
+                                    .get();
     }
 
     const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
@@ -6570,12 +6646,12 @@
             renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          WRITEABLE);
-    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, texture,
+    return captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture,
                                false /* regionSampling */, grayscale, captureListener);
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
-        RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+        RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
         bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
@@ -6598,7 +6674,7 @@
 
                 ftl::SharedFuture<FenceResult> renderFuture;
                 renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
-                    renderFuture = renderScreenImpl(renderArea, traverseLayers, buffer,
+                    renderFuture = renderScreenImpl(renderArea, getLayerSnapshots, buffer,
                                                     canCaptureBlackoutContent, regionSampling,
                                                     grayscale, captureResults);
                 });
@@ -6626,18 +6702,26 @@
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
-        std::shared_ptr<const RenderArea> renderArea, TraverseLayersFunction traverseLayers,
+        std::shared_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer,
         bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
         ScreenCaptureResults& captureResults) {
     ATRACE_CALL();
 
-    size_t layerCount = 0;
-    traverseLayers([&](Layer* layer) {
-        layerCount++;
-        captureResults.capturedSecureLayers =
-                captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
-    });
+    const auto& display = renderArea->getDisplayDevice();
+    const auto& transform = renderArea->getTransform();
+    std::unordered_set<compositionengine::LayerFE*> filterForScreenshot;
+    auto layers = getLayerSnapshots();
+    for (auto& [layer, layerFE] : layers) {
+        frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
+        captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
+        captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
+        layerFE->mSnapshot->geomLayerTransform =
+                renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
+        if (layer->needsFilteringForScreenshots(display.get(), transform)) {
+            filterForScreenshot.insert(layerFE.get());
+        }
+    }
 
     // We allow the system server to take screenshots of secure layers for
     // use in situations like the Screen-rotation animation and place
@@ -6671,31 +6755,6 @@
     }
     captureResults.capturedDataspace = dataspace;
 
-    const auto transform = renderArea->getTransform();
-    const auto display = renderArea->getDisplayDevice();
-
-    std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
-    layers.reserve(layerCount);
-    std::unordered_set<compositionengine::LayerFE*> filterForScreenshot;
-    traverseLayers([&](Layer* layer) {
-        captureResults.capturedHdrLayers |= isHdrLayer(layer);
-        // Layer::prepareClientComposition uses the layer's snapshot to populate the resulting
-        // LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are
-        // generated with the layer's current buffer and geometry.
-        layer->updateSnapshot(true /* updateGeometry */);
-
-        layers.emplace_back(layer, layer->copyCompositionEngineLayerFE());
-
-        sp<LayerFE>& layerFE = layers.back().second;
-
-        layerFE->mSnapshot->geomLayerTransform =
-                renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
-
-        if (layer->needsFilteringForScreenshots(display.get(), transform)) {
-            filterForScreenshot.insert(layerFE.get());
-        }
-    });
-
     ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
     if (!layers.empty()) {
         const sp<LayerFE>& layerFE = layers.back().second;
@@ -8112,6 +8171,10 @@
     return OK;
 }
 
+void SurfaceFlinger::forceFutureUpdate(int delayInMs) {
+    static_cast<void>(mScheduler->scheduleDelayed([&]() { scheduleRepaint(); }, ms2ns(delayInMs)));
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5457be8..207dfe2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -56,6 +56,7 @@
 #include <scheduler/Fps.h>
 #include <scheduler/PresentLatencyTracker.h>
 #include <scheduler/Time.h>
+#include <scheduler/TransactionSchedule.h>
 #include <ui/FenceResult.h>
 
 #include "Display/DisplayMap.h"
@@ -68,12 +69,12 @@
 #include "FlagManager.h"
 #include "FrontEnd/DisplayInfo.h"
 #include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerSnapshot.h"
 #include "FrontEnd/TransactionHandler.h"
 #include "LayerVector.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/RefreshRateStats.h"
 #include "Scheduler/Scheduler.h"
-#include "Scheduler/VsyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "ThreadContext.h"
 #include "Tracing/LayerTracing.h"
@@ -313,6 +314,8 @@
     // TODO(b/246793311): Clean up a temporary property
     bool mIgnoreHwcPhysicalDisplayOrientation = false;
 
+    void forceFutureUpdate(int delayInMs);
+
 protected:
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
@@ -344,15 +347,15 @@
     friend class LayerRenderArea;
     friend class LayerTracing;
     friend class SurfaceComposerAIDL;
+    friend class DisplayRenderArea;
 
     // For unit tests
     friend class TestableSurfaceFlinger;
     friend class TransactionApplicationTest;
     friend class TunnelModeEnabledReporterTest;
 
-    using VsyncModulator = scheduler::VsyncModulator;
     using TransactionSchedule = scheduler::TransactionSchedule;
-    using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+    using GetLayerSnapshotsFunction = std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>;
     using RenderAreaFuture = ftl::Future<std::unique_ptr<RenderArea>>;
     using DumpArgs = Vector<String16>;
     using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
@@ -468,14 +471,6 @@
         return std::bind(dump, this, _1, _2, _3);
     }
 
-    template <typename... Args,
-              typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
-    void modulateVsync(Handler handler, Args... args) {
-        if (const auto config = (*mVsyncModulator.*handler)(args...)) {
-            setVsyncConfig(*config, mScheduler->getLeaderVsyncPeriod());
-        }
-    }
-
     // Maximum allowed number of display frames that can be set through backdoor
     static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
 
@@ -501,7 +496,8 @@
                                  uint32_t flags, const sp<IBinder>& applyToken,
                                  const InputWindowCommands& inputWindowCommands,
                                  int64_t desiredPresentTime, bool isAutoTimestamp,
-                                 const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+                                 const std::vector<client_cache_t>& uncacheBuffers,
+                                 bool hasListenerCallbacks,
                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
                                  uint64_t transactionId) override;
     void bootFinished();
@@ -707,25 +703,25 @@
     void updateCursorAsync();
 
     void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
-    void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
-    void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
-
+    void updatePhaseConfiguration(Fps) REQUIRES(mStateLock);
 
     /*
      * Transactions
      */
-    bool applyTransactionState(const FrameTimelineInfo& info,
-                               std::vector<ResolvedComposerState>& state,
-                               Vector<DisplayState>& displays, uint32_t flags,
-                               const InputWindowCommands& inputWindowCommands,
-                               const int64_t desiredPresentTime, bool isAutoTimestamp,
-                               const client_cache_t& uncacheBuffer, const int64_t postTime,
-                               uint32_t permissions, bool hasListenerCallbacks,
-                               const std::vector<ListenerCallbacks>& listenerCallbacks,
-                               int originPid, int originUid, uint64_t transactionId)
-            REQUIRES(mStateLock);
+    bool applyTransactionState(
+            const FrameTimelineInfo& info, std::vector<ResolvedComposerState>& state,
+            Vector<DisplayState>& displays, uint32_t flags,
+            const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+            bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds,
+            const int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
+            const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
+            uint64_t transactionId) REQUIRES(mStateLock);
     // Flush pending transactions that were presented after desiredPresentTime.
+    // For test only
     bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
+
+    bool applyTransactions(std::vector<TransactionState>&, VsyncId) REQUIRES(kMainThreadContext);
+
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
     void addTransactionReadyFilters();
@@ -756,7 +752,7 @@
     static LatchUnsignaledConfig getLatchUnsignaledConfig();
     bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
                                bool firstTransaction) const;
-    bool applyTransactions(std::vector<TransactionState>& transactions, VsyncId)
+    bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
@@ -793,16 +789,16 @@
     // Boot animation, on/off animations and screen capture
     void startBootAnim();
 
-    ftl::SharedFuture<FenceResult> captureScreenCommon(RenderAreaFuture, TraverseLayersFunction,
+    ftl::SharedFuture<FenceResult> captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction,
                                                        ui::Size bufferSize, ui::PixelFormat,
                                                        bool allowProtected, bool grayscale,
                                                        const sp<IScreenCaptureListener>&);
     ftl::SharedFuture<FenceResult> captureScreenCommon(
-            RenderAreaFuture, TraverseLayersFunction,
+            RenderAreaFuture, GetLayerSnapshotsFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
             bool grayscale, const sp<IScreenCaptureListener>&);
     ftl::SharedFuture<FenceResult> renderScreenImpl(
-            std::shared_ptr<const RenderArea>, TraverseLayersFunction,
+            std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
             bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock)
             REQUIRES(kMainThreadContext);
@@ -923,7 +919,7 @@
     /*
      * Compositing
      */
-    void postComposition() REQUIRES(kMainThreadContext);
+    void postComposition(nsecs_t callTime) REQUIRES(kMainThreadContext);
 
     /*
      * Display management
@@ -1091,7 +1087,7 @@
     void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
             REQUIRES(mStateLock, kMainThreadContext);
 
-    bool isHdrLayer(Layer* layer) const;
+    bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
 
     ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
             REQUIRES(mStateLock);
@@ -1139,9 +1135,8 @@
     State mDrawingState{LayerVector::StateSet::Drawing};
     bool mVisibleRegionsDirty = false;
 
-    // VisibleRegions dirty is already cleared by postComp, but we need to track it to prevent
-    // extra work in the HDR layer info listener.
-    bool mVisibleRegionsWereDirtyThisFrame = false;
+    bool mHdrLayerInfoChanged = false;
+
     // Used to ensure we omit a callback when HDR layer info listener is newly added but the
     // scene hasn't changed
     bool mAddingHDRLayerInfoListener = false;
@@ -1152,7 +1147,6 @@
     // TODO: Also move visibleRegions over to a boolean system.
     bool mUpdateInputInfo = false;
     bool mSomeChildrenChanged;
-    bool mSomeDataspaceChanged = false;
     bool mForceTransactionDisplayChange = false;
 
     // Set if LayerMetadata has changed since the last LayerMetadata snapshot.
@@ -1271,6 +1265,8 @@
     float mDimmingRatio = -1.f;
 
     std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    std::atomic<int> mNumTrustedPresentationListeners = 0;
+
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
     // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
     // any mutex.
@@ -1288,9 +1284,6 @@
     // Stores phase offsets configured per refresh rate.
     std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
 
-    // Optional to defer construction until PhaseConfiguration is created.
-    sp<VsyncModulator> mVsyncModulator;
-
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
     scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
 
@@ -1339,6 +1332,7 @@
 
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
             GUARDED_BY(mStateLock);
+
     mutable std::mutex mCreatedLayersLock;
     struct LayerCreatedState {
         LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot)
@@ -1494,6 +1488,7 @@
     binder::Status removeHdrLayerInfoListener(
             const sp<IBinder>& displayToken,
             const sp<gui::IHdrLayerInfoListener>& listener) override;
+
     binder::Status notifyPowerBoost(int boostId) override;
     binder::Status setGlobalShadowSettings(const gui::Color& ambientColor,
                                            const gui::Color& spotColor, float lightPosY,
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 366b09d..5025c49 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -43,7 +43,7 @@
                      const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
                      const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
                      int64_t desiredPresentTime, bool isAutoTimestamp,
-                     const client_cache_t& uncacheBuffer, int64_t postTime, uint32_t permissions,
+                     std::vector<uint64_t> uncacheBufferIds, int64_t postTime, uint32_t permissions,
                      bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
                      int originPid, int originUid, uint64_t transactionId)
           : frameTimelineInfo(frameTimelineInfo),
@@ -54,7 +54,7 @@
             inputWindowCommands(inputWindowCommands),
             desiredPresentTime(desiredPresentTime),
             isAutoTimestamp(isAutoTimestamp),
-            buffer(uncacheBuffer),
+            uncacheBufferIds(std::move(uncacheBufferIds)),
             postTime(postTime),
             permissions(permissions),
             hasListenerCallbacks(hasListenerCallbacks),
@@ -109,7 +109,7 @@
     InputWindowCommands inputWindowCommands;
     int64_t desiredPresentTime;
     bool isAutoTimestamp;
-    client_cache_t buffer;
+    std::vector<uint64_t> uncacheBufferIds;
     int64_t postTime;
     uint32_t permissions;
     bool hasListenerCallbacks;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 81ca659..cdffbb4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -157,6 +157,10 @@
     return TimePoint::fromNs(fdp.ConsumeIntegral<nsecs_t>());
 }
 
+inline Duration getFuzzedDuration(FuzzedDataProvider& fdp) {
+    return Duration::fromNs(fdp.ConsumeIntegral<nsecs_t>());
+}
+
 inline FloatRect getFuzzedFloatRect(FuzzedDataProvider* fdp) {
     return FloatRect(fdp->ConsumeFloatingPoint<float>() /*left*/,
                      fdp->ConsumeFloatingPoint<float>() /*right*/,
@@ -196,9 +200,11 @@
     static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
     static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
 
-    VsyncConfigSet getConfigsForRefreshRate(Fps) const override { return getCurrentConfigs(); }
+    scheduler::VsyncConfigSet getConfigsForRefreshRate(Fps) const override {
+        return getCurrentConfigs();
+    }
 
-    VsyncConfigSet getCurrentConfigs() const override {
+    scheduler::VsyncConfigSet getCurrentConfigs() const override {
         return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
                  FAKE_DURATION_OFFSET_NS},
                 {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
@@ -218,16 +224,16 @@
 class TestableScheduler : public Scheduler, private ICompositor {
 public:
     TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
-                      ISchedulerCallback& callback)
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
                               std::make_unique<android::mock::VSyncTracker>(), selectorPtr,
-                              callback) {}
+                              std::move(modulatorPtr), callback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::unique_ptr<VSyncTracker> tracker,
                       std::shared_ptr<RefreshRateSelector> selectorPtr,
-                      ISchedulerCallback& callback)
-          : Scheduler(*this, callback, Feature::kContentDetection) {
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
+          : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
 
         const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
@@ -275,6 +281,8 @@
         return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
+    using Scheduler::setVsyncConfig;
+
 private:
     // ICompositor overrides:
     void configure() override {}
@@ -510,11 +518,6 @@
         mFlinger->getDesiredDisplayModeSpecs(display, &_);
     }
 
-    void setVsyncConfig(FuzzedDataProvider *fdp) {
-        const scheduler::VsyncModulator::VsyncConfig vsyncConfig{};
-        mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
-    }
-
     // TODO(b/248317436): extend to cover all displays for multi-display devices
     static std::optional<PhysicalDisplayId> getFirstDisplayId() {
         std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
@@ -592,14 +595,18 @@
         mFlinger->updateInputFlinger();
         mFlinger->updateCursorAsync();
 
-        setVsyncConfig(&mFdp);
+        mutableScheduler().setVsyncConfig({.sfOffset = mFdp.ConsumeIntegral<nsecs_t>(),
+                                           .appOffset = mFdp.ConsumeIntegral<nsecs_t>(),
+                                           .sfWorkDuration = getFuzzedDuration(mFdp),
+                                           .appWorkDuration = getFuzzedDuration(mFdp)},
+                                          getFuzzedDuration(mFdp));
 
         {
             ftl::FakeGuard guard(kMainThreadContext);
 
             mFlinger->commitTransactions();
             mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
-            mFlinger->postComposition();
+            mFlinger->postComposition(systemTime());
         }
 
         mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -658,15 +665,17 @@
         mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
         const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps();
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
-        mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
-                mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
                                                               hal::PowerMode::OFF);
 
+        auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
+                mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
         mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
                                                       std::move(vsyncTracker), mRefreshRateSelector,
-                                                      *(callback ?: this));
+                                                      std::move(modulatorPtr), *(callback ?: this));
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -730,17 +739,18 @@
         return mFlinger->mTransactionHandler.mPendingTransactionQueues;
     }
 
-    auto setTransactionState(const FrameTimelineInfo &frameTimelineInfo,
-                             Vector<ComposerState> &states, const Vector<DisplayState> &displays,
-                             uint32_t flags, const sp<IBinder> &applyToken,
-                             const InputWindowCommands &inputWindowCommands,
+    auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+                             Vector<ComposerState>& states, const Vector<DisplayState>& displays,
+                             uint32_t flags, const sp<IBinder>& applyToken,
+                             const InputWindowCommands& inputWindowCommands,
                              int64_t desiredPresentTime, bool isAutoTimestamp,
-                             const client_cache_t &uncacheBuffer, bool hasListenerCallbacks,
-                             std::vector<ListenerCallbacks> &listenerCallbacks,
+                             const std::vector<client_cache_t>& uncacheBuffers,
+                             bool hasListenerCallbacks,
+                             std::vector<ListenerCallbacks>& listenerCallbacks,
                              uint64_t transactionId) {
         return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
                                              inputWindowCommands, desiredPresentTime,
-                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+                                             isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
                                              listenerCallbacks, transactionId);
     }
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 0af3efa..44805db 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -280,17 +280,14 @@
     };
     using Schedule = scheduler::TransactionSchedule;
     using nanos = std::chrono::nanoseconds;
-    using VsyncModulator = scheduler::VsyncModulator;
     using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator;
-    const VsyncModulator::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
-                                            nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
-    const VsyncModulator::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
-                                               nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
-    const VsyncModulator::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE,
-                                           nanos(SF_DURATION_EARLY_GPU),
-                                           nanos(APP_DURATION_EARLY_GPU)};
-    const VsyncModulator::VsyncConfigSet offsets = {early, earlyGpu, late,
-                                                    nanos(HWC_MIN_WORK_DURATION)};
+    const scheduler::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE),
+                                       nanos(APP_DURATION_LATE)};
+    const scheduler::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
+                                          nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
+    const scheduler::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU),
+                                      nanos(APP_DURATION_EARLY_GPU)};
+    const scheduler::VsyncConfigSet offsets = {early, earlyGpu, late, nanos(HWC_MIN_WORK_DURATION)};
     sp<FuzzImplVsyncModulator> vSyncModulator =
             sp<FuzzImplVsyncModulator>::make(offsets, scheduler::Now);
     (void)vSyncModulator->setVsyncConfigSet(offsets);
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index bd6367d..6d12aa7 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -43,6 +43,7 @@
         "LayerRenderTypeTransaction_test.cpp",
         "LayerState_test.cpp",
         "LayerTransaction_test.cpp",
+        "LayerTrustedPresentationListener_test.cpp",
         "LayerTypeAndRenderTypeTransaction_test.cpp",
         "LayerTypeTransaction_test.cpp",
         "LayerUpdate_test.cpp",
@@ -56,6 +57,7 @@
         "SetFrameRateOverride_test.cpp",
         "SetGeometry_test.cpp",
         "Stress_test.cpp",
+        "TextureFiltering_test.cpp",
         "VirtualDisplay_test.cpp",
         "WindowInfosListener_test.cpp",
     ],
diff --git a/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp b/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
new file mode 100644
index 0000000..a843fd1
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferItemConsumer.h>
+#include <ui/Transform.h>
+#include <thread>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+struct PresentationCallbackHelper {
+    void callbackArrived(bool state) {
+        std::unique_lock l(mMutex);
+        mGotCallback = true;
+        mState = state;
+        mCondition.notify_all();
+    }
+    bool awaitCallback() {
+        std::unique_lock l(mMutex);
+        mGotCallback = false;
+        mCondition.wait_for(l, 5000ms);
+        EXPECT_TRUE(mGotCallback);
+        return mState;
+    }
+
+    bool mState;
+    bool mGotCallback;
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+};
+
+TrustedPresentationThresholds thresh() {
+    TrustedPresentationThresholds thresholds;
+    thresholds.minAlpha = 1.0;
+    thresholds.minFractionRendered = 1.0;
+    thresholds.stabilityRequirementMs = 100;
+    return thresholds;
+}
+
+class LayerTrustedPresentationListenerTest : public LayerTransactionTest {
+public:
+    void SetUp() override {
+        LayerTransactionTest::SetUp();
+        mainLayer = makeLayer();
+        thresholds = thresh();
+    }
+
+    void TearDown() override {
+        LayerTransactionTest::TearDown();
+        mCallback = nullptr;
+        t.reparent(mainLayer, nullptr).apply();
+        mainLayer = nullptr;
+    }
+
+    void thresholdsPrepared() {
+        t.show(mainLayer)
+                .setLayer(mainLayer, INT32_MAX)
+                .setTrustedPresentationCallback(
+                        mainLayer,
+                        [&](void* context, bool state) {
+                            PresentationCallbackHelper* helper =
+                                    (PresentationCallbackHelper*)context;
+                            helper->callbackArrived(state);
+                        },
+                        thresholds, &pch, mCallback)
+                .setPosition(mainLayer, 100, 100)
+                .apply();
+    }
+
+    sp<SurfaceControl> makeLayer() {
+        sp<SurfaceControl> layer =
+                createLayer("test", 100, 100, ISurfaceComposerClient::eFXSurfaceBufferState,
+                            mBlackBgSurface.get());
+        fillBufferLayerColor(layer, Color::RED, 100, 100);
+        return layer;
+    }
+    sp<SurfaceControl> mainLayer;
+    PresentationCallbackHelper pch;
+    SurfaceComposerClient::Transaction t;
+    TrustedPresentationThresholds thresholds;
+    sp<SurfaceComposerClient::PresentationCallbackRAII> mCallback;
+};
+
+// The layer is fully presented with the default test setup.
+TEST_F(LayerTrustedPresentationListenerTest, callback_arrives) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+// A hidden layer can't be considered presented!
+TEST_F(LayerTrustedPresentationListenerTest, hiding_layer_clears_state) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.hide(mainLayer).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+// A fully obscured layer can't be considered presented!
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_clears_state) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+// Even if the layer obscuring us has an Alpha channel, we are still considered
+// obscured.
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_transparency_clears_state) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setFlags(otherLayer, 0, layer_state_t::eLayerOpaque)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+// We can't be presented if our alpha is below the threshold.
+TEST_F(LayerTrustedPresentationListenerTest, alpha_below_threshold) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setAlpha(mainLayer, 0.9).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setAlpha(mainLayer, 1.0).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+// Verify that the passed in threshold is actually respected!
+TEST_F(LayerTrustedPresentationListenerTest, alpha_below_other_threshold) {
+    thresholds.minAlpha = 0.8;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setAlpha(mainLayer, 0.8).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setAlpha(mainLayer, 0.9).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+// (86*86)/(100*100) = 0.73...so a crop of 86x86 is below the threshold
+// (87*87)/(100*100) = 0.76...so a crop of 87x87 is above the threshold!
+TEST_F(LayerTrustedPresentationListenerTest, crop_below_threshold) {
+    thresholds.minFractionRendered = 0.75;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setCrop(mainLayer, Rect(0, 0, 86, 86)).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setCrop(mainLayer, Rect(0, 0, 87, 87)).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, scale_below_threshold) {
+    thresholds.minFractionRendered = 0.64;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    // 0.8 = sqrt(0.64)
+    t.setMatrix(mainLayer, 0.79, 0, 0, 0.79).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setMatrix(mainLayer, 0.81, 0, 0, 0.81).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_threshold_1) {
+    thresholds.minFractionRendered = 0.75;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setMatrix(otherLayer, 0.49, 0, 0, 0.49).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setMatrix(otherLayer, 0.51, 0, 0, 0.51).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_threshold_2) {
+    thresholds.minFractionRendered = 0.9;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setMatrix(otherLayer, 0.3, 0, 0, 0.3).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setMatrix(otherLayer, 0.33, 0, 0, 0.33).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_alpha) {
+    thresholds.minFractionRendered = 0.9;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .setAlpha(otherLayer, 0.01)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setAlpha(otherLayer, 0.0).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
new file mode 100644
index 0000000..e9b1fbb
--- /dev/null
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/gui/ISurfaceComposerClient.h>
+#include <gtest/gtest.h>
+#include <gui/DisplayCaptureArgs.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+bool operator==(const Color& left, const Color& right) {
+    return left.a == right.a && left.r == right.r && left.g == right.g && left.b == right.b;
+}
+
+class TextureFilteringTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+
+        mParent = createLayer("test-parent", 100, 100,
+                              gui::ISurfaceComposerClient::eFXSurfaceContainer);
+        mLayer = createLayer("test-child", 100, 100,
+                             gui::ISurfaceComposerClient::eFXSurfaceBufferState, mParent.get());
+        sp<GraphicBuffer> buffer =
+                sp<GraphicBuffer>::make(static_cast<uint32_t>(100), static_cast<uint32_t>(100),
+                                        PIXEL_FORMAT_RGBA_8888, 1u,
+                                        BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                                BufferUsage::COMPOSER_OVERLAY |
+                                                BufferUsage::GPU_TEXTURE,
+                                        "test");
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect{0, 0, 50, 100}, Color::RED);
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect{50, 0, 100, 100}, Color::BLUE);
+        Transaction()
+                .setBuffer(mLayer, buffer)
+                .setDataspace(mLayer, ui::Dataspace::V0_SRGB)
+                .setLayer(mLayer, INT32_MAX)
+                .apply();
+    }
+
+    virtual void TearDown() { LayerTransactionTest::TearDown(); }
+
+    void expectFiltered(Rect redRect, Rect blueRect) {
+        // Check that at least some of the pixels in the red rectangle aren't solid red
+        int redPixels = 0;
+        for (int x = redRect.left; x < redRect.right; x++) {
+            for (int y = redRect.top; y < redRect.bottom; y++) {
+                redPixels += mCapture->getPixelColor(static_cast<uint32_t>(x),
+                                                     static_cast<uint32_t>(y)) == Color::RED;
+            }
+        }
+        ASSERT_LT(redPixels, redRect.getWidth() * redRect.getHeight());
+
+        // Check that at least some of the pixels in the blue rectangle aren't solid blue
+        int bluePixels = 0;
+        for (int x = blueRect.left; x < blueRect.right; x++) {
+            for (int y = blueRect.top; y < blueRect.bottom; y++) {
+                bluePixels += mCapture->getPixelColor(static_cast<uint32_t>(x),
+                                                      static_cast<uint32_t>(y)) == Color::BLUE;
+            }
+        }
+        ASSERT_LT(bluePixels, blueRect.getWidth() * blueRect.getHeight());
+    }
+
+    sp<SurfaceControl> mParent;
+    sp<SurfaceControl> mLayer;
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(TextureFilteringTest, NoFiltering) {
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{100, 100};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+    mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
+TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
+    Transaction().setBufferCrop(mLayer, Rect{0, 0, 100, 100}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+    mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
+// Expect filtering because the buffer is stretched to the layer's bounds.
+TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
+    Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the output source crop is stretched to the output buffer's size.
+TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the layer crop and output source crop are stretched to the output
+// buffer's size.
+TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
+    Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the layer is scaled up.
+TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mLayer->getHandle();
+    captureArgs.frameScaleX = 2;
+    captureArgs.frameScaleY = 2;
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    expectFiltered({0, 0, 100, 200}, {100, 0, 200, 200});
+}
+
+// Expect no filtering because the output buffer's size matches the source crop.
+TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mLayer->getHandle();
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+    mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the output buffer's size matches the source crop (with a cropped
+// layer).
+TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) {
+    Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mLayer->getHandle();
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+    mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the output source crop and output buffer are the same size.
+TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
+    // Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 50;
+    captureArgs.height = 50;
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+    mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the layer crop shouldn't scale the layer.
+TEST_F(TextureFilteringTest, LayerCropDisplayFrameMatchNoFiltering) {
+    Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
+    mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
+}
+
+// Expect no filtering because the parent layer crop shouldn't scale the layer.
+TEST_F(TextureFilteringTest, ParentCropNoFiltering) {
+    Transaction().setCrop(mParent, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
+    mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index ba77600..0416e93 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -238,6 +238,8 @@
                                                    CaptureArgs::UNSET_UID, visitor);
     };
 
+    auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
     mCaptureScreenBuffer =
@@ -246,7 +248,7 @@
                                                                       HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                                                       usage);
 
-    auto future = mFlinger.renderScreenImpl(std::move(renderArea), traverseLayers,
+    auto future = mFlinger.renderScreenImpl(std::move(renderArea), getLayerSnapshots,
                                             mCaptureScreenBuffer, forSystem, regionSampling);
     ASSERT_TRUE(future.valid());
     const auto fenceResult = future.get();
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index f47ac6d..abd7789 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -44,6 +44,17 @@
 
 namespace android::frametimeline {
 
+static const std::string sLayerNameOne = "layer1";
+static const std::string sLayerNameTwo = "layer2";
+
+constexpr const uid_t sUidOne = 0;
+constexpr pid_t sPidOne = 10;
+constexpr pid_t sPidTwo = 20;
+constexpr int32_t sInputEventId = 5;
+constexpr int32_t sLayerIdOne = 1;
+constexpr int32_t sLayerIdTwo = 2;
+constexpr GameMode sGameMode = GameMode::Unsupported;
+
 class FrameTimelineTest : public testing::Test {
 public:
     FrameTimelineTest() {
@@ -106,6 +117,14 @@
         return packets;
     }
 
+    void addEmptySurfaceFrame() {
+        auto surfaceFrame =
+                mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ false, sGameMode);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame));
+    }
+
     void addEmptyDisplayFrame() {
         auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
         // Trigger a flushPresentFence by calling setSfPresent for the next frame
@@ -168,17 +187,6 @@
                                                                   kStartThreshold};
 };
 
-static const std::string sLayerNameOne = "layer1";
-static const std::string sLayerNameTwo = "layer2";
-
-constexpr const uid_t sUidOne = 0;
-constexpr pid_t sPidOne = 10;
-constexpr pid_t sPidTwo = 20;
-constexpr int32_t sInputEventId = 5;
-constexpr int32_t sLayerIdOne = 1;
-constexpr int32_t sLayerIdTwo = 2;
-constexpr GameMode sGameMode = GameMode::Unsupported;
-
 TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
     EXPECT_EQ(getPredictions().size(), 1u);
@@ -1054,6 +1062,9 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     tracingSession->StartBlocking();
+
+    // Add an empty surface frame so that display frame would get traced.
+    addEmptySurfaceFrame();
     int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 30, 30});
 
     // Set up the display frame
@@ -1135,6 +1146,9 @@
     // Flush the token so that it would expire
     flushTokens();
 
+    // Add an empty surface frame so that display frame would get traced.
+    addEmptySurfaceFrame();
+
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
     mFrameTimeline->setSfPresent(26, presentFence1);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 33c9440..763426a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -562,4 +562,148 @@
     hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor);
 }
 
+TEST_F(LayerHierarchyTest, zorderRespectsLayerSequenceId) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createRootLayer(2);
+    createRootLayer(4);
+    createRootLayer(5);
+    createLayer(11, 1);
+    createLayer(51, 5);
+    createLayer(53, 5);
+
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 4, 5, 51, 53};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // A new layer is added with a smaller sequence id. Make sure its sorted correctly. While
+    // sequence ids are always incremented, this scenario can happen when a layer is reparented.
+    createRootLayer(3);
+    createLayer(52, 5);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    expectedTraversalPath = {1, 11, 2, 3, 4, 5, 51, 52, 53};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, zorderRespectsLayerZ) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createLayer(11, 1);
+    createLayer(12, 1);
+    createLayer(13, 1);
+    setZ(11, -1);
+    setZ(12, 2);
+    setZ(13, 1);
+
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 13, 12};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+    expectedTraversalPath = {11, 1, 13, 12};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, zorderRespectsLayerStack) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createRootLayer(2);
+    createLayer(11, 1);
+    createLayer(21, 2);
+    setLayerStack(1, 20);
+    setLayerStack(2, 10);
+
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 21};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+    expectedTraversalPath = {1, 11, 2, 21};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorDisplay) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expected = {3, 1,  11,  111, 12,  121, 122,  1221, 13, 2,
+                                      1, 11, 111, 12,  121, 122, 1221, 13,   2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+    expected = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(5));
+    setLayerStack(3, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+    expected = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    createRootLayer(4);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expected = {3, 1,  11,  111, 12,  121, 122,  1221, 13, 2, 4,
+                                      1, 11, 111, 12,  121, 122, 1221, 13,   2,  4};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+    expected = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentLayer(1, UNASSIGNED_LAYER_ID);
+    destroyLayerHandle(1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expected = {3, 2, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+    expected = {11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 08727f2..852cb91 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -60,6 +60,14 @@
         return args;
     }
 
+    LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStack) {
+        LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+        args.addToRoot = true;
+        args.parentHandle.clear();
+        args.layerStackToMirror = layerStack;
+        return args;
+    }
+
     std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
         std::vector<uint32_t> layerIds;
         hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
@@ -90,6 +98,15 @@
         mLifecycleManager.addLayers(std::move(layers));
     }
 
+    void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
+        sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+        mHandles[id] = handle;
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createDisplayMirrorArgs(/*id=*/id, layerStack)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
     virtual void createLayer(uint32_t id, uint32_t parentId) {
         sp<LayerHandle> handle = sp<LayerHandle>::make(id);
         mHandles[id] = handle;
@@ -245,6 +262,18 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setLayerStack(uint32_t id, int32_t layerStack) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
+        transactions.back().states.front().state.surface = mHandles[id];
+        transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+        transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     LayerLifecycleManager mLifecycleManager;
     std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
 };
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 2441c06..aa6a14e 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -38,6 +38,9 @@
 
 namespace android::surfaceflinger::frontend {
 
+using ftl::Flags;
+using namespace ftl::flag_operators;
+
 // To run test:
 /**
  mp :libsurfaceflinger_unittest && adb sync; adb shell \
@@ -88,14 +91,11 @@
         ASSERT_TRUE(expectedBuilder.getSnapshots().size() > 0);
         ASSERT_TRUE(actualBuilder.getSnapshots().size() > 0);
 
-        std::vector<std::unique_ptr<LayerSnapshot>>& snapshots = actualBuilder.getSnapshots();
         std::vector<uint32_t> actualVisibleLayerIdsInZOrder;
-        for (auto& snapshot : snapshots) {
-            if (!snapshot->isVisible) {
-                break;
-            }
-            actualVisibleLayerIdsInZOrder.push_back(snapshot->path.id);
-        }
+        actualBuilder.forEachVisibleSnapshot(
+                [&actualVisibleLayerIdsInZOrder](const LayerSnapshot& snapshot) {
+                    actualVisibleLayerIdsInZOrder.push_back(snapshot.path.id);
+                });
         EXPECT_EQ(expectedVisibleLayerIdsInZOrder, actualVisibleLayerIdsInZOrder);
     }
 
@@ -103,7 +103,6 @@
 
     LayerHierarchyBuilder mHierarchyBuilder{{}};
     LayerSnapshotBuilder mSnapshotBuilder;
-    std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
     display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
     renderengine::ShadowSettings globalShadowSettings;
     static const std::vector<uint32_t> STARTING_ZORDER;
@@ -257,4 +256,77 @@
     EXPECT_EQ(getSnapshot(1)->changes, RequestedLayerState::Changes::Content);
 }
 
+TEST_F(LayerSnapshotTest, GameMode) {
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
+    transactions.back().states.front().state.metadata = LayerMetadata();
+    transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, 42);
+    transactions.back().states.front().state.surface = mHandles[1];
+    transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
+    mLifecycleManager.applyTransactions(transactions);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42);
+    EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42);
+}
+
+TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) {
+    // ROOT
+    // ├── 1
+    // │   ├── 11 (frame rate set)
+    // │   │   └── 111
+    // │   ├── 12
+    // │   │   ├── 121
+    // │   │   └── 122
+    // │   │       └── 1221
+    // │   └── 13
+    // └── 2
+
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
+    transactions.back().states.front().state.frameRate = 90.0;
+    transactions.back().states.front().state.frameRateCompatibility =
+            ANATIVEWINDOW_FRAME_RATE_EXACT;
+    transactions.back().states.front().state.changeFrameRateStrategy =
+            ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS;
+    transactions.back().states.front().state.surface = mHandles[11];
+    transactions.back().states.front().state.layerId = static_cast<int32_t>(11);
+    mLifecycleManager.applyTransactions(transactions);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+    EXPECT_EQ(getSnapshot(11)->frameRate.rate.getIntValue(), 90);
+    EXPECT_EQ(getSnapshot(11)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::Exact);
+    EXPECT_EQ(getSnapshot(111)->frameRate.rate.getIntValue(), 90);
+    EXPECT_EQ(getSnapshot(111)->frameRate.type,
+              scheduler::LayerInfo::FrameRateCompatibility::Exact);
+    EXPECT_EQ(getSnapshot(1)->frameRate.rate.getIntValue(), 0);
+    EXPECT_EQ(getSnapshot(1)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+}
+
+// Display Mirroring Tests
+// tree with 3 levels of children
+// ROOT (DISPLAY 0)
+// ├── 1
+// │   ├── 11
+// │   │   └── 111
+// │   ├── 12 (has skip screenshot flag)
+// │   │   ├── 121
+// │   │   └── 122
+// │   │       └── 1221
+// │   └── 13
+// └── 2
+// ROOT (DISPLAY 1)
+// └── 3 (mirrors display 0)
+TEST_F(LayerSnapshotTest, displayMirrorRespects) {
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+
+    std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 06f45f9..5fddda5 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -2973,5 +2973,15 @@
     EXPECT_EQ(kMode1, selector.getMinRefreshRateByPolicy());
 }
 
+// TODO(b/266481656): Once this bug is fixed, we can remove this test
+TEST_P(RefreshRateSelectorTest, noLowerFrameRateOnMinVote) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].vote = LayerVoteType::Min;
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
 } // namespace
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 0f53eb6..0cbfa63 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -37,12 +37,12 @@
     TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<mock::VsyncController>(),
                               std::make_unique<mock::VSyncTracker>(), std::move(selectorPtr),
-                              callback) {}
+                              /* modulatorPtr */ nullptr, callback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
-                      ISchedulerCallback& callback)
-          : Scheduler(*this, callback, Feature::kContentDetection) {
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
+          : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker),
                                              std::make_unique<mock::VSyncDispatch>(),
                                              std::move(controller)));
@@ -99,6 +99,7 @@
         Scheduler::setLeaderDisplay(displayId);
     }
 
+    auto& mutableVsyncModulator() { return *mVsyncModulator; }
     auto& mutableLayerHistory() { return mLayerHistory; }
 
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 584d52c..68c738f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -224,8 +224,6 @@
 
         const auto fps = selectorPtr->getActiveMode().fps;
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
-        mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
-                mFlinger->mVsyncConfiguration->getCurrentConfigs());
 
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
@@ -238,16 +236,21 @@
                 ? static_cast<Callback&>(mNoOpSchedulerCallback)
                 : static_cast<Callback&>(mSchedulerCallback);
 
+        auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
+                mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
         if (useNiceMock) {
             mScheduler =
                     new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController),
                                                                         std::move(vsyncTracker),
                                                                         std::move(selectorPtr),
+                                                                        std::move(modulatorPtr),
                                                                         callback);
         } else {
             mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
                                                           std::move(vsyncTracker),
-                                                          std::move(selectorPtr), callback);
+                                                          std::move(selectorPtr),
+                                                          std::move(modulatorPtr), callback);
         }
 
         mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), *mTokenManager, 0ms);
@@ -262,8 +265,6 @@
     scheduler::TestableScheduler& mutableScheduler() { return *mScheduler; }
     scheduler::mock::SchedulerCallback& mockSchedulerCallback() { return mSchedulerCallback; }
 
-    auto& mutableVsyncModulator() { return mFlinger->mVsyncModulator; }
-
     using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
         mFactory.mCreateBufferQueue = f;
@@ -406,7 +407,7 @@
     }
 
     auto renderScreenImpl(std::shared_ptr<const RenderArea> renderArea,
-                          SurfaceFlinger::TraverseLayersFunction traverseLayers,
+                          SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers,
                           const std::shared_ptr<renderengine::ExternalTexture>& buffer,
                           bool forSystem, bool regionSampling) {
         ScreenCaptureResults captureResults;
@@ -439,12 +440,13 @@
                              uint32_t flags, const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
                              int64_t desiredPresentTime, bool isAutoTimestamp,
-                             const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+                             const std::vector<client_cache_t>& uncacheBuffers,
+                             bool hasListenerCallbacks,
                              std::vector<ListenerCallbacks>& listenerCallbacks,
                              uint64_t transactionId) {
         return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
                                              inputWindowCommands, desiredPresentTime,
-                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+                                             isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
                                              listenerCallbacks, transactionId);
     }
 
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index d84698f..859f702 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -102,7 +102,7 @@
         int64_t desiredPresentTime = 0;
         bool isAutoTimestamp = true;
         FrameTimelineInfo frameTimelineInfo;
-        client_cache_t uncacheBuffer;
+        std::vector<client_cache_t> uncacheBuffers;
         uint64_t id = static_cast<uint64_t>(-1);
         static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
     };
@@ -138,7 +138,7 @@
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
                                      transaction.id);
 
         // If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
@@ -165,7 +165,7 @@
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
                                      transaction.id);
 
         nsecs_t returnedTime = systemTime();
@@ -196,7 +196,7 @@
                                      transactionA.displays, transactionA.flags,
                                      transactionA.applyToken, transactionA.inputWindowCommands,
                                      transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
-                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
                                      transactionA.id);
 
         // This thread should not have been blocked by the above transaction
@@ -211,7 +211,7 @@
                                      transactionB.displays, transactionB.flags,
                                      transactionB.applyToken, transactionB.inputWindowCommands,
                                      transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
-                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
                                      transactionB.id);
 
         // this thread should have been blocked by the above transaction
@@ -228,6 +228,11 @@
         EXPECT_EQ(0u, transactionQueue.size());
     }
 
+    void modulateVsync() {
+        static_cast<void>(
+                mFlinger.mutableScheduler().mutableVsyncModulator().onRefreshRateChangeInitiated());
+    }
+
     bool mHasListenerCallbacks = false;
     std::vector<ListenerCallbacks> mCallbacks;
     int mTransactionNumber = 0;
@@ -243,7 +248,7 @@
     mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
                                  transactionA.displays, transactionA.flags, transactionA.applyToken,
                                  transactionA.inputWindowCommands, transactionA.desiredPresentTime,
-                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
                                  mHasListenerCallbacks, mCallbacks, transactionA.id);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
@@ -263,7 +268,7 @@
     mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
                                  transactionA.displays, transactionA.flags, transactionA.applyToken,
                                  transactionA.inputWindowCommands, transactionA.desiredPresentTime,
-                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
                                  mHasListenerCallbacks, mCallbacks, transactionA.id);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
@@ -277,7 +282,7 @@
     mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
                                  empty.applyToken, empty.inputWindowCommands,
                                  empty.desiredPresentTime, empty.isAutoTimestamp,
-                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
+                                 empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id);
 
     // flush transaction queue should flush as desiredPresentTime has
     // passed
@@ -374,8 +379,7 @@
                                               transaction.applyToken,
                                               transaction.inputWindowCommands,
                                               transaction.desiredPresentTime,
-                                              transaction.isAutoTimestamp,
-                                              transaction.uncacheBuffer, systemTime(), 0,
+                                              transaction.isAutoTimestamp, {}, systemTime(), 0,
                                               mHasListenerCallbacks, mCallbacks, getpid(),
                                               static_cast<int>(getuid()), transaction.id);
             mFlinger.setTransactionStateInternal(transactionState);
@@ -624,9 +628,7 @@
                                                               layer_state_t::eBufferChanged),
                                   });
 
-    // Get VsyncModulator out of the default config
-    static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
-
+    modulateVsync();
     setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
@@ -986,9 +988,7 @@
                                                               layer_state_t::eBufferChanged),
                                   });
 
-    // Get VsyncModulator out of the default config
-    static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
-
+    modulateVsync();
     setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
index b519582..8acbd6f 100644
--- a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -57,17 +57,14 @@
 
     using Schedule = scheduler::TransactionSchedule;
     using nanos = std::chrono::nanoseconds;
-    const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
-                                             nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
-    const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
-                                                nanos(SF_DURATION_EARLY),
-                                                nanos(APP_DURATION_EARLY)};
-    const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE,
-                                            nanos(SF_DURATION_EARLY_GPU),
-                                            nanos(APP_DURATION_EARLY_GPU)};
+    const VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE),
+                             nanos(APP_DURATION_LATE)};
+    const VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU, nanos(SF_DURATION_EARLY),
+                                nanos(APP_DURATION_EARLY)};
+    const VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU),
+                            nanos(APP_DURATION_EARLY_GPU)};
 
-    const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate,
-                                                     nanos(HWC_MIN_WORK_DURATION)};
+    const VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate, nanos(HWC_MIN_WORK_DURATION)};
     sp<TestableVsyncModulator> mVsyncModulator = sp<TestableVsyncModulator>::make(mOffsets, Now);
 
     void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator->setVsyncConfigSet(mOffsets)); }