Merge "Added trace data for latch and release buffers"
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 32922ca..3b753c6 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -44,6 +44,7 @@
     defaults: ["servicemanager_defaults"],
     init_rc: ["servicemanager.rc"],
     srcs: ["main.cpp"],
+    bootstrap: true,
 }
 
 cc_binary {
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
index 451918b..b1e3f85 100644
--- a/include/input/PropertyMap.h
+++ b/include/input/PropertyMap.h
@@ -18,10 +18,8 @@
 #define _UTILS_PROPERTY_MAP_H
 
 #include <android-base/result.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
 #include <utils/Tokenizer.h>
+#include <unordered_map>
 
 namespace android {
 
@@ -58,30 +56,27 @@
     /* Adds a property.
      * Replaces the property with the same key if it is already present.
      */
-    void addProperty(const String8& key, const String8& value);
-
-    /* Returns true if the property map contains the specified key. */
-    bool hasProperty(const String8& key) const;
+    void addProperty(const std::string& key, const std::string& value);
 
     /* Gets the value of a property and parses it.
      * Returns true and sets outValue if the key was found and its value was parsed successfully.
      * Otherwise returns false and does not modify outValue.  (Also logs a warning.)
      */
-    bool tryGetProperty(const String8& key, String8& outValue) const;
-    bool tryGetProperty(const String8& key, bool& outValue) const;
-    bool tryGetProperty(const String8& key, int32_t& outValue) const;
-    bool tryGetProperty(const String8& key, float& outValue) const;
+    bool tryGetProperty(const std::string& key, std::string& outValue) const;
+    bool tryGetProperty(const std::string& key, bool& outValue) const;
+    bool tryGetProperty(const std::string& key, int32_t& outValue) const;
+    bool tryGetProperty(const std::string& key, float& outValue) const;
 
     /* Adds all values from the specified property map. */
     void addAll(const PropertyMap* map);
 
-    /* Gets the underlying property map. */
-    inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
-
     /* Loads a property map from a file. */
     static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename);
 
 private:
+    /* Returns true if the property map contains the specified key. */
+    bool hasProperty(const std::string& key) const;
+
     class Parser {
         PropertyMap* mMap;
         Tokenizer* mTokenizer;
@@ -95,11 +90,11 @@
         status_t parseType();
         status_t parseKey();
         status_t parseKeyProperty();
-        status_t parseModifier(const String8& token, int32_t* outMetaState);
+        status_t parseModifier(const std::string& token, int32_t* outMetaState);
         status_t parseCharacterLiteral(char16_t* outCharacter);
     };
 
-    KeyedVector<String8, String8> mProperties;
+    std::unordered_map<std::string, std::string> mProperties;
 };
 
 } // namespace android
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 49fc195..82ebdd7 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -358,6 +358,14 @@
     LOG_ALWAYS_FATAL_IF(recipient == nullptr,
                         "linkToDeath(): recipient must be non-NULL");
 
+    if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0) {
+        ALOGW("Linking to death on %s but there are no threads (yet?) listening to incoming "
+              "transactions. See ProcessState::startThreadPool and "
+              "ProcessState::setThreadPoolMaxThreadCount. Generally you should setup the binder "
+              "threadpool before other initialization steps.",
+              String8(getInterfaceDescriptor()).c_str());
+    }
+
     {
         AutoMutex _l(mLock);
 
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 7faff47..1f311ac 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -19,6 +19,7 @@
 #include <binder/ProcessState.h>
 
 #include <android-base/result.h>
+#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <binder/BpBinder.h>
 #include <binder/IPCThreadState.h>
@@ -420,6 +421,9 @@
 }
 
 size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
+    pthread_mutex_lock(&mThreadCountLock);
+    base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); };
+
     // may actually be one more than this, if join is called
     if (mThreadPoolStarted) {
         return mCurrentThreads < mKernelStartedThreads
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 096d5cc..49be4dd 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -209,9 +209,10 @@
 
         {
             RpcMutexLockGuard _l(mLock);
-            RpcMaybeThread thread = RpcMaybeThread(&RpcServer::establishConnection,
-                                                   sp<RpcServer>::fromExisting(this),
-                                                   std::move(clientFd), addr, addrLen);
+            RpcMaybeThread thread =
+                    RpcMaybeThread(&RpcServer::establishConnection,
+                                   sp<RpcServer>::fromExisting(this), std::move(clientFd), addr,
+                                   addrLen, RpcSession::join);
 
             auto& threadRef = mConnectingThreads[thread.get_id()];
             threadRef = std::move(thread);
@@ -294,8 +295,10 @@
     return mConnectingThreads.size();
 }
 
-void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
-                                    std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen) {
+void RpcServer::establishConnection(
+        sp<RpcServer>&& server, base::unique_fd clientFd, std::array<uint8_t, kRpcAddressSize> addr,
+        size_t addrLen,
+        std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn) {
     // mShutdownTrigger can only be cleared once connection threads have joined.
     // It must be set before this thread is started
     LOG_ALWAYS_FATAL_IF(server->mShutdownTrigger == nullptr);
@@ -478,7 +481,7 @@
     // avoid strong cycle
     server = nullptr;
 
-    RpcSession::join(std::move(session), std::move(setupResult));
+    joinFn(std::move(session), std::move(setupResult));
 }
 
 status_t RpcServer::setupSocketServer(const RpcSocketAddress& addr) {
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 882dfbf..9679a5f 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -130,7 +130,7 @@
     void* mVMStart;
 
     // Protects thread count and wait variables below.
-    pthread_mutex_t mThreadCountLock;
+    mutable pthread_mutex_t mThreadCountLock;
     // Broadcast whenever mWaitingForThreads > 0
     pthread_cond_t mThreadCountDecrement;
     // Number of binder threads current executing a command.
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 9318c27..52bda0e 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -29,6 +29,7 @@
 namespace android {
 
 class FdTrigger;
+class RpcServerTrusty;
 class RpcSocketAddress;
 
 /**
@@ -189,6 +190,7 @@
     ~RpcServer();
 
 private:
+    friend RpcServerTrusty;
     friend sp<RpcServer>;
     explicit RpcServer(std::unique_ptr<RpcTransportCtx> ctx);
 
@@ -196,8 +198,10 @@
     void onSessionIncomingThreadEnded() override;
 
     static constexpr size_t kRpcAddressSize = 128;
-    static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd,
-                                    std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen);
+    static void establishConnection(
+            sp<RpcServer>&& server, base::unique_fd clientFd,
+            std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen,
+            std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn);
     [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address);
 
     const std::unique_ptr<RpcTransportCtx> mCtx;
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index a2b28db..9d94e00 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -31,6 +31,7 @@
 
 class Parcel;
 class RpcServer;
+class RpcServerTrusty;
 class RpcSocketAddress;
 class RpcState;
 class RpcTransport;
@@ -202,6 +203,7 @@
 private:
     friend sp<RpcSession>;
     friend RpcServer;
+    friend RpcServerTrusty;
     friend RpcState;
     explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
 
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 4e41d8e..fca3c29 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -930,7 +930,7 @@
 
     size_t epochMsAfter = epochMillis();
 
-    EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
+    EXPECT_GE(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
 
     saturateThreadPool(1 + kNumExtraServerThreads, proc.rootIface);
 }
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 2ca6ebd..0210237 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -7,6 +7,22 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
+aidl_interface {
+    name: "binderReadParcelIface",
+    host_supported: true,
+    unstable: true,
+    srcs: [
+        "EmptyParcelable.aidl",
+        "SingleDataParcelable.aidl",
+        "GenericDataParcelable.aidl",
+    ],
+    backend: {
+        java: {
+            enabled: false,
+        },
+    },
+}
+
 cc_fuzz {
     name: "binder_parcel_fuzzer",
     host_supported: true,
@@ -29,6 +45,8 @@
         "libcutils",
         "libhidlbase",
         "liblog",
+        "binderReadParcelIface-cpp",
+        "binderReadParcelIface-ndk",
     ],
 
     target: {
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
new file mode 100644
index 0000000..96d6223
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+parcelable EmptyParcelable{
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
new file mode 100644
index 0000000..fc2542b
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+parcelable GenericDataParcelable {
+    int data;
+    float majorVersion;
+    float minorVersion;
+    IBinder binder;
+    ParcelFileDescriptor fileDescriptor;
+    int[] array;
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
new file mode 100644
index 0000000..d62891b
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
@@ -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.
+ */
+
+parcelable SingleDataParcelable{
+   int data;
+}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 7059d30..9dac2c9 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,6 +16,9 @@
 #define FUZZ_LOG_TAG "binder"
 
 #include "binder.h"
+#include "EmptyParcelable.h"
+#include "GenericDataParcelable.h"
+#include "SingleDataParcelable.h"
 #include "util.h"
 
 #include <android-base/hex.h>
@@ -354,6 +357,24 @@
         status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result);
         FUZZ_LOG() << " status: " << status  << " result: " << result;
     },
+    [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+        FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
+        EmptyParcelable emptyParcelable{};
+        status_t status = emptyParcelable.readFromParcel(&p);
+        FUZZ_LOG() << " status: " << status;
+    },
+    [] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
+        FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
+        SingleDataParcelable singleDataParcelable;
+        status_t status = singleDataParcelable.readFromParcel(&p);
+        FUZZ_LOG() <<" status: " << status;
+    },
+    [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+        FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
+        GenericDataParcelable genericDataParcelable;
+        status_t status = genericDataParcelable.readFromParcel(&p);
+        FUZZ_LOG() <<" status: " << status;
+    },
 };
 // clang-format on
 #pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 26d6770..af773a0 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,6 +16,9 @@
 #define FUZZ_LOG_TAG "binder_ndk"
 
 #include "binder_ndk.h"
+#include "aidl/EmptyParcelable.h"
+#include "aidl/GenericDataParcelable.h"
+#include "aidl/SingleDataParcelable.h"
 
 #include <android/binder_parcel_utils.h>
 #include <android/binder_parcelable_utils.h>
@@ -177,5 +180,24 @@
         PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData),
         PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData),
 #undef COMMA
