Merge changes I5b0dd111,I40135534,Ief7fff69 into main

* changes:
  [SF] Change VSyncDispatchTimerQueue::Schedule to return ScheduleResult
  [SF] Enable vrr_timeout_hint_enabled
  [SF] Send NotifyExpectedPresentHint at the transaction time
diff --git a/Android.bp b/Android.bp
index 8c4dfbb..2520a71 100644
--- a/Android.bp
+++ b/Android.bp
@@ -105,7 +105,13 @@
 
 cc_library_headers {
     name: "libandroid_headers_private",
+    host_supported: true,
     export_include_dirs: ["include/private"],
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
 }
 
 filegroup {
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index f999708..d4f30ef 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -48,9 +48,19 @@
 #ifndef ANDROID_CHOREOGRAPHER_H
 #define ANDROID_CHOREOGRAPHER_H
 
+#include <stddef.h>
 #include <stdint.h>
 #include <sys/cdefs.h>
 
+// This file may also be built on glibc or on Windows/MacOS libc's, so no-op
+// and deprecated definitions are provided.
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
+#if !defined(__DEPRECATED_IN)
+#define __DEPRECATED_IN(__api_level) __attribute__((__deprecated__))
+#endif
+
 __BEGIN_DECLS
 
 struct AChoreographer;
diff --git a/include/input/Input.h b/include/input/Input.h
index 7b253a5..a84dcfc 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -180,7 +180,8 @@
  * Declare a concrete type for the NDK's input event forward declaration.
  */
 struct AInputEvent {
-    virtual ~AInputEvent() { }
+    virtual ~AInputEvent() {}
+    bool operator==(const AInputEvent&) const = default;
 };
 
 /*
@@ -545,6 +546,8 @@
 
     static int32_t nextId();
 
+    bool operator==(const InputEvent&) const = default;
+
 protected:
     void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
                     std::array<uint8_t, 32> hmac);
@@ -598,6 +601,8 @@
 
     static const char* actionToString(int32_t action);
 
+    bool operator==(const KeyEvent&) const = default;
+
 protected:
     int32_t mAction;
     int32_t mFlags;
@@ -917,6 +922,9 @@
     // The rounding precision for transformed motion events.
     static constexpr float ROUNDING_PRECISION = 0.001f;
 
+    bool operator==(const MotionEvent&) const;
+    inline bool operator!=(const MotionEvent& o) const { return !(*this == o); };
+
 protected:
     int32_t mAction;
     int32_t mActionButton;
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 1a9766d..319716e 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -14,6 +14,7 @@
 
 package {
     default_applicable_licenses: ["frameworks_native_libs_arect_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 // Added automatically by a large-scale-change
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index bf7a0ba..3af9ec7 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -258,11 +258,24 @@
     }
 }
 
+void ABBinder::addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& /* recipient */,
+                                 void* /* cookie */) {
+    LOG_ALWAYS_FATAL("Should not reach this. Can't linkToDeath local binders.");
+}
+
 ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder)
     : AIBinder(nullptr /*clazz*/), mRemote(binder) {
     LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
 }
-ABpBinder::~ABpBinder() {}
+
+ABpBinder::~ABpBinder() {
+    for (auto& recip : mDeathRecipients) {
+        sp<AIBinder_DeathRecipient> strongRecip = recip.recipient.promote();
+        if (strongRecip) {
+            strongRecip->pruneThisTransferEntry(getBinder(), recip.cookie);
+        }
+    }
+}
 
 sp<AIBinder> ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android::IBinder>& binder) {
     if (binder == nullptr) {
@@ -301,6 +314,11 @@
     return ret;
 }
 
+void ABpBinder::addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+                                  void* cookie) {
+    mDeathRecipients.emplace_back(recipient, cookie);
+}
+
 struct AIBinder_Weak {
     wp<AIBinder> binder;
 };
@@ -426,6 +444,17 @@
     LOG_ALWAYS_FATAL_IF(onDied == nullptr, "onDied == nullptr");
 }
 
+void AIBinder_DeathRecipient::pruneThisTransferEntry(const sp<IBinder>& who, void* cookie) {
+    std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
+    mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(),
+                                          [&](const sp<TransferDeathRecipient>& tdr) {
+                                              auto tdrWho = tdr->getWho();
+                                              return tdrWho != nullptr && tdrWho.promote() == who &&
+                                                     cookie == tdr->getCookie();
+                                          }),
+                           mDeathRecipients.end());
+}
+
 void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() {
     mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(),
                                           [](const sp<TransferDeathRecipient>& tdr) {
@@ -554,8 +583,11 @@
         return STATUS_UNEXPECTED_NULL;
     }
 
-    // returns binder_status_t
-    return recipient->linkToDeath(binder->getBinder(), cookie);
+    binder_status_t ret = recipient->linkToDeath(binder->getBinder(), cookie);
+    if (ret == STATUS_OK) {
+        binder->addDeathRecipient(recipient, cookie);
+    }
+    return ret;
 }
 
 binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 9d5368f..a2682d8 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -51,6 +51,8 @@
         ::android::sp<::android::IBinder> binder = const_cast<AIBinder*>(this)->getBinder();
         return binder->remoteBinder() != nullptr;
     }
+    virtual void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+                                   void* cookie) = 0;
 
    private:
     // AIBinder instance is instance of this class for a local object. In order to transact on a
@@ -78,6 +80,8 @@
     ::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override;
     ::android::status_t onTransact(uint32_t code, const ::android::Parcel& data,
                                    ::android::Parcel* reply, binder_flags_t flags) override;
+    void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& /* recipient */,
+                           void* /* cookie */) override;
 
    private:
     ABBinder(const AIBinder_Class* clazz, void* userData);
@@ -106,12 +110,19 @@
 
     bool isServiceFuzzing() const { return mServiceFuzzing; }
     void setServiceFuzzing() { mServiceFuzzing = true; }
+    void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+                           void* cookie) override;
 
    private:
     friend android::sp<ABpBinder>;
     explicit ABpBinder(const ::android::sp<::android::IBinder>& binder);
     ::android::sp<::android::IBinder> mRemote;
     bool mServiceFuzzing = false;
+    struct DeathRecipientInfo {
+        android::wp<AIBinder_DeathRecipient> recipient;
+        void* cookie;
+    };
+    std::vector<DeathRecipientInfo> mDeathRecipients;
 };
 
 struct AIBinder_Class {
@@ -183,6 +194,7 @@
     binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie);
     binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie);
     void setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked);
+    void pruneThisTransferEntry(const ::android::sp<::android::IBinder>&, void* cookie);
 
    private:
     // When the user of this API deletes a Bp object but not the death recipient, the
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index b34b30d..a905dff 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -248,6 +248,8 @@
  * \param instance identifier of the passthrough service (e.g. "mapper")
  * \param instance identifier of the implemenatation (e.g. "default")
  * \param flag passed to dlopen()
+ *
+ * \return the result of dlopen of the specified HAL
  */
 void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance,
                                                  int flag)
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index 3ee36cd..ca92727 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -25,6 +25,7 @@
 
 const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo";
 const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die";
+const char* IFoo::kInstanceNameToDieFor2 = "libbinder_ndk-test-IFoo-to-die2";
 const char* IFoo::kIFooDescriptor = "my-special-IFoo-class";
 
 struct IFoo_Class_Data {
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index 0a562f0..0cdd50b 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -27,6 +27,7 @@
    public:
     static const char* kSomeInstanceName;
     static const char* kInstanceNameToDieFor;
+    static const char* kInstanceNameToDieFor2;
     static const char* kIFooDescriptor;
 
     static AIBinder_Class* kClass;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 966ec95..ce63b82 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -536,6 +536,7 @@
     bool deathReceived = false;
 
     std::function<void(void)> onDeath = [&] {
+        std::unique_lock<std::mutex> lockDeath(deathMutex);
         std::cerr << "Binder died (as requested)." << std::endl;
         deathReceived = true;
         deathCv.notify_one();
@@ -547,6 +548,7 @@
     bool wasDeathReceivedFirst = false;
 
     std::function<void(void)> onUnlink = [&] {
+        std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
         std::cerr << "Binder unlinked (as requested)." << std::endl;
         wasDeathReceivedFirst = deathReceived;
         unlinkReceived = true;
@@ -560,7 +562,6 @@
 
     EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(cookie)));
 
-    // the binder driver should return this if the service dies during the transaction
     EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
 
     foo = nullptr;
@@ -579,6 +580,123 @@
     binder = nullptr;
 }
 