+
+        [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+            FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
+            aidl::EmptyParcelable emptyParcelable;
+            binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
+            FUZZ_LOG() << "status: " << status;
+        },
+        [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+            FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
+            aidl::SingleDataParcelable singleDataParcelable;
+            binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
+            FUZZ_LOG() << "status: " << status;
+        },
+        [](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
+            FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
+            aidl::GenericDataParcelable genericDataParcelable;
+            binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
+            FUZZ_LOG() << "status: " << status;
+        },
 };
 // clang-format on
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
new file mode 100644
index 0000000..187add4
--- /dev/null
+++ b/libs/binder/trusty/OS.cpp
@@ -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.
+ */
+
+#include <openssl/rand.h>
+
+#include "../OS.h"
+
+using android::base::Result;
+
+namespace android {
+
+Result<void> setNonBlocking(android::base::borrowed_fd fd) {
+    // Trusty IPC syscalls are all non-blocking by default.
+    return {};
+}
+
+status_t getRandomBytes(uint8_t* data, size_t size) {
+    int res = RAND_bytes(data, size);
+    return res == 1 ? OK : UNKNOWN_ERROR;
+}
+
+} // namespace android
diff --git a/libs/binder/trusty/README.md b/libs/binder/trusty/README.md
new file mode 100644
index 0000000..1a273aa
--- /dev/null
+++ b/libs/binder/trusty/README.md
@@ -0,0 +1,39 @@
+# Binder for Trusty
+
+This is the Trusty port of the libbinder library.
+To build it, take the following steps:
+
+* Check out copies of the Trusty and AOSP repositories.
+* Apply the patches from the `trusty_binder` topic on both repositories.
+* Build Trusty normally using `build.py`.
+* Run the sample AIDL test for Trusty:
+  ```shell
+  $ ./build-root/.../run --headless --boot-test com.android.trusty.aidl.test
+  ```
+
+To run the Android-Trusty IPC test, do the following:
+
+* Build AOSP for the `qemu_trusty_arm64-userdebug` target:
+  ```shell
+  $ lunch qemu_trusty_arm64-userdebug
+  $ m
+  ```
+* In the Trusty directory, run the emulator with the newly built Android:
+  ```shell
+  $ ./build-root/.../run --android /path/to/aosp
+  ```
+* Using either `adb` or the shell inside the emulator itself, run the Trusty
+  Binder test as root:
+  ```shell
+  # /data/nativetest64/vendor/trusty_binder_test/trusty_binder_test
+  ```
+
+## Running the AIDL compiler
+For now, you will need to run the AIDL compiler manually to generate the C++
+source code for Trusty clients and services. The general syntax is:
+```shell
+$ aidl --lang=cpp -o <output directory> -h <output header directory> <AIDL files...>
+```
+
+The compiler will emit some `.cpp` files in the output directory and their
+corresponding `.h` files in the header directory.
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
new file mode 100644
index 0000000..e8b91e7
--- /dev/null
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 "RpcServerTrusty"
+
+#include <binder/Parcel.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcServerTrusty.h>
+#include <binder/RpcThreads.h>
+#include <binder/RpcTransportTipcTrusty.h>
+#include <log/log.h>
+
+#include "../FdTrigger.h"
+#include "../RpcState.h"
+#include "TrustyStatus.h"
+
+using android::base::unexpected;
+
+namespace android {
+
+android::base::expected<sp<RpcServerTrusty>, int> RpcServerTrusty::make(
+        tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
+        size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
+    // Default is without TLS.
+    if (rpcTransportCtxFactory == nullptr)
+        rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make();
+    auto ctx = rpcTransportCtxFactory->newServerCtx();
+    if (ctx == nullptr) {
+        return unexpected(ERR_NO_MEMORY);
+    }
+
+    auto srv = sp<RpcServerTrusty>::make(std::move(ctx), std::move(portName), std::move(portAcl),
+                                         msgMaxSize);
+    if (srv == nullptr) {
+        return unexpected(ERR_NO_MEMORY);
+    }
+
+    int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps);
+    if (rc != NO_ERROR) {
+        return unexpected(rc);
+    }
+    return srv;
+}
+
+RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
+                                 std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize)
+      : mRpcServer(sp<RpcServer>::make(std::move(ctx))),
+        mPortName(std::move(portName)),
+        mPortAcl(std::move(portAcl)) {
+    mTipcPort.name = mPortName.c_str();
+    mTipcPort.msg_max_size = msgMaxSize;
+    mTipcPort.msg_queue_len = 6; // Three each way
+    mTipcPort.priv = this;
+
+    if (mPortAcl) {
+        // Initialize the array of pointers to uuids.
+        // The pointers in mUuidPtrs should stay valid across moves of
+        // RpcServerTrusty (the addresses of a std::vector's elements
+        // shouldn't change when the vector is moved), but would be invalidated
+        // by a copy which is why we disable the copy constructor and assignment
+        // operator for RpcServerTrusty.
+        auto numUuids = mPortAcl->uuids.size();
+        mUuidPtrs.resize(numUuids);
+        for (size_t i = 0; i < numUuids; i++) {
+            mUuidPtrs[i] = &mPortAcl->uuids[i];
+        }
+
+        // Copy the contents of portAcl into the tipc_port_acl structure that we
+        // pass to tipc_add_service
+        mTipcPortAcl.flags = mPortAcl->flags;
+        mTipcPortAcl.uuid_num = numUuids;
+        mTipcPortAcl.uuids = mUuidPtrs.data();
+        mTipcPortAcl.extra_data = mPortAcl->extraData;
+
+        mTipcPort.acl = &mTipcPortAcl;
+    } else {
+        mTipcPort.acl = nullptr;
+    }
+}
+
+int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const uuid* peer,
+                                   void** ctx_p) {
+    auto* server = reinterpret_cast<RpcServerTrusty*>(const_cast<void*>(port->priv));
+    server->mRpcServer->mShutdownTrigger = FdTrigger::make();
+    server->mRpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread();
+
+    int rc = NO_ERROR;
+    auto joinFn = [&](sp<RpcSession>&& session, RpcSession::PreJoinSetupResult&& result) {
+        if (result.status != OK) {
+            rc = statusToTrusty(result.status);
+            return;
+        }
+
+        /* Save the session for easy access */
+        *ctx_p = session.get();
+    };
+
+    base::unique_fd clientFd(chan);
+    std::array<uint8_t, RpcServer::kRpcAddressSize> addr;
+    constexpr size_t addrLen = sizeof(*peer);
+    memcpy(addr.data(), peer, addrLen);
+    RpcServer::establishConnection(sp(server->mRpcServer), std::move(clientFd), addr, addrLen,
+                                   joinFn);
+
+    return rc;
+}
+
+int RpcServerTrusty::handleMessage(const tipc_port* port, handle_t chan, void* ctx) {
+    auto* session = reinterpret_cast<RpcSession*>(ctx);
+    status_t status = session->state()->drainCommands(session->mConnections.mIncoming[0], session,
+                                                      RpcState::CommandType::ANY);
+    if (status != OK) {
+        LOG_RPC_DETAIL("Binder connection thread closing w/ status %s",
+                       statusToString(status).c_str());
+    }
+
+    return NO_ERROR;
+}
+
+void RpcServerTrusty::handleDisconnect(const tipc_port* port, handle_t chan, void* ctx) {}
+
+void RpcServerTrusty::handleChannelCleanup(void* ctx) {
+    auto* session = reinterpret_cast<RpcSession*>(ctx);
+    auto& connection = session->mConnections.mIncoming.at(0);
+    LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection),
+                        "bad state: connection object guaranteed to be in list");
+}
+
+} // namespace android
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
new file mode 100644
index 0000000..e0d80fb
--- /dev/null
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -0,0 +1,248 @@
+/*
+ * 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 "RpcTransportTipcTrusty"
+
+#include <trusty_ipc.h>
+
+#include <binder/RpcSession.h>
+#include <binder/RpcTransportTipcTrusty.h>
+#include <log/log.h>
+
+#include "../FdTrigger.h"
+#include "../RpcState.h"
+#include "TrustyStatus.h"
+
+namespace android {
+
+namespace {
+
+// RpcTransport for Trusty.
+class RpcTransportTipcTrusty : public RpcTransport {
+public:
+    explicit RpcTransportTipcTrusty(android::base::unique_fd socket) : mSocket(std::move(socket)) {}
+    ~RpcTransportTipcTrusty() { releaseMessage(); }
+
+    status_t pollRead() override {
+        auto status = ensureMessage(false);
+        if (status != OK) {
+            return status;
+        }
+        return mHaveMessage ? OK : WOULD_BLOCK;
+    }
+
+    status_t interruptableWriteFully(
+            FdTrigger* fdTrigger, iovec* iovs, int niovs,
+            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
+            override {
+        if (niovs < 0) {
+            return BAD_VALUE;
+        }
+
+        size_t size = 0;
+        for (int i = 0; i < niovs; i++) {
+            size += iovs[i].iov_len;
+        }
+
+        ipc_msg_t msg{
+                .num_iov = static_cast<uint32_t>(niovs),
+                .iov = iovs,
+                .num_handles = 0, // TODO: add ancillaryFds
+                .handles = nullptr,
+        };
+        int rc = send_msg(mSocket.get(), &msg);
+        if (rc == ERR_NOT_ENOUGH_BUFFER) {
+            // Peer is blocked, wait until it unblocks.
+            // TODO: when tipc supports a send-unblocked handler,
+            // save the message here in a queue and retry it asynchronously
+            // when the handler gets called by the library
+            uevent uevt;
+            do {
+                rc = ::wait(mSocket.get(), &uevt, INFINITE_TIME);
+                if (rc < 0) {
+                    return statusFromTrusty(rc);
+                }
+                if (uevt.event & IPC_HANDLE_POLL_HUP) {
+                    return DEAD_OBJECT;
+                }
+            } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED));
+
+            // Retry the send, it should go through this time because
+            // sending is now unblocked
+            rc = send_msg(mSocket.get(), &msg);
+        }
+        if (rc < 0) {
+            return statusFromTrusty(rc);
+        }
+        LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size,
+                            "Sent the wrong number of bytes %d!=%zu", rc, size);
+
+        return OK;
+    }
+
+    status_t interruptableReadFully(
+            FdTrigger* fdTrigger, iovec* iovs, int niovs,
+            const std::optional<android::base::function_ref<status_t()>>& altPoll,
+            std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
+        if (niovs < 0) {
+            return BAD_VALUE;
+        }
+
+        // If iovs has one or more empty vectors at the end and
+        // we somehow advance past all the preceding vectors and
+        // pass some or all of the empty ones to sendmsg/recvmsg,
+        // the call will return processSize == 0. In that case
+        // we should be returning OK but instead return DEAD_OBJECT.
+        // To avoid this problem, we make sure here that the last
+        // vector at iovs[niovs - 1] has a non-zero length.
+        while (niovs > 0 && iovs[niovs - 1].iov_len == 0) {
+            niovs--;
+        }
+        if (niovs == 0) {
+            // The vectors are all empty, so we have nothing to read.
+            return OK;
+        }
+
+        while (true) {
+            auto status = ensureMessage(true);
+            if (status != OK) {
+                return status;
+            }
+
+            ipc_msg_t msg{
+                    .num_iov = static_cast<uint32_t>(niovs),
+                    .iov = iovs,
+                    .num_handles = 0, // TODO: support ancillaryFds
+                    .handles = nullptr,
+            };
+            int rc = read_msg(mSocket.get(), mMessageInfo.id, mMessageOffset, &msg);
+            if (rc < 0) {
+                return statusFromTrusty(rc);
+            }
+
+            size_t processSize = static_cast<size_t>(rc);
+            mMessageOffset += processSize;
+            LOG_ALWAYS_FATAL_IF(mMessageOffset > mMessageInfo.len,
+                                "Message offset exceeds length %zu/%zu", mMessageOffset,
+                                mMessageInfo.len);
+
+            // Release the message if all of it has been read
+            if (mMessageOffset == mMessageInfo.len) {
+                releaseMessage();
+            }
+
+            while (processSize > 0 && niovs > 0) {
+                auto& iov = iovs[0];
+                if (processSize < iov.iov_len) {
+                    // Advance the base of the current iovec
+                    iov.iov_base = reinterpret_cast<char*>(iov.iov_base) + processSize;
+                    iov.iov_len -= processSize;
+                    break;
+                }
+
+                // The current iovec was fully written
+                processSize -= iov.iov_len;
+                iovs++;
+                niovs--;
+            }
+            if (niovs == 0) {
+                LOG_ALWAYS_FATAL_IF(processSize > 0,
+                                    "Reached the end of iovecs "
+                                    "with %zd bytes remaining",
+                                    processSize);
+                return OK;
+            }
+        }
+    }
+
+private:
+    status_t ensureMessage(bool wait) {
+        int rc;
+        if (mHaveMessage) {
+            LOG_ALWAYS_FATAL_IF(mMessageOffset >= mMessageInfo.len, "No data left in message");
+            return OK;
+        }
+
+        /* TODO: interruptible wait, maybe with a timeout??? */
+        uevent uevt;
+        rc = ::wait(mSocket.get(), &uevt, wait ? INFINITE_TIME : 0);
+        if (rc < 0) {
+            if (rc == ERR_TIMED_OUT && !wait) {
+                // If we timed out with wait==false, then there's no message
+                return OK;
+            }
+            return statusFromTrusty(rc);
+        }
+        if (!(uevt.event & IPC_HANDLE_POLL_MSG)) {
+            /* No message, terminate here and leave mHaveMessage false */
+            return OK;
+        }
+
+        rc = get_msg(mSocket.get(), &mMessageInfo);
+        if (rc < 0) {
+            return statusFromTrusty(rc);
+        }
+
+        mHaveMessage = true;
+        mMessageOffset = 0;
+        return OK;
+    }
+
+    void releaseMessage() {
+        if (mHaveMessage) {
+            put_msg(mSocket.get(), mMessageInfo.id);
+            mHaveMessage = false;
+        }
+    }
+
+    base::unique_fd mSocket;
+
+    bool mHaveMessage = false;
+    ipc_msg_info mMessageInfo;
+    size_t mMessageOffset;
+};
+
+// RpcTransportCtx for Trusty.
+class RpcTransportCtxTipcTrusty : public RpcTransportCtx {
+public:
+    std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd,
+                                               FdTrigger*) const override {
+        return std::make_unique<RpcTransportTipcTrusty>(std::move(fd));
+    }
+    std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
+};
+
+} // namespace
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newServerCtx() const {
+    return std::make_unique<RpcTransportCtxTipcTrusty>();
+}
+
+std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTipcTrusty::newClientCtx() const {
+    return std::make_unique<RpcTransportCtxTipcTrusty>();
+}
+
+const char* RpcTransportCtxFactoryTipcTrusty::toCString() const {
+    return "trusty";
+}
+
+std::unique_ptr<RpcTransportCtxFactory> RpcTransportCtxFactoryTipcTrusty::make() {
+    return std::unique_ptr<RpcTransportCtxFactoryTipcTrusty>(
+            new RpcTransportCtxFactoryTipcTrusty());
+}
+
+} // namespace android
diff --git a/libs/binder/trusty/TrustyStatus.cpp b/libs/binder/trusty/TrustyStatus.cpp
new file mode 100644
index 0000000..b1caf61
--- /dev/null
+++ b/libs/binder/trusty/TrustyStatus.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include "TrustyStatus.h"
+#include "../RpcState.h"
+
+namespace android {
+
+status_t statusFromTrusty(int rc) {
+    LOG_RPC_DETAIL("Trusty error: %d", rc);
+    switch (rc) {
+        case NO_ERROR:
+            return OK;
+        case ERR_NOT_FOUND:
+            return NAME_NOT_FOUND;
+        case ERR_NOT_READY:
+            // We get this error if we try to perform an IPC operation when the
+            // channel is not ready
+            return INVALID_OPERATION;
+        case ERR_NO_MSG:
+            return WOULD_BLOCK;
+        case ERR_NO_MEMORY:
+            return NO_MEMORY;
+        case ERR_INVALID_ARGS:
+            return BAD_VALUE;
+        case ERR_NOT_ENOUGH_BUFFER:
+            return WOULD_BLOCK;
+        case ERR_TIMED_OUT:
+            return TIMED_OUT;
+        case ERR_ALREADY_EXISTS:
+            return ALREADY_EXISTS;
+        case ERR_CHANNEL_CLOSED:
+            return DEAD_OBJECT;
+        case ERR_NOT_ALLOWED:
+            return INVALID_OPERATION;
+        case ERR_NOT_SUPPORTED:
+            return INVALID_OPERATION;
+        case ERR_TOO_BIG:
+            return BAD_INDEX;
+        case ERR_CMD_UNKNOWN:
+            return UNKNOWN_TRANSACTION;
+        case ERR_BAD_STATE:
+            return INVALID_OPERATION;
+        case ERR_BAD_LEN:
+            return NOT_ENOUGH_DATA;
+        case ERR_BAD_HANDLE:
+            return BAD_VALUE;
+        case ERR_ACCESS_DENIED:
+            return PERMISSION_DENIED;
+        default:
+            return UNKNOWN_ERROR;
+    }
+}
+
+int statusToTrusty(status_t status) {
+    switch (status) {
+        case OK:
+            return NO_ERROR;
+        case NO_MEMORY:
+            return ERR_NO_MEMORY;
+        case INVALID_OPERATION:
+        case BAD_VALUE:
+        case BAD_TYPE:
+            return ERR_NOT_VALID;
+        case NAME_NOT_FOUND:
+            return ERR_NOT_FOUND;
+        case PERMISSION_DENIED:
+            return ERR_ACCESS_DENIED;
+        case NO_INIT:
+            return ERR_NOT_CONFIGURED;
+        case ALREADY_EXISTS:
+            return ERR_ALREADY_EXISTS;
+        case DEAD_OBJECT:
+            return ERR_CHANNEL_CLOSED;
+        case BAD_INDEX:
+            return ERR_TOO_BIG;
+        case NOT_ENOUGH_DATA:
+            return ERR_BAD_LEN;
+        case WOULD_BLOCK:
+            return ERR_NO_MSG;
+        case TIMED_OUT:
+            return ERR_TIMED_OUT;
+        case UNKNOWN_TRANSACTION:
+            return ERR_CMD_UNKNOWN;
+        case FDS_NOT_ALLOWED:
+            return ERR_NOT_SUPPORTED;
+        case UNEXPECTED_NULL:
+            return ERR_NOT_VALID;
+        default:
+            return ERR_GENERIC;
+    }
+}
+
+} // namespace android
diff --git a/libs/binder/trusty/TrustyStatus.h b/libs/binder/trusty/TrustyStatus.h
new file mode 100644
index 0000000..fcb43f8
--- /dev/null
+++ b/libs/binder/trusty/TrustyStatus.h
@@ -0,0 +1,26 @@
+/*
+ * 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
+
+#include <uapi/err.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+status_t statusFromTrusty(int rc);
+int statusToTrusty(status_t status);
+
+} // namespace android
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
new file mode 100644
index 0000000..e8fc9f9
--- /dev/null
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -0,0 +1,100 @@
+/*
+ * 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
+
+#include <android-base/expected.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcTransport.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <vector>
+
+#include <lib/tipc/tipc_srv.h>
+
+namespace android {
+
+/**
+ * This is the Trusty-specific RPC server code.
+ */
+class RpcServerTrusty final : public virtual RefBase {
+public:
+    // C++ equivalent to tipc_port_acl that uses safe data structures instead of
+    // raw pointers, except for |extraData| which doesn't have a good
+    // equivalent.
+    struct PortAcl {
+        uint32_t flags;
+        std::vector<const uuid> uuids;
+        const void* extraData;
+    };
+
+    /**
+     * Creates an RPC server listening on the given port and adds it to the
+     * Trusty handle set at |handleSet|.
+     *
+     * The caller is responsible for calling tipc_run_event_loop() to start
+     * the TIPC event loop after creating one or more services here.
+     */
+    static android::base::expected<sp<RpcServerTrusty>, int> make(
+            tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
+            size_t msgMaxSize,
+            std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
+
+    void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); }
+    void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); }
+    void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); }
+    void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) {
+        mRpcServer->setPerSessionRootObject(std::move(object));
+    }
+    sp<IBinder> getRootObject() { return mRpcServer->getRootObject(); }
+
+private:
+    // Both this class and RpcServer have multiple non-copyable fields,
+    // including mPortAcl below which can't be copied because mUuidPtrs
+    // holds pointers into it
+    DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty);
+
+    friend sp<RpcServerTrusty>;
+    explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
+                             std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize);
+
+    static int handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, void** ctx_p);
+    static int handleMessage(const tipc_port* port, handle_t chan, void* ctx);
+    static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx);
+    static void handleChannelCleanup(void* ctx);
+
+    static constexpr tipc_srv_ops kTipcOps = {
+            .on_connect = &handleConnect,
+            .on_message = &handleMessage,
+            .on_disconnect = &handleDisconnect,
+            .on_channel_cleanup = &handleChannelCleanup,
+    };
+
+    sp<RpcServer> mRpcServer;
+    std::string mPortName;
+    std::shared_ptr<const PortAcl> mPortAcl;
+    std::vector<const uuid*> mUuidPtrs;
+    tipc_port_acl mTipcPortAcl;
+    tipc_port mTipcPort;
+};
+
+} // namespace android
diff --git a/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h
new file mode 100644
index 0000000..8eae8c2
--- /dev/null
+++ b/libs/binder/trusty/include/binder/RpcTransportTipcTrusty.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+// Wraps the transport layer of RPC. Implementation uses plain sockets.
+// Note: don't use directly. You probably want newServerRpcTransportCtx / newClientRpcTransportCtx.
+
+#pragma once
+
+#include <memory>
+
+#include <binder/RpcTransport.h>
+
+namespace android {
+
+// RpcTransportCtxFactory with TLS disabled.
+class RpcTransportCtxFactoryTipcTrusty : public RpcTransportCtxFactory {
+public:
+    static std::unique_ptr<RpcTransportCtxFactory> make();
+
+    std::unique_ptr<RpcTransportCtx> newServerCtx() const override;
+    std::unique_ptr<RpcTransportCtx> newClientCtx() const override;
+    const char* toCString() const override;
+
+private:
+    RpcTransportCtxFactoryTipcTrusty() = default;
+};
+
+} // namespace android
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
new file mode 100644
index 0000000..bf877a3
--- /dev/null
+++ b/libs/binder/trusty/include/log/log.h
@@ -0,0 +1,122 @@
+/*
+ * 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 BINDER_LOG_LEVEL_NONE 0
+#define BINDER_LOG_LEVEL_NORMAL 1
+#define BINDER_LOG_LEVEL_VERBOSE 2
+
+#ifndef BINDER_LOG_LEVEL
+#define BINDER_LOG_LEVEL BINDER_LOG_LEVEL_NORMAL
+#endif // BINDER_LOG_LEVEL
+
+#ifndef TLOG_TAG
+#ifdef LOG_TAG
+#define TLOG_TAG "libbinder-" LOG_TAG
+#else // LOG_TAG
+#define TLOG_TAG "libbinder"
+#endif // LOG_TAG
+#endif // TLOG_TAG
+
+#include <stdlib.h>
+#include <trusty_log.h>
+
+static inline void __ignore_va_args__(...) {}
+
+#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
+#define ALOGD(fmt, ...) TLOGD(fmt "\n", ##__VA_ARGS__)
+#define ALOGI(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__)
+#define ALOGW(fmt, ...) TLOGW(fmt "\n", ##__VA_ARGS__)
+#define ALOGE(fmt, ...) TLOGE(fmt "\n", ##__VA_ARGS__)
+#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
+#define ALOGD(fmt, ...)                  \
+    while (0) {                          \
+        __ignore_va_args__(__VA_ARGS__); \
+    }
+#define ALOGI(fmt, ...)                  \
+    while (0) {                          \
+        __ignore_va_args__(__VA_ARGS__); \
+    }
+#define ALOGW(fmt, ...)                  \
+    while (0) {                          \
+        __ignore_va_args__(__VA_ARGS__); \
+    }
+#define ALOGE(fmt, ...)                  \
+    while (0) {                          \
+        __ignore_va_args__(__VA_ARGS__); \
+    }
+#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
+
+#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
+#define IF_ALOGV() if (TLOG_LVL >= TLOG_LVL_INFO)
+#define ALOGV(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__)
+#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
+#define IF_ALOGV() if (false)
+#define ALOGV(fmt, ...)                  \
+    while (0) {                          \
+        __ignore_va_args__(__VA_ARGS__); \
+    }
+#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
+
+#define ALOGI_IF(cond, ...)                \
+    do {                                   \
+        if (cond) {                        \
+            ALOGI(#cond ": " __VA_ARGS__); \
+        }                                  \
+    } while (0)
+#define ALOGE_IF(cond, ...)                \
+    do {                                   \
+        if (cond) {                        \
+            ALOGE(#cond ": " __VA_ARGS__); \
+        }                                  \
+    } while (0)
+#define ALOGW_IF(cond, ...)                \
+    do {                                   \
+        if (cond) {                        \
+            ALOGW(#cond ": " __VA_ARGS__); \
+        }                                  \
+    } while (0)
+
+#define LOG_ALWAYS_FATAL(fmt, ...)                                \
+    do {                                                          \
+        TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \
+        abort();                                                  \
+    } while (0)
+#define LOG_ALWAYS_FATAL_IF(cond, ...)                \
+    do {                                              \
+        if (cond) {                                   \
+            LOG_ALWAYS_FATAL(#cond ": " __VA_ARGS__); \
+        }                                             \
+    } while (0)
+#define LOG_FATAL(fmt, ...)                                       \
+    do {                                                          \
+        TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \
+        abort();                                                  \
+    } while (0)
+#define LOG_FATAL_IF(cond, ...)                \
+    do {                                       \
+        if (cond) {                            \
+            LOG_FATAL(#cond ": " __VA_ARGS__); \
+        }                                      \
+    } while (0)
+
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
+
+#define android_errorWriteLog(tag, subTag)                               \
+    do {                                                                 \
+        TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
+    } while (0)
diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp
new file mode 100644
index 0000000..fd54744
--- /dev/null
+++ b/libs/binder/trusty/logging.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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 TLOG_TAG "libbinder"
+
+#include "android-base/logging.h"
+
+#include <trusty_log.h>
+#include <iostream>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace base {
+
+static const char* GetFileBasename(const char* file) {
+    const char* last_slash = strrchr(file, '/');
+    if (last_slash != nullptr) {
+        return last_slash + 1;
+    }
+    return file;
+}
+
+// This splits the message up line by line, by calling log_function with a pointer to the start of
+// each line and the size up to the newline character.  It sends size = -1 for the final line.
+template <typename F, typename... Args>
+static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
+    const char* newline;
+    while ((newline = strchr(msg, '\n')) != nullptr) {
+        log_function(msg, newline - msg, args...);
+        msg = newline + 1;
+    }
+
+    log_function(msg, -1, args...);
+}
+
+void DefaultAborter(const char* abort_message) {
+    TLOGC("aborting: %s\n", abort_message);
+    abort();
+}
+
+static void TrustyLogLine(const char* msg, int length, android::base::LogSeverity severity,
+                          const char* tag) {
+    switch (severity) {
+        case VERBOSE:
+        case DEBUG:
+            TLOGD("%s: %s\n", tag, msg);
+            break;
+        case INFO:
+            TLOGI("%s: %s\n", tag, msg);
+            break;
+        case WARNING:
+            TLOGW("%s: %s\n", tag, msg);
+            break;
+        case ERROR:
+            TLOGE("%s: %s\n", tag, msg);
+            break;
+        case FATAL_WITHOUT_ABORT:
+        case FATAL:
+            TLOGC("%s: %s\n", tag, msg);
+            break;
+    }
+}
+
+void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
+                  const char*, unsigned int, const char* full_message) {
+    SplitByLines(full_message, TrustyLogLine, severity, tag);
+}
+
+// This indirection greatly reduces the stack impact of having lots of
+// checks/logging in a function.
+class LogMessageData {
+public:
+    LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                   int error)
+          : file_(GetFileBasename(file)),
+            line_number_(line),
+            severity_(severity),
+            tag_(tag),
+            error_(error) {}
+
+    const char* GetFile() const { return file_; }
+
+    unsigned int GetLineNumber() const { return line_number_; }
+
+    LogSeverity GetSeverity() const { return severity_; }
+
+    const char* GetTag() const { return tag_; }
+
+    int GetError() const { return error_; }
+
+    std::ostream& GetBuffer() { return buffer_; }
+
+    std::string ToString() const { return buffer_.str(); }
+
+private:
+    std::ostringstream buffer_;
+    const char* const file_;
+    const unsigned int line_number_;
+    const LogSeverity severity_;
+    const char* const tag_;
+    const int error_;
+
+    DISALLOW_COPY_AND_ASSIGN(LogMessageData);
+};
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
+                       const char* tag, int error)
+      : LogMessage(file, line, severity, tag, error) {}
+
+LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                       int error)
+      : data_(new LogMessageData(file, line, severity, tag, error)) {}
+
+LogMessage::~LogMessage() {
+    // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
+    if (!WOULD_LOG(data_->GetSeverity())) {
+        return;
+    }
+
+    // Finish constructing the message.
+    if (data_->GetError() != -1) {
+        data_->GetBuffer() << ": " << strerror(data_->GetError());
+    }
+    std::string msg(data_->ToString());
+
+    LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+            msg.c_str());
+
+    // Abort if necessary.
+    if (data_->GetSeverity() == FATAL) {
+        DefaultAborter(msg.c_str());
+    }
+}
+
+std::ostream& LogMessage::stream() {
+    return data_->GetBuffer();
+}
+
+void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
+                         const char* message) {
+    TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message);
+}
+
+bool ShouldLog(LogSeverity severity, const char* tag) {
+    // This is controlled by Trusty's log level.
+    return true;
+}
+
+} // namespace base
+} // namespace android
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
new file mode 100644
index 0000000..cd81a09
--- /dev/null
+++ b/libs/binder/trusty/rules.mk
@@ -0,0 +1,82 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+LIBBINDER_DIR := frameworks/native/libs/binder
+LIBBASE_DIR := system/libbase
+LIBCUTILS_DIR := system/core/libcutils
+LIBUTILS_DIR := system/core/libutils
+FMTLIB_DIR := external/fmtlib
+
+MODULE_SRCS := \
+	$(LOCAL_DIR)/logging.cpp \
+	$(LOCAL_DIR)/OS.cpp \
+	$(LOCAL_DIR)/RpcServerTrusty.cpp \
+	$(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \
+	$(LOCAL_DIR)/TrustyStatus.cpp \
+	$(LOCAL_DIR)/socket.cpp \
+	$(LIBBINDER_DIR)/Binder.cpp \
+	$(LIBBINDER_DIR)/BpBinder.cpp \
+	$(LIBBINDER_DIR)/FdTrigger.cpp \
+	$(LIBBINDER_DIR)/IInterface.cpp \
+	$(LIBBINDER_DIR)/IResultReceiver.cpp \
+	$(LIBBINDER_DIR)/Parcel.cpp \
+	$(LIBBINDER_DIR)/RpcServer.cpp \
+	$(LIBBINDER_DIR)/RpcSession.cpp \
+	$(LIBBINDER_DIR)/RpcState.cpp \
+	$(LIBBINDER_DIR)/Stability.cpp \
+	$(LIBBINDER_DIR)/Status.cpp \
+	$(LIBBINDER_DIR)/Utils.cpp \
+	$(LIBBASE_DIR)/hex.cpp \
+	$(LIBBASE_DIR)/stringprintf.cpp \
+	$(LIBUTILS_DIR)/Errors.cpp \
+	$(LIBUTILS_DIR)/misc.cpp \
+	$(LIBUTILS_DIR)/RefBase.cpp \
+	$(LIBUTILS_DIR)/StrongPointer.cpp \
+	$(LIBUTILS_DIR)/Unicode.cpp \
+
+# TODO: remove the following when libbinder supports std::string
+# instead of String16 and String8 for Status and descriptors
+MODULE_SRCS += \
+	$(LIBUTILS_DIR)/SharedBuffer.cpp \
+	$(LIBUTILS_DIR)/String16.cpp \
+	$(LIBUTILS_DIR)/String8.cpp \
+
+# TODO: disable dump() transactions to get rid of Vector
+MODULE_SRCS += \
+	$(LIBUTILS_DIR)/VectorImpl.cpp \
+
+MODULE_EXPORT_INCLUDES += \
+	$(LOCAL_DIR)/include \
+	$(LIBBINDER_DIR)/include \
+	$(LIBBASE_DIR)/include \
+	$(LIBCUTILS_DIR)/include \
+	$(LIBUTILS_DIR)/include \
+	$(FMTLIB_DIR)/include \
+
+MODULE_EXPORT_COMPILEFLAGS += \
+	-DBINDER_NO_KERNEL_IPC \
+	-DBINDER_RPC_SINGLE_THREADED \
+	-D__ANDROID_VNDK__ \
+
+MODULE_LIBRARY_DEPS += \
+	trusty/user/base/lib/libstdc++-trusty \
+	trusty/user/base/lib/tipc \
+	external/boringssl \
+
+include make/library.mk
diff --git a/libs/binder/trusty/socket.cpp b/libs/binder/trusty/socket.cpp
new file mode 100644
index 0000000..02df8af
--- /dev/null
+++ b/libs/binder/trusty/socket.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#include <log/log.h>
+
+// On some versions of clang, RpcServer.cpp refuses to link without accept4
+__attribute__((weak)) extern "C" int accept4(int, void*, void*, int) {
+    LOG_ALWAYS_FATAL("accept4 called on Trusty");
+    return 0;
+}
+
+// Placeholder for poll used by FdTrigger
+__attribute__((weak)) extern "C" int poll(void*, long, int) {
+    LOG_ALWAYS_FATAL("poll called on Trusty");
+    return 0;
+}
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d098e3a..d7bc500 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -193,6 +193,7 @@
         "BufferHubConsumer.cpp",
         "BufferHubProducer.cpp",
         "BufferItemConsumer.cpp",
+        "CompositorTiming.cpp",
         "ConsumerBase.cpp",
         "CpuConsumer.cpp",
         "DebugEGLImageTracker.cpp",
diff --git a/libs/gui/CompositorTiming.cpp b/libs/gui/CompositorTiming.cpp
new file mode 100644
index 0000000..50f7b25
--- /dev/null
+++ b/libs/gui/CompositorTiming.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CompositorTiming"
+
+#include <cutils/compiler.h>
+#include <gui/CompositorTiming.h>
+#include <log/log.h>
+
+namespace android::gui {
+
+CompositorTiming::CompositorTiming(nsecs_t vsyncDeadline, nsecs_t vsyncPeriod, nsecs_t vsyncPhase,
+                                   nsecs_t presentLatency) {
+    if (CC_UNLIKELY(vsyncPeriod <= 0)) {
+        ALOGE("Invalid VSYNC period");
+        return;
+    }
+
+    const nsecs_t idealLatency = [=] {
+        // Modulo rounds toward 0 not INT64_MIN, so treat signs separately.
+        if (vsyncPhase < 0) return -vsyncPhase % vsyncPeriod;
+
+        const nsecs_t latency = (vsyncPeriod - vsyncPhase) % vsyncPeriod;
+        return latency > 0 ? latency : vsyncPeriod;
+    }();
+
+    // Snap the latency to a value that removes scheduling jitter from the composite and present
+    // times, which often have >1ms of jitter. Reducing jitter is important if an app attempts to
+    // extrapolate something like user input to an accurate present time. Snapping also allows an
+    // app to precisely calculate vsyncPhase with (presentLatency % interval).
+    const nsecs_t bias = vsyncPeriod / 2;
+    const nsecs_t extraVsyncs = (presentLatency - idealLatency + bias) / vsyncPeriod;
+    const nsecs_t snappedLatency =
+            extraVsyncs > 0 ? idealLatency + extraVsyncs * vsyncPeriod : idealLatency;
+
+    this->deadline = vsyncDeadline - idealLatency;
+    this->interval = vsyncPeriod;
+    this->presentLatency = snappedLatency;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 952b6a9..f1c529d 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -13,6 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_defaults {
     name: "libgui_fuzzer_defaults",
     static_libs: [
diff --git a/libs/gui/include/gui/CompositorTiming.h b/libs/gui/include/gui/CompositorTiming.h
new file mode 100644
index 0000000..cb8ca7a
--- /dev/null
+++ b/libs/gui/include/gui/CompositorTiming.h
@@ -0,0 +1,43 @@
+/*
+ * 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
+
+#include <utils/Timers.h>
+
+namespace android::gui {
+
+// Expected timing of the next composited frame, based on the timing of the latest frames.
+struct CompositorTiming {
+    static constexpr nsecs_t kDefaultVsyncPeriod = 16'666'667;
+
+    CompositorTiming() = default;
+    CompositorTiming(nsecs_t vsyncDeadline, nsecs_t vsyncPeriod, nsecs_t vsyncPhase,
+                     nsecs_t presentLatency);
+
+    // Time point when compositing is expected to start.
+    nsecs_t deadline = 0;
+
+    // Duration between consecutive frames. In other words, the VSYNC period.
+    nsecs_t interval = kDefaultVsyncPeriod;
+
+    // Duration between composite start and present. For missed frames, the extra latency is rounded
+    // to a multiple of the VSYNC period, such that the remainder (presentLatency % interval) always
+    // evaluates to the VSYNC phase offset.
+    nsecs_t presentLatency = kDefaultVsyncPeriod;
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index f73bc3b..c08a9b1 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -19,6 +19,7 @@
 
 #include <android/gui/FrameEvent.h>
 
+#include <gui/CompositorTiming.h>
 #include <ui/FenceTime.h>
 #include <utils/Flattenable.h>
 #include <utils/StrongPointer.h>
@@ -33,6 +34,7 @@
 struct FrameEvents;
 class FrameEventHistoryDelta;
 
+using gui::CompositorTiming;
 using gui::FrameEvent;
 
 // A collection of timestamps corresponding to a single frame.
@@ -83,12 +85,6 @@
     std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE};
 };
 
-struct CompositorTiming {
-    nsecs_t deadline{0};
-    nsecs_t interval{16666667};
-    nsecs_t presentLatency{16666667};
-};
-
 // A short history of frames that are synchronized between the consumer and
 // producer via deltas.
 class FrameEventHistory {
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index fc68ad2..7b37b25 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -24,6 +24,7 @@
         "BLASTBufferQueue_test.cpp",
         "BufferItemConsumer_test.cpp",
         "BufferQueue_test.cpp",
+        "CompositorTiming_test.cpp",
         "CpuConsumer_test.cpp",
         "EndToEndNativeInputTest.cpp",
         "DisplayInfo_test.cpp",
diff --git a/libs/gui/tests/CompositorTiming_test.cpp b/libs/gui/tests/CompositorTiming_test.cpp
new file mode 100644
index 0000000..d8bb21d
--- /dev/null
+++ b/libs/gui/tests/CompositorTiming_test.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <gui/CompositorTiming.h>
+
+namespace android::test {
+namespace {
+
+constexpr nsecs_t kMillisecond = 1'000'000;
+constexpr nsecs_t kVsyncPeriod = 8'333'333;
+constexpr nsecs_t kVsyncPhase = -2'166'667;
+constexpr nsecs_t kIdealLatency = -kVsyncPhase;
+
+} // namespace
+
+TEST(CompositorTimingTest, InvalidVsyncPeriod) {
+    const nsecs_t vsyncDeadline = systemTime();
+    constexpr nsecs_t kInvalidVsyncPeriod = -1;
+
+    const gui::CompositorTiming timing(vsyncDeadline, kInvalidVsyncPeriod, kVsyncPhase,
+                                       kIdealLatency);
+
+    EXPECT_EQ(timing.deadline, 0);
+    EXPECT_EQ(timing.interval, gui::CompositorTiming::kDefaultVsyncPeriod);
+    EXPECT_EQ(timing.presentLatency, gui::CompositorTiming::kDefaultVsyncPeriod);
+}
+
+TEST(CompositorTimingTest, PresentLatencySnapping) {
+    for (nsecs_t presentDelay = 0, compositeTime = systemTime(); presentDelay < 10 * kVsyncPeriod;
+         presentDelay += kMillisecond, compositeTime += kVsyncPeriod) {
+        const nsecs_t presentLatency = kIdealLatency + presentDelay;
+        const nsecs_t vsyncDeadline = compositeTime + presentLatency + kVsyncPeriod;
+
+        const gui::CompositorTiming timing(vsyncDeadline, kVsyncPeriod, kVsyncPhase,
+                                           presentLatency);
+
+        EXPECT_EQ(timing.deadline, compositeTime + presentDelay + kVsyncPeriod);
+        EXPECT_EQ(timing.interval, kVsyncPeriod);
+
+        // The presentDelay should be rounded to a multiple of the VSYNC period, such that the
+        // remainder (presentLatency % interval) always evaluates to the VSYNC phase offset.
+        EXPECT_GE(timing.presentLatency, kIdealLatency);
+        EXPECT_EQ(timing.presentLatency % timing.interval, kIdealLatency);
+    }
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 7eff3b3..391a0aa 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -1087,20 +1087,30 @@
 protected:
     struct FenceAndFenceTime {
         explicit FenceAndFenceTime(FenceToFenceTimeMap& fenceMap)
-           : mFence(new Fence),
-             mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {}
-        sp<Fence> mFence { nullptr };
-        std::shared_ptr<FenceTime> mFenceTime { nullptr };
+              : mFenceTime(fenceMap.createFenceTimeForTest(mFence)) {}
+
+        sp<Fence> mFence = sp<Fence>::make();
+        std::shared_ptr<FenceTime> mFenceTime;
     };
 
+    static CompositorTiming makeCompositorTiming(nsecs_t deadline = 1'000'000'000,
+                                                 nsecs_t interval = 16'666'667,
+                                                 nsecs_t presentLatency = 50'000'000) {
+        CompositorTiming timing;
+        timing.deadline = deadline;
+        timing.interval = interval;
+        timing.presentLatency = presentLatency;
+        return timing;
+    }
+
     struct RefreshEvents {
         RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart)
-          : mFenceMap(fenceMap),
-            kCompositorTiming(
-                {refreshStart, refreshStart + 1, refreshStart + 2 }),
-            kStartTime(refreshStart + 3),
-            kGpuCompositionDoneTime(refreshStart + 4),
-            kPresentTime(refreshStart + 5) {}
+              : mFenceMap(fenceMap),
+                kCompositorTiming(
+                        makeCompositorTiming(refreshStart, refreshStart + 1, refreshStart + 2)),
+                kStartTime(refreshStart + 3),
+                kGpuCompositionDoneTime(refreshStart + 4),
+                kPresentTime(refreshStart + 5) {}
 
         void signalPostCompositeFences() {
             mFenceMap.signalAllForTest(
@@ -1110,8 +1120,8 @@
 
         FenceToFenceTimeMap& mFenceMap;
 
-        FenceAndFenceTime mGpuCompositionDone { mFenceMap };
-        FenceAndFenceTime mPresent { mFenceMap };
+        FenceAndFenceTime mGpuCompositionDone{mFenceMap};
+        FenceAndFenceTime mPresent{mFenceMap};
 
         const CompositorTiming kCompositorTiming;
 
@@ -1377,11 +1387,7 @@
 // This test verifies that the frame timestamps are retrieved if explicitly
 // enabled via native_window_enable_frame_timestamps.
 TEST_F(GetFrameTimestampsTest, EnabledSimple) {
-    CompositorTiming initialCompositorTiming {
-        1000000000, // 1s deadline
-        16666667, // 16ms interval
-        50000000, // 50ms present latency
-    };
+    const CompositorTiming initialCompositorTiming = makeCompositorTiming();
     mCfeh->initializeCompositorTiming(initialCompositorTiming);
 
     enableFrameTimestamps();
@@ -1520,11 +1526,7 @@
 // This verifies the compositor timing is updated by refresh events
 // and piggy backed on a queue, dequeue, and enabling of timestamps..
 TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) {
-    CompositorTiming initialCompositorTiming {
-        1000000000, // 1s deadline
-        16666667, // 16ms interval
-        50000000, // 50ms present latency
-    };
+    const CompositorTiming initialCompositorTiming = makeCompositorTiming();
     mCfeh->initializeCompositorTiming(initialCompositorTiming);
 
     enableFrameTimestamps();
@@ -1605,11 +1607,7 @@
 // This verifies the compositor deadline properly snaps to the the next
 // deadline based on the current time.
 TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) {
-    CompositorTiming initialCompositorTiming {
-        1000000000, // 1s deadline
-        16666667, // 16ms interval
-        50000000, // 50ms present latency
-    };
+    const CompositorTiming initialCompositorTiming = makeCompositorTiming();
     mCfeh->initializeCompositorTiming(initialCompositorTiming);
 
     enableFrameTimestamps();
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index c3f5151..3f8467d 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -49,25 +49,23 @@
         const PropertyMap* deviceConfiguration) {
     // Use the configured key layout if available.
     if (deviceConfiguration) {
-        String8 keyLayoutName;
-        if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
-                keyLayoutName)) {
+        std::string keyLayoutName;
+        if (deviceConfiguration->tryGetProperty("keyboard.layout", keyLayoutName)) {
             status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str());
             if (status == NAME_NOT_FOUND) {
                 ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
-                        "it was not found.",
-                        deviceIdentifier.name.c_str(), keyLayoutName.string());
+                      "it was not found.",
+                      deviceIdentifier.name.c_str(), keyLayoutName.c_str());
             }
         }
 
-        String8 keyCharacterMapName;
-        if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
-                keyCharacterMapName)) {
+        std::string keyCharacterMapName;
+        if (deviceConfiguration->tryGetProperty("keyboard.characterMap", keyCharacterMapName)) {
             status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str());
             if (status == NAME_NOT_FOUND) {
                 ALOGE("Configuration for keyboard device '%s' requested keyboard character "
-                        "map '%s' but it was not found.",
-                        deviceIdentifier.name.c_str(), keyCharacterMapName.string());
+                      "map '%s' but it was not found.",
+                      deviceIdentifier.name.c_str(), keyCharacterMapName.c_str());
             }
         }
 
@@ -165,7 +163,7 @@
         return false;
     }
     bool isSpecialFunction = false;
-    config->tryGetProperty(String8("keyboard.specialFunction"), isSpecialFunction);
+    config->tryGetProperty("keyboard.specialFunction", isSpecialFunction);
     return isSpecialFunction;
 }
 
@@ -180,8 +178,7 @@
 
     if (deviceConfiguration) {
         bool builtIn = false;
-        if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn)
-                && builtIn) {
+        if (deviceConfiguration->tryGetProperty("keyboard.builtIn", builtIn) && builtIn) {
             return true;
         }
     }
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index a842166..662e568 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "PropertyMap"
 
 #include <input/PropertyMap.h>
+#include <log/log.h>
 
 // Enables debug output for the parser.
 #define DEBUG_PARSER 0
@@ -39,25 +40,25 @@
     mProperties.clear();
 }
 
-void PropertyMap::addProperty(const String8& key, const String8& value) {
-    mProperties.add(key, value);
+void PropertyMap::addProperty(const std::string& key, const std::string& value) {
+    mProperties.emplace(key, value);
 }
 
-bool PropertyMap::hasProperty(const String8& key) const {
-    return mProperties.indexOfKey(key) >= 0;
+bool PropertyMap::hasProperty(const std::string& key) const {
+    return mProperties.find(key) != mProperties.end();
 }
 
-bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
-    ssize_t index = mProperties.indexOfKey(key);
-    if (index < 0) {
+bool PropertyMap::tryGetProperty(const std::string& key, std::string& outValue) const {
+    auto it = mProperties.find(key);
+    if (it == mProperties.end()) {
         return false;
     }
 
-    outValue = mProperties.valueAt(index);
+    outValue = it->second;
     return true;
 }
 
-bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+bool PropertyMap::tryGetProperty(const std::string& key, bool& outValue) const {
     int32_t intValue;
     if (!tryGetProperty(key, intValue)) {
         return false;
@@ -67,34 +68,34 @@
     return true;
 }
 
-bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
-    String8 stringValue;
+bool PropertyMap::tryGetProperty(const std::string& key, int32_t& outValue) const {
+    std::string stringValue;
     if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
         return false;
     }
 
     char* end;
-    int value = strtol(stringValue.string(), &end, 10);
+    int value = strtol(stringValue.c_str(), &end, 10);
     if (*end != '\0') {
-        ALOGW("Property key '%s' has invalid value '%s'.  Expected an integer.", key.string(),
-              stringValue.string());
+        ALOGW("Property key '%s' has invalid value '%s'.  Expected an integer.", key.c_str(),
+              stringValue.c_str());
         return false;
     }
     outValue = value;
     return true;
 }
 
-bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
-    String8 stringValue;
+bool PropertyMap::tryGetProperty(const std::string& key, float& outValue) const {
+    std::string stringValue;
     if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
         return false;
     }
 
     char* end;
-    float value = strtof(stringValue.string(), &end);
+    float value = strtof(stringValue.c_str(), &end);
     if (*end != '\0') {
-        ALOGW("Property key '%s' has invalid value '%s'.  Expected a float.", key.string(),
-              stringValue.string());
+        ALOGW("Property key '%s' has invalid value '%s'.  Expected a float.", key.c_str(),
+              stringValue.c_str());
         return false;
     }
     outValue = value;
@@ -102,8 +103,8 @@
 }
 
 void PropertyMap::addAll(const PropertyMap* map) {
-    for (size_t i = 0; i < map->mProperties.size(); i++) {
-        mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
+    for (const auto& [key, value] : map->mProperties) {
+        mProperties.emplace(key, value);
     }
 }
 
@@ -184,13 +185,13 @@
                 return BAD_VALUE;
             }
 
-            if (mMap->hasProperty(keyToken)) {
+            if (mMap->hasProperty(keyToken.string())) {
                 ALOGE("%s: Duplicate property value for key '%s'.",
                       mTokenizer->getLocation().string(), keyToken.string());
                 return BAD_VALUE;
             }
 
-            mMap->addProperty(keyToken, valueToken);
+            mMap->addProperty(keyToken.string(), valueToken.string());
         }
 
         mTokenizer->nextLine();
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
index afb97a1..d985dc1 100755
--- a/libs/input/PropertyMap_fuzz.cpp
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -17,32 +17,22 @@
 #include "android-base/file.h"
 #include "fuzzer/FuzzedDataProvider.h"
 #include "input/PropertyMap.h"
-#include "utils/String8.h"
 
 static constexpr int MAX_FILE_SIZE = 256;
 static constexpr int MAX_STR_LEN = 2048;
 static constexpr int MAX_OPERATIONS = 1000;
 
-static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>>
+static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap&)>>
         operations = {
-                [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
-                    propertyMap.getProperties();
-                },
-                [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+                [](FuzzedDataProvider*, android::PropertyMap& propertyMap) -> void {
                     propertyMap.clear();
                 },
-                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
-                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
-                    android::String8 key = android::String8(keyStr.c_str());
-                    propertyMap.hasProperty(key);
-                },
-                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
-                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
-                    android::String8 key = android::String8(keyStr.c_str());
-                    android::String8 out;
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void {
+                    std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    std::string out;
                     propertyMap.tryGetProperty(key, out);
                 },
-                [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void {
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap& /*unused*/) -> void {
                     TemporaryFile tf;
                     // Generate file contents
                     std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
@@ -54,17 +44,15 @@
                     }
                     android::PropertyMap::load(tf.path);
                 },
-                [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
-                    std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
-                    std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
-                    android::String8 key = android::String8(keyStr.c_str());
-                    android::String8 val = android::String8(valStr.c_str());
+                [](FuzzedDataProvider* dataProvider, android::PropertyMap& propertyMap) -> void {
+                    std::string key = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+                    std::string val = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
                     propertyMap.addProperty(key, val);
                 },
 };
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     FuzzedDataProvider dataProvider(data, size);
-    android::PropertyMap propertyMap = android::PropertyMap();
+    android::PropertyMap propertyMap;
 
     int opsRun = 0;
     while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 22dd866..6dc01b9 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -921,7 +921,8 @@
 
     // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
     // and the middle part without rounded corners.
-    const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+    const int32_t radius = ceil(
+            (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0);
     const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
     setScissor(topRect);
     drawMesh(mesh);
@@ -1266,23 +1267,24 @@
 
         const half3 solidColor = layer.source.solidColor;
         const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+        const float radius =
+                (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) /
+                2.0f;
         // Buffer sources will have a black solid color ignored in the shader,
         // so in that scenario the solid color passed here is arbitrary.
-        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
-                           layer.geometry.roundedCornersRadius);
+        setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius);
         if (layer.disableBlending) {
             glDisable(GL_BLEND);
         }
         setSourceDataSpace(layer.sourceDataspace);
 
         if (layer.shadow.length > 0.0f) {
-            handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
-                         layer.shadow);
+            handleShadow(layer.geometry.boundaries, radius, layer.shadow);
         }
         // We only want to do a special handling for rounded corners when having rounded corners
         // is the only reason it needs to turn on blending, otherwise, we handle it like the
         // usual way since it needs to turn on blending anyway.