+TEST(NdkBinder, DeathRecipientDropBinderNoDeath) {
+    using namespace std::chrono_literals;
+
+    std::mutex deathMutex;
+    std::condition_variable deathCv;
+    bool deathReceived = false;
+
+    std::function<void(void)> onDeath = [&] {
+        std::unique_lock<std::mutex> lockDeath(deathMutex);
+        std::cerr << "Binder died (as requested)." << std::endl;
+        deathReceived = true;
+        deathCv.notify_one();
+    };
+
+    std::mutex unlinkMutex;
+    std::condition_variable unlinkCv;
+    bool unlinkReceived = false;
+    bool wasDeathReceivedFirst = false;
+
+    std::function<void(void)> onUnlink = [&] {
+        std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+        std::cerr << "Binder unlinked (as requested)." << std::endl;
+        wasDeathReceivedFirst = deathReceived;
+        unlinkReceived = true;
+        unlinkCv.notify_one();
+    };
+
+    // keep the death recipient around
+    ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath));
+    AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlink);
+
+    {
+        AIBinder* binder;
+        sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor2, &binder);
+        ASSERT_NE(nullptr, foo.get());
+        ASSERT_NE(nullptr, binder);
+
+        DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
+
+        EXPECT_EQ(STATUS_OK,
+                  AIBinder_linkToDeath(binder, recipient.get(), static_cast<void*>(cookie)));
+        // let the sp<IFoo> and AIBinder fall out of scope
+        AIBinder_decStrong(binder);
+        binder = nullptr;
+    }
+
+    {
+        std::unique_lock<std::mutex> lockDeath(deathMutex);
+        EXPECT_FALSE(deathCv.wait_for(lockDeath, 100ms, [&] { return deathReceived; }));
+        EXPECT_FALSE(deathReceived);
+    }
+
+    {
+        std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+        EXPECT_TRUE(deathCv.wait_for(lockUnlink, 1s, [&] { return unlinkReceived; }));
+        EXPECT_TRUE(unlinkReceived);
+        EXPECT_FALSE(wasDeathReceivedFirst);
+    }
+}
+
+TEST(NdkBinder, DeathRecipientDropBinderOnDied) {
+    using namespace std::chrono_literals;
+
+    std::mutex deathMutex;
+    std::condition_variable deathCv;
+    bool deathReceived = false;
+
+    sp<IFoo> foo;
+    AIBinder* binder;
+    std::function<void(void)> onDeath = [&] {
+        std::unique_lock<std::mutex> lockDeath(deathMutex);
+        std::cerr << "Binder died (as requested)." << std::endl;
+        deathReceived = true;
+        AIBinder_decStrong(binder);
+        binder = nullptr;
+        deathCv.notify_one();
+    };
+
+    std::mutex unlinkMutex;
+    std::condition_variable unlinkCv;
+    bool unlinkReceived = false;
+    bool wasDeathReceivedFirst = false;
+
+    std::function<void(void)> onUnlink = [&] {
+        std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+        std::cerr << "Binder unlinked (as requested)." << std::endl;
+        wasDeathReceivedFirst = deathReceived;
+        unlinkReceived = true;
+        unlinkCv.notify_one();
+    };
+
+    ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath));
+    AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlink);
+
+    foo = IFoo::getService(IFoo::kInstanceNameToDieFor2, &binder);
+    ASSERT_NE(nullptr, foo.get());
+    ASSERT_NE(nullptr, binder);
+
+    DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
+    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient.get(), static_cast<void*>(cookie)));
+
+    EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
+
+    {
+        std::unique_lock<std::mutex> lockDeath(deathMutex);
+        EXPECT_TRUE(deathCv.wait_for(lockDeath, 1s, [&] { return deathReceived; }));
+        EXPECT_TRUE(deathReceived);
+    }
+
+    {
+        std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+        EXPECT_TRUE(deathCv.wait_for(lockUnlink, 100ms, [&] { return unlinkReceived; }));
+        EXPECT_TRUE(unlinkReceived);
+        EXPECT_TRUE(wasDeathReceivedFirst);
+    }
+}
+
 TEST(NdkBinder, RetrieveNonNdkService) {
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -958,6 +1076,10 @@
     }
     if (fork() == 0) {
         prctl(PR_SET_PDEATHSIG, SIGHUP);
+        return manualThreadPoolService(IFoo::kInstanceNameToDieFor2);
+    }
+    if (fork() == 0) {
+        prctl(PR_SET_PDEATHSIG, SIGHUP);
         return manualPollingService(IFoo::kSomeInstanceName);
     }
     if (fork() == 0) {
diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
index 3fe71ce..3c8c41f 100644
--- a/libs/bufferqueueconverter/Android.bp
+++ b/libs/bufferqueueconverter/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_library_headers {
diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp
index 6c2a980..03ab31e 100644
--- a/libs/bufferstreams/Android.bp
+++ b/libs/bufferstreams/Android.bp
@@ -14,6 +14,7 @@
 
 package {
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 aconfig_declarations {
diff --git a/libs/bufferstreams/aidl/Android.bp b/libs/bufferstreams/aidl/Android.bp
new file mode 100644
index 0000000..3f1fa4e
--- /dev/null
+++ b/libs/bufferstreams/aidl/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2024 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.
+
+aidl_interface {
+    name: "android.graphics.bufferstreams",
+    unstable: true,
+    flags: ["-Werror"],
+    srcs: ["android/graphics/bufferstreams/*.aidl"],
+    headers: [
+        "HardwareBuffer_aidl",
+    ],
+    imports: [
+        "android.hardware.common-V2",
+    ],
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            enabled: false,
+        },
+        ndk: {
+            enabled: false,
+        },
+        rust: {
+            enabled: true,
+            additional_rustlibs: [
+                "libnativewindow_rs",
+            ],
+        },
+    },
+}
diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl
new file mode 100644
index 0000000..5c905b1
--- /dev/null
+++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferAttachment.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.bufferstreams;
+
+import android.graphics.bufferstreams.IBufferOwner;
+import android.hardware.HardwareBuffer;
+
+// Single mapping between a buffer reference and heavy-weight data (like the
+// buffer itself) and data that is stable between frames.
+parcelable BufferAttachment {
+    // The HardwareBuffer itself.
+    //
+    // This field is @nullable for codegen, since HardwareBuffer doesn't implement Default in Rust.
+    // In practice, it should never be null.
+    @nullable HardwareBuffer buffer;
+    // The buffer owner to which this buffer should be returned.
+    IBufferOwner owner;
+}
diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl
new file mode 100644
index 0000000..7504119
--- /dev/null
+++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/BufferCacheUpdate.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.bufferstreams;
+
+import android.graphics.bufferstreams.BufferAttachment;
+
+// A event that changes the state downstream buffer caches. Clients are responsible for forwarding
+// these messages to their clients.
+union BufferCacheUpdate {
+    // Event requiring downstream caches to add new entries.
+    CacheBuffers cacheBuffers;
+    // Event requiring downstream caches to remove entries.
+    ForgetBuffers forgetBuffers;
+
+    parcelable CacheBuffers {
+        // Attachments to add.
+        List<BufferAttachment> attachments;
+    }
+
+    parcelable ForgetBuffers {
+        // References to remove.
+        long[] bufferIds;
+    }
+}
diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl
new file mode 100644
index 0000000..1e0ec3b
--- /dev/null
+++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/Frame.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.bufferstreams;
+
+import android.os.ParcelFileDescriptor;
+
+// A Frame represents a single buffer passing through the stream.
+parcelable Frame {
+    // The service must have provided an associated BufferAttachment and the client is required to
+    // maintain a cache between the two.
+    long bufferId;
+    // The expected present time of this frame, or -1 if immediate.
+    long presentTimeNs;
+    // The acquire fence of the buffer for this frame.
+    @nullable ParcelFileDescriptor fence;
+}
diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl
new file mode 100644
index 0000000..8b25a62
--- /dev/null
+++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferOwner.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.bufferstreams;
+
+import android.os.ParcelFileDescriptor;
+
+// Interface from a client back to the owner of a buffer.
+interface IBufferOwner {
+    // Called when the buffer is done being processed by the stream to return its owner.
+    oneway void onBufferReleased(in long bufferId, in @nullable ParcelFileDescriptor releaseFence);
+}
diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl
new file mode 100644
index 0000000..52e8216
--- /dev/null
+++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscriber.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.bufferstreams;
+
+import android.graphics.bufferstreams.BufferCacheUpdate;
+import android.graphics.bufferstreams.IBufferSubscription;
+import android.graphics.bufferstreams.Frame;
+
+// Interface provided by clients to a service, mirroring the non-IPC interface.
+//
+// Clients are required to maintain a local cache of Buffer IDs to BufferAttachments.
+interface IBufferSubscriber {
+    // Provide a BufferSubscription object which the client can use to request frames.
+    oneway void onSubscribe(in IBufferSubscription subscription);
+
+    // Notifies the client to update its local caches.
+    oneway void onBufferCacheUpdate(in BufferCacheUpdate update);
+
+    // Notifies the client that a requested frame is available.
+    oneway void onNext(in Frame frame);
+
+    // Notifies the client that a fatal error has occurred. No subsequent on_next events will be
+    // sent by the service.
+    //
+    // Clients must empty their caches.
+    oneway void onError();
+
+    // Notifies the client that no further on_next events will be sent by the service in response
+    // to it cancelling the subscription.
+    //
+    // Clients must empty their caches.
+    oneway void onComplete();
+}
diff --git a/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl
new file mode 100644
index 0000000..c37f4e6
--- /dev/null
+++ b/libs/bufferstreams/aidl/android/graphics/bufferstreams/IBufferSubscription.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.bufferstreams;
+
+// Interface provided to a IBufferSubscriber to request frames or gracefully cancel their
+// subscription.
+interface IBufferSubscription {
+    // Request n more frames.
+    oneway void request(long n);
+    // Cancel the subscription. Requested frames may continue to arrive.
+    oneway void cancel();
+}
diff --git a/libs/bufferstreams/examples/app/Android.bp b/libs/bufferstreams/examples/app/Android.bp
index bb573c5..5b3ec30 100644
--- a/libs/bufferstreams/examples/app/Android.bp
+++ b/libs/bufferstreams/examples/app/Android.bp
@@ -12,6 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
+}
+
 android_app {
     name: "BufferStreamsDemoApp",
     srcs: ["java/**/*.kt"],
diff --git a/libs/bufferstreams/examples/app/jni/Android.bp b/libs/bufferstreams/examples/app/jni/Android.bp
index 67910a1..003f4ed 100644
--- a/libs/bufferstreams/examples/app/jni/Android.bp
+++ b/libs/bufferstreams/examples/app/jni/Android.bp
@@ -12,6 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
+}
+
 cc_library_shared {
     name: "libbufferstreamdemoapp",
     cflags: [
diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp
index 7fcb222..34feb5d 100644
--- a/libs/bufferstreams/rust/Android.bp
+++ b/libs/bufferstreams/rust/Android.bp
@@ -12,6 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
+}
+
 rust_defaults {
     name: "libbufferstreams_defaults",
     srcs: ["src/lib.rs"],
diff --git a/libs/bufferstreams/rust/src/buffers/buffer_owner.rs b/libs/bufferstreams/rust/src/buffers/buffer_owner.rs
index a4abb9d..155a8bf 100644
--- a/libs/bufferstreams/rust/src/buffers/buffer_owner.rs
+++ b/libs/bufferstreams/rust/src/buffers/buffer_owner.rs
@@ -16,7 +16,7 @@
 
 /// Trait that represents an owner of a buffer that might need to handle events such as a buffer
 /// being dropped.
-pub trait BufferOwner {
+pub trait BufferOwner: Send + Sync {
     /// Called when a buffer is dropped.
     fn on_return(&self, buffer: &Buffer);
 }
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
index be1525d..17d4d87 100644
--- a/libs/bufferstreams/rust/src/lib.rs
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -23,8 +23,6 @@
 use buffers::Buffer;
 pub use stream_config::*;
 
-use std::time::Instant;
-
 /// This function will print Hello World.
 #[no_mangle]
 pub extern "C" fn hello() -> bool {
@@ -106,7 +104,8 @@
 /// BufferSubscriptions serve as the bridge between BufferPublishers and
 /// BufferSubscribers. BufferSubscribers receive a BufferSubscription when they
 /// subscribe to a BufferPublisher via on_subscribe.
-/// This object is to be used by the BufferSubscriber to cancel its subscription
+///
+/// This object is used by the BufferSubscriber to cancel its subscription
 /// or request more buffers.
 ///
 /// BufferSubcriptions are required to adhere to the following, based on the
@@ -147,7 +146,7 @@
 /// no other Subscription exists at this point.
 /// * Calling Subscription.cancel MUST return normally.
 /// * Calling Subscription.request MUST return normally.
-pub trait BufferSubscription {
+pub trait BufferSubscription: Send + Sync + 'static {
     /// request
     fn request(&self, n: u64);
     /// cancel
@@ -161,8 +160,8 @@
 pub struct Frame {
     /// A buffer to be used this frame.
     pub buffer: Buffer,
-    /// The time at which the buffer was dispatched.
-    pub present_time: Instant,
+    /// The time at which this buffer is expected to be displayed.
+    pub present_time: i64,
     /// A fence used for reading/writing safely.
     pub fence: i32,
 }
@@ -175,14 +174,12 @@
     use anyhow::anyhow;
     use buffers::Buffer;
     use nativewindow::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
-    use std::borrow::BorrowMut;
-    use std::error::Error;
-    use std::ops::Add;
-    use std::sync::Arc;
-    use std::time::Duration;
+    use std::{borrow::BorrowMut, error::Error, ops::Add, sync::Arc};
 
-    use crate::publishers::testing::*;
-    use crate::subscribers::{testing::*, SharedSubscriber};
+    use crate::{
+        publishers::testing::*,
+        subscribers::{testing::*, SharedSubscriber},
+    };
 
     const STREAM_CONFIG: StreamConfig = StreamConfig {
         width: 1,
@@ -200,7 +197,7 @@
                     .create_hardware_buffer()
                     .expect("Unable to create hardware buffer for test"),
             ),
-            present_time: Instant::now() + Duration::from_secs(1),
+            present_time: 1,
             fence: 0,
         }
     }
diff --git a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
index 846105d..73a15be 100644
--- a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
+++ b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
@@ -14,8 +14,6 @@
 
 //!
 
-use std::time::Instant;
-
 use crate::{
     buffers::BufferPool, subscriptions::SharedBufferSubscription, BufferPublisher,
     BufferSubscriber, Frame, StreamConfig,
@@ -43,7 +41,7 @@
 
     /// If the [SharedBufferSubscription] is ready for a [Frame], a buffer will be requested from
     /// [BufferPool] and sent over to the [BufferSubscriber].
-    pub fn send_next_frame(&mut self, present_time: Instant) -> bool {
+    pub fn send_next_frame(&mut self, present_time: i64) -> bool {
         if let Some(subscriber) = self.subscriber.as_mut() {
             if self.subscription.take_request() {
                 if let Some(buffer) = self.buffer_pool.next_buffer() {
@@ -103,7 +101,7 @@
 
         subscriber.map_inner(|s| s.request(1));
 
-        assert!(buffer_pool_publisher.send_next_frame(Instant::now()));
+        assert!(buffer_pool_publisher.send_next_frame(1));
 
         let events = subscriber.map_inner_mut(|s| s.take_events());
         assert!(matches!(events.last().unwrap(), TestingSubscriberEvent::Next(_)));
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 5ac965f..32b2b68 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index aab1276..f300da5 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_library {
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
index 3c3b6af..8337182 100644
--- a/libs/gralloc/types/fuzzer/Android.bp
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_fuzz {
diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp
index 66eb0aa..b796c03 100644
--- a/libs/gralloc/types/tests/Android.bp
+++ b/libs/gralloc/types/tests/Android.bp
@@ -21,6 +21,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
@@ -30,5 +31,8 @@
         "libhidlbase",
     ],
     srcs: ["Gralloc4_test.cpp"],
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 }
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4c3cc6c..70cb36b 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -18,6 +18,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 aconfig_declarations {
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index cd738ac..35dd9ec 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -20,6 +20,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_defaults {
diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp
index cc33e4c..386767b 100644
--- a/libs/gui/sysprop/Android.bp
+++ b/libs/gui/sysprop/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 sysprop_library {
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 1c713f9..9e0ce1d 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -1007,6 +1007,33 @@
     return out;
 }
 
+bool MotionEvent::operator==(const android::MotionEvent& o) const {
+    // We use NaN values to represent invalid cursor positions. Since NaN values are not equal
+    // to themselves according to IEEE 754, we cannot use the default equality operator to compare
+    // MotionEvents. Therefore we define a custom equality operator with special handling for NaNs.
+    // clang-format off
+    return InputEvent::operator==(static_cast<const InputEvent&>(o)) &&
+            mAction == o.mAction &&
+            mActionButton == o.mActionButton &&
+            mFlags == o.mFlags &&
+            mEdgeFlags == o.mEdgeFlags &&
+            mMetaState == o.mMetaState &&
+            mButtonState == o.mButtonState &&
+            mClassification == o.mClassification &&
+            mTransform == o.mTransform &&
+            mXPrecision == o.mXPrecision &&
+            mYPrecision == o.mYPrecision &&
+            ((std::isnan(mRawXCursorPosition) && std::isnan(o.mRawXCursorPosition)) ||
+                mRawXCursorPosition == o.mRawXCursorPosition) &&
+            ((std::isnan(mRawYCursorPosition) && std::isnan(o.mRawYCursorPosition)) ||
+                mRawYCursorPosition == o.mRawYCursorPosition) &&
+            mRawTransform == o.mRawTransform && mDownTime == o.mDownTime &&
+            mPointerProperties == o.mPointerProperties &&
+            mSampleEventTimes == o.mSampleEventTimes &&
+            mSamplePointerCoords == o.mSamplePointerCoords;
+    // clang-format on
+}
+
 std::ostream& operator<<(std::ostream& out, const MotionEvent& event) {
     out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction());
     if (event.getActionButton() != 0) {
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 5af4855..bdec5c3 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -119,3 +119,10 @@
   description: "Controls the API to provide InputDevice view behavior."
   bug: "246946631"
 }
+
+flag {
+  name: "enable_touchpad_fling_stop"
+  namespace: "input"
+  description: "Enable fling scrolling to be stopped by putting a finger on the touchpad again"
+  bug: "281106755"
+}
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 342f5de..f7f20b4 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -16,6 +16,7 @@
     default_applicable_licenses: [
         "frameworks_native_libs_nativedisplay_license",
     ],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 // Added automatically by a large-scale-change
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index bc0bfc5..0e90327 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -16,6 +16,7 @@
     default_applicable_licenses: [
         "frameworks_native_libs_nativewindow_license",
     ],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 // Added automatically by a large-scale-change
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index bcfae10..48fb02f 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -107,7 +107,7 @@
 };
 
 /**
- * Additional options for AHardwareBuffer_allocate2. These correspond to
+ * Additional options for AHardwareBuffer_allocateWithOptions. These correspond to
  * android.hardware.graphics.common.ExtendableType
  */
 typedef struct {
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index 90d0a8e..a3df482 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -16,6 +16,7 @@
     default_applicable_licenses: [
         "frameworks_native_libs_nativewindow_license",
     ],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 rust_bindgen {
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index d7c7eb3..a4ebcac 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -23,6 +23,7 @@
     default_applicable_licenses: [
         "frameworks_native_libs_nativewindow_license",
     ],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
diff --git a/libs/nativewindow/tests/benchmark/Android.bp b/libs/nativewindow/tests/benchmark/Android.bp
index 6f844cf..b815d80 100644
--- a/libs/nativewindow/tests/benchmark/Android.bp
+++ b/libs/nativewindow/tests/benchmark/Android.bp
@@ -12,6 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
+}
+
 cc_defaults {
     name: "nativewindow_benchmark_defaults_cc",
     shared_libs: ["libnativewindow"],
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index fd45840..b501d40 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_defaults {
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index 87e21c2..e1a6f6a 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_benchmark {
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 473e1d4..0eea187 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
index 960f845..82fbfda 100644
--- a/libs/shaders/Android.bp
+++ b/libs/shaders/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_library_static {
diff --git a/libs/shaders/tests/Android.bp b/libs/shaders/tests/Android.bp
index 5639d74..2103679 100644
--- a/libs/shaders/tests/Android.bp
+++ b/libs/shaders/tests/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
index 8c8815d..d569ac3 100644
--- a/libs/tonemap/Android.bp
+++ b/libs/tonemap/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_library_static {
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
index 5c5fc6c..79ee7c2 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index ec0ab4e..9cb298a 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -14,6 +14,7 @@
 
 package {
     default_applicable_licenses: ["frameworks_native_libs_ui_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 // Added automatically by a large-scale-change
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 9a20215..2d8a1e3 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -21,6 +21,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_libs_ui_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp
index 5d6070c..cc233b9 100644
--- a/libs/ui/tools/Android.bp
+++ b/libs/ui/tools/Android.bp
@@ -21,6 +21,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_libs_ui_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_defaults {
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index 80e911c..c97e496 100644
--- a/libs/vibrator/ExternalVibration.cpp
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -17,7 +17,7 @@
 #include <vibrator/ExternalVibration.h>
 #include <vibrator/ExternalVibrationUtils.h>
 
-#include <android/os/IExternalVibratorService.h>
+#include <android/os/ExternalVibrationScale.h>
 #include <binder/Parcel.h>
 #include <log/log.h>
 #include <utils/Errors.h>
@@ -65,24 +65,36 @@
     return mToken == rhs.mToken;
 }
 
-os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(int externalVibrationScale) {
-    switch (externalVibrationScale) {
-        case IExternalVibratorService::SCALE_MUTE:
-            return os::HapticScale::MUTE;
-        case IExternalVibratorService::SCALE_VERY_LOW:
-            return os::HapticScale::VERY_LOW;
-        case IExternalVibratorService::SCALE_LOW:
-            return os::HapticScale::LOW;
-        case IExternalVibratorService::SCALE_NONE:
-            return os::HapticScale::NONE;
-        case IExternalVibratorService::SCALE_HIGH:
-            return os::HapticScale::HIGH;
-        case IExternalVibratorService::SCALE_VERY_HIGH:
-            return os::HapticScale::VERY_HIGH;
+os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(
+        os::ExternalVibrationScale externalVibrationScale) {
+    os::HapticLevel scaleLevel = os::HapticLevel::NONE;
+
+    switch (externalVibrationScale.scaleLevel) {
+        case os::ExternalVibrationScale::ScaleLevel::SCALE_MUTE:
+            scaleLevel = os::HapticLevel::MUTE;
+            break;
+        case os::ExternalVibrationScale::ScaleLevel::SCALE_VERY_LOW:
+            scaleLevel = os::HapticLevel::VERY_LOW;
+            break;
+        case os::ExternalVibrationScale::ScaleLevel::SCALE_LOW:
+            scaleLevel = os::HapticLevel::LOW;
+            break;
+        case os::ExternalVibrationScale::ScaleLevel::SCALE_NONE:
+            scaleLevel = os::HapticLevel::NONE;
+            break;
+        case os::ExternalVibrationScale::ScaleLevel::SCALE_HIGH:
+            scaleLevel = os::HapticLevel::HIGH;
+            break;
+        case os::ExternalVibrationScale::ScaleLevel::SCALE_VERY_HIGH:
+            scaleLevel = os::HapticLevel::VERY_HIGH;
+            break;
         default:
-          ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", externalVibrationScale);
-          return os::HapticScale::NONE;
-      }
+            ALOGE("Unknown ExternalVibrationScale %d, not applying scaling",
+                  externalVibrationScale.scaleLevel);
+    }
+
+    return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/
+                      externalVibrationScale.adaptiveHapticsScale};
 }
 
 } // namespace os
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
index 980b08b..761ac1b 100644
--- a/libs/vibrator/ExternalVibrationUtils.cpp
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -26,30 +26,30 @@
 static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
 static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
 
-float getHapticScaleGamma(HapticScale scale) {
-    switch (scale) {
-    case HapticScale::VERY_LOW:
+float getHapticScaleGamma(HapticLevel level) {
+    switch (level) {
+    case HapticLevel::VERY_LOW:
         return 2.0f;
-    case HapticScale::LOW:
+    case HapticLevel::LOW:
         return 1.5f;
-    case HapticScale::HIGH:
+    case HapticLevel::HIGH:
         return 0.5f;
-    case HapticScale::VERY_HIGH:
+    case HapticLevel::VERY_HIGH:
         return 0.25f;
     default:
         return 1.0f;
     }
 }
 
-float getHapticMaxAmplitudeRatio(HapticScale scale) {
-    switch (scale) {
-    case HapticScale::VERY_LOW:
+float getHapticMaxAmplitudeRatio(HapticLevel level) {
+    switch (level) {
+    case HapticLevel::VERY_LOW:
         return HAPTIC_SCALE_VERY_LOW_RATIO;
-    case HapticScale::LOW:
+    case HapticLevel::LOW:
         return HAPTIC_SCALE_LOW_RATIO;
-    case HapticScale::NONE:
-    case HapticScale::HIGH:
-    case HapticScale::VERY_HIGH:
+    case HapticLevel::NONE:
+    case HapticLevel::HIGH:
+    case HapticLevel::VERY_HIGH:
         return 1.0f;
     default:
         return 0.0f;
@@ -57,19 +57,28 @@
 }
 
 void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
-    if (scale == HapticScale::MUTE) {
+    if (scale.isScaleMute()) {
         memset(buffer, 0, length * sizeof(float));
         return;
     }
-    if (scale == HapticScale::NONE) {
+    if (scale.isScaleNone()) {
         return;
     }
-    float gamma = getHapticScaleGamma(scale);
-    float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale);
+    HapticLevel hapticLevel = scale.getLevel();
+    float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
+    float gamma = getHapticScaleGamma(hapticLevel);
+    float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel);
+
     for (size_t i = 0; i < length; i++) {
-        float sign = buffer[i] >= 0 ? 1.0 : -1.0;
-        buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
-                * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+        if (hapticLevel != HapticLevel::NONE) {
+            float sign = buffer[i] >= 0 ? 1.0 : -1.0;
+            buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+                        * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+        }
+
+        if (adaptiveScaleFactor != 1.0f) {
+            buffer[i] *= adaptiveScaleFactor;
+        }
     }
 }
 
@@ -89,13 +98,13 @@
 } // namespace
 
 bool isValidHapticScale(HapticScale scale) {
-    switch (scale) {
-    case HapticScale::MUTE:
-    case HapticScale::VERY_LOW:
-    case HapticScale::LOW:
-    case HapticScale::NONE:
-    case HapticScale::HIGH:
-    case HapticScale::VERY_HIGH:
+    switch (scale.getLevel()) {
+    case HapticLevel::MUTE:
+    case HapticLevel::VERY_LOW:
+    case HapticLevel::LOW:
+    case HapticLevel::NONE:
+    case HapticLevel::HIGH:
+    case HapticLevel::VERY_HIGH:
         return true;
     }
     return false;
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
index 00cd3cd..ac2767e 100644
--- a/libs/vibrator/include/vibrator/ExternalVibration.h
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -24,6 +24,7 @@
 #include <system/audio.h>
 #include <utils/RefBase.h>
 #include <vibrator/ExternalVibrationUtils.h>
+#include <android/os/ExternalVibrationScale.h>
 
 namespace android {
 namespace os {
@@ -45,10 +46,11 @@
     audio_attributes_t getAudioAttributes() const { return mAttrs; }
     sp<IExternalVibrationController> getController() { return mController; }
 
-    /* Converts the scale from non-public ExternalVibrationService into the HapticScale
+    /* Converts the scale from non-public ExternalVibrationService into the HapticScaleLevel
      * used by the utils.
      */
-    static os::HapticScale externalVibrationScaleToHapticScale(int externalVibrationScale);
+    static os::HapticScale externalVibrationScaleToHapticScale(
+            os::ExternalVibrationScale externalVibrationScale);
 
 private:
     int32_t mUid;
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index ca219d3..d9a2b81 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -19,7 +19,7 @@
 
 namespace android::os {
 
-enum class HapticScale {
+enum class HapticLevel {
     MUTE = -100,
     VERY_LOW = -2,
     LOW = -1,
@@ -28,10 +28,41 @@
     VERY_HIGH = 2,
 };
 
+class HapticScale {
+private:
+HapticLevel mLevel = HapticLevel::NONE;
+float mAdaptiveScaleFactor = 1.0f;
+
+public:
+constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor)
+    : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
+constexpr HapticScale(HapticLevel level) : mLevel(level) {}
+constexpr HapticScale() {}
+
+HapticLevel getLevel() const { return mLevel; }
+float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
+
+bool operator==(const HapticScale& other) const {
+    return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
+}
+
+bool isScaleNone() const {
+    return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f;
+}
+
+bool isScaleMute() const {
+    return mLevel == HapticLevel::MUTE;
+}
+
+static HapticScale mute() {
+    return {/*level=*/os::HapticLevel::MUTE};
+}
+};
+
 bool isValidHapticScale(HapticScale scale);
 
-/* Scales the haptic data in given buffer using the selected HapticScale and ensuring no absolute
- * value will be larger than the absolute of given limit.
+/* Scales the haptic data in given buffer using the selected HapticScaleLevel and ensuring no
+ * absolute value will be larger than the absolute of given limit.
  * The limit will be ignored if it is NaN or zero.
  */
 void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit);
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 513cbfa..293ad66 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -285,5 +285,10 @@
     {
       "name": "CtsInputHostTestCases"
     }
+  ],
+  "staged-platinum-postsubmit": [
+    {
+      "name": "inputflinger_tests"
+    }
   ]
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8d43f3b..f31884e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -365,17 +365,20 @@
     return i;
 }
 
-std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerator,
+                                                   const InputTarget& inputTarget,
                                                    std::shared_ptr<const EventEntry> eventEntry,
                                                    ftl::Flags<InputTarget::Flags> inputTargetFlags,
                                                    int64_t vsyncId) {
+    const bool zeroCoords = inputTargetFlags.test(InputTarget::Flags::ZERO_COORDS);
     const sp<WindowInfoHandle> win = inputTarget.windowHandle;
     const std::optional<int32_t> windowId =
             win ? std::make_optional(win->getInfo()->id) : std::nullopt;
     // Assume the only targets that are not associated with a window are global monitors, and use
     // the system UID for global monitors for tracing purposes.
     const gui::Uid uid = win ? win->getInfo()->ownerUid : gui::Uid(AID_SYSTEM);
-    if (inputTarget.useDefaultPointerTransform()) {
+
+    if (inputTarget.useDefaultPointerTransform() && !zeroCoords) {
         const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
         return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
                                                inputTarget.displayTransform,
@@ -386,33 +389,39 @@
     ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
     const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
 
-    std::vector<PointerCoords> pointerCoords;
-    pointerCoords.resize(motionEntry.getPointerCount());
+    std::vector<PointerCoords> pointerCoords{motionEntry.getPointerCount()};
 
-    // Use the first pointer information to normalize all other pointers. This could be any pointer
-    // as long as all other pointers are normalized to the same value and the final DispatchEntry
-    // uses the transform for the normalized pointer.
-    const ui::Transform& firstPointerTransform =
-            inputTarget.pointerTransforms[firstMarkedBit(inputTarget.pointerIds)];
-    ui::Transform inverseFirstTransform = firstPointerTransform.inverse();
+    const ui::Transform* transform = &kIdentityTransform;
+    const ui::Transform* displayTransform = &kIdentityTransform;
+    if (zeroCoords) {
+        std::for_each(pointerCoords.begin(), pointerCoords.end(), [](auto& pc) { pc.clear(); });
+    } else {
+        // Use the first pointer information to normalize all other pointers. This could be any
+        // pointer as long as all other pointers are normalized to the same value and the final
+        // DispatchEntry uses the transform for the normalized pointer.
+        transform =
+                &inputTarget.getTransformForPointer(firstMarkedBit(inputTarget.getPointerIds()));
+        const ui::Transform inverseTransform = transform->inverse();
+        displayTransform = &inputTarget.displayTransform;
 
-    // Iterate through all pointers in the event to normalize against the first.
-    for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount(); pointerIndex++) {
-        const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
-        uint32_t pointerId = uint32_t(pointerProperties.id);
-        const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId];
+        // Iterate through all pointers in the event to normalize against the first.
+        for (size_t i = 0; i < motionEntry.getPointerCount(); i++) {
+            PointerCoords& newCoords = pointerCoords[i];
+            const auto pointerId = motionEntry.pointerProperties[i].id;
+            const ui::Transform& currTransform = inputTarget.getTransformForPointer(pointerId);
 
-        pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
-        // First, apply the current pointer's transform to update the coordinates into
-        // window space.
-        pointerCoords[pointerIndex].transform(currTransform);
-        // Next, apply the inverse transform of the normalized coordinates so the
-        // current coordinates are transformed into the normalized coordinate space.
-        pointerCoords[pointerIndex].transform(inverseFirstTransform);
+            newCoords.copyFrom(motionEntry.pointerCoords[i]);
+            // First, apply the current pointer's transform to update the coordinates into
+            // window space.
+            newCoords.transform(currTransform);
+            // Next, apply the inverse transform of the normalized coordinates so the
+            // current coordinates are transformed into the normalized coordinate space.
+            newCoords.transform(inverseTransform);
+        }
     }
 
     std::unique_ptr<MotionEntry> combinedMotionEntry =
-            std::make_unique<MotionEntry>(motionEntry.id, motionEntry.injectionState,
+            std::make_unique<MotionEntry>(idGenerator.nextId(), motionEntry.injectionState,
                                           motionEntry.eventTime, motionEntry.deviceId,
                                           motionEntry.source, motionEntry.displayId,
                                           motionEntry.policyFlags, motionEntry.action,
@@ -426,7 +435,7 @@
 
     std::unique_ptr<DispatchEntry> dispatchEntry =
             std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
-                                            firstPointerTransform, inputTarget.displayTransform,
+                                            *transform, *displayTransform,
                                             inputTarget.globalScaleFactor, uid, vsyncId, windowId);
     return dispatchEntry;
 }
@@ -1303,6 +1312,7 @@
                 ALOGD("Received a new pointer down event, stop waiting for events to process and "
                       "just send the pending key event to the currently focused window.");
                 mKeyIsWaitingForEventsTimeout = now();
+                needWake = true;
             }
             break;
         }
@@ -1630,14 +1640,12 @@
     if (connection == nullptr) {
         return; // Connection has gone away
     }
-    InputTarget target;
-    target.connection = connection;
     entry->dispatchInProgress = true;
     std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
             connection->getInputChannelName();
     std::string reason = std::string("reason=").append(entry->reason);
     android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS;
-    dispatchEventLocked(currentTime, entry, {target});
+    dispatchEventLocked(currentTime, entry, {{connection}});
 }
 
 void InputDispatcher::dispatchPointerCaptureChangedLocked(
@@ -1703,10 +1711,8 @@
         }
         return;
     }
-    InputTarget target;
-    target.connection = connection;
     entry->dispatchInProgress = true;
-    dispatchEventLocked(currentTime, entry, {target});
+    dispatchEventLocked(currentTime, entry, {{connection}});
 
     dropReason = DropReason::NOT_DROPPED;
 }
@@ -1739,9 +1745,7 @@
         if (connection == nullptr) {
             continue; // Connection has gone away
         }
-        InputTarget target;
-        target.connection = connection;
-        inputTargets.push_back(target);
+        inputTargets.emplace_back(connection);
     }
     return inputTargets;
 }
@@ -2022,10 +2026,8 @@
     if (connection == nullptr) {
         return; // Connection has gone away
     }
-    InputTarget target;
-    target.connection = connection;
     entry->dispatchInProgress = true;
-    dispatchEventLocked(currentTime, entry, {target});
+    dispatchEventLocked(currentTime, entry, {{connection}});
 }
 
 void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
@@ -2868,8 +2870,7 @@
         ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str());
         return {};
     }
-    InputTarget inputTarget;
-    inputTarget.connection = connection;
+    InputTarget inputTarget{connection};
     inputTarget.windowHandle = windowHandle;
     inputTarget.dispatchMode = dispatchMode;
     inputTarget.flags = targetFlags;
@@ -2982,8 +2983,7 @@
     if (monitorsIt == mGlobalMonitorsByDisplay.end()) return;
 
     for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
-        InputTarget target;
-        target.connection = monitor.connection;
+        InputTarget target{monitor.connection};
         // target.firstDownTimeInTarget is not set for global monitors. It is only required in split
         // touch and global monitoring works as intended even without setting firstDownTimeInTarget
         if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
@@ -3275,7 +3275,7 @@
         ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, "
               "globalScaleFactor=%f, pointerIds=%s %s",
               connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(),
-              inputTarget.globalScaleFactor, bitsetToString(inputTarget.pointerIds).c_str(),
+              inputTarget.globalScaleFactor, bitsetToString(inputTarget.getPointerIds()).c_str(),
               inputTarget.getPointerInfoString().c_str());
     }
 
@@ -3297,7 +3297,7 @@
                             ftl::enum_string(eventEntry->type).c_str());
 
         const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
-        if (inputTarget.pointerIds.count() != originalMotionEntry.getPointerCount()) {
+        if (inputTarget.getPointerIds().count() != originalMotionEntry.getPointerCount()) {
             if (!inputTarget.firstDownTimeInTarget.has_value()) {
                 logDispatchStateLocked();
                 LOG(FATAL) << "Splitting motion events requires a down time to be set for the "
@@ -3306,7 +3306,7 @@
                            << originalMotionEntry.getDescription();
             }
             std::unique_ptr<MotionEntry> splitMotionEntry =
-                    splitMotionEvent(originalMotionEntry, inputTarget.pointerIds,
+                    splitMotionEvent(originalMotionEntry, inputTarget.getPointerIds(),
                                      inputTarget.firstDownTimeInTarget.value());
             if (!splitMotionEntry) {
                 return; // split event was dropped
@@ -3364,7 +3364,8 @@
     // This is a new event.
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
     std::unique_ptr<DispatchEntry> dispatchEntry =
-            createDispatchEntry(inputTarget, eventEntry, inputTarget.flags, mWindowInfosVsyncId);
+            createDispatchEntry(mIdGenerator, inputTarget, eventEntry, inputTarget.flags,
+                                mWindowInfosVsyncId);
 
     // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
     // different EventEntry than what was passed in.
@@ -3485,7 +3486,7 @@
                           << connection->getInputChannelName() << " with event "
                           << cancelEvent->getDescription();
                 std::unique_ptr<DispatchEntry> cancelDispatchEntry =
-                        createDispatchEntry(inputTarget, std::move(cancelEvent),
+                        createDispatchEntry(mIdGenerator, inputTarget, std::move(cancelEvent),
                                             ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId);
 
                 // Send these cancel events to the queue before sending the event from the new
@@ -3664,12 +3665,6 @@
             }
             usingCoords = scaledCoords;
         }
-    } else if (dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS)) {
-        // We don't want the dispatch target to know the coordinates
-        for (uint32_t i = 0; i < motionEntry.getPointerCount(); i++) {
-            scaledCoords[i].clear();
-        }
-        usingCoords = scaledCoords;
     }
 
     std::array<uint8_t, 32> hmac = getSignature(motionEntry, dispatchEntry);
@@ -4109,7 +4104,7 @@
 
     const bool wasEmpty = connection->outboundQueue.empty();
     // The target to use if we don't find a window associated with the channel.
-    const InputTarget fallbackTarget{.connection = connection};
+    const InputTarget fallbackTarget{connection};
     const auto& token = connection->getToken();
 
     for (size_t i = 0; i < cancelationEvents.size(); i++) {
@@ -4227,8 +4222,7 @@
                                                  targetFlags, pointerIds, motionEntry.downTime,
                                                  targets);
                 } else {
-                    targets.emplace_back(
-                            InputTarget{.connection = connection, .flags = targetFlags});
+                    targets.emplace_back(connection, targetFlags);
                     const auto it = mDisplayInfos.find(motionEntry.displayId);
                     if (it != mDisplayInfos.end()) {
                         targets.back().displayTransform = it->second.transform;
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 28e3c6d..35ad858 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -26,6 +26,15 @@
 
 namespace android::inputdispatcher {
 
+namespace {
+
+const static ui::Transform kIdentityTransform{};
+
+}
+
+InputTarget::InputTarget(const std::shared_ptr<Connection>& connection, ftl::Flags<Flags> flags)
+      : connection(connection), flags(flags) {}
+
 void InputTarget::addPointers(std::bitset<MAX_POINTER_ID + 1> newPointerIds,
                               const ui::Transform& transform) {
     // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
@@ -36,31 +45,46 @@
     }
 
     // Ensure that the new set of pointers doesn't overlap with the current set of pointers.
-    if ((pointerIds & newPointerIds).any()) {
+    if ((getPointerIds() & newPointerIds).any()) {
         LOG(FATAL) << __func__ << " - overlap with incoming pointers "
                    << bitsetToString(newPointerIds) << " in " << *this;
     }
 
-    pointerIds |= newPointerIds;
-    for (size_t i = 0; i < newPointerIds.size(); i++) {
-        if (!newPointerIds.test(i)) {
-            continue;
+    for (auto& [existingTransform, existingPointers] : mPointerTransforms) {
+        if (transform == existingTransform) {
+            existingPointers |= newPointerIds;
+            return;
         }
-        pointerTransforms[i] = transform;
     }
+    mPointerTransforms.emplace_back(transform, newPointerIds);
 }
 
 void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) {
-    pointerIds.reset();
-    pointerTransforms[0] = transform;
+    mPointerTransforms = {{transform, {}}};
 }
 
 bool InputTarget::useDefaultPointerTransform() const {
-    return pointerIds.none();
+    return mPointerTransforms.size() <= 1;
 }
 
 const ui::Transform& InputTarget::getDefaultPointerTransform() const {
-    return pointerTransforms[0];
+    if (!useDefaultPointerTransform()) {
+        LOG(FATAL) << __func__ << ": Not using default pointer transform";
+    }
+    return mPointerTransforms.size() == 1 ? mPointerTransforms[0].first : kIdentityTransform;
+}
+
+const ui::Transform& InputTarget::getTransformForPointer(int32_t pointerId) const {
+    for (const auto& [transform, ids] : mPointerTransforms) {
+        if (ids.test(pointerId)) {
+            return transform;
+        }
+    }
+
+    LOG(FATAL) << __func__
+               << ": Cannot get transform: The following Pointer ID does not exist in target: "
+               << pointerId;
+    return kIdentityTransform;
 }
 
 std::string InputTarget::getPointerInfoString() const {
@@ -71,17 +95,21 @@
         return out;
     }
 
-    for (uint32_t i = 0; i < pointerIds.size(); i++) {
-        if (!pointerIds.test(i)) {
-            continue;
-        }
-
-        const std::string name = "pointerId " + std::to_string(i) + ":";
-        pointerTransforms[i].dump(out, name.c_str(), "        ");
+    for (const auto& [transform, ids] : mPointerTransforms) {
+        const std::string name = "pointerIds " + bitsetToString(ids) + ":";
+        transform.dump(out, name.c_str(), "        ");
     }
     return out;
 }
 
+std::bitset<MAX_POINTER_ID + 1> InputTarget::getPointerIds() const {
+    PointerIds allIds;
+    for (const auto& [_, ids] : mPointerTransforms) {
+        allIds |= ids;
+    }
+    return allIds;
+}
+
 std::ostream& operator<<(std::ostream& out, const InputTarget& target) {
     out << "{connection=";
     if (target.connection != nullptr) {
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 5728bdf..058639a 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -33,7 +33,8 @@
  * be added to input event coordinates to compensate for the absolute position of the
  * window area.
  */
-struct InputTarget {
+class InputTarget {
+public:
     using Flags = InputTargetFlags;
 
     enum class DispatchMode {
@@ -80,20 +81,17 @@
     // Current display transform. Used for compatibility for raw coordinates.
     ui::Transform displayTransform;
 
-    // The subset of pointer ids to include in motion events dispatched to this input target
-    // if FLAG_SPLIT is set.
-    std::bitset<MAX_POINTER_ID + 1> pointerIds;
     // Event time for the first motion event (ACTION_DOWN) dispatched to this input target if
     // FLAG_SPLIT is set.
     std::optional<nsecs_t> firstDownTimeInTarget;
-    // The data is stored by the pointerId. Use the bit position of pointerIds to look up
-    // Transform per pointerId.
-    ui::Transform pointerTransforms[MAX_POINTERS];
 
     // The window that this input target is being dispatched to. It is possible for this to be
     // null for cases like global monitors.
     sp<gui::WindowInfoHandle> windowHandle;
 
+    InputTarget() = default;
+    InputTarget(const std::shared_ptr<Connection>&, ftl::Flags<Flags> = {});
+
     void addPointers(std::bitset<MAX_POINTER_ID + 1> pointerIds, const ui::Transform& transform);
     void setDefaultPointerTransform(const ui::Transform& transform);
 
@@ -111,7 +109,22 @@
      */
     const ui::Transform& getDefaultPointerTransform() const;
 
+    const ui::Transform& getTransformForPointer(int32_t pointerId) const;
+
+    std::bitset<MAX_POINTER_ID + 1> getPointerIds() const;
+
     std::string getPointerInfoString() const;
+
+private:
+    template <typename K, typename V>
+    using ArrayMap = std::vector<std::pair<K, V>>;
+    using PointerIds = std::bitset<MAX_POINTER_ID + 1>;
+    // The mapping of pointer IDs to the transform that should be used for that collection of IDs.
+    // Each of the pointer IDs are mutually disjoint, and their union makes up pointer IDs to
+    // include in the motion events dispatched to this target. We use an ArrayMap to store this to
+    // avoid having to define hash or comparison functions for ui::Transform, which would be needed
+    // to use std::unordered_map or std::map respectively.
+    ArrayMap<ui::Transform, PointerIds> mPointerTransforms;
 };
 
 std::ostream& operator<<(std::ostream& out, const InputTarget& target);
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 19788ce..797ebcc 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -67,7 +67,8 @@
       : mDeviceId(deviceId),
         mReaderContext(readerContext),
         mPointerController(readerContext.getPointerController(deviceId)),
-        mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {
+        mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()),
+        mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) {
     deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
     deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
 }
@@ -400,20 +401,55 @@
                 // ensure consistency between touchscreen and touchpad flings), so we're just using
                 // the "start fling" gestures as a marker for the end of a two-finger scroll
                 // gesture.
+                mFlingMayBeInProgress = true;
                 return endScroll(when, readTime);
             }
             break;
         case GESTURES_FLING_TAP_DOWN:
             if (mCurrentClassification == MotionClassification::NONE) {
-                // Use the tap down state of a fling gesture as an indicator that a contact
-                // has been initiated with the touchpad. We treat this as a move event with zero
-                // magnitude, which will also result in the pointer icon being updated.
-                // TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
-                //  initiated with a touchpad.
-                return handleMove(when, readTime, gestureStartTime,
-                                  Gesture(kGestureMove, gesture.start_time, gesture.end_time,
-                                          /*dx=*/0.f,
-                                          /*dy=*/0.f));
+                if (mEnableFlingStop && mFlingMayBeInProgress) {
+                    // The user has just touched the pad again after ending a two-finger scroll
+                    // motion, which might have started a fling. We want to stop the fling, but
+                    // unfortunately there's currently no API for doing so. Instead, send and
+                    // immediately cancel a fake finger to cause the scrolling to stop and hopefully
+                    // avoid side effects (e.g. activation of UI elements).
+                    // TODO(b/326056750): add an API for fling stops.
+                    mFlingMayBeInProgress = false;
+                    const auto [xCursorPosition, yCursorPosition] = mEnablePointerChoreographer
+                            ? FloatPoint{0, 0}
+                            : mPointerController->getPosition();
+                    PointerCoords coords;
+                    coords.clear();
+                    coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+                    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+                    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+                    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+
+                    std::list<NotifyArgs> out;
+                    mDownTime = when;
+                    out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
+                    // TODO(b/281106755): add a MotionClassification value for fling stops.
+                    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
+                                                 /*actionButton=*/0, /*buttonState=*/0,
+                                                 /*pointerCount=*/1, &coords, xCursorPosition,
+                                                 yCursorPosition));
+                    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_CANCEL,
+                                                 /*actionButton=*/0, /*buttonState=*/0,
+                                                 /*pointerCount=*/1, &coords, xCursorPosition,
+                                                 yCursorPosition));
+                    out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
+                    return out;
+                } else {
+                    // Use the tap down state of a fling gesture as an indicator that a contact
+                    // has been initiated with the touchpad. We treat this as a move event with zero
+                    // magnitude, which will also result in the pointer icon being updated.
+                    // TODO(b/282023644): Add a signal in libgestures for when a stable contact has
+                    //  been initiated with a touchpad.
+                    return handleMove(when, readTime, gestureStartTime,
+                                      Gesture(kGestureMove, gesture.start_time, gesture.end_time,
+                                              /*dx=*/0.f,
+                                              /*dy=*/0.f));
+                }
             }
             break;
         default:
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 07cc56c..ffab039 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -106,6 +106,7 @@
     InputReaderContext& mReaderContext;
     std::shared_ptr<PointerControllerInterface> mPointerController;
     const bool mEnablePointerChoreographer;
+    const bool mEnableFlingStop;
 
     std::optional<int32_t> mDisplayId;
     FloatRect mBoundsInLogicalDisplay{};
@@ -120,6 +121,9 @@
     // Whether we are currently in a hover state (i.e. a HOVER_ENTER event has been sent without a
     // matching HOVER_EXIT).
     bool mIsHovering = false;
+    // Whether we've received a "fling start" gesture (i.e. the end of a scroll) but no "fling tap
+    // down" gesture to match it yet.
+    bool mFlingMayBeInProgress = false;
 
     MotionClassification mCurrentClassification = MotionClassification::NONE;
     // Only used when mCurrentClassification is MULTI_FINGER_SWIPE.
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp
index f4a06f7..b7af356 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.cpp
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -37,6 +37,30 @@
     return std::visit([](const auto& event) { return event.id; }, v);
 }
 
+MotionEvent toInputEvent(
+        const trace::TracedMotionEvent& e,
+        const trace::InputTracingBackendInterface::WindowDispatchArgs& dispatchArgs,
+        const std::array<uint8_t, 32>& hmac) {
+    MotionEvent traced;
+    traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action, e.actionButton,
+                      dispatchArgs.resolvedFlags, e.edgeFlags, e.metaState, e.buttonState,
+                      e.classification, dispatchArgs.transform, e.xPrecision, e.yPrecision,
+                      e.xCursorPosition, e.yCursorPosition, dispatchArgs.rawTransform, e.downTime,
+                      e.eventTime, e.pointerProperties.size(), e.pointerProperties.data(),
+                      e.pointerCoords.data());
+    return traced;
+}
+
+KeyEvent toInputEvent(const trace::TracedKeyEvent& e,
+                      const trace::InputTracingBackendInterface::WindowDispatchArgs& dispatchArgs,
+                      const std::array<uint8_t, 32>& hmac) {
+    KeyEvent traced;
+    traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action,
+                      dispatchArgs.resolvedFlags, e.keyCode, e.scanCode, e.metaState, e.repeatCount,
+                      e.downTime, e.eventTime);
+    return traced;
+}
+
 } // namespace
 
 // --- VerifyingTrace ---
@@ -55,6 +79,7 @@
     std::unique_lock lock(mLock);
     base::ScopedLockAssertion assumeLocked(mLock);
 
+    // Poll for all expected events to be traced, and keep track of the latest poll result.
     base::Result<void> result;
     mEventTracedCondition.wait_for(lock, TRACE_TIMEOUT, [&]() REQUIRES(mLock) {
         for (const auto& [expectedEvent, windowId] : mExpectedEvents) {
@@ -101,12 +126,39 @@
                          });
     if (tracedDispatchesIt == mTracedWindowDispatches.end()) {
         msg << "Expected dispatch of event with ID 0x" << std::hex << expectedEvent.getId()
-            << " to window with ID 0x" << expectedWindowId << " to be traced, but it was not."
-            << "\nExpected event: " << expectedEvent;
+            << " to window with ID 0x" << expectedWindowId << " to be traced, but it was not.\n"
+            << "Expected event: " << expectedEvent;
         return error(msg);
     }
 
-    return {};
+    // Verify that the traced event matches the expected event exactly.
+    return std::visit(
+            [&](const auto& traced) -> base::Result<void> {
+                Event tracedEvent;
+                using T = std::decay_t<decltype(traced)>;
+                if constexpr (std::is_same_v<Event, MotionEvent> &&
+                              std::is_same_v<T, trace::TracedMotionEvent>) {
+                    tracedEvent =
+                            toInputEvent(traced, *tracedDispatchesIt, expectedEvent.getHmac());
+                } else if constexpr (std::is_same_v<Event, KeyEvent> &&
+                                     std::is_same_v<T, trace::TracedKeyEvent>) {
+                    tracedEvent =
+                            toInputEvent(traced, *tracedDispatchesIt, expectedEvent.getHmac());
+                } else {
+                    msg << "Received the wrong event type!\n"
+                        << "Expected event: " << expectedEvent;
+                    return error(msg);
+                }
+
+                const auto result = testing::internal::CmpHelperEQ("expectedEvent", "tracedEvent",
+                                                                   expectedEvent, tracedEvent);
+                if (!result) {
+                    msg << result.failure_message();
+                    return error(msg);
+                }
+                return {};
+            },
+            tracedEventsIt->second);
 }
 
 // --- FakeInputTracingBackend ---
@@ -114,7 +166,7 @@
 void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) const {
     {
         std::scoped_lock lock(mTrace->mLock);
-        mTrace->mTracedEvents.emplace(event.id);
+        mTrace->mTracedEvents.emplace(event.id, event);
     }
     mTrace->mEventTracedCondition.notify_all();
 }
@@ -122,7 +174,7 @@
 void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) const {
     {
         std::scoped_lock lock(mTrace->mLock);
-        mTrace->mTracedEvents.emplace(event.id);
+        mTrace->mTracedEvents.emplace(event.id, event);
     }
     mTrace->mEventTracedCondition.notify_all();
 }
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h
index 40ca3a2..108419a 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.h
+++ b/services/inputflinger/tests/FakeInputTracingBackend.h
@@ -25,7 +25,7 @@
 #include <condition_variable>
 #include <memory>
 #include <mutex>
-#include <unordered_set>
+#include <unordered_map>
 #include <vector>
 
 namespace android::inputdispatcher {
@@ -58,7 +58,7 @@
 private:
     std::mutex mLock;
     std::condition_variable mEventTracedCondition;
-    std::unordered_set<uint32_t /*eventId*/> mTracedEvents GUARDED_BY(mLock);
+    std::unordered_map<uint32_t /*eventId*/, trace::TracedEvent> mTracedEvents GUARDED_BY(mLock);
     using WindowDispatchArgs = trace::InputTracingBackendInterface::WindowDispatchArgs;
     std::vector<WindowDispatchArgs> mTracedWindowDispatches GUARDED_BY(mLock);
     std::vector<std::pair<std::variant<KeyEvent, MotionEvent>, int32_t /*windowId*/>>
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index dd88165..ca9ef8e 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -1167,6 +1167,38 @@
     ASSERT_TRUE(mFakePointerController->isPointerShown());
 }
 
+TEST_F(GestureConverterTest, FlingTapDownAfterScrollStopsFling) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    input_flags::enable_touchpad_fling_stop(true);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+    converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+    Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+    std::list<NotifyArgs> args =
+            converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
+    Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+                         GESTURES_FLING_START);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+
+    Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+                           /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)),
+                            VariantWith<NotifyMotionArgs>(
+                                    WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(
+                                    WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+                                          WithMotionClassification(MotionClassification::NONE)))));
+    ASSERT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
+                                                         WithToolType(ToolType::FINGER),
+                                                         WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
 TEST_F(GestureConverterTest, Tap) {
     // Tap should produce button press/release events
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
@@ -2556,6 +2588,38 @@
                       WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
 }
 
+TEST_F(GestureConverterTestWithChoreographer, FlingTapDownAfterScrollStopsFling) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    input_flags::enable_touchpad_fling_stop(true);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+    converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+    Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+    std::list<NotifyArgs> args =
+            converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
+    Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+                         GESTURES_FLING_START);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+
+    Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+                           /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)),
+                            VariantWith<NotifyMotionArgs>(
+                                    WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(
+                                    WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+                                          WithMotionClassification(MotionClassification::NONE)))));
+    ASSERT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
+                                                         WithToolType(ToolType::FINGER),
+                                                         WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
 TEST_F(GestureConverterTestWithChoreographer, Tap) {
     // Tap should produce button press/release events
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ceb3c41..f0f4d93 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3865,48 +3865,57 @@
 
     // Touch down on the first window
     mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
-
     mDispatcher->waitForIdle();
 
-    std::unique_ptr<MotionEvent> motionEvent1 = window1->consumeMotionEvent();
-    ASSERT_NE(nullptr, motionEvent1);
+    const std::unique_ptr<MotionEvent> firstDown =
+            window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+    ASSERT_EQ(firstDown->getDownTime(), firstDown->getEventTime());
     window2->assertNoEvents();
-    nsecs_t downTimeForWindow1 = motionEvent1->getDownTime();
-    ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime());
 
     // Now touch down on the window with another pointer
     mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
     mDispatcher->waitForIdle();