-        else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+        else if (radius > 0.0 && color.a >= 1.0f && isOpaque) {
             handleRoundedCorners(display, layer, mesh);
         } else {
             drawMesh(mesh);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 154e526..b3a617c 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -87,7 +87,7 @@
     // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
     // in local layer coordinate space, so we have to take the layer transform into account when
     // walking up the tree.
-    float roundedCornersRadius = 0.0;
+    vec2 roundedCornersRadius = vec2(0.0f, 0.0f);
 
     // Rectangle within which corners will be rounded.
     FloatRect roundedCornersCrop = FloatRect();
@@ -258,7 +258,8 @@
     PrintTo(settings.boundaries, os);
     *os << "\n    .positionTransform = ";
     PrintMatrix(settings.positionTransform, os);
-    *os << "\n    .roundedCornersRadius = " << settings.roundedCornersRadius;
+    *os << "\n    .roundedCornersRadiusX = " << settings.roundedCornersRadius.x;
+    *os << "\n    .roundedCornersRadiusY = " << settings.roundedCornersRadius.y;
     *os << "\n    .roundedCornersCrop = ";
     PrintTo(settings.roundedCornersCrop, os);
     *os << "\n}";
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index f3064f3..c39f0a9 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -66,7 +66,7 @@
                     Geometry{
                             .boundaries = rect,
                             .roundedCornersCrop = rect,
-                            .roundedCornersRadius = 50.f,
+                            .roundedCornersRadius = {50.f, 50.f},
                     },
             // drawShadow ignores alpha
             .shadow =
@@ -87,7 +87,7 @@
                     Geometry{
                             .boundaries = smallerRect,
                             .roundedCornersCrop = rect,
-                            .roundedCornersRadius = 50.f,
+                            .roundedCornersRadius = {50.f, 50.f},
                     },
             .source =
                     PixelSource{
@@ -148,7 +148,7 @@
         // In reduced shader mode, all non-zero round rect radii get the same code path.
         for (float roundedCornersRadius : {0.0f, 50.0f}) {
             // roundedCornersCrop is always set, but the radius triggers the behavior
-            layer.geometry.roundedCornersRadius = roundedCornersRadius;
+            layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
             for (bool isOpaque : {true, false}) {
                 layer.source.buffer.isOpaque = isOpaque;
                 for (auto alpha : {half(.2f), half(1.0f)}) {
@@ -181,7 +181,7 @@
     for (auto transform : {mat4(), kScaleAndTranslate}) {
         layer.geometry.positionTransform = transform;
         for (float roundedCornersRadius : {0.0f, 50.f}) {
-            layer.geometry.roundedCornersRadius = roundedCornersRadius;
+            layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
             auto layers = std::vector<LayerSettings>{layer};
             renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
                                      base::unique_fd());
@@ -238,7 +238,7 @@
             .geometry =
                     Geometry{
                             .boundaries = rect,
-                            .roundedCornersRadius = 27, // larger than the 20 above.
+                            .roundedCornersRadius = {27.f, 27.f},
                             .roundedCornersCrop =
                                     FloatRect(0, 0, displayRect.width(), displayRect.height()),
                     },
@@ -275,9 +275,9 @@
                             // larger than the layer bounds.
                             .positionTransform = kFlip,
                             .boundaries = rect,
-                            .roundedCornersRadius = 94.2551,
-                            .roundedCornersCrop = FloatRect(
-                                -93.75, 0, displayRect.width() + 93.75, displayRect.height()),
+                            .roundedCornersRadius = {94.2551f, 94.2551f},
+                            .roundedCornersCrop = FloatRect(-93.75, 0, displayRect.width() + 93.75,
+                                                            displayRect.height()),
                     },
             .source = PixelSource{.buffer =
                                           Buffer{
@@ -307,10 +307,11 @@
                             // the boundaries have to be smaller than the rounded crop so that
                             // clipRRect is used instead of drawRRect
                             .boundaries = small,
-                            .roundedCornersRadius = 50.f,
+                            .roundedCornersRadius = {50.f, 50.f},
                             .roundedCornersCrop = rect,
                     },
-            .source = PixelSource{
+            .source =
+                    PixelSource{
                             .solidColor = half3(0.f, 0.f, 0.f),
                     },
             .sourceDataspace = kDestDataSpace,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 5187d7b..a0bba59 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1307,7 +1307,7 @@
  *  produce the insected roundRect. If false, the returned state of the radii param is undefined.
  */
 static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
-                                    const SkRect& insetCrop, float cornerRadius,
+                                    const SkRect& insetCrop, const vec2& cornerRadius,
                                     SkVector radii[4]) {
     const bool leftEqual = bounds.fLeft == crop.fLeft;
     const bool topEqual = bounds.fTop == crop.fTop;
@@ -1319,8 +1319,8 @@
     // In particular the round rect implementation will scale the value of all corner radii
     // if the sum of the radius along any edge is greater than the length of that edge.
     // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
-    const bool requiredWidth = bounds.width() > (cornerRadius * 2);
-    const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+    const bool requiredWidth = bounds.width() > (cornerRadius.x * 2);
+    const bool requiredHeight = bounds.height() > (cornerRadius.y * 2);
     if (!requiredWidth || !requiredHeight) {
         return false;
     }
@@ -1329,7 +1329,7 @@
     // contained within the cropped shape and does not need rounded.
     // compute the UpperLeft corner radius
     if (leftEqual && topEqual) {
-        radii[0].set(cornerRadius, cornerRadius);
+        radii[0].set(cornerRadius.x, cornerRadius.y);
     } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
                (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
         radii[0].set(0, 0);
@@ -1338,7 +1338,7 @@
     }
     // compute the UpperRight corner radius
     if (rightEqual && topEqual) {
-        radii[1].set(cornerRadius, cornerRadius);
+        radii[1].set(cornerRadius.x, cornerRadius.y);
     } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
                (topEqual && bounds.fRight <= insetCrop.fRight)) {
         radii[1].set(0, 0);
@@ -1347,7 +1347,7 @@
     }
     // compute the BottomRight corner radius
     if (rightEqual && bottomEqual) {
-        radii[2].set(cornerRadius, cornerRadius);
+        radii[2].set(cornerRadius.x, cornerRadius.y);
     } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
                (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
         radii[2].set(0, 0);
@@ -1356,7 +1356,7 @@
     }
     // compute the BottomLeft corner radius
     if (leftEqual && bottomEqual) {
-        radii[3].set(cornerRadius, cornerRadius);
+        radii[3].set(cornerRadius.x, cornerRadius.y);
     } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
                (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
         radii[3].set(0, 0);
@@ -1369,22 +1369,22 @@
 
 inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
                                                                         const FloatRect& cropRect,
-                                                                        const float cornerRadius) {
+                                                                        const vec2& cornerRadius) {
     const SkRect bounds = getSkRect(boundsRect);
     const SkRect crop = getSkRect(cropRect);
 
     SkRRect clip;
-    if (cornerRadius > 0) {
+    if (cornerRadius.x > 0 && cornerRadius.y > 0) {
         // it the crop and the bounds are equivalent or there is no crop then we don't need a clip
         if (bounds == crop || crop.isEmpty()) {
-            return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+            return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
         }
 
         // This makes an effort to speed up common, simple bounds + clip combinations by
         // converting them to a single RRect draw. It is possible there are other cases
         // that can be converted.
         if (crop.contains(bounds)) {
-            const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+            const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
             if (insetCrop.contains(bounds)) {
                 return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
             }
@@ -1398,7 +1398,7 @@
         }
 
         // we didn't hit any of our fast paths so set the clip to the cropRect
-        clip.setRectXY(crop, cornerRadius, cornerRadius);
+        clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
     }
 
     // if we hit this point then we either don't have rounded corners or we are going to rely
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index a8bc4f7..34f18c1 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -94,7 +94,8 @@
     inline SkRect getSkRect(const FloatRect& layer);
     inline SkRect getSkRect(const Rect& layer);
     inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
-                                                        const FloatRect& crop, float cornerRadius);
+                                                        const FloatRect& crop,
+                                                        const vec2& cornerRadius);
     inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
     inline SkColor getSkColor(const vec4& color);
     inline SkM44 getSkM44(const mat4& matrix);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 3340857..f289730 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -444,7 +444,9 @@
                            const ubyte4& backgroundColor) {
         const Rect casterRect(castingLayer.geometry.boundaries);
         Region casterRegion = Region(casterRect);
-        const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+        const float casterCornerRadius = (castingLayer.geometry.roundedCornersRadius.x +
+                                          castingLayer.geometry.roundedCornersRadius.y) /
+                2.0;
         if (casterCornerRadius > 0.0f) {
             // ignore the corners if a corner radius is set
             Rect cornerRect(casterCornerRadius, casterCornerRadius);
@@ -1129,7 +1131,7 @@
     renderengine::LayerSettings layer;
     layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
-    layer.geometry.roundedCornersRadius = 5.0f;
+    layer.geometry.roundedCornersRadius = {5.0f, 5.0f};
     layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
     SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0f;
@@ -2161,7 +2163,7 @@
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
     renderengine::LayerSettings castingLayer;
     castingLayer.geometry.boundaries = casterBounds.toFloatRect();
-    castingLayer.geometry.roundedCornersRadius = 3.0f;
+    castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
     castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
     castingLayer.alpha = 1.0f;
     renderengine::ShadowSettings settings =
@@ -2249,7 +2251,8 @@
     renderengine::LayerSettings redLayer;
     redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
-    redLayer.geometry.roundedCornersRadius = 5.0f;
+    redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
+
     redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
     // Red background.
     redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2261,7 +2264,7 @@
     renderengine::LayerSettings greenLayer;
     greenLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     greenLayer.geometry.boundaries = fullscreenRect().toFloatRect();
-    greenLayer.geometry.roundedCornersRadius = 5.0f;
+    greenLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
     // Bottom right corner is not going to be rounded.
     greenLayer.geometry.roundedCornersCrop =
             Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3, DEFAULT_DISPLAY_HEIGHT,
@@ -2298,7 +2301,7 @@
     renderengine::LayerSettings redLayer;
     redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
-    redLayer.geometry.roundedCornersRadius = 5.0f;
+    redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
     redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
     // Red background.
     redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2343,7 +2346,7 @@
     renderengine::LayerSettings redLayer;
     redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
-    redLayer.geometry.roundedCornersRadius = 64;
+    redLayer.geometry.roundedCornersRadius = {64.0f, 64.0f};
     redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
     // Red background.
     redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2364,6 +2367,49 @@
     expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
 }
 
+TEST_P(RenderEngineTest, testRoundedCornersXY) {
+    if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+        GTEST_SKIP();
+    }
+
+    initializeRenderEngine();
+
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings redLayer;
+    redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    redLayer.geometry.roundedCornersRadius = {5.0f, 20.0f};
+    redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+    // Red background.
+    redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+    redLayer.alpha = 1.0f;
+
+    layers.push_back(redLayer);
+
+    invokeDraw(settings, layers);
+
+    // Due to roundedCornersRadius, the corners are untouched.
+    expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+    expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+    // Y-axis draws a larger radius, check that its untouched as well
+    expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 5), 0, 0, 0, 0);
+    expectBufferColor(Point(0, 5), 0, 0, 0, 0);
+
+    //  middle should be red
+    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+}
+
 TEST_P(RenderEngineTest, testClear) {
     initializeRenderEngine();
 
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index 2ebdbcf..97d57e4 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -240,19 +240,16 @@
     return nullptr;
 }
 
-input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map,
-        const char* key) {
-    String8 keyString(key);
+input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* key) {
     if (map != nullptr) {
-        if (map->propertyMap->hasProperty(keyString)) {
-            auto prop = new input_property_t();
-            if (!map->propertyMap->tryGetProperty(keyString, prop->value)) {
-                delete prop;
-                return nullptr;
-            }
-            prop->key = keyString;
-            return prop;
+        std::string value;
+        auto prop = std::make_unique<input_property_t>();
+        if (!map->propertyMap->tryGetProperty(key, value)) {
+            return nullptr;
         }
+        prop->key = key;
+        prop->value = value.c_str();
+        return prop.release();
     }
     return nullptr;
 }
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index fe4d443..a17d2c0 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -502,7 +502,7 @@
 bool EventHub::Device::isExternalDeviceLocked() {
     if (configuration) {
         bool value;
-        if (configuration->tryGetProperty(String8("device.internal"), value)) {
+        if (configuration->tryGetProperty("device.internal", value)) {
             return !value;
         }
     }
@@ -512,7 +512,7 @@
 bool EventHub::Device::deviceHasMicLocked() {
     if (configuration) {
         bool value;
-        if (configuration->tryGetProperty(String8("audio.mic"), value)) {
+        if (configuration->tryGetProperty("audio.mic", value)) {
             return value;
         }
     }
@@ -2076,10 +2076,9 @@
     }
 
     // See if this is a rotary encoder type device.
-    String8 deviceType = String8();
-    if (device->configuration &&
-        device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
-        if (!deviceType.compare(String8("rotaryEncoder"))) {
+    std::string deviceType;
+    if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
+        if (deviceType == "rotaryEncoder") {
             device->classes |= InputDeviceClass::ROTARY_ENCODER;
         }
     }
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index b2d50fc..79188aa 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -36,7 +36,6 @@
 #include <sys/epoll.h>
 #include <utils/BitSet.h>
 #include <utils/Errors.h>
-#include <utils/KeyedVector.h>
 #include <utils/List.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 91dc619..baa6007 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -225,18 +225,17 @@
 
 void CursorInputMapper::configureParameters() {
     mParameters.mode = Parameters::Mode::POINTER;
-    String8 cursorModeString;
-    if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
-                                                             cursorModeString)) {
+    std::string cursorModeString;
+    if (getDeviceContext().getConfiguration().tryGetProperty("cursor.mode", cursorModeString)) {
         if (cursorModeString == "navigation") {
             mParameters.mode = Parameters::Mode::NAVIGATION;
         } else if (cursorModeString != "pointer" && cursorModeString != "default") {
-            ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
+            ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.c_str());
         }
     }
 
     mParameters.orientationAware = false;
-    getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
+    getDeviceContext().getConfiguration().tryGetProperty("cursor.orientationAware",
                                                          mParameters.orientationAware);
 
     mParameters.hasAssociatedDisplay = false;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8eb870f..6a406d2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -160,7 +160,7 @@
 
 static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
     int32_t mapped = 0;
-    if (config.tryGetProperty(String8(property), mapped) && mapped > 0) {
+    if (config.tryGetProperty(property, mapped) && mapped > 0) {
         for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
             if (stemKeyRotationMap[i][0] == keyCode) {
                 stemKeyRotationMap[i][1] = mapped;
@@ -173,7 +173,7 @@
 void KeyboardInputMapper::configureParameters() {
     mParameters.orientationAware = false;
     const PropertyMap& config = getDeviceContext().getConfiguration();
-    config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware);
+    config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);
 
     if (mParameters.orientationAware) {
         mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
@@ -183,10 +183,10 @@
     }
 
     mParameters.handlesKeyRepeat = false;
-    config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+    config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);
 
     mParameters.doNotWakeByDefault = false;
-    config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault);
+    config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
 }
 
 void KeyboardInputMapper::dumpParameters(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index eca25f6..05973f7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -40,10 +40,10 @@
 
     if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
         float res = 0.0f;
-        if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.res"), res)) {
+        if (!getDeviceContext().getConfiguration().tryGetProperty("device.res", res)) {
             ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
         }
-        if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.scalingFactor"),
+        if (!getDeviceContext().getConfiguration().tryGetProperty("device.scalingFactor",
                                                                   mScalingFactor)) {
             ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
                   "default to 1.0!\n");
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index b01c2bc..573f99c 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -64,7 +64,7 @@
 template <typename T>
 bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) {
     const auto& config = getDeviceContext().getConfiguration();
-    return config.tryGetProperty(String8(keyName.c_str()), outValue);
+    return config.tryGetProperty(keyName, outValue);
 }
 
 void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index f07a3e8..2f3337b 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -415,15 +415,15 @@
             ? Parameters::GestureMode::SINGLE_TOUCH
             : Parameters::GestureMode::MULTI_TOUCH;
 
-    String8 gestureModeString;
-    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"),
+    std::string gestureModeString;
+    if (getDeviceContext().getConfiguration().tryGetProperty("touch.gestureMode",
                                                              gestureModeString)) {
         if (gestureModeString == "single-touch") {
             mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
         } else if (gestureModeString == "multi-touch") {
             mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
         } else if (gestureModeString != "default") {
-            ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
+            ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.c_str());
         }
     }
 
@@ -445,8 +445,8 @@
 
     mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
 
-    String8 deviceTypeString;
-    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"),
+    std::string deviceTypeString;
+    if (getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType",
                                                              deviceTypeString)) {
         if (deviceTypeString == "touchScreen") {
             mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
@@ -457,17 +457,17 @@
         } else if (deviceTypeString == "pointer") {
             mParameters.deviceType = Parameters::DeviceType::POINTER;
         } else if (deviceTypeString != "default") {
-            ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
+            ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
         }
     }
 
     mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
-    getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
+    getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
                                                          mParameters.orientationAware);
 
     mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
-    String8 orientationString;
-    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"),
+    std::string orientationString;
+    if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
                                                              orientationString)) {
         if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
             ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
@@ -478,7 +478,7 @@
         } else if (orientationString == "ORIENTATION_270") {
             mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
         } else if (orientationString != "ORIENTATION_0") {
-            ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string());
+            ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
         }
     }
 
@@ -490,8 +490,8 @@
         mParameters.hasAssociatedDisplay = true;
         if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
             mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
-            String8 uniqueDisplayId;
-            getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"),
+            std::string uniqueDisplayId;
+            getDeviceContext().getConfiguration().tryGetProperty("touch.displayId",
                                                                  uniqueDisplayId);
             mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
         }
@@ -504,7 +504,7 @@
     // Normally we don't do this for internal touch screens to prevent them from waking
     // up in your pocket but you can enable it using the input device configuration.
     mParameters.wake = getDeviceContext().isExternal();
-    getDeviceContext().getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake);
+    getDeviceContext().getConfiguration().tryGetProperty("touch.wake", mParameters.wake);
 }
 
 void TouchInputMapper::dumpParameters(std::string& dump) {
@@ -1192,8 +1192,8 @@
 
     // Size
     out.sizeCalibration = Calibration::SizeCalibration::DEFAULT;
-    String8 sizeCalibrationString;
-    if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
+    std::string sizeCalibrationString;
+    if (in.tryGetProperty("touch.size.calibration", sizeCalibrationString)) {
         if (sizeCalibrationString == "none") {
             out.sizeCalibration = Calibration::SizeCalibration::NONE;
         } else if (sizeCalibrationString == "geometric") {
@@ -1205,18 +1205,18 @@
         } else if (sizeCalibrationString == "area") {
             out.sizeCalibration = Calibration::SizeCalibration::AREA;
         } else if (sizeCalibrationString != "default") {
-            ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string());
+            ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.c_str());
         }
     }
 
-    out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale);
-    out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias);
-    out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed);
+    out.haveSizeScale = in.tryGetProperty("touch.size.scale", out.sizeScale);
+    out.haveSizeBias = in.tryGetProperty("touch.size.bias", out.sizeBias);
+    out.haveSizeIsSummed = in.tryGetProperty("touch.size.isSummed", out.sizeIsSummed);
 
     // Pressure
     out.pressureCalibration = Calibration::PressureCalibration::DEFAULT;
-    String8 pressureCalibrationString;
-    if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
+    std::string pressureCalibrationString;
+    if (in.tryGetProperty("touch.pressure.calibration", pressureCalibrationString)) {
         if (pressureCalibrationString == "none") {
             out.pressureCalibration = Calibration::PressureCalibration::NONE;
         } else if (pressureCalibrationString == "physical") {
@@ -1225,16 +1225,16 @@
             out.pressureCalibration = Calibration::PressureCalibration::AMPLITUDE;
         } else if (pressureCalibrationString != "default") {
             ALOGW("Invalid value for touch.pressure.calibration: '%s'",
-                  pressureCalibrationString.string());
+                  pressureCalibrationString.c_str());
         }
     }
 
-    out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale);
+    out.havePressureScale = in.tryGetProperty("touch.pressure.scale", out.pressureScale);
 
     // Orientation
     out.orientationCalibration = Calibration::OrientationCalibration::DEFAULT;