-    std::unique_ptr<MotionEvent> motionEvent2 = window2->consumeMotionEvent();
-    ASSERT_NE(nullptr, motionEvent2);
-    nsecs_t downTimeForWindow2 = motionEvent2->getDownTime();
-    ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
-    ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime());
+
+    const std::unique_ptr<MotionEvent> secondDown =
+            window2->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+    ASSERT_EQ(secondDown->getDownTime(), secondDown->getEventTime());
+    ASSERT_NE(firstDown->getDownTime(), secondDown->getDownTime());
+    // We currently send MOVE events to all windows receiving a split touch when there is any change
+    // in the touch state, even when none of the pointers in the split window actually moved.
+    // Document this behavior in the test.
+    window1->consumeMotionMove();
 
     // Now move the pointer on the second window
     mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
     mDispatcher->waitForIdle();
-    window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
+
+    window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime()));
+    window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime()));
 
     // Now add new touch down on the second window
     mDispatcher->notifyMotion(generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}}));
     mDispatcher->waitForIdle();
-    window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
 
-    // TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line
-    window1->consumeMotionMove();
-    window1->assertNoEvents();
+    window2->consumeMotionEvent(
+            AllOf(WithMotionAction(POINTER_1_DOWN), WithDownTime(secondDown->getDownTime())));
+    window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime()));
 
     // Now move the pointer on the first window
     mDispatcher->notifyMotion(
             generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}, {150, 50}}));
     mDispatcher->waitForIdle();