-    String8 orientationCalibrationString;
-    if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
+    std::string orientationCalibrationString;
+    if (in.tryGetProperty("touch.orientation.calibration", orientationCalibrationString)) {
         if (orientationCalibrationString == "none") {
             out.orientationCalibration = Calibration::OrientationCalibration::NONE;
         } else if (orientationCalibrationString == "interpolated") {
@@ -1243,36 +1243,36 @@
             out.orientationCalibration = Calibration::OrientationCalibration::VECTOR;
         } else if (orientationCalibrationString != "default") {
             ALOGW("Invalid value for touch.orientation.calibration: '%s'",
-                  orientationCalibrationString.string());
+                  orientationCalibrationString.c_str());
         }
     }
 
     // Distance
     out.distanceCalibration = Calibration::DistanceCalibration::DEFAULT;
-    String8 distanceCalibrationString;
-    if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
+    std::string distanceCalibrationString;
+    if (in.tryGetProperty("touch.distance.calibration", distanceCalibrationString)) {
         if (distanceCalibrationString == "none") {
             out.distanceCalibration = Calibration::DistanceCalibration::NONE;
         } else if (distanceCalibrationString == "scaled") {
             out.distanceCalibration = Calibration::DistanceCalibration::SCALED;
         } else if (distanceCalibrationString != "default") {
             ALOGW("Invalid value for touch.distance.calibration: '%s'",
-                  distanceCalibrationString.string());
+                  distanceCalibrationString.c_str());
         }
     }
 
-    out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale);
+    out.haveDistanceScale = in.tryGetProperty("touch.distance.scale", out.distanceScale);
 
     out.coverageCalibration = Calibration::CoverageCalibration::DEFAULT;
-    String8 coverageCalibrationString;
-    if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) {
+    std::string coverageCalibrationString;
+    if (in.tryGetProperty("touch.coverage.calibration", coverageCalibrationString)) {
         if (coverageCalibrationString == "none") {
             out.coverageCalibration = Calibration::CoverageCalibration::NONE;
         } else if (coverageCalibrationString == "box") {
             out.coverageCalibration = Calibration::CoverageCalibration::BOX;
         } else if (coverageCalibrationString != "default") {
             ALOGW("Invalid value for touch.coverage.calibration: '%s'",
-                  coverageCalibrationString.string());
+                  coverageCalibrationString.c_str());
         }
     }
 }
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 901b4de..0fdffdf 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -546,7 +546,7 @@
         enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
     }
 
-    void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
+    void addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
         Device* device = getDevice(deviceId);
         device->configuration.addProperty(key, value);
     }
@@ -2731,7 +2731,7 @@
 
 TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
     // Configuration.
-    mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8("key"), String8("value"));
+    mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "key", "value");
 
     FakeInputMapper& mapper1 =
             mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
@@ -2752,10 +2752,10 @@
     InputReaderConfiguration config;
     mDevice->configure(ARBITRARY_TIME, &config, 0);
 
-    String8 propertyValue;
-    ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue))
+    std::string propertyValue;
+    ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue))
             << "Device should have read configuration during configuration phase.";
-    ASSERT_STREQ("value", propertyValue.string());
+    ASSERT_EQ("value", propertyValue);
 
     ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
@@ -2953,7 +2953,7 @@
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
-        mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value));
+        mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
     }
 
     void configureDevice(uint32_t changes) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 20e86a0..eb17995 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -142,7 +142,6 @@
     srcs: [
         "BackgroundExecutor.cpp",
         "BufferLayer.cpp",
-        "BufferLayerConsumer.cpp",
         "BufferStateLayer.cpp",
         "ClientCache.cpp",
         "Client.cpp",
@@ -168,10 +167,8 @@
         "WindowInfosListenerInvoker.cpp",
         "Layer.cpp",
         "LayerProtoHelper.cpp",
-        "LayerRejecter.cpp",
         "LayerRenderArea.cpp",
         "LayerVector.cpp",
-        "MonitoredProducer.cpp",
         "NativeWindowSurface.cpp",
         "RefreshRateOverlay.cpp",
         "RegionSamplingThread.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 4ba5e90..dd918c5 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -54,7 +54,6 @@
 #include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "FrameTracer/FrameTracer.h"
-#include "LayerRejecter.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 3bb0fb3..7cc67a2 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -34,14 +34,12 @@
 #include <utils/String8.h>
 #include <utils/Timers.h>
 
-#include "BufferLayerConsumer.h"
 #include "Client.h"
 #include "DisplayHardware/HWComposer.h"
 #include "FrameTimeline.h"
 #include "FrameTracker.h"
 #include "Layer.h"
 #include "LayerVector.h"
-#include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
deleted file mode 100644
index 7361a4f..0000000
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2010 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"
-
-#undef LOG_TAG
-#define LOG_TAG "BufferLayerConsumer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include "BufferLayerConsumer.h"
-#include "Layer.h"
-#include "Scheduler/VsyncController.h"
-
-#include <inttypes.h>
-
-#include <cutils/compiler.h>
-
-#include <hardware/hardware.h>
-
-#include <math/mat4.h>
-
-#include <gui/BufferItem.h>
-#include <gui/GLConsumer.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-#include <renderengine/RenderEngine.h>
-#include <renderengine/impl/ExternalTexture.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// Macros for including the BufferLayerConsumer name in log messages
-#define BLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
-
-static const mat4 mtxIdentity;
-
-BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq,
-                                         renderengine::RenderEngine& engine, uint32_t tex,
-                                         Layer* layer)
-      : ConsumerBase(bq, false),
-        mCurrentCrop(Rect::EMPTY_RECT),
-        mCurrentTransform(0),
-        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
-        mCurrentFence(Fence::NO_FENCE),
-        mCurrentTimestamp(0),
-        mCurrentDataSpace(ui::Dataspace::UNKNOWN),
-        mCurrentFrameNumber(0),
-        mCurrentTransformToDisplayInverse(false),
-        mCurrentSurfaceDamage(),
-        mCurrentApi(0),
-        mDefaultWidth(1),
-        mDefaultHeight(1),
-        mFilteringEnabled(true),
-        mRE(engine),
-        mTexName(tex),
-        mLayer(layer),
-        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {
-    BLC_LOGV("BufferLayerConsumer");
-
-    memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
-
-    mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
-}
-
-status_t BufferLayerConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        BLC_LOGE("setDefaultBufferSize: BufferLayerConsumer is abandoned!");
-        return NO_INIT;
-    }
-    mDefaultWidth = w;
-    mDefaultHeight = h;
-    return mConsumer->setDefaultBufferSize(w, h);
-}
-
-void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) {
-    setFrameAvailableListener(listener);
-    Mutex::Autolock lock(mMutex);
-    mContentsChangedListener = listener;
-}
-
-status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
-                                             bool* autoRefresh, bool* queuedBuffer,
-                                             uint64_t maxFrameNumber) {
-    ATRACE_CALL();
-    BLC_LOGV("updateTexImage");
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        BLC_LOGE("updateTexImage: BufferLayerConsumer is abandoned!");
-        return NO_INIT;
-    }
-
-    BufferItem item;
-
-    // Acquire the next buffer.
-    // In asynchronous mode the list is guaranteed to be one buffer
-    // deep, while in synchronous mode we use the oldest buffer.
-    status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
-    if (err != NO_ERROR) {
-        if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
-            err = NO_ERROR;
-        } else if (err == BufferQueue::PRESENT_LATER) {
-            // return the error, without logging
-        } else {
-            BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
-        }
-        return err;
-    }
-
-    if (autoRefresh) {
-        *autoRefresh = item.mAutoRefresh;
-    }
-
-    if (queuedBuffer) {
-        *queuedBuffer = item.mQueuedBuffer;
-    }
-
-    // We call the rejecter here, in case the caller has a reason to
-    // not accept this buffer.  This is used by SurfaceFlinger to
-    // reject buffers which have the wrong size
-    int slot = item.mSlot;
-    if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) {
-        releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
-        return BUFFER_REJECTED;
-    }
-
-    // Release the previous buffer.
-    err = updateAndReleaseLocked(item, &mPendingRelease);
-    if (err != NO_ERROR) {
-        return err;
-    }
-    return err;
-}
-
-void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
-    if (!fence->isValid()) {
-        return;
-    }
-
-    auto slot = mPendingRelease.isPending ? mPendingRelease.currentTexture : mCurrentTexture;
-    if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
-        return;
-    }
-
-    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
-                                            : mCurrentTextureBuffer->getBuffer();
-    auto err = addReleaseFence(slot, buffer, fence);
-    if (err != OK) {
-        BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
-    }
-}
-
-bool BufferLayerConsumer::releasePendingBuffer() {
-    if (!mPendingRelease.isPending) {
-        BLC_LOGV("Pending buffer already released");
-        return false;
-    }
-    BLC_LOGV("Releasing pending buffer");
-    Mutex::Autolock lock(mMutex);
-    status_t result =
-            releaseBufferLocked(mPendingRelease.currentTexture, mPendingRelease.graphicBuffer);
-    if (result < NO_ERROR) {
-        BLC_LOGE("releasePendingBuffer failed: %s (%d)", strerror(-result), result);
-    }
-    mPendingRelease = PendingRelease();
-    return true;
-}
-
-sp<Fence> BufferLayerConsumer::getPrevFinalReleaseFence() const {
-    Mutex::Autolock lock(mMutex);
-    return ConsumerBase::mPrevFinalReleaseFence;
-}
-
-status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
-                                                  uint64_t maxFrameNumber) {
-    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    // If item->mGraphicBuffer is not null, this buffer has not been acquired
-    // before, so we need to clean up old references.
-    if (item->mGraphicBuffer != nullptr) {
-        std::lock_guard<std::mutex> lock(mImagesMutex);
-        if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr ||
-            mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) {
-            mImages[item->mSlot] = std::make_shared<
-                    renderengine::impl::ExternalTexture>(item->mGraphicBuffer, mRE,
-                                                         renderengine::impl::ExternalTexture::
-                                                                 Usage::READABLE);
-        }
-    }
-
-    return NO_ERROR;
-}
-
-status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
-                                                     PendingRelease* pendingRelease) {
-    status_t err = NO_ERROR;
-
-    int slot = item.mSlot;
-
-    BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
-             (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->getBuffer() != nullptr)
-                     ? mCurrentTextureBuffer->getBuffer()->handle
-                     : 0,
-             slot, mSlots[slot].mGraphicBuffer->handle);
-
-    // Hang onto the pointer so that it isn't freed in the call to
-    // releaseBufferLocked() if we're in shared buffer mode and both buffers are
-    // the same.
-
-    std::shared_ptr<renderengine::ExternalTexture> nextTextureBuffer;
-    {
-        std::lock_guard<std::mutex> lock(mImagesMutex);
-        nextTextureBuffer = mImages[slot];
-    }
-
-    // release old buffer
-    if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
-        if (pendingRelease == nullptr) {
-            status_t status =
-                    releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->getBuffer());
-            if (status < NO_ERROR) {
-                BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
-                         status);
-                err = status;
-                // keep going, with error raised [?]
-            }
-        } else {
-            pendingRelease->currentTexture = mCurrentTexture;
-            pendingRelease->graphicBuffer = mCurrentTextureBuffer->getBuffer();
-            pendingRelease->isPending = true;
-        }
-    }
-
-    // Update the BufferLayerConsumer state.
-    mCurrentTexture = slot;
-    mCurrentTextureBuffer = nextTextureBuffer;
-    mCurrentCrop = item.mCrop;
-    mCurrentTransform = item.mTransform;
-    mCurrentScalingMode = item.mScalingMode;
-    mCurrentTimestamp = item.mTimestamp;
-    mCurrentDataSpace = static_cast<ui::Dataspace>(item.mDataSpace);
-    mCurrentHdrMetadata = item.mHdrMetadata;
-    mCurrentFence = item.mFence;
-    mCurrentFenceTime = item.mFenceTime;
-    mCurrentFrameNumber = item.mFrameNumber;
-    mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse;
-    mCurrentSurfaceDamage = item.mSurfaceDamage;
-    mCurrentApi = item.mApi;
-
-    computeCurrentTransformMatrixLocked();
-
-    return err;
-}
-
-void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
-    Mutex::Autolock lock(mMutex);
-    memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
-}
-
-void BufferLayerConsumer::setFilteringEnabled(bool enabled) {
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        BLC_LOGE("setFilteringEnabled: BufferLayerConsumer is abandoned!");
-        return;
-    }
-    bool needsRecompute = mFilteringEnabled != enabled;
-    mFilteringEnabled = enabled;
-
-    if (needsRecompute && mCurrentTextureBuffer == nullptr) {
-        BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr");
-    }
-
-    if (needsRecompute && mCurrentTextureBuffer != nullptr) {
-        computeCurrentTransformMatrixLocked();
-    }
-}
-
-void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
-    BLC_LOGV("computeCurrentTransformMatrixLocked");
-    if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->getBuffer() == nullptr) {
-        BLC_LOGD("computeCurrentTransformMatrixLocked: "
-                 "mCurrentTextureBuffer is nullptr");
-    }
-    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
-                                       mCurrentTextureBuffer == nullptr
-                                               ? nullptr
-                                               : mCurrentTextureBuffer->getBuffer(),
-                                       getCurrentCropLocked(), mCurrentTransform,
-                                       mFilteringEnabled);
-}
-
-nsecs_t BufferLayerConsumer::getTimestamp() {
-    BLC_LOGV("getTimestamp");
-    Mutex::Autolock lock(mMutex);
-    return mCurrentTimestamp;
-}
-
-ui::Dataspace BufferLayerConsumer::getCurrentDataSpace() {
-    BLC_LOGV("getCurrentDataSpace");
-    Mutex::Autolock lock(mMutex);
-    return mCurrentDataSpace;
-}
-
-const HdrMetadata& BufferLayerConsumer::getCurrentHdrMetadata() const {
-    BLC_LOGV("getCurrentHdrMetadata");
-    Mutex::Autolock lock(mMutex);
-    return mCurrentHdrMetadata;
-}
-
-uint64_t BufferLayerConsumer::getFrameNumber() {
-    BLC_LOGV("getFrameNumber");
-    Mutex::Autolock lock(mMutex);
-    return mCurrentFrameNumber;
-}
-
-bool BufferLayerConsumer::getTransformToDisplayInverse() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentTransformToDisplayInverse;
-}
-
-const Region& BufferLayerConsumer::getSurfaceDamage() const {
-    return mCurrentSurfaceDamage;
-}
-
-void BufferLayerConsumer::mergeSurfaceDamage(const Region& damage) {
-    if (damage.bounds() == Rect::INVALID_RECT ||
-        mCurrentSurfaceDamage.bounds() == Rect::INVALID_RECT) {
-        mCurrentSurfaceDamage = Region::INVALID_REGION;
-    } else {
-        mCurrentSurfaceDamage |= damage;
-    }
-}
-
-int BufferLayerConsumer::getCurrentApi() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentApi;
-}
-
-std::shared_ptr<renderengine::ExternalTexture> BufferLayerConsumer::getCurrentBuffer(
-        int* outSlot, sp<Fence>* outFence) const {
-    Mutex::Autolock lock(mMutex);
-
-    if (outSlot != nullptr) {
-        *outSlot = mCurrentTexture;
-    }
-
-    if (outFence != nullptr) {
-        *outFence = mCurrentFence;
-    }
-
-    return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer;
-}
-
-Rect BufferLayerConsumer::getCurrentCrop() const {
-    Mutex::Autolock lock(mMutex);
-    return getCurrentCropLocked();
-}
-
-Rect BufferLayerConsumer::getCurrentCropLocked() const {
-    uint32_t width = mDefaultWidth;
-    uint32_t height = mDefaultHeight;
-    // If the buffer comes with a rotated bit for 90 (or 270) degrees, switch width/height in order
-    // to scale and crop correctly.
-    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
-        width = mDefaultHeight;
-        height = mDefaultWidth;
-    }
-
-    return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
-            ? GLConsumer::scaleDownCrop(mCurrentCrop, width, height)
-            : mCurrentCrop;
-}
-
-uint32_t BufferLayerConsumer::getCurrentTransform() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentTransform;
-}
-
-uint32_t BufferLayerConsumer::getCurrentScalingMode() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentScalingMode;
-}
-
-sp<Fence> BufferLayerConsumer::getCurrentFence() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentFence;
-}
-
-std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentFenceTime;
-}
-
-void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
-    BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
-    std::lock_guard<std::mutex> lock(mImagesMutex);
-    if (slotIndex == mCurrentTexture) {
-        mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
-    }
-    mImages[slotIndex] = nullptr;
-    ConsumerBase::freeBufferLocked(slotIndex);
-}
-
-void BufferLayerConsumer::onDisconnect() {
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        // Nothing to do if we're already abandoned.
-        return;
-    }
-
-    mLayer->onDisconnect();
-}
-
-void BufferLayerConsumer::onSidebandStreamChanged() {
-    [[maybe_unused]] FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
-    {
-        Mutex::Autolock lock(mFrameAvailableMutex);
-        unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
-    }
-    sp<ContentsChangedListener> listener;
-    { // scope for the lock
-        Mutex::Autolock lock(mMutex);
-        ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
-        listener = mContentsChangedListener.promote();
-    }
-
-    if (listener != nullptr) {
-        listener->onSidebandStreamChanged();
-    }
-}
-
-void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
-    if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
-        std::lock_guard<std::mutex> lock(mImagesMutex);
-        const std::shared_ptr<renderengine::ExternalTexture>& oldImage = mImages[item.mSlot];
-        if (oldImage == nullptr || oldImage->getBuffer() == nullptr ||
-            oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) {
-            mImages[item.mSlot] = std::make_shared<
-                    renderengine::impl::ExternalTexture>(item.mGraphicBuffer, mRE,
-                                                         renderengine::impl::ExternalTexture::
-                                                                 Usage::READABLE);
-        }
-    }
-}
-
-void BufferLayerConsumer::abandonLocked() {
-    BLC_LOGV("abandonLocked");
-    mCurrentTextureBuffer = nullptr;
-    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        std::lock_guard<std::mutex> lock(mImagesMutex);
-        mImages[i] = nullptr;
-    }
-    ConsumerBase::abandonLocked();
-}
-
-status_t BufferLayerConsumer::setConsumerUsageBits(uint64_t usage) {
-    return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
-}
-
-void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const {
-    result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
-                        "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
-                        prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
-                        mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
-                        mCurrentTransform);
-
-    ConsumerBase::dumpLocked(result, prefix);
-}
-}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
deleted file mode 100644
index 23ad2a3..0000000
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef ANDROID_BUFFERLAYERCONSUMER_H
-#define ANDROID_BUFFERLAYERCONSUMER_H
-
-#include <android-base/thread_annotations.h>
-#include <gui/BufferQueueDefs.h>
-#include <gui/ConsumerBase.h>
-#include <gui/HdrMetadata.h>
-#include <renderengine/ExternalTexture.h>
-#include <ui/FenceTime.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
-#include <ui/Region.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Layer;
-class String8;
-
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
-/*
- * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue,
- * and makes them available to RenderEngine as a texture.
- *
- * A typical usage pattern is to call updateTexImage() when a new frame is
- * desired.  If a new frame is available, the frame is latched.  If not, the
- * previous contents are retained.  The texture is attached and updated after
- * bindTextureImage() is called.
- *
- * All calls to updateTexImage must be made with RenderEngine being current.
- * The texture is attached to the TEXTURE_EXTERNAL texture target.
- */
-class BufferLayerConsumer : public ConsumerBase {
-public:
-    static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
-
-    class BufferRejecter {
-        friend class BufferLayerConsumer;
-        virtual bool reject(const sp<GraphicBuffer>& buf, const BufferItem& item) = 0;
-
-    protected:
-        virtual ~BufferRejecter() {}
-    };
-
-    struct ContentsChangedListener : public FrameAvailableListener {
-        virtual void onSidebandStreamChanged() = 0;
-    };
-
-    // BufferLayerConsumer constructs a new BufferLayerConsumer object.  The
-    // tex parameter indicates the name of the RenderEngine texture to which
-    // images are to be streamed.
-    BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine,
-                        uint32_t tex, Layer* layer);
-
-    // Sets the contents changed listener. This should be used instead of
-    // ConsumerBase::setFrameAvailableListener().
-    void setContentsChangedListener(const wp<ContentsChangedListener>& listener);
-
-    // updateTexImage acquires the most recently queued buffer, and sets the
-    // image contents of the target texture to it.
-    //
-    // This call may only be made while RenderEngine is current.
-    //
-    // This calls doFenceWait to ensure proper synchronization unless native
-    // fence is supported.
-    //
-    // Unlike the GLConsumer version, this version takes a functor that may be
-    // used to reject the newly acquired buffer.  It also does not bind the
-    // RenderEngine texture until bindTextureImage is called.
-    status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
-                            bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);
-
-    // setReleaseFence stores a fence that will signal when the current buffer
-    // is no longer being read. This fence will be returned to the producer
-    // when the current buffer is released by updateTexImage(). Multiple
-    // fences can be set for a given buffer; they will be merged into a single
-    // union fence.
-    void setReleaseFence(const sp<Fence>& fence);
-
-    bool releasePendingBuffer();
-
-    sp<Fence> getPrevFinalReleaseFence() const;
-
-    // See GLConsumer::getTransformMatrix.
-    void getTransformMatrix(float mtx[16]);
-
-    // getTimestamp retrieves the timestamp associated with the texture image
-    // set by the most recent call to updateTexImage.
-    //
-    // The timestamp is in nanoseconds, and is monotonically increasing. Its
-    // other semantics (zero point, etc) are source-dependent and should be
-    // documented by the source.
-    int64_t getTimestamp();
-
-    // getDataSpace retrieves the DataSpace associated with the texture image
-    // set by the most recent call to updateTexImage.
-    ui::Dataspace getCurrentDataSpace();
-
-    // getCurrentHdrMetadata retrieves the HDR metadata associated with the
-    // texture image set by the most recent call to updateTexImage.
-    const HdrMetadata& getCurrentHdrMetadata() const;
-
-    // getFrameNumber retrieves the frame number associated with the texture
-    // image set by the most recent call to updateTexImage.
-    //
-    // The frame number is an incrementing counter set to 0 at the creation of
-    // the BufferQueue associated with this consumer.
-    uint64_t getFrameNumber();
-
-    bool getTransformToDisplayInverse() const;
-
-    // must be called from SF main thread
-    const Region& getSurfaceDamage() const;
-
-    // Merge the given damage region into the current damage region value.
-    void mergeSurfaceDamage(const Region& damage);
-
-    // getCurrentApi retrieves the API which queues the current buffer.
-    int getCurrentApi() const;
-
-    // See GLConsumer::setDefaultBufferSize.
-    status_t setDefaultBufferSize(uint32_t width, uint32_t height);
-
-    // setFilteringEnabled sets whether the transform matrix should be computed
-    // for use with bilinear filtering.
-    void setFilteringEnabled(bool enabled);
-
-    // getCurrentBuffer returns the buffer associated with the current image.
-    // When outSlot is not nullptr, the current buffer slot index is also
-    // returned. Simiarly, when outFence is not nullptr, the current output
-    // fence is returned.
-    std::shared_ptr<renderengine::ExternalTexture> getCurrentBuffer(
-            int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
-
-    // getCurrentCrop returns the cropping rectangle of the current buffer.
-    Rect getCurrentCrop() const;
-
-    // getCurrentTransform returns the transform of the current buffer.
-    uint32_t getCurrentTransform() const;
-
-    // getCurrentScalingMode returns the scaling mode of the current buffer.
-    uint32_t getCurrentScalingMode() const;
-
-    // getCurrentFence returns the fence indicating when the current buffer is
-    // ready to be read from.
-    sp<Fence> getCurrentFence() const;
-
-    // getCurrentFence returns the FenceTime indicating when the current
-    // buffer is ready to be read from.
-    std::shared_ptr<FenceTime> getCurrentFenceTime() const;
-
-    // setConsumerUsageBits overrides the ConsumerBase method to OR
-    // DEFAULT_USAGE_FLAGS to usage.
-    status_t setConsumerUsageBits(uint64_t usage);
-    void onBufferAvailable(const BufferItem& item) EXCLUDES(mImagesMutex);
-
-protected:
-    // abandonLocked overrides the ConsumerBase method to clear
-    // mCurrentTextureImage in addition to the ConsumerBase behavior.
-    virtual void abandonLocked() EXCLUDES(mImagesMutex);
-
-    // dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
-    // specific info in addition to the ConsumerBase behavior.
-    virtual void dumpLocked(String8& result, const char* prefix) const;
-
-    // See ConsumerBase::acquireBufferLocked
-    virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
-                                         uint64_t maxFrameNumber = 0) override
-            EXCLUDES(mImagesMutex);
-
-    bool canUseImageCrop(const Rect& crop) const;
-
-    struct PendingRelease {
-        PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {}
-
-        bool isPending;
-        int currentTexture;
-        sp<GraphicBuffer> graphicBuffer;
-    };
-
-    // This releases the buffer in the slot referenced by mCurrentTexture,
-    // then updates state to refer to the BufferItem, which must be a
-    // newly-acquired buffer. If pendingRelease is not null, the parameters
-    // which would have been passed to releaseBufferLocked upon the successful
-    // completion of the method will instead be returned to the caller, so that
-    // it may call releaseBufferLocked itself later.
-    status_t updateAndReleaseLocked(const BufferItem& item,
-                                    PendingRelease* pendingRelease = nullptr)
-            EXCLUDES(mImagesMutex);
-
-private:
-    // Utility class for managing GraphicBuffer references into renderengine
-    class Image {
-    public:
-        Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& engine);
-        virtual ~Image();
-        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
-
-    private:
-        // mGraphicBuffer is the buffer that was used to create this image.
-        sp<GraphicBuffer> mGraphicBuffer;
-        // Back-reference into renderengine to initiate cleanup.
-        renderengine::RenderEngine& mRE;
-        DISALLOW_COPY_AND_ASSIGN(Image);
-    };
-
-    // freeBufferLocked frees up the given buffer slot. If the slot has been
-    // initialized this will release the reference to the GraphicBuffer in
-    // that slot.  Otherwise it has no effect.
-    //
-    // This method must be called with mMutex locked.
-    virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex);
-
-    // IConsumerListener interface
-    void onDisconnect() override;
-    void onSidebandStreamChanged() override;
-    void addAndGetFrameTimestamps(const NewFrameEventsEntry*, FrameEventHistoryDelta*) override {}
-
-    // computeCurrentTransformMatrixLocked computes the transform matrix for the
-    // current texture.  It uses mCurrentTransform and the current GraphicBuffer
-    // to compute this matrix and stores it in mCurrentTransformMatrix.
-    // mCurrentTextureImage must not be nullptr.
-    void computeCurrentTransformMatrixLocked();
-
-    // getCurrentCropLocked returns the cropping rectangle of the current buffer.
-    Rect getCurrentCropLocked() const;
-
-    // The default consumer usage flags that BufferLayerConsumer always sets on its
-    // BufferQueue instance; these will be OR:d with any additional flags passed
-    // from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always
-    // consume buffers as hardware textures.
-    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
-
-    // mCurrentTextureBuffer is the buffer containing the current texture. It's
-    // possible that this buffer is not associated with any buffer slot, so we
-    // must track it separately in order to support the getCurrentBuffer method.
-    std::shared_ptr<renderengine::ExternalTexture> mCurrentTextureBuffer;
-
-    // mCurrentCrop is the crop rectangle that applies to the current texture.
-    // It gets set each time updateTexImage is called.
-    Rect mCurrentCrop;
-
-    // mCurrentTransform is the transform identifier for the current texture. It
-    // gets set each time updateTexImage is called.
-    uint32_t mCurrentTransform;
-
-    // mCurrentScalingMode is the scaling mode for the current texture. It gets
-    // set each time updateTexImage is called.
-    uint32_t mCurrentScalingMode;
-
-    // mCurrentFence is the fence received from BufferQueue in updateTexImage.
-    sp<Fence> mCurrentFence;
-
-    // The FenceTime wrapper around mCurrentFence.
-    std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
-
-    // mCurrentTransformMatrix is the transform matrix for the current texture.
-    // It gets computed by computeTransformMatrix each time updateTexImage is
-    // called.
-    float mCurrentTransformMatrix[16];
-
-    // mCurrentTimestamp is the timestamp for the current texture. It
-    // gets set each time updateTexImage is called.
-    int64_t mCurrentTimestamp;
-
-    // mCurrentDataSpace is the dataspace for the current texture. It
-    // gets set each time updateTexImage is called.
-    ui::Dataspace mCurrentDataSpace;
-
-    // mCurrentHdrMetadata is the HDR metadata for the current texture. It
-    // gets set each time updateTexImage is called.
-    HdrMetadata mCurrentHdrMetadata;
-
-    // mCurrentFrameNumber is the frame counter for the current texture.
-    // It gets set each time updateTexImage is called.
-    uint64_t mCurrentFrameNumber;
-
-    // Indicates this buffer must be transformed by the inverse transform of the screen
-    // it is displayed onto. This is applied after BufferLayerConsumer::mCurrentTransform.
-    // This must be set/read from SurfaceFlinger's main thread.
-    bool mCurrentTransformToDisplayInverse;
-
-    // The portion of this surface that has changed since the previous frame
-    Region mCurrentSurfaceDamage;
-
-    int mCurrentApi;
-
-    uint32_t mDefaultWidth, mDefaultHeight;
-
-    // mFilteringEnabled indicates whether the transform matrix is computed for
-    // use with bilinear filtering. It defaults to true and is changed by
-    // setFilteringEnabled().
-    bool mFilteringEnabled;
-
-    renderengine::RenderEngine& mRE;
-
-    // mTexName is the name of the RenderEngine texture to which streamed
-    // images will be bound when bindTexImage is called. It is set at
-    // construction time.
-    const uint32_t mTexName;
-
-    // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing.
-    Layer* mLayer GUARDED_BY(mMutex);
-
-    wp<ContentsChangedListener> mContentsChangedListener;
-
-    // mCurrentTexture is the buffer slot index of the buffer that is currently
-    // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
-    // indicating that no buffer slot is currently bound to the texture. Note,
-    // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
-    // that no buffer is bound to the texture. A call to setBufferCount will
-    // reset mCurrentTexture to INVALID_BUFFER_SLOT.
-    int mCurrentTexture;
-
-    // Shadow buffer cache for cleaning up renderengine references.
-    std::shared_ptr<renderengine::ExternalTexture>
-            mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
-
-    // Separate mutex guarding the shadow buffer cache.
-    // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
-    // which is contentious enough that we can't just use mMutex.
-    mutable std::mutex mImagesMutex;
-
-    // A release that is pending on the receipt of a new release fence from
-    // presentDisplay
-    PendingRelease mPendingRelease;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_BUFFERLAYERCONSUMER_H
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 509312f..aea6798 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -586,8 +586,29 @@
     // Remove the transparent area from the visible region
     if (!layerFEState->isOpaque) {
         if (tr.preserveRects()) {
-            // transform the transparent region
-            transparentRegion = tr.transform(layerFEState->transparentRegionHint);
+            // Clip the transparent region to geomLayerBounds first
+            // The transparent region may be influenced by applications, for
+            // instance, by overriding ViewGroup#gatherTransparentRegion with a
+            // custom view. Once the layer stack -> display mapping is known, we
+            // must guard against very wrong inputs to prevent underflow or
+            // overflow errors. We do this here by constraining the transparent
+            // region to be within the pre-transform layer bounds, since the
+            // layer bounds are expected to play nicely with the full
+            // transform.
+            const Region clippedTransparentRegionHint =
+                    layerFEState->transparentRegionHint.intersect(
+                            Rect(layerFEState->geomLayerBounds));
+
+            if (clippedTransparentRegionHint.isEmpty()) {
+                if (!layerFEState->transparentRegionHint.isEmpty()) {
+                    ALOGD("Layer: %s had an out of bounds transparent region",
+                          layerFE->getDebugName());
+                    layerFEState->transparentRegionHint.dump("transparentRegionHint");
+                }
+                transparentRegion.clear();
+            } else {
+                transparentRegion = tr.transform(clippedTransparentRegionHint);
+            }
         } else {
             // transformation too complex, can't do the
             // transparent region optimization.
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 505f94e..cf12890 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1505,6 +1505,8 @@
     static const Region kTransparentRegionHint;
     static const Region kTransparentRegionHintTwo;
     static const Region kTransparentRegionHintTwo90Rotation;
+    static const Region kTransparentRegionHintNegative;
+    static const Region kTransparentRegionHintNegativeIntersectsBounds;
 
     StrictMock<OutputPartialMock> mOutput;
     LayerFESet mGeomSnapshots;
@@ -1528,6 +1530,10 @@
         Region(Rect(25, 20, 50, 75));
 const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation =
         Region(Rect(125, 25, 180, 50));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegative =
+        Region(Rect(INT32_MIN, INT32_MIN, INT32_MIN + 100, INT32_MIN + 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativeIntersectsBounds =
+        Region(Rect(INT32_MIN, INT32_MIN, 100, 100));
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
     EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
@@ -1997,6 +2003,41 @@
                 RegionEq(kTransparentRegionHintTwo90Rotation));
 }
 
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionExcludesOutputLayer) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+    mLayer.layerFEState.transparentRegionHint = kFullBoundsNoRotation;
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionIgnoredWhenOutsideBounds) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect();
+    mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegative;
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionClipsWhenOutsideBounds) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.compositionType =
+            aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+    mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegativeIntersectsBounds;
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+    ensureOutputLayerIfVisible();
+
+    // Check that the blocking region clips an out-of-bounds transparent region.
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint,
+                RegionEq(kTransparentRegionHint));
+}
+
 /*
  * Output::present()
  */
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6e9dad1..cabadb7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -70,8 +70,6 @@
 #include "FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "LayerProtoHelper.h"