-    window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
 
+    window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime()));
+    window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime()));
+
+    // Now add new touch down on the first window
     mDispatcher->notifyMotion(
             generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}}));
     mDispatcher->waitForIdle();
-    window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
+
+    window1->consumeMotionEvent(
+            AllOf(WithMotionAction(POINTER_1_DOWN), WithDownTime(firstDown->getDownTime())));
+    window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime()));
 }
 
 TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
@@ -10799,6 +10808,7 @@
                                 InputEventInjectionSync::WAIT_FOR_RESULT))
             << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
     mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    mSecondWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
 
     // Perform drag and drop from first window.
     ASSERT_TRUE(startDrag(false));
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 476f755..835f8b8 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -58,6 +58,7 @@
 using namespace ftl::flag_operators;
 using testing::AllOf;
 using std::chrono_literals::operator""ms;
+using std::chrono_literals::operator""s;
 
 // Arbitrary display properties.
 static constexpr int32_t DISPLAY_ID = 0;
@@ -149,7 +150,7 @@
     std::istringstream iss(dump);
     for (std::string line; std::getline(iss, line);) {
         ALOGE("%s", line.c_str());
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+        std::this_thread::sleep_for(1ms);
     }
 }
 
@@ -1374,13 +1375,23 @@
         mFakePolicy.clear();
     }
 
-    std::optional<InputDeviceInfo> findDeviceByName(const std::string& name) {
-        const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
-        const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(),
-                                      [&name](const InputDeviceInfo& info) {
-                                          return info.getIdentifier().name == name;
-                                      });
-        return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
+    std::optional<InputDeviceInfo> waitForDevice(const std::string& deviceName) {
+        std::chrono::time_point start = std::chrono::steady_clock::now();
+        while (true) {
+            const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
+            const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(),
+                                          [&deviceName](const InputDeviceInfo& info) {
+                                              return info.getIdentifier().name == deviceName;
+                                          });
+            if (it != inputDevices.end()) {
+                return std::make_optional(*it);
+            }
+            std::this_thread::sleep_for(1ms);
+            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+            if (elapsed > 5s) {
+                return {};
+            }
+        }
     }
 
     void setupInputReader() {
@@ -1433,7 +1444,7 @@
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
     ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
 
-    const auto device = findDeviceByName(keyboard->getName());
+    const auto device = waitForDevice(keyboard->getName());
     ASSERT_TRUE(device.has_value());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources());
@@ -1476,7 +1487,7 @@
     std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
 
-    const auto device = findDeviceByName(stylus->getName());
+    const auto device = waitForDevice(stylus->getName());
     ASSERT_TRUE(device.has_value());
 
     // An external stylus with buttons should also be recognized as a keyboard.