-#include "LayerRejecter.h"
-#include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
@@ -156,9 +154,9 @@
         mDrawingState.color.g = -1.0_hf;
         mDrawingState.color.b = -1.0_hf;
     }
-    CompositorTiming compositorTiming;
-    args.flinger->getCompositorTiming(&compositorTiming);
-    mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
+
+    mFrameTracker.setDisplayRefreshPeriod(
+            args.flinger->mScheduler->getVsyncPeriodFromRefreshRateConfigs());
 
     mCallingPid = args.callingPid;
     mCallingUid = args.callingUid;
@@ -410,7 +408,7 @@
     const auto& drawingState{getDrawingState()};
     const auto alpha = static_cast<float>(getAlpha());
     const bool opaque = isOpaque(drawingState);
-    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+    const bool usesRoundedCorners = hasRoundedCorners();
 
     auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
     if (!opaque || alpha != 1.0f) {
@@ -486,7 +484,7 @@
     compositionState->hasProtectedContent = isProtected();
     compositionState->dimmingEnabled = isDimmingEnabled();
 
-    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+    const bool usesRoundedCorners = hasRoundedCorners();
 
     compositionState->isOpaque =
             isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
@@ -1968,27 +1966,22 @@
     const auto& parent = mDrawingParent.promote();
     if (parent != nullptr) {
         parentSettings = parent->getRoundedCornerState();
-        if (parentSettings.radius > 0) {
+        if (parentSettings.hasRoundedCorners()) {
             ui::Transform t = getActiveTransform(getDrawingState());
             t = t.inverse();
             parentSettings.cropRect = t.transform(parentSettings.cropRect);
-            // The rounded corners shader only accepts 1 corner radius for performance reasons,
-            // but a transform matrix can define horizontal and vertical scales.
-            // Let's take the average between both of them and pass into the shader, practically we
-            // never do this type of transformation on windows anyway.
-            auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
-            auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
-            parentSettings.radius *= (scaleX + scaleY) / 2.0f;
+            parentSettings.radius.x *= t.getScaleX();
+            parentSettings.radius.y *= t.getScaleY();
         }
     }
 
     // Get layer settings
     Rect layerCropRect = getCroppedBufferSize(getDrawingState());
-    const float radius = getDrawingState().cornerRadius;
+    const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius);
     RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
-    const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid();
+    const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid();
 
-    if (layerSettingsValid && parentSettings.radius > 0) {
+    if (layerSettingsValid && parentSettings.hasRoundedCorners()) {
         // If the parent and the layer have rounded corner settings, use the parent settings if the
         // parent crop is entirely inside the layer crop.
         // This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
@@ -2002,7 +1995,7 @@
         }
     } else if (layerSettingsValid) {
         return layerSettings;
-    } else if (parentSettings.radius > 0) {
+    } else if (parentSettings.hasRoundedCorners()) {
         return parentSettings;
     }
     return {};
@@ -2123,7 +2116,8 @@
     layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
 
     layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
-    layerInfo->set_corner_radius(getRoundedCornerState().radius);
+    layerInfo->set_corner_radius(
+            (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
     layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
     layerInfo->set_is_trusted_overlay(isTrustedOverlay());
     LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index fb9b663..b809c8a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -53,7 +53,6 @@
 #include "DisplayHardware/HWComposer.h"
 #include "FrameTracker.h"
 #include "LayerVector.h"
-#include "MonitoredProducer.h"
 #include "RenderArea.h"
 #include "Scheduler/LayerInfo.h"
 #include "SurfaceFlinger.h"
@@ -137,13 +136,14 @@
 
     struct RoundedCornerState {
         RoundedCornerState() = default;
-        RoundedCornerState(FloatRect cropRect, float radius)
+        RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
               : cropRect(cropRect), radius(radius) {}
 
         // Rounded rectangle in local layer coordinate space.
         FloatRect cropRect = FloatRect();
         // Radius of the rounded rectangle.
-        float radius = 0.0f;
+        vec2 radius;
+        bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
     };
 
     using FrameRate = scheduler::LayerInfo::FrameRate;
@@ -606,7 +606,7 @@
     // corner crop does not intersect with its own rounded corner crop.
     virtual RoundedCornerState getRoundedCornerState() const;
 
-    bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+    bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
 
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     /**
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
deleted file mode 100644
index 1c0263b..0000000
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2011 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 "LayerRejecter.h"
-
-#include <gui/BufferItem.h>
-#include <system/window.h>
-
-#define DEBUG_RESIZE 0
-
-namespace android {
-
-LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
-                             bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
-                             bool transformToDisplayInverse)
-      : mFront(front),
-        mCurrent(current),
-        mRecomputeVisibleRegions(recomputeVisibleRegions),
-        mStickyTransformSet(stickySet),
-        mName(name),
-        mTransformToDisplayInverse(transformToDisplayInverse) {}
-
-bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
-    if (buf == nullptr) {
-        return false;
-    }
-
-    uint32_t bufWidth = buf->getWidth();
-    uint32_t bufHeight = buf->getHeight();
-
-    // check that we received a buffer of the right size
-    // (Take the buffer's orientation into account)
-    if (item.mTransform & ui::Transform::ROT_90) {
-        std::swap(bufWidth, bufHeight);
-    }
-
-    if (mTransformToDisplayInverse) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufWidth, bufHeight);
-        }
-    }
-
-    int actualScalingMode = item.mScalingMode;
-    bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
-    if (mFront.active_legacy != mFront.requested_legacy) {
-        if (isFixedSize ||
-            (bufWidth == mFront.requested_legacy.w && bufHeight == mFront.requested_legacy.h)) {
-            // Here we pretend the transaction happened by updating the
-            // current and drawing states. Drawing state is only accessed
-            // in this thread, no need to have it locked
-            mFront.active_legacy = mFront.requested_legacy;
-
-            // We also need to update the current state so that
-            // we don't end-up overwriting the drawing state with
-            // this stale current state during the next transaction
-            //
-            // NOTE: We don't need to hold the transaction lock here
-            // because State::active_legacy is only accessed from this thread.
-            mCurrent.active_legacy = mFront.active_legacy;
-            mCurrent.modified = true;
-
-            // recompute visible region
-            mRecomputeVisibleRegions = true;
-
-            if (mFront.crop != mFront.requestedCrop) {
-                mFront.crop = mFront.requestedCrop;
-                mCurrent.crop = mFront.requestedCrop;
-                mRecomputeVisibleRegions = true;
-            }
-        }
-
-        ALOGD_IF(DEBUG_RESIZE,
-                 "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
-                 "  drawing={ active_legacy   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} "
-                 "(%4d,%4d) "
-                 "}\n"
-                 "            requested_legacy={ wh={%4u,%4u} }}\n",
-                 mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
-                 mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top,
-                 mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(),
-                 mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h);
-    }
-
-    if (!isFixedSize && !mStickyTransformSet) {
-        if (mFront.active_legacy.w != bufWidth || mFront.active_legacy.h != bufHeight) {
-            // reject this buffer
-            ALOGE("[%s] rejecting buffer: "
-                  "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}",
-                  mName.c_str(), bufWidth, bufHeight, mFront.active_legacy.w,
-                  mFront.active_legacy.h);
-            return true;
-        }
-    }
-
-    // if the transparent region has changed (this test is
-    // conservative, but that's fine, worst case we're doing
-    // a bit of extra work), we latch the new one and we
-    // trigger a visible-region recompute.
-    //
-    // We latch the transparent region here, instead of above where we latch
-    // the rest of the geometry because it is only content but not necessarily
-    // resize dependent.
-    if (!mFront.activeTransparentRegion_legacy.hasSameRects(
-                mFront.requestedTransparentRegion_legacy)) {
-        mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
-
-        // We also need to update the current state so that
-        // we don't end-up overwriting the drawing state with
-        // this stale current state during the next transaction
-        //
-        // NOTE: We don't need to hold the transaction lock here
-        // because State::active_legacy is only accessed from this thread.
-        mCurrent.activeTransparentRegion_legacy = mFront.activeTransparentRegion_legacy;
-
-        // recompute visible region
-        mRecomputeVisibleRegions = true;
-    }
-
-    return false;
-}
-
-}  // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
deleted file mode 100644
index 4981f45..0000000
--- a/services/surfaceflinger/LayerRejecter.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 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 "Layer.h"
-#include "BufferLayerConsumer.h"
-
-namespace android {
-
-class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
-public:
-    LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
-                  bool stickySet, const std::string& name,
-                  bool transformToDisplayInverse);
-
-    virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
-
-private:
-    Layer::State& mFront;
-    Layer::State& mCurrent;
-    bool& mRecomputeVisibleRegions;
-    const bool mStickyTransformSet;
-    const std::string& mName;
-    const bool mTransformToDisplayInverse;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
deleted file mode 100644
index df76f50..0000000
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2014 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 "MonitoredProducer.h"
-#include "Layer.h"
-#include "SurfaceFlinger.h"
-
-#include "Scheduler/MessageQueue.h"
-
-namespace android {
-
-MonitoredProducer::MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
-        const sp<SurfaceFlinger>& flinger,
-        const wp<Layer>& layer) :
-    mProducer(producer),
-    mFlinger(flinger),
-    mLayer(layer) {}
-
-MonitoredProducer::~MonitoredProducer() {}
-
-status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    return mProducer->requestBuffer(slot, buf);
-}
-
-status_t MonitoredProducer::setMaxDequeuedBufferCount(
-        int maxDequeuedBuffers) {
-    return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
-}
-
-status_t MonitoredProducer::setAsyncMode(bool async) {
-    return mProducer->setAsyncMode(async);
-}
-
-status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
-                                          PixelFormat format, uint64_t usage,
-                                          uint64_t* outBufferAge,
-                                          FrameEventHistoryDelta* outTimestamps) {
-    return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
-}
-
-status_t MonitoredProducer::detachBuffer(int slot) {
-    return mProducer->detachBuffer(slot);
-}
-
-status_t MonitoredProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
-        sp<Fence>* outFence) {
-    return mProducer->detachNextBuffer(outBuffer, outFence);
-}
-
-status_t MonitoredProducer::attachBuffer(int* outSlot,
-        const sp<GraphicBuffer>& buffer) {
-    return mProducer->attachBuffer(outSlot, buffer);
-}
-
-status_t MonitoredProducer::queueBuffer(int slot, const QueueBufferInput& input,
-        QueueBufferOutput* output) {
-    return mProducer->queueBuffer(slot, input, output);
-}
-
-status_t MonitoredProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
-    return mProducer->cancelBuffer(slot, fence);
-}
-
-int MonitoredProducer::query(int what, int* value) {
-    return mProducer->query(what, value);
-}
-
-status_t MonitoredProducer::connect(const sp<IProducerListener>& listener,
-        int api, bool producerControlledByApp, QueueBufferOutput* output) {
-    return mProducer->connect(listener, api, producerControlledByApp, output);
-}
-
-status_t MonitoredProducer::disconnect(int api, DisconnectMode mode) {
-    return mProducer->disconnect(api, mode);
-}
-
-status_t MonitoredProducer::setSidebandStream(const sp<NativeHandle>& stream) {
-    return mProducer->setSidebandStream(stream);
-}
-
-void MonitoredProducer::allocateBuffers(uint32_t width, uint32_t height,
-        PixelFormat format, uint64_t usage) {
-    mProducer->allocateBuffers(width, height, format, usage);
-}
-
-status_t MonitoredProducer::allowAllocation(bool allow) {
-    return mProducer->allowAllocation(allow);
-}
-
-status_t MonitoredProducer::setGenerationNumber(uint32_t generationNumber) {
-    return mProducer->setGenerationNumber(generationNumber);
-}
-
-String8 MonitoredProducer::getConsumerName() const {
-    return mProducer->getConsumerName();
-}
-
-status_t MonitoredProducer::setSharedBufferMode(bool sharedBufferMode) {
-    return mProducer->setSharedBufferMode(sharedBufferMode);
-}
-
-status_t MonitoredProducer::setAutoRefresh(bool autoRefresh) {
-    return mProducer->setAutoRefresh(autoRefresh);
-}
-
-status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
-    return mProducer->setDequeueTimeout(timeout);
-}
-
-status_t MonitoredProducer::setLegacyBufferDrop(bool drop) {
-    return mProducer->setLegacyBufferDrop(drop);
-}
-
-status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
-        sp<Fence>* outFence, float outTransformMatrix[16]) {
-    return mProducer->getLastQueuedBuffer(outBuffer, outFence,
-            outTransformMatrix);
-}
-
-status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
-                                                Rect* outRect, uint32_t* outTransform) {
-    return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
-}
-
-void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
-    mProducer->getFrameTimestamps(outDelta);
-}
-
-status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
-    return mProducer->getUniqueId(outId);
-}
-
-status_t MonitoredProducer::getConsumerUsage(uint64_t* outUsage) const {
-    return mProducer->getConsumerUsage(outUsage);
-}
-
-status_t MonitoredProducer::setAutoPrerotation(bool autoPrerotation) {
-    return mProducer->setAutoPrerotation(autoPrerotation);
-}
-
-IBinder* MonitoredProducer::onAsBinder() {
-    return this;
-}
-
-sp<Layer> MonitoredProducer::getLayer() const {
-    return mLayer.promote();
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
deleted file mode 100644
index 3778277..0000000
--- a/services/surfaceflinger/MonitoredProducer.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2014 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.
- */
-
-#ifndef ANDROID_MONITORED_PRODUCER_H
-#define ANDROID_MONITORED_PRODUCER_H
-
-#include <gui/IGraphicBufferProducer.h>
-
-namespace android {
-
-class IProducerListener;
-class NativeHandle;
-class SurfaceFlinger;
-class Layer;
-
-// MonitoredProducer wraps an IGraphicBufferProducer so that SurfaceFlinger will
-// be notified upon its destruction
-class MonitoredProducer : public BnGraphicBufferProducer {
-public:
-    MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
-            const sp<SurfaceFlinger>& flinger,
-            const wp<Layer>& layer);
-    virtual ~MonitoredProducer();
-
-    // From IGraphicBufferProducer
-    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
-    virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
-    virtual status_t setAsyncMode(bool async);
-    virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
-                                   PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
-                                   FrameEventHistoryDelta* outTimestamps);
-    virtual status_t detachBuffer(int slot);
-    virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
-            sp<Fence>* outFence);
-    virtual status_t attachBuffer(int* outSlot,
-            const sp<GraphicBuffer>& buffer);
-    virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
-            QueueBufferOutput* output);
-    virtual status_t cancelBuffer(int slot, const sp<Fence>& fence);
-    virtual int query(int what, int* value);
-    virtual status_t connect(const sp<IProducerListener>& token, int api,
-            bool producerControlledByApp, QueueBufferOutput* output);
-    virtual status_t disconnect(int api, DisconnectMode mode);
-    virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
-    virtual void allocateBuffers(uint32_t width, uint32_t height,
-            PixelFormat format, uint64_t usage);
-    virtual status_t allowAllocation(bool allow);
-    virtual status_t setGenerationNumber(uint32_t generationNumber);
-    virtual String8 getConsumerName() const override;
-    virtual status_t setDequeueTimeout(nsecs_t timeout) override;
-    virtual status_t setLegacyBufferDrop(bool drop) override;
-    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
-            sp<Fence>* outFence, float outTransformMatrix[16]) override;
-    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
-                                         Rect* outRect, uint32_t* outTransform) override;
-    virtual IBinder* onAsBinder();
-    virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
-    virtual status_t setAutoRefresh(bool autoRefresh) override;
-    virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
-    virtual status_t getUniqueId(uint64_t* outId) const override;
-    virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
-    virtual status_t setAutoPrerotation(bool autoPrerotation) override;
-
-    // The Layer which created this producer, and on which queued Buffer's will be displayed.
-    sp<Layer> getLayer() const;
-
-private:
-    sp<IGraphicBufferProducer> mProducer;
-    sp<SurfaceFlinger> mFlinger;
-    // The Layer which created this producer, and on which queued Buffer's will be displayed.
-    wp<Layer> mLayer;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MONITORED_PRODUCER_H
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d4435c2..a9180d4 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,9 +16,10 @@
 
 #include <algorithm>
 