@@ -1516,7 +1527,7 @@
                                                                           BTN_STYLUS3});
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
 
-    const auto device = findDeviceByName(keyboard->getName());
+    const auto device = waitForDevice(keyboard->getName());
     ASSERT_TRUE(device.has_value());
 
     // An alphabetical keyboard that reports stylus buttons should not be recognized as a stylus.
@@ -1533,7 +1544,7 @@
                     std::initializer_list<int>{KEY_VOLUMEUP, KEY_VOLUMEDOWN});
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
 
-    const auto device = findDeviceByName(keyboard->getName());
+    const auto device = waitForDevice(keyboard->getName());
     ASSERT_TRUE(device.has_value());
 
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources())
@@ -1587,7 +1598,7 @@
         mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
-        const auto info = findDeviceByName(mDevice->getName());
+        const auto info = waitForDevice(mDevice->getName());
         ASSERT_TRUE(info);
         mDeviceInfo = *info;
     }
@@ -1658,7 +1669,7 @@
                                      ViewportType::INTERNAL);
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
-        const auto info = findDeviceByName(mDevice->getName());
+        const auto info = waitForDevice(mDevice->getName());
         ASSERT_TRUE(info);
         mDeviceInfo = *info;
     }
@@ -1991,7 +2002,7 @@
     auto externalStylus = createUinputDevice<UinputExternalStylus>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
-    const auto stylusInfo = findDeviceByName(externalStylus->getName());
+    const auto stylusInfo = waitForDevice(externalStylus->getName());
     ASSERT_TRUE(stylusInfo);
 
     // Move
@@ -2062,7 +2073,7 @@
         mStylus = mStylusDeviceLifecycleTracker.get();
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
-        const auto info = findDeviceByName(mStylus->getName());
+        const auto info = waitForDevice(mStylus->getName());
         ASSERT_TRUE(info);
         mStylusInfo = *info;
     }
@@ -2332,11 +2343,11 @@
             createUinputDevice<UinputExternalStylusWithPressure>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
-    const auto stylusInfo = findDeviceByName(stylus->getName());
+    const auto stylusInfo = waitForDevice(stylus->getName());
     ASSERT_TRUE(stylusInfo);
 
     // Connecting an external stylus changes the source of the touchscreen.
-    const auto deviceInfo = findDeviceByName(mDevice->getName());
+    const auto deviceInfo = waitForDevice(mDevice->getName());
     ASSERT_TRUE(deviceInfo);
     ASSERT_TRUE(isFromSource(deviceInfo->getSources(), STYLUS_FUSION_SOURCE));
 }
@@ -2350,7 +2361,7 @@
             createUinputDevice<UinputExternalStylusWithPressure>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
-    const auto stylusInfo = findDeviceByName(stylus->getName());
+    const auto stylusInfo = waitForDevice(stylus->getName());
     ASSERT_TRUE(stylusInfo);
 
     ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
@@ -2396,7 +2407,7 @@
             createUinputDevice<UinputExternalStylusWithPressure>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
-    const auto stylusInfo = findDeviceByName(stylus->getName());
+    const auto stylusInfo = waitForDevice(stylus->getName());
     ASSERT_TRUE(stylusInfo);
 
     ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
@@ -2476,7 +2487,7 @@
     std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
     ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
-    const auto stylusInfo = findDeviceByName(stylus->getName());
+    const auto stylusInfo = waitForDevice(stylus->getName());
     ASSERT_TRUE(stylusInfo);
 
     ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
index 759485f..61ab47a 100644
--- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -70,12 +70,14 @@
 
     if (hal == nullptr) {
         ALOGV("Power HAL not available, skipping test...");
+        state.SkipWithMessage("Power HAL unavailable");
         return;
     }
 
     ndk::ScopedAStatus ret = (*hal.*fn)(std::forward<Args1>(args1)...);
     if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
         ALOGV("Power HAL does not support this operation, skipping test...");
+        state.SkipWithMessage("operation unsupported");
         return;
     }
 
@@ -97,6 +99,7 @@
 
     if (hal == nullptr) {
         ALOGV("Power HAL not available, skipping test...");
+        state.SkipWithMessage("Power HAL unavailable");
         return;
     }
 
@@ -109,12 +112,14 @@
 
     if (session == nullptr) {
         ALOGV("Power HAL doesn't support session, skipping test...");
+        state.SkipWithMessage("operation unsupported");
         return;
     }
 
     ndk::ScopedAStatus ret = (*session.*fn)(std::forward<Args1>(args1)...);
     if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
         ALOGV("Power HAL does not support this operation, skipping test...");
+        state.SkipWithMessage("operation unsupported");
         return;
     }
 
@@ -163,6 +168,7 @@
 
     if (hal == nullptr) {
         ALOGV("Power HAL not available, skipping test...");
+        state.SkipWithMessage("Power HAL unavailable");
         return;
     }
 
@@ -170,6 +176,7 @@
             hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
     if (ret.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
         ALOGV("Power HAL does not support this operation, skipping test...");
+        state.SkipWithMessage("operation unsupported");
         return;
     }
 
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
index 111b5d7..bcb376b 100644
--- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -50,6 +50,7 @@
 
     if (hal == nullptr) {
         ALOGV("Power HAL HIDL not available, skipping test...");
+        state.SkipWithMessage("Power HAL unavailable");
         return;
     }
 
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index dcef9a3..6a0ea22 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 aconfig_declarations {
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index ae2f2db..0b01c66 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_defaults {
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 29c9432..8e28cc3 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_library_static {
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index d714848..16776cf 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_defaults {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6979f03..e6c58a3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -60,14 +60,6 @@
 #include "VsyncController.h"
 #include "VsyncSchedule.h"
 
-#define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
-    do {                                                             \
-        if (mConnections.count(handle) == 0) {                       \
-            ALOGE("Invalid connection handle %" PRIuPTR, handle.id); \
-            return __VA_ARGS__;                                      \
-        }                                                            \
-    } while (false)
-
 namespace android::scheduler {
 
 Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features,
@@ -344,40 +336,26 @@
     }
 }
 
-ConnectionHandle Scheduler::createEventThread(Cycle cycle,
-                                              frametimeline::TokenManager* tokenManager,
-                                              std::chrono::nanoseconds workDuration,
-                                              std::chrono::nanoseconds readyDuration) {
+void Scheduler::createEventThread(Cycle cycle, frametimeline::TokenManager* tokenManager,
+                                  std::chrono::nanoseconds workDuration,
+                                  std::chrono::nanoseconds readyDuration) {
     auto eventThread =
             std::make_unique<android::impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
                                                          getVsyncSchedule(), tokenManager, *this,
                                                          workDuration, readyDuration);
 
-    auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle;
-    handle = createConnection(std::move(eventThread));
-    return handle;
-}
-
-ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
-    const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
-    ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
-
-    auto connection = eventThread->createEventConnection();
-
-    std::lock_guard<std::mutex> lock(mConnectionsLock);
-    mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
-    return handle;
+    if (cycle == Cycle::Render) {
+        mRenderEventThread = std::move(eventThread);
+        mRenderEventConnection = mRenderEventThread->createEventConnection();
+    } else {
+        mLastCompositeEventThread = std::move(eventThread);
+        mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection();
+    }
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
-        ConnectionHandle handle, EventRegistrationFlags eventRegistration,
-        const sp<IBinder>& layerHandle) {
-    const auto connection = [&]() -> sp<EventThreadConnection> {
-        std::scoped_lock lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle, nullptr);
-
-        return mConnections[handle].thread->createEventConnection(eventRegistration);
-    }();
+        Cycle cycle, EventRegistrationFlags eventRegistration, const sp<IBinder>& layerHandle) {
+    const auto connection = eventThreadFor(cycle).createEventConnection(eventRegistration);
     const auto layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
 
     if (layerId != static_cast<int32_t>(UNASSIGNED_LAYER_ID)) {
@@ -397,85 +375,48 @@
     return connection;
 }
 
-sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
-    std::lock_guard<std::mutex> lock(mConnectionsLock);
-    RETURN_IF_INVALID_HANDLE(handle, nullptr);
-    return mConnections[handle].connection;
+void Scheduler::onHotplugReceived(Cycle cycle, PhysicalDisplayId displayId, bool connected) {
+    if (hasEventThreads()) {
+        eventThreadFor(cycle).onHotplugReceived(displayId, connected);
+    }
 }
 
-void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                  bool connected) {
-    android::EventThread* thread;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections[handle].thread.get();
+void Scheduler::onHotplugConnectionError(Cycle cycle, int32_t errorCode) {
+    if (hasEventThreads()) {
+        eventThreadFor(cycle).onHotplugConnectionError(errorCode);
     }
-
-    thread->onHotplugReceived(displayId, connected);
-}
-
-void Scheduler::onHotplugConnectionError(ConnectionHandle handle, int32_t errorCode) {
-    android::EventThread* thread;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections[handle].thread.get();
-    }
-
-    thread->onHotplugConnectionError(errorCode);
 }
 
 void Scheduler::enableSyntheticVsync(bool enable) {
-    // TODO(b/241285945): Remove connection handles.
-    const ConnectionHandle handle = mAppConnectionHandle;
-    android::EventThread* thread;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections[handle].thread.get();
-    }
-    thread->enableSyntheticVsync(enable);
+    eventThreadFor(Cycle::Render).enableSyntheticVsync(enable);
 }
 
-void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
+void Scheduler::onFrameRateOverridesChanged(Cycle cycle, PhysicalDisplayId displayId) {
     const bool supportsFrameRateOverrideByContent =
             pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
 
     std::vector<FrameRateOverride> overrides =
             mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
 
-    android::EventThread* thread;
-    {
-        std::lock_guard lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections[handle].thread.get();
-    }
-    thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
+    eventThreadFor(cycle).onFrameRateOverridesChanged(displayId, std::move(overrides));
 }
 
-void Scheduler::onHdcpLevelsChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId,
                                     int32_t connectedLevel, int32_t maxLevel) {
-    android::EventThread* thread;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections[handle].thread.get();
-    }
-    thread->onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
+    eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
 }
 
-void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
+void Scheduler::onPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) {
     {
         std::lock_guard<std::mutex> lock(mPolicyLock);
         // Cache the last reported modes for primary display.
-        mPolicy.cachedModeChangedParams = {handle, mode};
+        mPolicy.cachedModeChangedParams = {cycle, mode};
 
         // Invalidate content based refresh rate selection so it could be calculated
         // again for the new refresh rate.
         mPolicy.contentRequirements.clear();
     }
-    onNonPrimaryDisplayModeChanged(handle, mode);
+    onNonPrimaryDisplayModeChanged(cycle, mode);
 }
 
 void Scheduler::dispatchCachedReportedMode() {
@@ -502,39 +443,25 @@
     }
 
     mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt;
-    onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle,
+    onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->cycle,
                                    mPolicy.cachedModeChangedParams->mode);
 }
 
-void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
-    android::EventThread* thread;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections[handle].thread.get();
+void Scheduler::onNonPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) {
+    if (hasEventThreads()) {
+        eventThreadFor(cycle).onModeChanged(mode);
     }
-    thread->onModeChanged(mode);
 }
 
-void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
-    android::EventThread* thread;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections.at(handle).thread.get();
-    }
-    thread->dump(result);
+void Scheduler::dump(Cycle cycle, std::string& result) const {
+    eventThreadFor(cycle).dump(result);
 }
 
-void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration,
+void Scheduler::setDuration(Cycle cycle, std::chrono::nanoseconds workDuration,
                             std::chrono::nanoseconds readyDuration) {
-    android::EventThread* thread;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections[handle].thread.get();
+    if (hasEventThreads()) {
+        eventThreadFor(cycle).setDuration(workDuration, readyDuration);
     }
-    thread->setDuration(workDuration, readyDuration);
 }
 
 void Scheduler::updatePhaseConfiguration(Fps refreshRate) {
@@ -557,10 +484,10 @@
 }
 
 void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) {
-    setDuration(mAppConnectionHandle,
+    setDuration(Cycle::Render,
                 /* workDuration */ config.appWorkDuration,
                 /* readyDuration */ config.sfWorkDuration);
-    setDuration(mSfConnectionHandle,
+    setDuration(Cycle::LastComposite,
                 /* workDuration */ vsyncPeriod,
                 /* readyDuration */ config.sfWorkDuration);
     setDuration(config.sfWorkDuration);
@@ -1027,16 +954,10 @@
 
 void Scheduler::applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule> vsyncSchedule) {
     onNewVsyncSchedule(vsyncSchedule->getDispatch());
-    std::vector<android::EventThread*> threads;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        threads.reserve(mConnections.size());
-        for (auto& [_, connection] : mConnections) {
-            threads.push_back(connection.thread.get());
-        }
-    }
-    for (auto* thread : threads) {
-        thread->onNewVsyncSchedule(vsyncSchedule);
+
+    if (hasEventThreads()) {
+        eventThreadFor(Cycle::Render).onNewVsyncSchedule(vsyncSchedule);
+        eventThreadFor(Cycle::LastComposite).onNewVsyncSchedule(vsyncSchedule);
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index d1934f2..dc32225 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -56,35 +56,6 @@
 
 #include <FrontEnd/LayerHierarchy.h>
 
-namespace android::scheduler {
-
-// Opaque handle to scheduler connection.
-struct ConnectionHandle {
-    using Id = std::uintptr_t;
-    static constexpr Id INVALID_ID = static_cast<Id>(-1);
-
-    Id id = INVALID_ID;
-
-    explicit operator bool() const { return id != INVALID_ID; }
-};
-
-inline bool operator==(ConnectionHandle lhs, ConnectionHandle rhs) {
-    return lhs.id == rhs.id;
-}
-
-} // namespace android::scheduler
-
-namespace std {
-
-template <>
-struct hash<android::scheduler::ConnectionHandle> {
-    size_t operator()(android::scheduler::ConnectionHandle handle) const {
-        return hash<android::scheduler::ConnectionHandle::Id>()(handle.id);
-    }
-};
-
-} // namespace std
-
 namespace android {
 
 class FenceTime;
@@ -106,6 +77,11 @@
 class VsyncConfiguration;
 class VsyncSchedule;
 
+enum class Cycle {
+    Render,       // Surface rendering.
+    LastComposite // Ahead of display compositing by one refresh period.
+};
+
 class Scheduler : public IEventThreadCallback, android::impl::MessageQueue {
     using Impl = android::impl::MessageQueue;
 
@@ -154,36 +130,32 @@
         return std::move(future);
     }
 
-    enum class Cycle {
-        Render,       // Surface rendering.
-        LastComposite // Ahead of display compositing by one refresh period.
-    };
-
-    ConnectionHandle createEventThread(Cycle, frametimeline::TokenManager*,
-                                       std::chrono::nanoseconds workDuration,
-                                       std::chrono::nanoseconds readyDuration);
+    void createEventThread(Cycle, frametimeline::TokenManager*,
+                           std::chrono::nanoseconds workDuration,
+                           std::chrono::nanoseconds readyDuration);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(
-            ConnectionHandle, EventRegistrationFlags eventRegistration = {},
+            Cycle, EventRegistrationFlags eventRegistration = {},
             const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock);
 
-    sp<EventThreadConnection> getEventConnection(ConnectionHandle);
+    const sp<EventThreadConnection>& getEventConnection(Cycle cycle) const {
+        return cycle == Cycle::Render ? mRenderEventConnection : mLastCompositeEventConnection;
+    }
 
-    void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
-    void onHotplugConnectionError(ConnectionHandle, int32_t errorCode);
+    void onHotplugReceived(Cycle, PhysicalDisplayId, bool connected);
+    void onHotplugConnectionError(Cycle, int32_t errorCode);
 
-    void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock);
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&);
+    void onPrimaryDisplayModeChanged(Cycle, const FrameRateMode&) EXCLUDES(mPolicyLock);
+    void onNonPrimaryDisplayModeChanged(Cycle, const FrameRateMode&);
 
     void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
 
-    void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
-            EXCLUDES(mConnectionsLock);
+    void onFrameRateOverridesChanged(Cycle, PhysicalDisplayId);
 
-    void onHdcpLevelsChanged(ConnectionHandle, PhysicalDisplayId, int32_t, int32_t);
+    void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t);
 
     // Modifies work duration in the event thread.
-    void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
+    void setDuration(Cycle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
     VsyncModulator& vsyncModulator() { return *mVsyncModulator; }
@@ -288,7 +260,7 @@
     bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const;
 
     void dump(utils::Dumper&) const;
-    void dump(ConnectionHandle, std::string&) const;
+    void dump(Cycle, std::string&) const;
     void dumpVsync(std::string&) const EXCLUDES(mDisplayLock);
 
     // Returns the preferred refresh rate and frame rate for the pacesetter display.
@@ -369,8 +341,15 @@
     void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override
             REQUIRES(kMainThreadContext, mDisplayLock);
 
-    // Create a connection on the given EventThread.
-    ConnectionHandle createConnection(std::unique_ptr<EventThread>);
+    // Used to skip event dispatch before EventThread creation during boot.
+    // TODO: b/241285191 - Reorder Scheduler initialization to avoid this.
+    bool hasEventThreads() const {
+        return CC_LIKELY(mRenderEventThread && mLastCompositeEventThread);
+    }
+
+    EventThread& eventThreadFor(Cycle cycle) const {
+        return *(cycle == Cycle::Render ? mRenderEventThread : mLastCompositeEventThread);
+    }
 
     // Update feature state machine to given state when corresponding timer resets or expires.
     void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
@@ -460,18 +439,11 @@
     void resync() override EXCLUDES(mDisplayLock);
     void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
 
-    // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
-    struct Connection {
-        sp<EventThreadConnection> connection;
-        std::unique_ptr<EventThread> thread;
-    };
+    std::unique_ptr<EventThread> mRenderEventThread;
+    sp<EventThreadConnection> mRenderEventConnection;
 
-    ConnectionHandle::Id mNextConnectionHandleId = 0;
-    mutable std::mutex mConnectionsLock;
-    std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
-
-    ConnectionHandle mAppConnectionHandle;
-    ConnectionHandle mSfConnectionHandle;
+    std::unique_ptr<EventThread> mLastCompositeEventThread;
+    sp<EventThreadConnection> mLastCompositeEventConnection;
 
     std::atomic<nsecs_t> mLastResyncTime = 0;
 
@@ -585,7 +557,7 @@
         ftl::Optional<FrameRateMode> modeOpt;
 
         struct ModeChangedParams {
-            ConnectionHandle handle;
+            Cycle cycle;
             FrameRateMode mode;
         };
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0436c3c..08a78cc 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -233,31 +233,25 @@
 }
 
 std::chrono::milliseconds getIdleTimerTimeout(DisplayId displayId) {
-    const auto displayIdleTimerMsKey = [displayId] {
-        std::stringstream ss;
-        ss << "debug.sf.set_idle_timer_ms_" << displayId.value;
-        return ss.str();
-    }();
-
-    const int32_t displayIdleTimerMs = base::GetIntProperty(displayIdleTimerMsKey, 0);
-    if (displayIdleTimerMs > 0) {
+    if (const int32_t displayIdleTimerMs =
+                base::GetIntProperty("debug.sf.set_idle_timer_ms_"s +
+                                             std::to_string(displayId.value),
+                                     0);
+        displayIdleTimerMs > 0) {
         return std::chrono::milliseconds(displayIdleTimerMs);
     }
 
-    const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms", 0);
+    const int32_t setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
     const int32_t millis = setIdleTimerMs ? setIdleTimerMs : sysprop::set_idle_timer_ms(0);
     return std::chrono::milliseconds(millis);
 }
 
 bool getKernelIdleTimerSyspropConfig(DisplayId displayId) {
-    const auto displaySupportKernelIdleTimerKey = [displayId] {
-        std::stringstream ss;
-        ss << "debug.sf.support_kernel_idle_timer_" << displayId.value;
-        return ss.str();
-    }();
+    const bool displaySupportKernelIdleTimer =
+            base::GetBoolProperty("debug.sf.support_kernel_idle_timer_"s +
+                                          std::to_string(displayId.value),
+                                  false);
 
-    const auto displaySupportKernelIdleTimer =
-            base::GetBoolProperty(displaySupportKernelIdleTimerKey, false);
     return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false);
 }
 
@@ -467,11 +461,10 @@
     wideColorGamutCompositionPixelFormat =
             static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
 
-    mLayerCachingEnabled = [] {
-        const bool enable =
-                android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
-        return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
-    }();
+    mLayerCachingEnabled =
+            base::GetBoolProperty("debug.sf.enable_layer_caching"s,
+                                  sysprop::SurfaceFlingerProperties::enable_layer_caching()
+                                          .value_or(false));
 
     useContextPriority = use_context_priority(true);
 
@@ -2092,12 +2085,11 @@
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
         gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration,
         const sp<IBinder>& layerHandle) {
-    const auto& handle =
-            vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
-            ? mSfConnectionHandle
-            : mAppConnectionHandle;
+    const auto cycle = vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
+            ? scheduler::Cycle::LastComposite
+            : scheduler::Cycle::Render;
 
-    return mScheduler->createDisplayEventConnection(handle, eventRegistration, layerHandle);
+    return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
 }
 
 void SurfaceFlinger::scheduleCommit(FrameHint hint) {
@@ -2139,7 +2131,7 @@
             const int32_t hotplugErrorCode = static_cast<int32_t>(-timestamp);
             ALOGD("SurfaceFlinger got hotplugErrorCode=%d for display %" PRIu64, hotplugErrorCode,
                   hwcDisplayId);
-            mScheduler->onHotplugConnectionError(mAppConnectionHandle, hotplugErrorCode);
+            mScheduler->onHotplugConnectionError(scheduler::Cycle::Render, hotplugErrorCode);
             return;
         }
 
@@ -2190,7 +2182,7 @@
     if (FlagManager::getInstance().hotplug2()) {
         ALOGD("SurfaceFlinger got hotplug event=%d", static_cast<int32_t>(event));
         // TODO(b/311403559): use enum type instead of int
-        mScheduler->onHotplugConnectionError(mAppConnectionHandle, static_cast<int32_t>(event));
+        mScheduler->onHotplugConnectionError(scheduler::Cycle::Render, static_cast<int32_t>(event));
     }
 }
 
@@ -3484,7 +3476,7 @@
     if (!activeMode) {
         ALOGE("Failed to hotplug display %s", to_string(displayId).c_str());
         if (FlagManager::getInstance().hotplug2()) {
-            mScheduler->onHotplugConnectionError(mAppConnectionHandle,
+            mScheduler->onHotplugConnectionError(scheduler::Cycle::Render,
                                                  static_cast<int32_t>(
                                                          DisplayHotplugEvent::ERROR_UNKNOWN));
         }
@@ -3537,8 +3529,8 @@
 }
 
 void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected);
-    mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
+    mScheduler->onHotplugReceived(scheduler::Cycle::Render, displayId, connected);
+    mScheduler->onHotplugReceived(scheduler::Cycle::LastComposite, displayId, connected);
 }
 
 void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId,
@@ -3548,7 +3540,7 @@
             ? &scheduler::Scheduler::onPrimaryDisplayModeChanged
             : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged;
 
-    ((*mScheduler).*onDisplayModeChanged)(mAppConnectionHandle, mode);
+    ((*mScheduler).*onDisplayModeChanged)(scheduler::Cycle::Render, mode);
 }
 
 sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
@@ -3575,10 +3567,10 @@
         const auto enableFrameRateOverride = sysprop::enable_frame_rate_override(true)
                 ? Config::FrameRateOverride::Enabled
                 : Config::FrameRateOverride::Disabled;
-        Config config =
+        const Config config =
                 {.enableFrameRateOverride = enableFrameRateOverride,
                  .frameRateMultipleThreshold =
-                         base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
+                         base::GetIntProperty("debug.sf.frame_rate_multiple_threshold"s, 0),
                  .idleTimerTimeout = idleTimerTimeoutMs,
                  .kernelIdleTimerController = kernelIdleTimerController};
 
@@ -4200,7 +4192,7 @@
         return getDefaultDisplayDeviceLocked()->getPhysicalId();
     }();
 
-    mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+    mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
 }
 
 void SurfaceFlinger::notifyCpuLoadUp() {
@@ -4396,24 +4388,22 @@
     mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
                                              static_cast<ISchedulerCallback&>(*this), features,
                                              getFactory(), activeRefreshRate, *mTimeStats);