-#include "RefreshRateOverlay.h"
+#include "BackgroundExecutor.h"
 #include "Client.h"
 #include "Layer.h"
+#include "RefreshRateOverlay.h"
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
@@ -56,6 +57,14 @@
 
 } // namespace
 
+SurfaceControlHolder::~SurfaceControlHolder() {
+    // Hand the sp<SurfaceControl> to the helper thread to release the last
+    // reference. This makes sure that the SurfaceControl is destructed without
+    // SurfaceFlinger::mStateLock held.
+    BackgroundExecutor::getInstance().sendCallbacks(
+            {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }});
+}
+
 void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
                                                          SkCanvas& canvas) {
     const SkRect rect = [&]() {
@@ -210,21 +219,27 @@
     return buffers;
 }
 
+std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
+    sp<SurfaceControl> surfaceControl =
+            SurfaceComposerClient::getDefault()
+                    ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight,
+                                    PIXEL_FORMAT_RGBA_8888,
+                                    ISurfaceComposerClient::eFXSurfaceBufferState);
+    return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
+}
+
 RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
       : mFpsRange(fpsRange),
         mShowSpinner(showSpinner),
-        mSurfaceControl(SurfaceComposerClient::getDefault()
-                                ->createSurface(String8("RefreshRateOverlay"), kBufferWidth,
-                                                kBufferHeight, PIXEL_FORMAT_RGBA_8888,
-                                                ISurfaceComposerClient::eFXSurfaceBufferState)) {
+        mSurfaceControl(createSurfaceControlHolder()) {
     if (!mSurfaceControl) {
         ALOGE("%s: Failed to create buffer state layer", __func__);
         return;
     }
 
-    createTransaction(mSurfaceControl)
-            .setLayer(mSurfaceControl, INT32_MAX - 2)
-            .setTrustedOverlay(mSurfaceControl, true)
+    createTransaction(mSurfaceControl->get())
+            .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+            .setTrustedOverlay(mSurfaceControl->get(), true)
             .apply();
 }
 
@@ -233,7 +248,7 @@
     if (!mSurfaceControl) return kNoBuffers;
 
     const auto transformHint =
-            static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
+            static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
 
     // Tell SurfaceFlinger about the pre-rotation on the buffer.
     const auto transform = [&] {
@@ -247,7 +262,9 @@
         }
     }();
 
-    createTransaction(mSurfaceControl).setTransform(mSurfaceControl, transform).apply();
+    createTransaction(mSurfaceControl->get())
+            .setTransform(mSurfaceControl->get(), transform)
+            .apply();
 
     BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
     if (it == mBufferCache.end()) {
@@ -289,21 +306,21 @@
     Rect frame((3 * width) >> 4, height >> 5);
     frame.offsetBy(width >> 5, height >> 4);
 
-    createTransaction(mSurfaceControl)
-            .setMatrix(mSurfaceControl, frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0,
-                       frame.getHeight() / static_cast<float>(kBufferHeight))
-            .setPosition(mSurfaceControl, frame.left, frame.top)
+    createTransaction(mSurfaceControl->get())
+            .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+                       0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+            .setPosition(mSurfaceControl->get(), frame.left, frame.top)
             .apply();
 }
 
 void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
-    createTransaction(mSurfaceControl).setLayerStack(mSurfaceControl, stack).apply();
+    createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
 }
 
 void RefreshRateOverlay::changeRefreshRate(Fps fps) {
     mCurrentFps = fps;
     const auto buffer = getOrCreateBuffers(fps)[mFrame];
-    createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+    createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 void RefreshRateOverlay::animate() {
@@ -312,7 +329,7 @@
     const auto& buffers = getOrCreateBuffers(*mCurrentFps);
     mFrame = (mFrame + 1) % buffers.size();
     const auto buffer = buffers[mFrame];
-    createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
+    createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a465a36..a2966e6 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -33,6 +33,20 @@
 
 class GraphicBuffer;
 class SurfaceControl;
+class SurfaceFlinger;
+
+// Helper class to delete the SurfaceControl on a helper thread as
+// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held.
+class SurfaceControlHolder {
+public:
+    explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){};
+    ~SurfaceControlHolder();
+
+    const sp<SurfaceControl>& get() const { return mSurfaceControl; }
+
+private:
+    sp<SurfaceControl> mSurfaceControl;
+};
 
 class RefreshRateOverlay {
 public:
@@ -75,7 +89,7 @@
     const FpsRange mFpsRange; // For color interpolation.
     const bool mShowSpinner;
 
-    const sp<SurfaceControl> mSurfaceControl;
+    const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 3181a7f..a3d4e8d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -28,7 +28,6 @@
 #include <ftl/fake_guard.h>
 #include <gui/WindowInfo.h>
 #include <system/window.h>
-#include <ui/DisplayStatInfo.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
@@ -172,8 +171,7 @@
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
         const Fps refreshRate = holdRefreshRateConfigs()->getActiveMode()->getFps();
-        const auto currentPeriod =
-                mVsyncSchedule->getTracker().currentPeriod() ?: refreshRate.getPeriodNsecs();
+        const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
 
         const auto frameRate = getFrameRateOverride(uid);
         if (!frameRate.has_value()) {
@@ -361,12 +359,6 @@
     thread->setDuration(workDuration, readyDuration);
 }
 
-DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) {
-    const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(now);
-    const auto vsyncPeriod = mVsyncSchedule->getTracker().currentPeriod();
-    return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod};
-}
-
 ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
     if (mInjectVSyncs == enable) {
         return {};
@@ -767,11 +759,4 @@
     mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
 }
 
-std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
-        nsecs_t expectedPresentTime) const {
-    const auto presentTime = std::chrono::nanoseconds(expectedPresentTime);
-    const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule->getTracker().currentPeriod());
-    return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
-}
-
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 7f76d1e..f800eeb 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -32,9 +32,8 @@
 #include <ui/GraphicTypes.h>
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
-#include <ui/DisplayStatInfo.h>
-
 #include <scheduler/Features.h>
+#include <scheduler/Time.h>
 
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
@@ -150,8 +149,6 @@
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
-    DisplayStatInfo getDisplayStatInfo(nsecs_t now);
-
     // Returns injector handle if injection has toggled, or an invalid handle otherwise.
     ConnectionHandle enableVSyncInjection(bool enable);
     // Returns false if injection is disabled.
@@ -191,14 +188,12 @@
 
     void setDisplayPowerMode(hal::PowerMode powerMode);
 
-    VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
+    VsyncSchedule& getVsyncSchedule() { return *mVsyncSchedule; }
 
     // Returns true if a given vsync timestamp is considered valid vsync
     // for a given uid
     bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
 
-    std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
-
     void dump(std::string&) const;
     void dump(ConnectionHandle, std::string&) const;
     void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 3a918a1..95bc31f 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -68,6 +68,14 @@
 VsyncSchedule::VsyncSchedule(VsyncSchedule&&) = default;
 VsyncSchedule::~VsyncSchedule() = default;
 
+Period VsyncSchedule::period() const {
+    return Period::fromNs(mTracker->currentPeriod());
+}
+
+TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const {
+    return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns()));
+}
+
 void VsyncSchedule::dump(std::string& out) const {
     out.append("VsyncController:\n");
     mController->dump(out);
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 0d9b114..8c17409 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -20,6 +20,7 @@
 #include <string>
 
 #include <scheduler/Features.h>
+#include <scheduler/Time.h>
 
 namespace android::scheduler {
 
@@ -38,6 +39,9 @@
     VsyncSchedule(VsyncSchedule&&);
     ~VsyncSchedule();
 
+    Period period() const;
+    TimePoint vsyncDeadlineAfter(TimePoint) const;
+
     // TODO(b/185535769): Hide behind API.
     const VsyncTracker& getTracker() const { return *mTracker; }
     VsyncTracker& getTracker() { return *mTracker; }
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
new file mode 100644
index 0000000..6fa548e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
@@ -0,0 +1,67 @@
+/*
+ * 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
+
+#include <chrono>
+
+#include <utils/Timers.h>
+
+namespace android {
+namespace scheduler {
+
+// TODO(b/185535769): Pull Clock.h to libscheduler to reuse this.
+using SchedulerClock = std::chrono::high_resolution_clock;
+static_assert(SchedulerClock::is_steady);
+
+} // namespace scheduler
+
+struct Duration;
+
+struct TimePoint : scheduler::SchedulerClock::time_point {
+    explicit TimePoint(const Duration&);
+
+    // Implicit conversion from std::chrono counterpart.
+    constexpr TimePoint(scheduler::SchedulerClock::time_point p)
+          : scheduler::SchedulerClock::time_point(p) {}
+
+    static TimePoint fromNs(nsecs_t);
+
+    nsecs_t ns() const;
+};
+
+struct Duration : TimePoint::duration {
+    // Implicit conversion from std::chrono counterpart.
+    constexpr Duration(TimePoint::duration d) : TimePoint::duration(d) {}
+
+    static Duration fromNs(nsecs_t ns) { return {std::chrono::nanoseconds(ns)}; }
+
+    nsecs_t ns() const { return std::chrono::nanoseconds(*this).count(); }
+};
+
+using Period = Duration;
+
+inline TimePoint::TimePoint(const Duration& d) : scheduler::SchedulerClock::time_point(d) {}
+
+inline TimePoint TimePoint::fromNs(nsecs_t ns) {
+    return TimePoint(Duration::fromNs(ns));
+}
+
+inline nsecs_t TimePoint::ns() const {
+    return Duration(time_since_epoch()).ns();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ab5bb77..2beb946 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -129,7 +129,6 @@
 #include "LayerProtoHelper.h"
 #include "LayerRenderArea.h"
 #include "LayerVector.h"
-#include "MonitoredProducer.h"
 #include "MutexUtils.h"
 #include "NativeWindowSurface.h"
 #include "RefreshRateOverlay.h"
@@ -1055,12 +1054,14 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) {
-    if (!stats) {
+status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* outStats) {
+    if (!outStats) {
         return BAD_VALUE;
     }
 
-    *stats = mScheduler->getDisplayStatInfo(systemTime());
+    const auto& schedule = mScheduler->getVsyncSchedule();
+    outStats->vsyncTime = schedule.vsyncDeadlineAfter(scheduler::SchedulerClock::now()).ns();
+    outStats->vsyncPeriod = schedule.period().ns();
     return NO_ERROR;
 }
 
@@ -1569,12 +1570,10 @@
 
 status_t SurfaceFlinger::injectVSync(nsecs_t when) {
     Mutex::Autolock lock(mStateLock);
-    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(when);
-    const auto expectedPresent = calculateExpectedPresentTime(stats);
-    return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent,
-                                   /*deadlineTimestamp=*/expectedPresent)
-            ? NO_ERROR
-            : BAD_VALUE;
+    const nsecs_t expectedPresentTime = calculateExpectedPresentTime(when).ns();
+    const nsecs_t deadlineTimestamp = expectedPresentTime;
+    return mScheduler->injectVSync(when, expectedPresentTime, deadlineTimestamp) ? NO_ERROR
+                                                                                 : BAD_VALUE;
 }
 
 status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
@@ -1886,11 +1885,6 @@
     }
 }
 
-void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
-    std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
-    *compositorTiming = getBE().mCompositorTiming;
-}
-
 void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
                                           hal::Connection connection) {
     const bool connected = connection == hal::Connection::CONNECTED;
@@ -1951,18 +1945,15 @@
     }));
 }
 
-SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
-    const auto now = systemTime();
-    const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod;
-    const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod;
-    return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0]
-                                             : mPreviousPresentFences[1];
+auto SurfaceFlinger::getPreviousPresentFence(nsecs_t frameTime, Period vsyncPeriod)
+        -> const FenceTimePtr& {
+    const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod.ns();
+    const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
+    return mPreviousPresentFences[i].fenceTime;
 }
 
-bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
+bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
     ATRACE_CALL();
-    const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
-
     if (fence == FenceTime::NO_FENCE) {
         return false;
     }
@@ -1973,37 +1964,32 @@
     return status == -ETIME;
 }
 