+
+    // The pacesetter must be registered before EventThread creation below.
     mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
     if (FlagManager::getInstance().vrr_config()) {
         mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps);
     }
-    mScheduler->startTimers();
 
     const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs();
 
-    mAppConnectionHandle =
-            mScheduler->createEventThread(Scheduler::Cycle::Render,
-                                          mFrameTimeline->getTokenManager(),
-                                          /* workDuration */ configs.late.appWorkDuration,
-                                          /* readyDuration */ configs.late.sfWorkDuration);
-    mSfConnectionHandle =
-            mScheduler->createEventThread(Scheduler::Cycle::LastComposite,
-                                          mFrameTimeline->getTokenManager(),
-                                          /* workDuration */ activeRefreshRate.getPeriod(),
-                                          /* readyDuration */ configs.late.sfWorkDuration);
+    mScheduler->createEventThread(scheduler::Cycle::Render, mFrameTimeline->getTokenManager(),
+                                  /* workDuration */ configs.late.appWorkDuration,
+                                  /* readyDuration */ configs.late.sfWorkDuration);
+    mScheduler->createEventThread(scheduler::Cycle::LastComposite,
+                                  mFrameTimeline->getTokenManager(),
+                                  /* workDuration */ activeRefreshRate.getPeriod(),
+                                  /* readyDuration */ configs.late.sfWorkDuration);
 
     mScheduler->initVsync(*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
 
@@ -4421,6 +4411,9 @@
             sp<RegionSamplingThread>::make(*this,
                                            RegionSamplingThread::EnvironmentTimingTunables());
     mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline);
+
+    // Timer callbacks may fire, so do this last.
+    mScheduler->startTimers();
 }
 
 void SurfaceFlinger::doCommitTransactions() {
@@ -6362,7 +6355,7 @@
 }
 
 void SurfaceFlinger::dumpEvents(std::string& result) const {
-    mScheduler->dump(mAppConnectionHandle, result);
+    mScheduler->dump(scheduler::Cycle::Render, result);
 }
 
 void SurfaceFlinger::dumpVsync(std::string& result) const {
@@ -7134,14 +7127,15 @@
                 mForceFullDamage = n != 0;
                 return NO_ERROR;
             }
-            case 1018: { // Modify Choreographer's duration
+            case 1018: { // Set the render deadline as a duration until VSYNC.
                 n = data.readInt32();
-                mScheduler->setDuration(mAppConnectionHandle, std::chrono::nanoseconds(n), 0ns);
+                mScheduler->setDuration(scheduler::Cycle::Render, std::chrono::nanoseconds(n), 0ns);
                 return NO_ERROR;
             }
-            case 1019: { // Modify SurfaceFlinger's duration
+            case 1019: { // Set the deadline of the last composite as a duration until VSYNC.
                 n = data.readInt32();
-                mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns);
+                mScheduler->setDuration(scheduler::Cycle::LastComposite,
+                                        std::chrono::nanoseconds(n), 0ns);
                 return NO_ERROR;
             }
             case 1020: { // Unused
@@ -7373,7 +7367,7 @@
                 auto inUid = static_cast<uid_t>(data.readInt32());
                 const auto refreshRate = data.readFloat();
                 mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
-                mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+                mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
                 return NO_ERROR;
             }
             // Toggle caching feature
@@ -7731,7 +7725,7 @@
 
 status_t SurfaceFlinger::setSchedAttr(bool enabled) {
     static const unsigned int kUclampMin =
-            base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min", 0U);
+            base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min"s, 0U);
 
     if (!kUclampMin) {
         // uclamp.min set to 0 (default), skip setting
@@ -8513,10 +8507,10 @@
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
     if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) {
-        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
+        mScheduler->onPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode);
         toggleKernelIdleTimer();
     } else {
-        mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
+        mScheduler->onNonPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode);
     }
 
     auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
@@ -8704,7 +8698,7 @@
     }();
 
     mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
-    mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+    mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
     return NO_ERROR;
 }
 
@@ -8970,7 +8964,8 @@
             Mutex::Autolock lock(mStateLock);
             display->setSecure(connectedLevel >= 2 /* HDCP_V1 */);
         }
-        mScheduler->onHdcpLevelsChanged(mAppConnectionHandle, displayId, connectedLevel, maxLevel);
+        mScheduler->onHdcpLevelsChanged(scheduler::Cycle::Render, displayId, connectedLevel,
+                                        maxLevel);
     }));
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ce9fa15..bada829 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1356,12 +1356,7 @@
 
     const std::string mHwcServiceName;
 
-    /*
-     * Scheduler
-     */
     std::unique_ptr<scheduler::Scheduler> mScheduler;
-    scheduler::ConnectionHandle mAppConnectionHandle;
-    scheduler::ConnectionHandle mSfConnectionHandle;
 
     scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
 
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index c3141be..a631074 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_defaults {
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
index 0cf086f..be65510 100644
--- a/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_library {
@@ -33,4 +34,4 @@
         "-Wno-undef",
         "-Wno-unused-parameter",
     ],
-}
\ No newline at end of file
+}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index 972edaa..e097da3 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_library {
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index 2ff09c3..8afca41 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_binary {
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index e125bbe..f2ff00b 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_defaults {
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index 33bfe8a..ae502cf 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -22,6 +22,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_defaults {
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index a4dc8a0..f77b137 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_library {
diff --git a/services/surfaceflinger/sysprop/Android.bp b/services/surfaceflinger/sysprop/Android.bp
index f579119..4ea00cc 100644
--- a/services/surfaceflinger/sysprop/Android.bp
+++ b/services/surfaceflinger/sysprop/Android.bp
@@ -5,6 +5,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 sysprop_library {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 5449aeb..dab0a3f 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
index aeceadb..bce1406 100644
--- a/services/surfaceflinger/tests/tracing/Android.bp
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_test {
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index da4e47f..f529f7c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 filegroup {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index b059525..10e2220 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -99,7 +99,6 @@
     TestableScheduler* mScheduler = new TestableScheduler{mSelector, mFlinger, mSchedulerCallback};
     surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder;
 
-    ConnectionHandle mConnectionHandle;
     MockEventThread* mEventThread;
     sp<MockEventThreadConnection> mEventThreadConnection;
 };
@@ -116,54 +115,13 @@
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
             .WillRepeatedly(Return(mEventThreadConnection));
 
-    mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
-    EXPECT_TRUE(mConnectionHandle);
+    mScheduler->setEventThread(Cycle::Render, std::move(eventThread));
 
     mFlinger.resetScheduler(mScheduler);
 }
 
 } // namespace
 
-TEST_F(SchedulerTest, invalidConnectionHandle) {
-    ConnectionHandle handle;
-
-    const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
-
-    EXPECT_FALSE(connection);
-    EXPECT_FALSE(mScheduler->getEventConnection(handle));
-
-    // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
-    EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
-    mScheduler->onHotplugReceived(handle, kDisplayId1, false);
-
-    std::string output;
-    EXPECT_CALL(*mEventThread, dump(_)).Times(0);
-    mScheduler->dump(handle, output);
-    EXPECT_TRUE(output.empty());
-
-    EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0);
-    mScheduler->setDuration(handle, 10ns, 20ns);
-}
-
-TEST_F(SchedulerTest, validConnectionHandle) {
-    const sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle);
-
-    ASSERT_EQ(mEventThreadConnection, connection);
-    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
-
-    EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1);
-    mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false);
-
-    std::string output("dump");
-    EXPECT_CALL(*mEventThread, dump(output)).Times(1);
-    mScheduler->dump(mConnectionHandle, output);
-    EXPECT_FALSE(output.empty());
-
-    EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
-    mScheduler->setDuration(mConnectionHandle, 10ns, 20ns);
-}
-
 TEST_F(SchedulerTest, registerDisplay) FTL_FAKE_GUARD(kMainThreadContext) {
     // Hardware VSYNC should not change if the display is already registered.
     EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId1, false)).Times(0);
@@ -235,22 +193,6 @@
     EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
 }
 
-TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
-    const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
-                              .setId(DisplayModeId(111))
-                              .setPhysicalDisplayId(kDisplayId1)
-                              .setVsyncPeriod(111111)
-                              .build();
-
-    // If the handle is incorrect, the function should return before
-    // onModeChange is called.
-    ConnectionHandle invalidHandle = {.id = 123};
-    EXPECT_NO_FATAL_FAILURE(
-            mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
-                                                       {90_Hz, ftl::as_non_null(mode)}));
-    EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
-}
-
 TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
     EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms));
     EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms));
@@ -753,7 +695,7 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
     const sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle());
 
     EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size());
     ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
@@ -782,9 +724,9 @@
             .WillOnce(Return(mockConnection2));
 
     const sp<IDisplayEventConnection> connection1 =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle);
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, handle);
     const sp<IDisplayEventConnection> connection2 =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, handle);
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, handle);
 
     EXPECT_EQ(1u, mScheduler->mutableAttachedChoreographers().size());
     ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
@@ -802,9 +744,9 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached).Times(2);
     const sp<IDisplayEventConnection> connection1 =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer1->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer1->getHandle());
     const sp<IDisplayEventConnection> connection2 =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer2->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer2->getHandle());
 
     EXPECT_EQ(2u, mScheduler->mutableAttachedChoreographers().size());
 
@@ -831,7 +773,7 @@
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
 
     sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle());
 
     ASSERT_EQ(1u, mScheduler->mutableAttachedChoreographers().count(layer->getSequence()));
     EXPECT_EQ(1u,
@@ -861,7 +803,7 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
     const sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle());
 
     layer.clear();
     mFlinger.mutableLayersPendingRemoval().clear();
@@ -875,7 +817,7 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
     sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle());
 
     RequestedLayerState layerState(LayerCreationArgs(layer->getSequence()));
     LayerHierarchy hierarchy(&layerState);
@@ -935,7 +877,7 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
     sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle());
 
     RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
     LayerHierarchy parentHierarchy(&parentState);
@@ -962,7 +904,7 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
     sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle());
 
     RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
     LayerHierarchy parentHierarchy(&parentState);
@@ -997,7 +939,7 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
     sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, parent->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, parent->getHandle());
 
     RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
     LayerHierarchy parentHierarchy(&parentState);
@@ -1031,7 +973,7 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
     sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle());
 
     RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
     LayerHierarchy parentHierarchy(&parentState);
@@ -1057,7 +999,7 @@
 
     EXPECT_CALL(mSchedulerCallback, onChoreographerAttached);
     sp<IDisplayEventConnection> connection =
-            mScheduler->createDisplayEventConnection(mConnectionHandle, {}, layer->getHandle());
+            mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle());
 
     RequestedLayerState parentState(LayerCreationArgs(parent->getSequence()));
     LayerHierarchy parentHierarchy(&parentState);
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 25a85df..1472ebf 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -71,9 +71,14 @@
         Scheduler::onFrameSignal(compositor, vsyncId, TimePoint());
     }
 
-    // Used to inject mock event thread.
-    ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
-        return Scheduler::createConnection(std::move(eventThread));
+    void setEventThread(Cycle cycle, std::unique_ptr<EventThread> eventThreadPtr) {
+        if (cycle == Cycle::Render) {
+            mRenderEventThread = std::move(eventThreadPtr);
+            mRenderEventConnection = mRenderEventThread->createEventConnection();
+        } else {
+            mLastCompositeEventThread = std::move(eventThreadPtr);
+            mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection();
+        }
     }
 
     auto refreshRateSelector() { return pacesetterSelectorPtr(); }
@@ -124,7 +129,6 @@
 
     using Scheduler::resyncAllToHardwareVsync;
 
-    auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
     auto& mutableLayerHistory() { return mLayerHistory; }
     auto& mutableAttachedChoreographers() { return mAttachedChoreographers; }
 
@@ -180,10 +184,6 @@
         mPolicy.cachedModeChangedParams.reset();
     }
 
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
-        Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
-    }
-
     void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) {
         auto schedule = getVsyncSchedule(id);
         std::lock_guard<std::mutex> lock(schedule->mHwVsyncLock);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 46a079c..bce7729 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -258,11 +258,9 @@
 
         mScheduler->initVsync(*mTokenManager, 0ms);
 
-        mScheduler->mutableAppConnectionHandle() =
-                mScheduler->createConnection(std::move(appEventThread));
+        mScheduler->setEventThread(scheduler::Cycle::Render, std::move(appEventThread));
+        mScheduler->setEventThread(scheduler::Cycle::LastComposite, std::move(sfEventThread));
 
-        mFlinger->mAppConnectionHandle = mScheduler->mutableAppConnectionHandle();
-        mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
         resetScheduler(mScheduler);
     }
 
diff --git a/services/surfaceflinger/tests/vsync/Android.bp b/services/surfaceflinger/tests/vsync/Android.bp
index bae9796..c7daa88 100644
--- a/services/surfaceflinger/tests/vsync/Android.bp
+++ b/services/surfaceflinger/tests/vsync/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_binary {
@@ -33,6 +34,6 @@
         "libgui",
         "libui",
         "libutils",
-    ]
+    ],
 
 }
diff --git a/services/surfaceflinger/tests/waitforvsync/Android.bp b/services/surfaceflinger/tests/waitforvsync/Android.bp
index ffed4d7..734fc20 100644
--- a/services/surfaceflinger/tests/waitforvsync/Android.bp
+++ b/services/surfaceflinger/tests/waitforvsync/Android.bp
@@ -19,6 +19,7 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     default_applicable_licenses: ["frameworks_native_license"],
+    default_team: "trendy_team_android_core_graphics_stack",
 }
 
 cc_binary {
@@ -31,5 +32,5 @@
     ],
     shared_libs: [
         "libcutils",
-    ]
+    ],
 }