-nsecs_t SurfaceFlinger::previousFramePresentTime() {
-    const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
+TimePoint SurfaceFlinger::calculateExpectedPresentTime(nsecs_t frameTime) const {
+    const auto& schedule = mScheduler->getVsyncSchedule();
 
-    if (fence == FenceTime::NO_FENCE) {
-        return Fence::SIGNAL_TIME_INVALID;
+    const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(TimePoint::fromNs(frameTime));
+    if (mVsyncModulator->getVsyncConfig().sfOffset > 0) {
+        return vsyncDeadline;
     }
 
-    return fence->getSignalTime();
-}
-
-nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) const {
-    // Inflate the expected present time if we're targetting the next vsync.
-    return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
-                                                          : stats.vsyncTime + stats.vsyncPeriod;
+    // Inflate the expected present time if we're targeting the next vsync.
+    return vsyncDeadline + schedule.period();
 }
 
 bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
         FTL_FAKE_GUARD(kMainThreadContext) {
-    // calculate the expected present time once and use the cached
-    // value throughout this frame to make sure all layers are
-    // seeing this same value.
-    if (expectedVsyncTime >= frameTime) {
-        mExpectedPresentTime = expectedVsyncTime;
-    } else {
-        const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameTime);
-        mExpectedPresentTime = calculateExpectedPresentTime(stats);
-    }
-
+    // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
+    // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime
+    // accordingly, but not mScheduledPresentTime.
     const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
     mScheduledPresentTime = expectedVsyncTime;
 
+    // Calculate the expected present time once and use the cached value throughout this frame to
+    // make sure all layers are seeing this same value.
+    mExpectedPresentTime = expectedVsyncTime >= frameTime
+            ? expectedVsyncTime
+            : calculateExpectedPresentTime(frameTime).ns();
+
     const auto vsyncIn = [&] {
         if (!ATRACE_ENABLED()) return 0.f;
         return (mExpectedPresentTime - systemTime()) / 1e6f;
@@ -2011,6 +1997,9 @@
     ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId, vsyncIn,
                   mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
 
+    const Period vsyncPeriod = mScheduler->getVsyncSchedule().period();
+    const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod);
+
     // When Backpressure propagation is enabled we want to give a small grace period
     // for the present fence to fire instead of just giving up on this frame to handle cases
     // where present fence is just about to get signaled.
@@ -2019,7 +2008,8 @@
 
     // Pending frames may trigger backpressure propagation.
     const TracedOrdinal<bool> framePending = {"PrevFramePending",
-                                              previousFramePending(graceTimeForPresentFenceMs)};
+                                              isFencePending(previousPresentFence,
+                                                             graceTimeForPresentFenceMs)};
 
     // Frame missed counts for metrics tracking.
     // A frame is missed if the prior frame is still pending. If no longer pending,
@@ -2029,9 +2019,8 @@
     // Add some slop to correct for drift. This should generally be
     // smaller than a typical frame duration, but should not be so small
     // that it reports reasonable drift as a missed frame.
-    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
-    const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
-    const nsecs_t previousPresentTime = previousFramePresentTime();
+    const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
+    const nsecs_t previousPresentTime = previousPresentFence->getSignalTime();
     const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
                                              framePending ||
                                                      (previousPresentTime >= 0 &&
@@ -2118,7 +2107,7 @@
     // Composite if transactions were committed, or if requested by HWC.
     bool mustComposite = mMustComposite.exchange(false);
     {
-        mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod));
+        mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(vsyncPeriod.ns()));
 
         bool needsTraversal = false;
         if (clearTransactionFlags(eTransactionFlushNeeded)) {
@@ -2239,7 +2228,9 @@
     }
 
     const auto expectedPresentTime = mExpectedPresentTime.load();
-    const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(expectedPresentTime);
+    const auto prevVsyncTime =
+            TimePoint::fromNs(expectedPresentTime) - mScheduler->getVsyncSchedule().period();
+
     const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
     refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
     refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
@@ -2326,11 +2317,11 @@
     mLayersPendingRefresh.clear();
 }
 
-void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
-                                            std::shared_ptr<FenceTime>& presentFenceTime) {
+nsecs_t SurfaceFlinger::trackPresentLatency(nsecs_t compositeTime,
+                                            std::shared_ptr<FenceTime> presentFenceTime) {
     // Update queue of past composite+present times and determine the
     // most recently known composite to present latency.
-    getBE().mCompositePresentTimes.push({compositeTime, presentFenceTime});
+    getBE().mCompositePresentTimes.push({compositeTime, std::move(presentFenceTime)});
     nsecs_t compositeToPresentLatency = -1;
     while (!getBE().mCompositePresentTimes.empty()) {
         SurfaceFlingerBE::CompositePresentTime& cpt = getBE().mCompositePresentTimes.front();
@@ -2349,41 +2340,7 @@
         getBE().mCompositePresentTimes.pop();
     }
 
-    setCompositorTimingSnapped(stats, compositeToPresentLatency);
-}
-
-void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats,
-                                                nsecs_t compositeToPresentLatency) {
-    // Avoid division by 0 by defaulting to 60Hz
-    const auto vsyncPeriod = stats.vsyncPeriod ?: (60_Hz).getPeriodNsecs();
-
-    // Integer division and modulo round toward 0 not -inf, so we need to
-    // treat negative and positive offsets differently.
-    nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
-            ? (vsyncPeriod -
-               (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % vsyncPeriod))
-            : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % vsyncPeriod);
-
-    // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
-    if (idealLatency <= 0) {
-        idealLatency = vsyncPeriod;
-    }
-
-    // Snap the latency to a value that removes scheduling jitter from the
-    // composition and present times, which often have >1ms of jitter.
-    // Reducing jitter is important if an app attempts to extrapolate
-    // something (such as user input) to an accurate diasplay time.
-    // Snapping also allows an app to precisely calculate
-    // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
-    const nsecs_t bias = vsyncPeriod / 2;
-    const int64_t extraVsyncs = ((compositeToPresentLatency - idealLatency + bias) / vsyncPeriod);
-    const nsecs_t snappedCompositeToPresentLatency =
-            (extraVsyncs > 0) ? idealLatency + (extraVsyncs * vsyncPeriod) : idealLatency;
-
-    std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
-    getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency;
-    getBE().mCompositorTiming.interval = vsyncPeriod;
-    getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
+    return compositeToPresentLatency;
 }
 
 bool SurfaceFlinger::isHdrLayer(Layer* layer) const {
@@ -2470,18 +2427,20 @@
     mFrameTimeline->setSfPresent(/* sfPresentTime */ now, mPreviousPresentFences[0].fenceTime,
                                  glCompositionDoneFenceTime);
 
-    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
-
     // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
     // be sampled a little later than when we started doing work for this frame,
-    // but that should be okay since updateCompositorTiming has snapping logic.
-    updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
-                           mPreviousPresentFences[0].fenceTime);
-    CompositorTiming compositorTiming;
-    {
-        std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
-        compositorTiming = getBE().mCompositorTiming;
-    }
+    // but that should be okay since CompositorTiming has snapping logic.
+    const nsecs_t compositeTime = mCompositionEngine->getLastFrameRefreshTimestamp();
+    const nsecs_t presentLatency =
+            trackPresentLatency(compositeTime, mPreviousPresentFences[0].fenceTime);
+
+    const auto& schedule = mScheduler->getVsyncSchedule();
+    const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(TimePoint::fromNs(now));
+    const Period vsyncPeriod = schedule.period();
+    const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset;
+
+    const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
+                                            presentLatency);
 
     for (const auto& layer: mLayersWithQueuedFrames) {
         layer->onPostComposition(display, glCompositionDoneFenceTime,
@@ -2593,7 +2552,7 @@
         mHasPoweredOff = false;
     } else {
         nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime;
-        size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod);
+        const size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncPeriod.ns());
         if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
             getBE().mFrameBuckets[numPeriods] += elapsedTime;
         } else {
@@ -3470,8 +3429,8 @@
                                              mInterceptor->saveVSyncEvent(timestamp);
                                          });
 
-    mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
-                          configs.late.sfWorkDuration);
+    mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
+                          *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
 
     mRegionSamplingThread =
             new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
@@ -3941,10 +3900,10 @@
 bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
     // The amount of time SF can delay a frame if it is considered early based
     // on the VsyncModulator::VsyncConfig::appWorkDuration
-    constexpr static std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
+    constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
 
-    const auto currentVsyncPeriod = mScheduler->getDisplayStatInfo(systemTime()).vsyncPeriod;
-    const auto earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
+    const nsecs_t currentVsyncPeriod = mScheduler->getVsyncSchedule().period().ns();
+    const nsecs_t earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
 
     const auto prediction = mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
     if (!prediction.has_value()) {
@@ -4847,10 +4806,6 @@
     const nsecs_t vsyncPeriod = display->refreshRateConfigs().getActiveMode()->getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
     mActiveDisplayTransformHint = display->getTransformHint();
-    // Use phase of 0 since phase is not known.
-    // Use latency of 0, which will snap to the ideal latency.
-    DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
-    setCompositorTimingSnapped(stats, 0);
 }
 
 void SurfaceFlinger::initializeDisplays() {
@@ -6632,24 +6587,21 @@
         });
 
         if (captureListener) {
-            // TODO: The future returned by std::async blocks the main thread. Return a chain of
-            // futures to the Binder thread instead.
-            std::async([=]() mutable {
-                ATRACE_NAME("captureListener is nonnull!");
-                auto fenceResult = renderFuture.get();
-                // TODO(b/232535621): Change ScreenCaptureResults to store a FenceResult.
-                captureResults.result = fenceStatus(fenceResult);
-                captureResults.fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
-                captureListener->onScreenCaptureCompleted(captureResults);
-            });
+            // Defer blocking on renderFuture back to the Binder thread.
+            return ftl::Future(std::move(renderFuture))
+                    .then([captureListener, captureResults = std::move(captureResults)](
+                                  FenceResult fenceResult) mutable -> FenceResult {
+                        // TODO(b/232535621): Change ScreenCaptureResults to store a FenceResult.
+                        captureResults.result = fenceStatus(fenceResult);
+                        captureResults.fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
+                        captureListener->onScreenCaptureCompleted(captureResults);
+                        return base::unexpected(NO_ERROR);
+                    })
+                    .share();
         }
         return renderFuture;
     });
 
-    if (captureListener) {
-        return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
-    }
-
     // Flatten nested futures.
     auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
         return future;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ed6c8ce..7d23c3c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -31,6 +31,7 @@
 #include <ftl/future.h>
 #include <ftl/small_map.h>
 #include <gui/BufferQueue.h>
+#include <gui/CompositorTiming.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ITransactionCompletedListener.h>
@@ -168,10 +169,6 @@
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
 struct SurfaceFlingerBE {
-    // protected by mCompositorTimingLock;
-    mutable std::mutex mCompositorTimingLock;
-    CompositorTiming mCompositorTiming;
-
     // Only accessed from the main thread.
     struct CompositePresentTime {
         nsecs_t composite = -1;
@@ -377,7 +374,6 @@
     friend class FpsReporter;
     friend class TunnelModeEnabledReporter;
     friend class Layer;
-    friend class MonitoredProducer;
     friend class RefreshRateOverlay;
     friend class RegionSamplingThread;
     friend class LayerRenderArea;
@@ -958,11 +954,9 @@
      * Compositing
      */
     void postComposition();
-    void getCompositorTiming(CompositorTiming* compositorTiming);
-    void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
-                                std::shared_ptr<FenceTime>& presentFenceTime);
-    void setCompositorTimingSnapped(const DisplayStatInfo& stats,
-                                    nsecs_t compositeToPresentLatency);
+
+    // Returns the composite-to-present latency of the latest presented frame.
+    nsecs_t trackPresentLatency(nsecs_t compositeTime, std::shared_ptr<FenceTime> presentFenceTime);
 
     void postFrame() REQUIRES(kMainThreadContext);
 
@@ -997,31 +991,17 @@
         getHwComposer().setVsyncEnabled(id, enabled);
     }
 
-    struct FenceWithFenceTime {
-        sp<Fence> fence = Fence::NO_FENCE;
-        std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
-    };
+    using FenceTimePtr = std::shared_ptr<FenceTime>;
 
-    // Gets the fence for the previous frame.
-    // Must be called on the main thread.
-    FenceWithFenceTime previousFrameFence();
+    const FenceTimePtr& getPreviousPresentFence(nsecs_t frameTime, Period)
+            REQUIRES(kMainThreadContext);
 
-    // Whether the previous frame has not yet been presented to the display.
-    // If graceTimeMs is positive, this method waits for at most the provided
-    // grace period before reporting if the frame missed.
-    // Must be called on the main thread.
-    bool previousFramePending(int graceTimeMs = 0);
-
-    // Returns the previous time that the frame was presented. If the frame has
-    // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
-    // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
-    // Must be called on the main thread.
-    nsecs_t previousFramePresentTime();
+    // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal.
+    static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
 
     // Calculates the expected present time for this frame. For negative offsets, performs a
     // correction using the predicted vsync for the next frame instead.
-
-    nsecs_t calculateExpectedPresentTime(DisplayStatInfo) const;
+    TimePoint calculateExpectedPresentTime(nsecs_t frameTime) const;
 
     /*
      * Display identification
@@ -1209,7 +1189,7 @@
     std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
     // Tracks layers that need to update a display's dirty region.
     std::vector<sp<Layer>> mLayersPendingRefresh;
-    std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
+
     // True if in the previous frame at least one layer was composed via the GPU.
     bool mHadClientComposition = false;
     // True if in the previous frame at least one layer was composed via HW Composer.
@@ -1346,6 +1326,12 @@
 
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
+    struct FenceWithFenceTime {
+        sp<Fence> fence = Fence::NO_FENCE;
+        FenceTimePtr fenceTime = FenceTime::NO_FENCE;
+    };
+    std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
+
     std::atomic<nsecs_t> mExpectedPresentTime = 0;
     nsecs_t mScheduledPresentTime = 0;
     hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 39a5d0f..2399a39 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -22,14 +22,12 @@
 #include <cutils/properties.h>
 #include <ui/GraphicBuffer.h>
 
-#include "BufferLayerConsumer.h"
 #include "BufferStateLayer.h"
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
 #include "EffectLayer.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
-#include "MonitoredProducer.h"
 #include "NativeWindowSurface.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerDefaultFactory.h"
@@ -83,18 +81,6 @@
     BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
 }
 
-sp<IGraphicBufferProducer> DefaultFactory::createMonitoredProducer(
-        const sp<IGraphicBufferProducer>& producer, const sp<SurfaceFlinger>& flinger,
-        const wp<Layer>& layer) {
-    return new MonitoredProducer(producer, flinger, layer);
-}
-
-sp<BufferLayerConsumer> DefaultFactory::createBufferLayerConsumer(
-        const sp<IGraphicBufferConsumer>& consumer, renderengine::RenderEngine& renderEngine,
-        uint32_t textureName, Layer* layer) {
-    return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
-}
-
 std::unique_ptr<surfaceflinger::NativeWindowSurface> DefaultFactory::createNativeWindowSurface(
         const sp<IGraphicBufferProducer>& producer) {
     return surfaceflinger::impl::createNativeWindowSurface(producer);
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 173ca81..4000e09 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -38,12 +38,6 @@
     void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                            sp<IGraphicBufferConsumer>* outConsumer,
                            bool consumerIsSurfaceFlinger) override;
-    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
-                                                       const sp<SurfaceFlinger>&,
-                                                       const wp<Layer>&) override;
-    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
-                                                      renderengine::RenderEngine&, uint32_t tex,
-                                                      Layer*) override;
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>&) override;
     std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index e117e96..77a75c4 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -85,12 +85,6 @@
     virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                                    sp<IGraphicBufferConsumer>* outConsumer,
                                    bool consumerIsSurfaceFlinger) = 0;
-    virtual sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
-                                                               const sp<SurfaceFlinger>&,
-                                                               const wp<Layer>&) = 0;
-    virtual sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
-                                                              renderengine::RenderEngine&,
-                                                              uint32_t tex, Layer*) = 0;
 
     virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>&) = 0;
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 5e20b74..c4b1b0f 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -71,19 +71,6 @@
                            sp<IGraphicBufferConsumer>* /* outConsumer */,
                            bool /* consumerIsSurfaceFlinger */) override {}
 
-    sp<IGraphicBufferProducer> createMonitoredProducer(
-            const sp<IGraphicBufferProducer>& /* producer */,
-            const sp<SurfaceFlinger>& /* flinger */, const wp<Layer>& /* layer */) override {
-        return nullptr;
-    }
-
-    sp<BufferLayerConsumer> createBufferLayerConsumer(
-            const sp<IGraphicBufferConsumer>& /* consumer */,
-            renderengine::RenderEngine& /* renderEngine */, uint32_t /* textureName */,
-            Layer* /* layer */) override {
-        return nullptr;
-    }
-
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>& /* producer */) override {
         return nullptr;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 456a498..3338da0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -333,18 +333,6 @@
         mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
     }
 
-    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer> &producer,
-                                                       const sp<SurfaceFlinger> &flinger,
-                                                       const wp<Layer> &layer) override {
-        return new MonitoredProducer(producer, flinger, layer);
-    }
-
-    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer> &consumer,
-                                                      renderengine::RenderEngine &renderEngine,
-                                                      uint32_t textureName, Layer *layer) override {
-        return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
-    }
-
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer> &producer) override {
         if (!mCreateNativeWindowSurface) return nullptr;
@@ -535,16 +523,6 @@
         mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
     }
 
-    void updateCompositorTiming(FuzzedDataProvider *fdp) {
-        std::shared_ptr<FenceTime> presentFenceTime = FenceTime::NO_FENCE;
-        mFlinger->updateCompositorTiming({}, fdp->ConsumeIntegral<nsecs_t>(), presentFenceTime);
-    }
-
-    void getCompositorTiming() {
-        CompositorTiming compositorTiming;
-        mFlinger->getCompositorTiming(&compositorTiming);
-    }
-
     sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
         mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
         const sp<Client> client = new Client(mFlinger);
@@ -639,11 +617,8 @@
 
         mFlinger->postComposition();
 
-        getCompositorTiming();
+        mFlinger->trackPresentLatency(mFdp.ConsumeIntegral<nsecs_t>(), FenceTime::NO_FENCE);
 
-        updateCompositorTiming(&mFdp);
-
-        mFlinger->setCompositorTimingSnapped({}, mFdp.ConsumeIntegral<nsecs_t>());
         FTL_FAKE_GUARD(kMainThreadContext, mFlinger->postFrame());
         mFlinger->calculateExpectedPresentTime({});
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 4aef017..4d90b1e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -18,9 +18,7 @@
 #include <Client.h>
 #include <DisplayDevice.h>
 #include <EffectLayer.h>
-#include <LayerRejecter.h>
 #include <LayerRenderArea.h>
-#include <MonitoredProducer.h>
 #include <ftl/future.h>
 #include <fuzzer/FuzzedDataProvider.h>
 #include <gui/IProducerListener.h>
@@ -117,17 +115,18 @@
     sp<Fence> fence = sp<Fence>::make();
     const std::shared_ptr<FenceTime> fenceTime = std::make_shared<FenceTime>(fence);
 
-    const CompositorTiming compositor = {mFdp.ConsumeIntegral<int64_t>(),
-                                         mFdp.ConsumeIntegral<int64_t>(),
-                                         mFdp.ConsumeIntegral<int64_t>()};
+    const CompositorTiming compositorTiming(mFdp.ConsumeIntegral<int64_t>(),
+                                            mFdp.ConsumeIntegral<int64_t>(),
+                                            mFdp.ConsumeIntegral<int64_t>(),
+                                            mFdp.ConsumeIntegral<int64_t>());
 
     layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share());
     layer->onLayerDisplayed(
             ftl::yield<FenceResult>(base::unexpected(mFdp.ConsumeIntegral<status_t>())).share());
 
     layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
-    layer->finalizeFrameEventHistory(fenceTime, compositor);
-    layer->onPostComposition(nullptr, fenceTime, fenceTime, compositor);
+    layer->finalizeFrameEventHistory(fenceTime, compositorTiming);
+    layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming);
     layer->isBufferDue(mFdp.ConsumeIntegral<int64_t>());
 
     layer->setTransform(mFdp.ConsumeIntegral<uint32_t>());
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 19eaa19..f6ebfe5 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -629,7 +629,8 @@
                     EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
                     EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
                     EXPECT_EQ(false, layer.source.buffer.isOpaque);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
                     EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
                     return resultFuture;
@@ -679,7 +680,8 @@
                     EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                     LayerProperties::COLOR[2]),
                               layer.source.solidColor);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
                     EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
                     return resultFuture;
@@ -757,7 +759,8 @@
                     const renderengine::LayerSettings layer = layerSettings.back();
                     EXPECT_THAT(layer.source.buffer.buffer, IsNull());
                     EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
                     EXPECT_EQ(1.0f, layer.alpha);
                     return resultFuture;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index 37cf05e..3d576f4 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -85,12 +85,6 @@
 
     // The display transaction needed flag should be set.
     EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // The compositor timing should be set to default values
-    const auto& compositorTiming = mFlinger.getCompositorTiming();
-    EXPECT_EQ(-DEFAULT_VSYNC_PERIOD, compositorTiming.deadline);
-    EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.interval);
-    EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.presentLatency);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3a05e2f..60285f9 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -111,18 +111,6 @@
         mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
     }
 
-    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>& producer,
-                                                       const sp<SurfaceFlinger>& flinger,
-                                                       const wp<Layer>& layer) override {
-        return new MonitoredProducer(producer, flinger, layer);
-    }
-
-    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>& consumer,
-                                                      renderengine::RenderEngine& renderEngine,
-                                                      uint32_t textureName, Layer* layer) override {
-        return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
-    }
-
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>& producer) override {
         if (!mCreateNativeWindowSurface) return nullptr;
@@ -501,8 +489,6 @@
     }
     auto& getCompositionEngine() const { return mFlinger->getCompositionEngine(); }
 
-    const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
-
     mock::FrameTracer* getFrameTracer() const {
         return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
     }