Merge "Revert " Remove TransferDeathRecipients when ABpBinder is deleted"" into main
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/data/etc/android.software.contextualsearch.xml b/data/etc/android.software.contextualsearch.xml
new file mode 100644
index 0000000..4564ac8
--- /dev/null
+++ b/data/etc/android.software.contextualsearch.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<!-- Feature for devices supporting contextual search helper. -->
+<permissions>
+    <feature name="android.software.contextualsearch" />
+</permissions>
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/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/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
deleted file mode 100644
index cd738ac..0000000
--- a/libs/gui/fuzzer/Android.bp
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_defaults {
-    name: "libgui_fuzzer_defaults",
-    defaults: ["android.hardware.power-ndk_shared"],
-    static_libs: [
-        "android.hidl.token@1.0-utils",
-        "libbinder_random_parcel",
-        "libgui_aidl_static",
-        "libgui_window_info_static",
-        "libpdx",
-        "libgmock",
-        "libgui_mocks",
-        "libgmock_ndk",
-        "libgmock_main",
-        "libgtest_ndk_c++",
-        "libgmock_main_ndk",
-        "librenderengine_mocks",
-        "perfetto_trace_protos",
-        "libcompositionengine_mocks",
-        "perfetto_trace_protos",
-    ],
-    shared_libs: [
-        "android.hardware.configstore@1.0",
-        "android.hardware.configstore-utils",
-        "android.hardware.graphics.bufferqueue@1.0",
-        "android.hardware.graphics.bufferqueue@2.0",
-        "android.hidl.token@1.0",
-        "libSurfaceFlingerProp",
-        "libgui",
-        "libbase",
-        "liblog",
-        "libEGL",
-        "libGLESv2",
-        "libbinder",
-        "libcutils",
-        "libhidlbase",
-        "libinput",
-        "libui",
-        "libutils",
-        "libnativewindow",
-        "libvndksupport",
-    ],
-    header_libs: [
-        "libdvr_headers",
-        "libui_fuzzableDataspaces_headers",
-    ],
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-        hotlists: [
-            "4593311",
-        ],
-        description: "The fuzzer targets the APIs of libgui library",
-        vector: "local_no_privileges_required",
-        service_privilege: "privileged",
-        users: "multi_user",
-        fuzzed_code_usage: "shipped",
-    },
-}
-
-cc_fuzz {
-    name: "libgui_surfaceComposer_fuzzer",
-    srcs: [
-        "libgui_surfaceComposer_fuzzer.cpp",
-    ],
-    defaults: [
-        "libgui_fuzzer_defaults",
-        "service_fuzzer_defaults",
-    ],
-}
-
-cc_fuzz {
-    name: "libgui_surfaceComposerClient_fuzzer",
-    srcs: [
-        "libgui_surfaceComposerClient_fuzzer.cpp",
-    ],
-    defaults: [
-        "libgui_fuzzer_defaults",
-        "service_fuzzer_defaults",
-    ],
-}
-
-cc_fuzz {
-    name: "libgui_parcelable_fuzzer",
-    srcs: [
-        "libgui_parcelable_fuzzer.cpp",
-    ],
-    defaults: [
-        "libgui_fuzzer_defaults",
-    ],
-}
-
-cc_fuzz {
-    name: "libgui_bufferQueue_fuzzer",
-    srcs: [
-        "libgui_bufferQueue_fuzzer.cpp",
-    ],
-    defaults: [
-        "libgui_fuzzer_defaults",
-    ],
-}
-
-cc_fuzz {
-    name: "libgui_consumer_fuzzer",
-    srcs: [
-        "libgui_consumer_fuzzer.cpp",
-    ],
-    defaults: [
-        "libgui_fuzzer_defaults",
-    ],
-}
-
-cc_fuzz {
-    name: "libgui_displayEvent_fuzzer",
-    srcs: [
-        "libgui_displayEvent_fuzzer.cpp",
-    ],
-    defaults: [
-        "libgui_fuzzer_defaults",
-    ],
-}
diff --git a/libs/gui/fuzzer/README.md b/libs/gui/fuzzer/README.md
deleted file mode 100644
index 96e27c9..0000000
--- a/libs/gui/fuzzer/README.md
+++ /dev/null
@@ -1,219 +0,0 @@
-# Fuzzers for Libgui
-
-## Table of contents
-+ [libgui_surfaceComposer_fuzzer](#SurfaceComposer)
-+ [libgui_surfaceComposerClient_fuzzer](#SurfaceComposerClient)
-+ [libgui_parcelable_fuzzer](#Libgui_Parcelable)
-+ [libgui_bufferQueue_fuzzer](#BufferQueue)
-+ [libgui_consumer_fuzzer](#Libgui_Consumer)
-+ [libgui_displayEvent_fuzzer](#LibGui_DisplayEvent)
-
-# <a name="libgui_surfaceComposer_fuzzer"></a> Fuzzer for SurfaceComposer
-
-SurfaceComposer supports the following parameters:
-1. SurfaceWidth (parameter name:`width`)
-2. SurfaceHeight (parameter name:`height`)
-3. TransactionStateFlags (parameter name:`flags`)
-4. TransformHint (parameter name:`outTransformHint`)
-5. SurfacePixelFormat (parameter name:`format`)
-6. LayerId (parameter name:`outLayerId`)
-7. SurfaceComposerTags (parameter name:`surfaceTag`)
-8. PowerBoostID (parameter name:`boostId`)
-9. VsyncSource (parameter name:`vsyncSource`)
-10. EventRegistrationFlags (parameter name:`eventRegistration`)
-11. FrameRateCompatibility (parameter name:`frameRateCompatibility`)
-12. ChangeFrameRateStrategy (parameter name:`changeFrameRateStrategy`)
-13. HdrTypes (parameter name:`hdrTypes`)
-
-| Parameter| Valid Values| Configured Value|
-|------------- |-------------| ----- |
-|`surfaceTag` | 0.`BnSurfaceComposer::BOOT_FINISHED`, 1.`BnSurfaceComposer::CREATE_CONNECTION`, 2.`BnSurfaceComposer::GET_STATIC_DISPLAY_INFO`, 3.`BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION`, 4.`BnSurfaceComposer::CREATE_DISPLAY`, 5.`BnSurfaceComposer::DESTROY_DISPLAY`, 6.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN`, 7.`BnSurfaceComposer::SET_TRANSACTION_STATE`, 8.`BnSurfaceComposer::AUTHENTICATE_SURFACE`, 9.`BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS`, 10.`BnSurfaceComposer::GET_DISPLAY_STATE`, 11.`BnSurfaceComposer::CAPTURE_DISPLAY`, 12.`BnSurfaceComposer::CAPTURE_LAYERS`, 13.`BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS`, 14.`BnSurfaceComposer::GET_ANIMATION_FRAME_STATS`, 15.`BnSurfaceComposer::SET_POWER_MODE`, 16.`BnSurfaceComposer::GET_DISPLAY_STATS`, 17.`BnSurfaceComposer::SET_ACTIVE_COLOR_MODE`, 18.`BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS`, 19.`BnSurfaceComposer::INJECT_VSYNC`, 20.`BnSurfaceComposer::GET_LAYER_DEBUG_INFO`, 21.`BnSurfaceComposer::GET_COMPOSITION_PREFERENCE`, 22.`BnSurfaceComposer::GET_COLOR_MANAGEMENT`, 23.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES`, 24.`BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED`, 25.`BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE`, 26.`BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT`, 27.`BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY`, 28.`BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES`, 29.`BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS`, 30.`BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER`, 31.`BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER`, 32.`BnSurfaceComposer::SET_DESIRED_DISPLAY_MODE_SPECS`, 33.`BnSurfaceComposer::GET_DESIRED_DISPLAY_MODE_SPECS`, 34.`BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT`, 35.`BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS`, 36.`BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID`, 37.`BnSurfaceComposer::NOTIFY_POWER_BOOST`, 38.`BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS`, 39.`BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE`, 40.`BnSurfaceComposer::SET_GAME_CONTENT_TYPE`, 41.`BnSurfaceComposer::SET_FRAME_RATE`, 42.`BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN`, 43.`BnSurfaceComposer::SET_FRAME_TIMELINE_INFO`, 44.`BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER`, 45.`BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY`, 46.`BnSurfaceComposer::GET_MAX_ACQUIRED_BUFFER_COUNT`, 47.`BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO`, 48.`BnSurfaceComposer::ADD_FPS_LISTENER`, 49.`BnSurfaceComposer::REMOVE_FPS_LISTENER`, 50.`BnSurfaceComposer::OVERRIDE_HDR_TYPES`, 51.`BnSurfaceComposer::ADD_HDR_LAYER_INFO_LISTENER`, 52.`BnSurfaceComposer::REMOVE_HDR_LAYER_INFO_LISTENER`, 53.`BnSurfaceComposer::ON_PULL_ATOM`, 54.`BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER`, 55.`BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER` | Value obtained from FuzzedDataProvider|
-|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider|
-|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider|
-|`eventRegistration`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride` |Value obtained from FuzzedDataProvider|
-|`frameRateCompatibility`| 0.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT`, 1.`ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE` |Value obtained from FuzzedDataProvider|
-|`changeFrameRateStrategy`| 0.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS`, 1.`ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS` |Value obtained from FuzzedDataProvider|
-|`hdrTypes`| 0.`ui::Hdr::DOLBY_VISION`, 1.`ui::Hdr::HDR10`, 2.`ui::Hdr::HLG`, 3.`ui::Hdr::HDR10_PLUS` |Value obtained from FuzzedDataProvider|
-
-#### Steps to run
-1. Build the fuzzer
-```
-  $ mm -j$(nproc) libgui_surfaceComposer_fuzzer
-```
-2. Run on device
-```
-  $ adb sync data
-  $ adb shell /data/fuzz/arm64/libgui_surfaceComposer_fuzzer/libgui_surfaceComposer_fuzzer
-```
-
-# <a name="libgui_surfaceComposerClient_fuzzer"></a> Fuzzer for SurfaceComposerClient
-
-SurfaceComposerClient supports the following data sources:
-1. SurfaceWidth (parameter name:`width`)
-2. SurfaceHeight (parameter name:`height`)
-3. TransactionStateFlags (parameter name:`flags`)
-4. TransformHint (parameter name:`outTransformHint`)
-5. SurfacePixelFormat (parameter name:`format`)
-6. LayerId (parameter name:`outLayerId`)
-7. SurfaceComposerClientTags (parameter name:`surfaceTag`)
-8. DefaultMode (parameter name:`defaultMode`)
-9. PrimaryRefreshRateMin (parameter name:`primaryRefreshRateMin`)
-10. PrimaryRefreshRateMax (parameter name:`primaryRefreshRateMax`)
-11. AppRefreshRateMin (parameter name:`appRefreshRateMin`)
-12. AppRefreshRateMax (parameter name:`appRefreshRateMax`)
-13. DisplayPowerMode (parameter name:`mode`)
-14. CacheId (parameter name:`cacheId`)
-15. DisplayBrightness (parameter name:`brightness`)
-16. PowerBoostID (parameter name:`boostId`)
-17. AtomId (parameter name:`atomId`)
-18. ComponentMask (parameter name:`componentMask`)
-19. MaxFrames (parameter name:`maxFrames`)
-20. TaskId (parameter name:`taskId`)
-21. Alpha (parameter name:`aplha`)
-22. CornerRadius (parameter name:`cornerRadius`)
-23. BackgroundBlurRadius (parameter name:`backgroundBlurRadius`)
-24. Half3Color (parameter name:`color`)
-25. LayerStack (parameter name:`layerStack`)
-26. Dataspace (parameter name:`dataspace`)
-27. Api (parameter name:`api`)
-28. Priority (parameter name:`priority`)
-29. TouchableRegionPointX (parameter name:`pointX`)
-30. TouchableRegionPointY (parameter name:`pointY`)
-31. ColorMode (parameter name:`colorMode`)
-32. WindowInfoFlags (parameter name:`flags`)
-33. WindowInfoTransformOrientation (parameter name:`transform`)
-
-| Parameter| Valid Values| Configured Value|
-|------------- |-------------| ----- |
-|`surfaceTag`| 0.`Tag::CREATE_SURFACE`, 1.`Tag::CREATE_WITH_SURFACE_PARENT`, 2.`Tag::CLEAR_LAYER_FRAME_STATS`, 3.`Tag::GET_LAYER_FRAME_STATS`, 4.`Tag::MIRROR_SURFACE`, 5.`Tag::LAST` |Value obtained from FuzzedDataProvider|
-|`mode`| 0.`gui::TouchOcclusionMode::BLOCK_UNTRUSTED`, 1.`gui::TouchOcclusionMode::USE_OPACITY`, 2.`gui::TouchOcclusionMode::ALLOW` |Value obtained from FuzzedDataProvider|
-|`boostId`| 0.`hardware::power::Boost::INTERACTION`, 1.`hardware::power::Boost::DISPLAY_UPDATE_IMMINENT`, 2.`hardware::power::Boost::ML_ACC`, 3.`hardware::power::Boost::AUDIO_LAUNCH`, 4.`hardware::power::Boost::CAMERA_LAUNCH`, 5.`hardware::power::Boost::CAMERA_SHOT` |Value obtained from FuzzedDataProvider|
-|`colorMode`|0.`ui::ColorMode::NATIVE`, 1.`ui::ColorMode::STANDARD_BT601_625`, 2.`ui::ColorMode::STANDARD_BT601_625_UNADJUSTED`, 3.`ui::ColorMode::STANDARD_BT601_525`, 4.`ui::ColorMode::STANDARD_BT601_525_UNADJUSTED`, 5.`ui::ColorMode::STANDARD_BT709`, 6.`ui::ColorMode::DCI_P3`, 7.`ui::ColorMode::SRGB`, 8.`ui::ColorMode::ADOBE_RGB`, 9.`ui::ColorMode::DISPLAY_P3`, 10.`ui::ColorMode::BT2020`, 11.`ui::ColorMode::BT2100_PQ`, 12.`ui::ColorMode::BT2100_HLG`, 13.`ui::ColorMode::DISPLAY_BT2020` |Value obtained from FuzzedDataProvider|
-|`flags`|0 .`gui::WindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON`, 1.`gui::WindowInfo::Flag::DIM_BEHIND`, 2.`gui::WindowInfo::Flag::BLUR_BEHIND`, 3.`gui::WindowInfo::Flag::NOT_FOCUSABLE`, 4.`gui::WindowInfo::Flag::NOT_TOUCHABLE`, 5.`gui::WindowInfo::Flag::NOT_TOUCH_MODAL`, 6.`gui::WindowInfo::Flag::TOUCHABLE_WHEN_WAKING`, 7.`gui::WindowInfo::Flag::KEEP_SCREEN_ON`, 8.`gui::WindowInfo::Flag::LAYOUT_IN_SCREEN`, 9.`gui::WindowInfo::Flag::LAYOUT_NO_LIMITS`, 10.`gui::WindowInfo::Flag::FULLSCREEN`, 11.`gui::WindowInfo::Flag::FORCE_NOT_FULLSCREEN`, 12.`gui::WindowInfo::Flag::DITHER`, 13.`gui::WindowInfo::Flag::SECURE`, 14.`gui::WindowInfo::Flag::SCALED`, 15.`gui::WindowInfo::Flag::IGNORE_CHEEK_PRESSES`, 16.`gui::WindowInfo::Flag::LAYOUT_INSET_DECOR`, 17.`gui::WindowInfo::Flag::ALT_FOCUSABLE_IM`, 18.`gui::WindowInfo::Flag::WATCH_OUTSIDE_TOUCH`, 19.`gui::WindowInfo::Flag::SHOW_WHEN_LOCKED`, 20.`gui::WindowInfo::Flag::SHOW_WALLPAPER`, 21.`gui::WindowInfo::Flag::TURN_SCREEN_ON`, 22.`gui::WindowInfo::Flag::DISMISS_KEYGUARD`, 23.`gui::WindowInfo::Flag::SPLIT_TOUCH`, 24.`gui::WindowInfo::Flag::HARDWARE_ACCELERATED`, 25.`gui::WindowInfo::Flag::LAYOUT_IN_OVERSCAN`, 26.`gui::WindowInfo::Flag::TRANSLUCENT_STATUS`, 27.`gui::WindowInfo::Flag::TRANSLUCENT_NAVIGATION`, 28.`gui::WindowInfo::Flag::LOCAL_FOCUS_MODE`, 29.`gui::WindowInfo::Flag::SLIPPERY`, 30.`gui::WindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR`, 31.`gui::WindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS`, |Value obtained from FuzzedDataProvider|
-|`dataspace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider|
-|`transform`| 0.`ui::Transform::ROT_0`, 1.`ui::Transform::FLIP_H`, 2.`ui::Transform::FLIP_V`, 3.`ui::Transform::ROT_90`, 4.`ui::Transform::ROT_180`, 5.`ui::Transform::ROT_270` |Value obtained from FuzzedDataProvider|
-
-#### Steps to run
-1. Build the fuzzer
-```
-  $ mm -j$(nproc) libgui_surfaceComposerClient_fuzzer
-```
-2. To run on device
-```
-  $ adb sync data
-  $ adb shell /data/fuzz/arm64/libgui_surfaceComposerClient_fuzzer/libgui_surfaceComposerClient_fuzzer
-```
-
-# <a name="libgui_parcelable_fuzzer"></a> Fuzzer for Libgui_Parcelable
-
-Libgui_Parcelable supports the following parameters:
-1. LayerMetadataKey (parameter name:`key`)
-2. Dataspace (parameter name:`mDataspace`)
-
-| Parameter| Valid Values| Configured Value|
-|------------- |-------------| ----- |
-|`key`| 0.`view::LayerMetadataKey::METADATA_OWNER_UID`, 1.`view::LayerMetadataKey::METADATA_WINDOW_TYPE`, 2.`view::LayerMetadataKey::METADATA_TASK_ID`, 3.`view::LayerMetadataKey::METADATA_MOUSE_CURSOR`, 4.`view::LayerMetadataKey::METADATA_ACCESSIBILITY_ID`, 5.`view::LayerMetadataKey::METADATA_OWNER_PID`, 6.`view::LayerMetadataKey::METADATA_DEQUEUE_TIME`, 7.`view::LayerMetadataKey::METADATA_GAME_MODE`, |Value obtained from FuzzedDataProvider|
-|`mDataSpace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider|
-
-#### Steps to run
-1. Build the fuzzer
-```
-  $ mm -j$(nproc) libgui_fuzzer
-```
-2. Run on device
-```
-  $ adb sync data
-  $ adb shell /data/fuzz/arm64/libgui_fuzzer/libgui_fuzzer
-```
-
-# <a name="libgui_bufferQueue_fuzzer"></a> Fuzzer for BufferQueue
-
-BufferQueue supports the following parameters:
-1. SurfaceWidth (parameter name:`width`)
-2. SurfaceHeight (parameter name:`height`)
-3. TransactionStateFlags (parameter name:`flags`)
-4. TransformHint (parameter name:`outTransformHint`)
-5. SurfacePixelFormat (parameter name:`format`)
-6. LayerId (parameter name:`layerId`)
-7. BufferId (parameter name:`bufferId`)
-8. FrameNumber (parameter name:`frameNumber`)
-9. FrameRate (parameter name:`frameRate`)
-10. Compatability (parameter name:`compatability`)
-11. LatchTime (parameter name:`latchTime`)
-12. AcquireTime (parameter name:`acquireTime`)
-13. RefreshTime (parameter name:`refreshTime`)
-14. DequeueTime (parameter name:`dequeueTime`)
-15. Slot (parameter name:`slot`)
-16. MaxBuffers (parameter name:`maxBuffers`)
-17. GenerationNumber (parameter name:`generationNumber`)
-18. Api (parameter name:`api`)
-19. Usage (parameter name:`usage`)
-20. MaxFrameNumber (parameter name:`maxFrameNumber`)
-21. BufferCount (parameter name:`bufferCount`)
-22. MaxAcquredBufferCount (parameter name:`maxAcquredBufferCount`)
-23. Status (parameter name:`status`)
-24. ApiConnection (parameter name:`apiConnection`)
-25. Dataspace (parameter name:`dataspace`)
-
-| Parameter| Valid Values| Configured Value|
-|------------- |-------------| ----- |
-|`status`| 0.`OK`, 1.`NO_MEMORY`, 2.`NO_INIT`, 3.`BAD_VALUE`, 4.`DEAD_OBJECT`, 5.`INVALID_OPERATION`, 6.`TIMED_OUT`, 7.`WOULD_BLOCK`, 8.`UNKNOWN_ERROR`, 9.`ALREADY_EXISTS`, |Value obtained from FuzzedDataProvider|
-|`apiConnection`| 0.`BufferQueueCore::CURRENTLY_CONNECTED_API`, 1.`BufferQueueCore::NO_CONNECTED_API`, 2.`NATIVE_WINDOW_API_EGL`, 3.`NATIVE_WINDOW_API_CPU`, 4.`NATIVE_WINDOW_API_MEDIA`, 5.`NATIVE_WINDOW_API_CAMERA`, |Value obtained from FuzzedDataProvider|
-|`dataspace`| 0.`ui::Dataspace::UNKNOWN`, 1.`ui::Dataspace::ARBITRARY`, 2.`ui::Dataspace::STANDARD_SHIFT`, 3.`ui::Dataspace::STANDARD_MASK`, 4.`ui::Dataspace::STANDARD_UNSPECIFIED`, 5.`ui::Dataspace::STANDARD_BT709`, 6.`ui::Dataspace::STANDARD_BT601_625`, 7.`ui::Dataspace::STANDARD_BT601_625_UNADJUSTED`, 8.`ui::Dataspace::STANDARD_BT601_525`, 9.`ui::Dataspace::STANDARD_BT601_525_UNADJUSTED`, 10.`ui::Dataspace::STANDARD_BT2020`, 11.`ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE`, 12.`ui::Dataspace::STANDARD_BT470M`, 13.`ui::Dataspace::STANDARD_FILM`, 14.`ui::Dataspace::STANDARD_DCI_P3`, 15.`ui::Dataspace::STANDARD_ADOBE_RGB`, 16.`ui::Dataspace::TRANSFER_SHIFT`, 17.`ui::Dataspace::TRANSFER_MASK`, 18.`ui::Dataspace::TRANSFER_UNSPECIFIED`, 19.`ui::Dataspace::TRANSFER_LINEAR`, 20.`ui::Dataspace::TRANSFER_SRGB`, 21.`ui::Dataspace::TRANSFER_SMPTE_170M`, 22.`ui::Dataspace::TRANSFER_GAMMA2_2`, 23.`ui::Dataspace::TRANSFER_GAMMA2_6`, 24.`ui::Dataspace::TRANSFER_GAMMA2_8`, 25.`ui::Dataspace::TRANSFER_ST2084`, 26.`ui::Dataspace::TRANSFER_HLG`, 27.`ui::Dataspace::RANGE_SHIFT`, 28.`ui::Dataspace::RANGE_MASK`, 29.`ui::Dataspace::RANGE_UNSPECIFIED`, 30.`ui::Dataspace::RANGE_FULL`, 31.`ui::Dataspace::RANGE_LIMITED`, 32.`ui::Dataspace::RANGE_EXTENDED`, 33.`ui::Dataspace::SRGB_LINEAR`, 34.`ui::Dataspace::V0_SRGB_LINEAR`, 35.`ui::Dataspace::V0_SCRGB_LINEAR`, 36.`ui::Dataspace::SRGB`, 37.`ui::Dataspace::V0_SRGB`, 38.`ui::Dataspace::V0_SCRGB`, 39.`ui::Dataspace::JFIF`, 40.`ui::Dataspace::V0_JFIF`, 41.`ui::Dataspace::BT601_625`, 42.`ui::Dataspace::V0_BT601_625`, 43.`ui::Dataspace::BT601_525`, 44.`ui::Dataspace::V0_BT601_525`, 45.`ui::Dataspace::BT709`, 46.`ui::Dataspace::V0_BT709`, 47.`ui::Dataspace::DCI_P3_LINEAR`, 48.`ui::Dataspace::DCI_P3`, 49.`ui::Dataspace::DISPLAY_P3_LINEAR`, 50.`ui::Dataspace::DISPLAY_P3`, 51.`ui::Dataspace::ADOBE_RGB`, 52.`ui::Dataspace::BT2020_LINEAR`, 53.`ui::Dataspace::BT2020`, 54.`ui::Dataspace::BT2020_PQ`, 55.`ui::Dataspace::DEPTH`, 56.`ui::Dataspace::SENSOR`, 57.`ui::Dataspace::BT2020_ITU`, 58.`ui::Dataspace::BT2020_ITU_PQ`, 59.`ui::Dataspace::BT2020_ITU_HLG`, 60.`ui::Dataspace::BT2020_HLG`, 61.`ui::Dataspace::DISPLAY_BT2020`, 62.`ui::Dataspace::DYNAMIC_DEPTH`, 63.`ui::Dataspace::JPEG_APP_SEGMENTS`, 64.`ui::Dataspace::HEIF`, |Value obtained from FuzzedDataProvider|
-
-#### Steps to run
-1. Build the fuzzer
-```
-  $ mm -j$(nproc) libgui_bufferQueue_fuzzer
-```
-2. To run on device
-```
-  $ adb sync data
-  $ adb shell /data/fuzz/arm64/libgui_bufferQueue_fuzzer/libgui_bufferQueue_fuzzer
-```
-
-# <a name="libgui_consumer_fuzzer"></a> Fuzzer for Libgui_Consumer
-
-Libgui_Consumer supports the following parameters:
-1. GraphicWidth (parameter name:`graphicWidth`)
-2. GraphicHeight (parameter name:`graphicHeight`)
-4. TransformHint (parameter name:`outTransformHint`)
-5. GraphicPixelFormat (parameter name:`format`)
-6. Usage (parameter name:`usage`)
-
-#### Steps to run
-1. Build the fuzzer
-```
-  $ mm -j$(nproc) libgui_consumer_fuzzer
-```
-2. Run on device
-```
-  $ adb sync data
-  $ adb shell /data/fuzz/arm64/libgui_consumer_fuzzer/libgui_consumer_fuzzer
-```
-
-# <a name="libgui_displayEvent_fuzzer"></a> Fuzzer for LibGui_DisplayEvent
-
-LibGui_DisplayEvent supports the following parameters:
-1. DisplayEventType (parameter name:`type`)
-2. Events (parameter name:`events`)
-3. VsyncSource (parameter name:`vsyncSource`)
-4. EventRegistrationFlags (parameter name:`flags`)
-
-| Parameter| Valid Values| Configured Value|
-|------------- |-------------| ----- |
-|`vsyncSource`| 0.`ISurfaceComposer::eVsyncSourceApp`, 1.`ISurfaceComposer::eVsyncSourceSurfaceFlinger`, |Value obtained from FuzzedDataProvider|
-|`flags`| 0.`ISurfaceComposer::EventRegistration::modeChanged`, 1.`ISurfaceComposer::EventRegistration::frameRateOverride`, |Value obtained from FuzzedDataProvider|
-|`type`| 0.`DisplayEventReceiver::DISPLAY_EVENT_NULL`, 1.`DisplayEventReceiver::DISPLAY_EVENT_VSYNC`, 2.`DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG`, 3.`DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE`, 4.`DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE`, 5.`DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH`, |Value obtained from FuzzedDataProvider|
-|`events`| 0.`Looper::EVENT_INPUT`, 1.`Looper::EVENT_OUTPUT`, 2.`Looper::EVENT_ERROR`, 3.`Looper::EVENT_HANGUP`, 4.`Looper::EVENT_INVALID`, |Value obtained from FuzzedDataProvider|
-
-#### Steps to run
-1. Build the fuzzer
-```
-  $ mm -j$(nproc) libgui_displayEvent_fuzzer
-```
-2. Run on device
-```
-  $ adb sync data
-  $ adb shell /data/fuzz/arm64/libgui_displayEvent_fuzzer/libgui_displayEvent_fuzzer
-```
diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
deleted file mode 100644
index 2e270b7..0000000
--- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <android-base/stringprintf.h>
-#include <gui/BufferQueueConsumer.h>
-#include <gui/BufferQueueCore.h>
-#include <gui/BufferQueueProducer.h>
-#include <gui/bufferqueue/2.0/types.h>
-#include <system/window.h>
-
-#include <libgui_fuzzer_utils.h>
-
-using namespace android;
-using namespace hardware::graphics::bufferqueue;
-using namespace V1_0::utils;
-using namespace V2_0::utils;
-
-constexpr int32_t kMaxBytes = 256;
-
-constexpr int32_t kError[] = {
-        OK,        NO_MEMORY,   NO_INIT,       BAD_VALUE,      DEAD_OBJECT, INVALID_OPERATION,
-        TIMED_OUT, WOULD_BLOCK, UNKNOWN_ERROR, ALREADY_EXISTS,
-};
-
-constexpr int32_t kAPIConnection[] = {
-        BufferQueueCore::CURRENTLY_CONNECTED_API,
-        BufferQueueCore::NO_CONNECTED_API,
-        NATIVE_WINDOW_API_EGL,
-        NATIVE_WINDOW_API_CPU,
-        NATIVE_WINDOW_API_MEDIA,
-        NATIVE_WINDOW_API_CAMERA,
-};
-
-class BufferQueueFuzzer {
-public:
-    BufferQueueFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
-    void process();
-
-private:
-    void invokeTypes();
-    void invokeH2BGraphicBufferV1();
-    void invokeH2BGraphicBufferV2();
-    void invokeBufferQueueConsumer();
-    void invokeBufferQueueProducer();
-    void invokeBlastBufferQueue();
-    void invokeQuery(sp<BufferQueueProducer>);
-    void invokeQuery(sp<V1_0::utils::H2BGraphicBufferProducer>);
-    void invokeQuery(sp<V2_0::utils::H2BGraphicBufferProducer>);
-    void invokeAcquireBuffer(sp<BufferQueueConsumer>);
-    void invokeOccupancyTracker(sp<BufferQueueConsumer>);
-    sp<SurfaceControl> makeSurfaceControl();
-    sp<BLASTBufferQueue> makeBLASTBufferQueue(sp<SurfaceControl>);
-
-    FuzzedDataProvider mFdp;
-};
-
-class ManageResourceHandle {
-public:
-    ManageResourceHandle(FuzzedDataProvider* fdp) {
-        mNativeHandle = native_handle_create(0 /*numFds*/, 1 /*numInts*/);
-        mShouldOwn = fdp->ConsumeBool();
-        mStream = NativeHandle::create(mNativeHandle, mShouldOwn);
-    }
-    ~ManageResourceHandle() {
-        if (!mShouldOwn) {
-            native_handle_close(mNativeHandle);
-            native_handle_delete(mNativeHandle);
-        }
-    }
-    sp<NativeHandle> getStream() { return mStream; }
-
-private:
-    bool mShouldOwn;
-    sp<NativeHandle> mStream;
-    native_handle_t* mNativeHandle;
-};
-
-sp<SurfaceControl> BufferQueueFuzzer::makeSurfaceControl() {
-    sp<IBinder> handle;
-    const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient());
-    sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient);
-    sp<BnGraphicBufferProducer> producer;
-    uint32_t layerId = mFdp.ConsumeIntegral<uint32_t>();
-    std::string layerName = base::StringPrintf("#%d", layerId);
-    return sp<SurfaceControl>::make(client, handle, layerId, layerName,
-                                    mFdp.ConsumeIntegral<int32_t>(),
-                                    mFdp.ConsumeIntegral<uint32_t>(),
-                                    mFdp.ConsumeIntegral<int32_t>(),
-                                    mFdp.ConsumeIntegral<uint32_t>(),
-                                    mFdp.ConsumeIntegral<uint32_t>());
-}
-
-sp<BLASTBufferQueue> BufferQueueFuzzer::makeBLASTBufferQueue(sp<SurfaceControl> surface) {
-    return sp<BLASTBufferQueue>::make(mFdp.ConsumeRandomLengthString(kMaxBytes), surface,
-                                      mFdp.ConsumeIntegral<uint32_t>(),
-                                      mFdp.ConsumeIntegral<uint32_t>(),
-                                      mFdp.ConsumeIntegral<int32_t>());
-}
-
-void BufferQueueFuzzer::invokeBlastBufferQueue() {
-    sp<SurfaceControl> surface = makeSurfaceControl();
-    sp<BLASTBufferQueue> queue = makeBLASTBufferQueue(surface);
-
-    BufferItem item;
-    queue->onFrameAvailable(item);
-    queue->onFrameReplaced(item);
-    uint64_t bufferId = mFdp.ConsumeIntegral<uint64_t>();
-    queue->onFrameDequeued(bufferId);
-    queue->onFrameCancelled(bufferId);
-
-    SurfaceComposerClient::Transaction next;
-    uint64_t frameNumber = mFdp.ConsumeIntegral<uint64_t>();
-    queue->mergeWithNextTransaction(&next, frameNumber);
-    queue->applyPendingTransactions(frameNumber);
-
-    queue->update(surface, mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(),
-                  mFdp.ConsumeIntegral<int32_t>());
-    queue->setFrameRate(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeIntegral<int8_t>(),
-                        mFdp.ConsumeBool() /*shouldBeSeamless*/);
-    FrameTimelineInfo info;
-    queue->setFrameTimelineInfo(mFdp.ConsumeIntegral<uint64_t>(), info);
-
-    ManageResourceHandle handle(&mFdp);
-    queue->setSidebandStream(handle.getStream());
-
-    queue->getLastTransformHint();
-    queue->getLastAcquiredFrameNum();
-
-    CompositorTiming compTiming;
-    sp<Fence> previousFence = new Fence(memfd_create("pfd", MFD_ALLOW_SEALING));
-    sp<Fence> gpuFence = new Fence(memfd_create("gfd", MFD_ALLOW_SEALING));
-    FrameEventHistoryStats frameStats(frameNumber, mFdp.ConsumeIntegral<uint64_t>(), gpuFence,
-                                      compTiming, mFdp.ConsumeIntegral<int64_t>(),
-                                      mFdp.ConsumeIntegral<int64_t>());
-    std::vector<SurfaceControlStats> stats;
-    sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING));
-    SurfaceControlStats controlStats(surface, mFdp.ConsumeIntegral<int64_t>(),
-                                     mFdp.ConsumeIntegral<int64_t>(), presentFence, previousFence,
-                                     mFdp.ConsumeIntegral<uint32_t>(), frameStats,
-                                     mFdp.ConsumeIntegral<uint32_t>());
-    stats.push_back(controlStats);
-}
-
-void BufferQueueFuzzer::invokeQuery(sp<BufferQueueProducer> producer) {
-    int32_t value;
-    producer->query(mFdp.ConsumeIntegral<int32_t>(), &value);
-}
-
-void BufferQueueFuzzer::invokeQuery(sp<V1_0::utils::H2BGraphicBufferProducer> producer) {
-    int32_t value;
-    producer->query(mFdp.ConsumeIntegral<int32_t>(), &value);
-}
-
-void BufferQueueFuzzer::invokeQuery(sp<V2_0::utils::H2BGraphicBufferProducer> producer) {
-    int32_t value;
-    producer->query(mFdp.ConsumeIntegral<int32_t>(), &value);
-}
-
-void BufferQueueFuzzer::invokeBufferQueueProducer() {
-    sp<BufferQueueCore> core(new BufferQueueCore());
-    sp<BufferQueueProducer> producer(new BufferQueueProducer(core));
-    const sp<android::IProducerListener> listener;
-    android::IGraphicBufferProducer::QueueBufferOutput output;
-    uint32_t api = mFdp.ConsumeIntegral<uint32_t>();
-    producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output);
-
-    sp<GraphicBuffer> buffer;
-    int32_t slot = mFdp.ConsumeIntegral<int32_t>();
-    uint32_t maxBuffers = mFdp.ConsumeIntegral<uint32_t>();
-    producer->requestBuffer(slot, &buffer);
-    producer->setMaxDequeuedBufferCount(maxBuffers);
-    producer->setAsyncMode(mFdp.ConsumeBool() /*async*/);
-
-    android::IGraphicBufferProducer::QueueBufferInput input;
-    producer->attachBuffer(&slot, buffer);
-    producer->queueBuffer(slot, input, &output);
-
-    int32_t format = mFdp.ConsumeIntegral<int32_t>();
-    uint32_t width = mFdp.ConsumeIntegral<uint32_t>();
-    uint32_t height = mFdp.ConsumeIntegral<uint32_t>();
-    uint64_t usage = mFdp.ConsumeIntegral<uint64_t>();
-    uint64_t outBufferAge;
-    FrameEventHistoryDelta outTimestamps;
-    sp<android::Fence> fence;
-    producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge,
-                            &outTimestamps);
-    producer->detachBuffer(slot);
-    producer->detachNextBuffer(&buffer, &fence);
-    producer->cancelBuffer(slot, fence);
-
-    invokeQuery(producer);
-
-    ManageResourceHandle handle(&mFdp);
-    producer->setSidebandStream(handle.getStream());
-
-    producer->allocateBuffers(width, height, format, usage);
-    producer->allowAllocation(mFdp.ConsumeBool() /*allow*/);
-    producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/);
-    producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/);
-    producer->setLegacyBufferDrop(mFdp.ConsumeBool() /*drop*/);
-    producer->setAutoPrerotation(mFdp.ConsumeBool() /*autoPrerotation*/);
-
-    producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>());
-    producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>());
-    producer->disconnect(api);
-}
-
-void BufferQueueFuzzer::invokeAcquireBuffer(sp<BufferQueueConsumer> consumer) {
-    BufferItem item;
-    consumer->acquireBuffer(&item, mFdp.ConsumeIntegral<uint32_t>(),
-                            mFdp.ConsumeIntegral<uint64_t>());
-}
-
-void BufferQueueFuzzer::invokeOccupancyTracker(sp<BufferQueueConsumer> consumer) {
-    String8 outResult;
-    String8 prefix((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str());
-    consumer->dumpState(prefix, &outResult);
-
-    std::vector<OccupancyTracker::Segment> outHistory;
-    consumer->getOccupancyHistory(mFdp.ConsumeBool() /*forceFlush*/, &outHistory);
-}
-
-void BufferQueueFuzzer::invokeBufferQueueConsumer() {
-    sp<BufferQueueCore> core(new BufferQueueCore());
-    sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
-    sp<android::IConsumerListener> listener;
-    consumer->consumerConnect(listener, mFdp.ConsumeBool() /*controlledByApp*/);
-    invokeAcquireBuffer(consumer);
-
-    int32_t slot = mFdp.ConsumeIntegral<int32_t>();
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(),
-                              mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint32_t>(),
-                              mFdp.ConsumeIntegral<uint64_t>());
-    consumer->attachBuffer(&slot, buffer);
-    consumer->detachBuffer(slot);
-
-    consumer->setDefaultBufferSize(mFdp.ConsumeIntegral<uint32_t>(),
-                                   mFdp.ConsumeIntegral<uint32_t>());
-    consumer->setMaxBufferCount(mFdp.ConsumeIntegral<int32_t>());
-    consumer->setMaxAcquiredBufferCount(mFdp.ConsumeIntegral<int32_t>());
-
-    String8 name((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str());
-    consumer->setConsumerName(name);
-    consumer->setDefaultBufferFormat(mFdp.ConsumeIntegral<int32_t>());
-    android_dataspace dataspace =
-            static_cast<android_dataspace>(mFdp.PickValueInArray(kDataspaces));
-    consumer->setDefaultBufferDataSpace(dataspace);
-
-    consumer->setTransformHint(mFdp.ConsumeIntegral<uint32_t>());
-    consumer->setConsumerUsageBits(mFdp.ConsumeIntegral<uint64_t>());
-    consumer->setConsumerIsProtected(mFdp.ConsumeBool() /*isProtected*/);
-    invokeOccupancyTracker(consumer);
-
-    sp<Fence> releaseFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING));
-    consumer->releaseBuffer(mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint64_t>(),
-                            EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
-    consumer->consumerDisconnect();
-}
-
-void BufferQueueFuzzer::invokeTypes() {
-    HStatus hStatus;
-    int32_t status = mFdp.PickValueInArray(kError);
-    bool bufferNeedsReallocation = mFdp.ConsumeBool();
-    bool releaseAllBuffers = mFdp.ConsumeBool();
-    b2h(status, &hStatus, &bufferNeedsReallocation, &releaseAllBuffers);
-    h2b(hStatus, &status);
-
-    HConnectionType type;
-    int32_t apiConnection = mFdp.PickValueInArray(kAPIConnection);
-    b2h(apiConnection, &type);
-    h2b(type, &apiConnection);
-}
-
-void BufferQueueFuzzer::invokeH2BGraphicBufferV1() {
-    sp<V1_0::utils::H2BGraphicBufferProducer> producer(
-            new V1_0::utils::H2BGraphicBufferProducer(new FakeGraphicBufferProducerV1()));
-    const sp<android::IProducerListener> listener;
-    android::IGraphicBufferProducer::QueueBufferOutput output;
-    uint32_t api = mFdp.ConsumeIntegral<uint32_t>();
-    producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output);
-
-    sp<GraphicBuffer> buffer;
-    int32_t slot = mFdp.ConsumeIntegral<int32_t>();
-    producer->requestBuffer(slot, &buffer);
-    producer->setMaxDequeuedBufferCount(mFdp.ConsumeIntegral<int32_t>());
-    producer->setAsyncMode(mFdp.ConsumeBool());
-
-    android::IGraphicBufferProducer::QueueBufferInput input;
-    input.fence = new Fence(memfd_create("ffd", MFD_ALLOW_SEALING));
-    producer->attachBuffer(&slot, buffer);
-    producer->queueBuffer(slot, input, &output);
-
-    int32_t format = mFdp.ConsumeIntegral<int32_t>();
-    uint32_t width = mFdp.ConsumeIntegral<uint32_t>();
-    uint32_t height = mFdp.ConsumeIntegral<uint32_t>();
-    uint64_t usage = mFdp.ConsumeIntegral<uint64_t>();
-    uint64_t outBufferAge;
-    FrameEventHistoryDelta outTimestamps;
-    sp<android::Fence> fence;
-    producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge,
-                            &outTimestamps);
-    producer->detachBuffer(slot);
-    producer->cancelBuffer(slot, fence);
-
-    invokeQuery(producer);
-
-    ManageResourceHandle handle(&mFdp);
-    producer->setSidebandStream(handle.getStream());
-
-    producer->allocateBuffers(width, height, format, usage);
-    producer->allowAllocation(mFdp.ConsumeBool() /*allow*/);
-    producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/);
-    producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/);
-
-    producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>());
-    producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>());
-    producer->disconnect(api);
-}
-
-void BufferQueueFuzzer::invokeH2BGraphicBufferV2() {
-    sp<V2_0::utils::H2BGraphicBufferProducer> producer(
-            new V2_0::utils::H2BGraphicBufferProducer(new FakeGraphicBufferProducerV2()));
-    const sp<android::IProducerListener> listener;
-    android::IGraphicBufferProducer::QueueBufferOutput output;
-    uint32_t api = mFdp.ConsumeIntegral<uint32_t>();
-    producer->connect(listener, api, mFdp.ConsumeBool() /*producerControlledByApp*/, &output);
-
-    sp<GraphicBuffer> buffer;
-    int32_t slot = mFdp.ConsumeIntegral<int32_t>();
-    producer->requestBuffer(slot, &buffer);
-    producer->setMaxDequeuedBufferCount(mFdp.ConsumeIntegral<uint32_t>());
-    producer->setAsyncMode(mFdp.ConsumeBool());
-
-    android::IGraphicBufferProducer::QueueBufferInput input;
-    input.fence = new Fence(memfd_create("ffd", MFD_ALLOW_SEALING));
-    producer->attachBuffer(&slot, buffer);
-    producer->queueBuffer(slot, input, &output);
-
-    int32_t format = mFdp.ConsumeIntegral<int32_t>();
-    uint32_t width = mFdp.ConsumeIntegral<uint32_t>();
-    uint32_t height = mFdp.ConsumeIntegral<uint32_t>();
-    uint64_t usage = mFdp.ConsumeIntegral<uint64_t>();
-    uint64_t outBufferAge;
-    FrameEventHistoryDelta outTimestamps;
-    sp<android::Fence> fence;
-    producer->dequeueBuffer(&slot, &fence, width, height, format, usage, &outBufferAge,
-                            &outTimestamps);
-    producer->detachBuffer(slot);
-    producer->cancelBuffer(slot, fence);
-
-    invokeQuery(producer);
-
-    ManageResourceHandle handle(&mFdp);
-    producer->setSidebandStream(handle.getStream());
-
-    producer->allocateBuffers(width, height, format, usage);
-    producer->allowAllocation(mFdp.ConsumeBool() /*allow*/);
-    producer->setSharedBufferMode(mFdp.ConsumeBool() /*sharedBufferMode*/);
-    producer->setAutoRefresh(mFdp.ConsumeBool() /*autoRefresh*/);
-
-    producer->setGenerationNumber(mFdp.ConsumeIntegral<uint32_t>());
-    producer->setDequeueTimeout(mFdp.ConsumeIntegral<uint32_t>());
-    producer->disconnect(api);
-}
-
-void BufferQueueFuzzer::process() {
-    invokeBlastBufferQueue();
-    invokeH2BGraphicBufferV1();
-    invokeH2BGraphicBufferV2();
-    invokeTypes();
-    invokeBufferQueueConsumer();
-    invokeBufferQueueProducer();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    BufferQueueFuzzer bufferQueueFuzzer(data, size);
-    bufferQueueFuzzer.process();
-    return 0;
-}
diff --git a/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp b/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp
deleted file mode 100644
index 24a046d..0000000
--- a/libs/gui/fuzzer/libgui_consumer_fuzzer.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <gui/BufferQueueConsumer.h>
-#include <gui/BufferQueueCore.h>
-#include <gui/BufferQueueProducer.h>
-#include <gui/GLConsumer.h>
-#include <libgui_fuzzer_utils.h>
-
-using namespace android;
-
-constexpr int32_t kMinBuffer = 0;
-constexpr int32_t kMaxBuffer = 100000;
-
-class ConsumerFuzzer {
-public:
-    ConsumerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
-    void process();
-
-private:
-    FuzzedDataProvider mFdp;
-};
-
-void ConsumerFuzzer::process() {
-    sp<BufferQueueCore> core(new BufferQueueCore());
-    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
-
-    uint64_t maxBuffers = mFdp.ConsumeIntegralInRange<uint64_t>(kMinBuffer, kMaxBuffer);
-    sp<CpuConsumer> cpu(
-            new CpuConsumer(consumer, maxBuffers, mFdp.ConsumeBool() /*controlledByApp*/));
-    CpuConsumer::LockedBuffer lockBuffer;
-    cpu->lockNextBuffer(&lockBuffer);
-    cpu->unlockBuffer(lockBuffer);
-    cpu->abandon();
-
-    uint32_t tex = mFdp.ConsumeIntegral<uint32_t>();
-    sp<GLConsumer> glComsumer(new GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL,
-                                             mFdp.ConsumeBool() /*useFenceSync*/,
-                                             mFdp.ConsumeBool() /*isControlledByApp*/));
-    sp<Fence> releaseFence = new Fence(memfd_create("rfd", MFD_ALLOW_SEALING));
-    glComsumer->setReleaseFence(releaseFence);
-    glComsumer->updateTexImage();
-    glComsumer->releaseTexImage();
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(),
-                              mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<uint32_t>(),
-                              mFdp.ConsumeIntegral<uint64_t>());
-    float mtx[16];
-    glComsumer->getTransformMatrix(mtx);
-    glComsumer->computeTransformMatrix(mtx, buffer, getRect(&mFdp),
-                                       mFdp.ConsumeIntegral<uint32_t>(),
-                                       mFdp.ConsumeBool() /*filtering*/);
-    glComsumer->scaleDownCrop(getRect(&mFdp), mFdp.ConsumeIntegral<uint32_t>(),
-                              mFdp.ConsumeIntegral<uint32_t>());
-
-    glComsumer->setDefaultBufferSize(mFdp.ConsumeIntegral<uint32_t>(),
-                                     mFdp.ConsumeIntegral<uint32_t>());
-    glComsumer->setFilteringEnabled(mFdp.ConsumeBool() /*enabled*/);
-
-    glComsumer->setConsumerUsageBits(mFdp.ConsumeIntegral<uint64_t>());
-    glComsumer->attachToContext(tex);
-    glComsumer->abandon();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    ConsumerFuzzer consumerFuzzer(data, size);
-    consumerFuzzer.process();
-    return 0;
-}
diff --git a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp
deleted file mode 100644
index 0d2a52b..0000000
--- a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android/gui/ISurfaceComposer.h>
-
-#include <libgui_fuzzer_utils.h>
-
-using namespace android;
-
-constexpr gui::ISurfaceComposer::VsyncSource kVsyncSource[] = {
-        gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
-        gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger,
-};
-
-constexpr gui::ISurfaceComposer::EventRegistration kEventRegistration[] = {
-        gui::ISurfaceComposer::EventRegistration::modeChanged,
-        gui::ISurfaceComposer::EventRegistration::frameRateOverride,
-};
-
-constexpr uint32_t kDisplayEvent[] = {
-        DisplayEventReceiver::DISPLAY_EVENT_NULL,
-        DisplayEventReceiver::DISPLAY_EVENT_VSYNC,
-        DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
-        DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
-        DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
-        DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH,
-};
-
-constexpr int32_t kEvents[] = {
-        Looper::EVENT_INPUT,  Looper::EVENT_OUTPUT,  Looper::EVENT_ERROR,
-        Looper::EVENT_HANGUP, Looper::EVENT_INVALID,
-};
-
-DisplayEventReceiver::Event buildDisplayEvent(FuzzedDataProvider* fdp, uint32_t type,
-                                              DisplayEventReceiver::Event event) {
-    switch (type) {
-        case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: {
-            event.vsync.count = fdp->ConsumeIntegral<uint32_t>();
-            event.vsync.vsyncData.frameInterval = fdp->ConsumeIntegral<uint64_t>();
-            event.vsync.vsyncData.preferredFrameTimelineIndex = fdp->ConsumeIntegral<uint32_t>();
-            for (size_t idx = 0; idx < gui::VsyncEventData::kFrameTimelinesCapacity; ++idx) {
-                event.vsync.vsyncData.frameTimelines[idx].vsyncId = fdp->ConsumeIntegral<int64_t>();
-                event.vsync.vsyncData.frameTimelines[idx].deadlineTimestamp =
-                        fdp->ConsumeIntegral<uint64_t>();
-                event.vsync.vsyncData.frameTimelines[idx].expectedPresentationTime =
-                        fdp->ConsumeIntegral<uint64_t>();
-            }
-            break;
-
-        }
-        case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: {
-            event.hotplug =
-                    DisplayEventReceiver::Event::Hotplug{fdp->ConsumeBool() /*connected*/,
-                                                         fdp->ConsumeIntegral<
-                                                                 int32_t>() /*connectionError*/};
-            break;
-        }
-        case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: {
-            event.modeChange =
-                    DisplayEventReceiver::Event::ModeChange{fdp->ConsumeIntegral<int32_t>(),
-                                                            fdp->ConsumeIntegral<int64_t>()};
-            break;
-        }
-        case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
-        case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: {
-            event.frameRateOverride =
-                    DisplayEventReceiver::Event::FrameRateOverride{fdp->ConsumeIntegral<uint32_t>(),
-                                                                   fdp->ConsumeFloatingPoint<
-                                                                           float>()};
-            break;
-        }
-    }
-    return event;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    FuzzedDataProvider fdp(data, size);
-    sp<Looper> looper;
-    sp<FakeDisplayEventDispatcher> dispatcher(
-            new FakeDisplayEventDispatcher(looper, fdp.PickValueInArray(kVsyncSource),
-                                           fdp.PickValueInArray(kEventRegistration)));
-
-    dispatcher->initialize();
-    DisplayEventReceiver::Event event;
-    uint32_t type = fdp.PickValueInArray(kDisplayEvent);
-    PhysicalDisplayId displayId;
-    event.header =
-            DisplayEventReceiver::Event::Header{type, displayId, fdp.ConsumeIntegral<int64_t>()};
-    event = buildDisplayEvent(&fdp, type, event);
-
-    dispatcher->injectEvent(event);
-    dispatcher->handleEvent(0, fdp.PickValueInArray(kEvents), nullptr);
-    return 0;
-}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
deleted file mode 100644
index 2bdbd43..0000000
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <android/gui/BnRegionSamplingListener.h>
-#include <android/gui/BnSurfaceComposer.h>
-#include <android/gui/BnSurfaceComposerClient.h>
-#include <android/gui/IDisplayEventConnection.h>
-#include <android/gui/ISurfaceComposerClient.h>
-#include <fuzzer/FuzzedDataProvider.h>
-#include <gmock/gmock.h>
-#include <gui/BLASTBufferQueue.h>
-#include <gui/DisplayEventDispatcher.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/LayerState.h>
-#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
-#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
-#include <ui/fuzzer/FuzzableDataspaces.h>
-
-namespace android {
-
-constexpr uint32_t kOrientation[] = {
-        ui::Transform::ROT_0,  ui::Transform::FLIP_H,  ui::Transform::FLIP_V,
-        ui::Transform::ROT_90, ui::Transform::ROT_180, ui::Transform::ROT_270,
-};
-
-Rect getRect(FuzzedDataProvider* fdp) {
-    const int32_t left = fdp->ConsumeIntegral<int32_t>();
-    const int32_t top = fdp->ConsumeIntegral<int32_t>();
-    const int32_t right = fdp->ConsumeIntegral<int32_t>();
-    const int32_t bottom = fdp->ConsumeIntegral<int32_t>();
-    return Rect(left, top, right, bottom);
-}
-
-gui::DisplayBrightness getBrightness(FuzzedDataProvider* fdp) {
-    static constexpr float kMinBrightness = 0;
-    static constexpr float kMaxBrightness = 1;
-    gui::DisplayBrightness brightness;
-    brightness.sdrWhitePoint =
-            fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness);
-    brightness.sdrWhitePointNits =
-            fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness);
-    brightness.displayBrightness =
-            fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness);
-    brightness.displayBrightnessNits =
-            fdp->ConsumeFloatingPointInRange<float>(kMinBrightness, kMaxBrightness);
-    return brightness;
-}
-
-class FakeBnSurfaceComposer : public gui::BnSurfaceComposer {
-public:
-    MOCK_METHOD(binder::Status, bootFinished, (), (override));
-    MOCK_METHOD(binder::Status, createDisplayEventConnection,
-                (gui::ISurfaceComposer::VsyncSource, gui::ISurfaceComposer::EventRegistration,
-                 const sp<IBinder>& /*layerHandle*/, sp<gui::IDisplayEventConnection>*),
-                (override));
-    MOCK_METHOD(binder::Status, createConnection, (sp<gui::ISurfaceComposerClient>*), (override));
-    MOCK_METHOD(binder::Status, createDisplay, (const std::string&, bool, float, sp<IBinder>*),
-                (override));
-    MOCK_METHOD(binder::Status, destroyDisplay, (const sp<IBinder>&), (override));
-    MOCK_METHOD(binder::Status, getPhysicalDisplayIds, (std::vector<int64_t>*), (override));
-    MOCK_METHOD(binder::Status, getPhysicalDisplayToken, (int64_t, sp<IBinder>*), (override));
-    MOCK_METHOD(binder::Status, setPowerMode, (const sp<IBinder>&, int), (override));
-    MOCK_METHOD(binder::Status, getSupportedFrameTimestamps, (std::vector<FrameEvent>*),
-                (override));
-    MOCK_METHOD(binder::Status, getDisplayStats, (const sp<IBinder>&, gui::DisplayStatInfo*),
-                (override));
-    MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*),
-                (override));
-    MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*),
-                (override));
-    MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*),
-                (override));
-    MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken,
-                (const sp<IBinder>&, gui::DynamicDisplayInfo*), (override));
-    MOCK_METHOD(binder::Status, getDisplayNativePrimaries,
-                (const sp<IBinder>&, gui::DisplayPrimaries*), (override));
-    MOCK_METHOD(binder::Status, setActiveColorMode, (const sp<IBinder>&, int), (override));
-    MOCK_METHOD(binder::Status, setBootDisplayMode, (const sp<IBinder>&, int), (override));
-    MOCK_METHOD(binder::Status, clearBootDisplayMode, (const sp<IBinder>&), (override));
-    MOCK_METHOD(binder::Status, getBootDisplayModeSupport, (bool*), (override));
-    MOCK_METHOD(binder::Status, getHdrConversionCapabilities,
-                (std::vector<gui::HdrConversionCapability>*), (override));
-    MOCK_METHOD(binder::Status, setHdrConversionStrategy,
-                (const gui::HdrConversionStrategy&, int32_t*), (override));
-    MOCK_METHOD(binder::Status, getHdrOutputConversionSupport, (bool*), (override));
-    MOCK_METHOD(binder::Status, setAutoLowLatencyMode, (const sp<IBinder>&, bool), (override));
-    MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override));
-    MOCK_METHOD(binder::Status, captureDisplay,
-                (const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
-    MOCK_METHOD(binder::Status, captureDisplayById,
-                (int64_t, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&), (override));
-    MOCK_METHOD(binder::Status, captureLayers,
-                (const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
-    MOCK_METHOD(binder::Status, captureLayersSync,
-                (const LayerCaptureArgs&, gui::ScreenCaptureResults*), (override));
-    MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override));
-    MOCK_METHOD(binder::Status, getAnimationFrameStats, (gui::FrameStats*), (override));
-    MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&),
-                (override));
-    MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override));
-    MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override));
-    MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*),
-                (override));
-    MOCK_METHOD(binder::Status, getDisplayedContentSamplingAttributes,
-                (const sp<IBinder>&, gui::ContentSamplingAttributes*), (override));
-    MOCK_METHOD(binder::Status, setDisplayContentSamplingEnabled,
-                (const sp<IBinder>&, bool, int8_t, int64_t), (override));
-    MOCK_METHOD(binder::Status, getDisplayedContentSample,
-                (const sp<IBinder>&, int64_t, int64_t, gui::DisplayedFrameStats*), (override));
-    MOCK_METHOD(binder::Status, getProtectedContentSupport, (bool*), (override));
-    MOCK_METHOD(binder::Status, isWideColorDisplay, (const sp<IBinder>&, bool*), (override));
-    MOCK_METHOD(binder::Status, addRegionSamplingListener,
-                (const gui::ARect&, const sp<IBinder>&, const sp<gui::IRegionSamplingListener>&),
-                (override));
-    MOCK_METHOD(binder::Status, removeRegionSamplingListener,
-                (const sp<gui::IRegionSamplingListener>&), (override));
-    MOCK_METHOD(binder::Status, addFpsListener, (int32_t, const sp<gui::IFpsListener>&),
-                (override));
-    MOCK_METHOD(binder::Status, removeFpsListener, (const sp<gui::IFpsListener>&), (override));
-    MOCK_METHOD(binder::Status, addTunnelModeEnabledListener,
-                (const sp<gui::ITunnelModeEnabledListener>&), (override));
-    MOCK_METHOD(binder::Status, removeTunnelModeEnabledListener,
-                (const sp<gui::ITunnelModeEnabledListener>&), (override));
-    MOCK_METHOD(binder::Status, setDesiredDisplayModeSpecs,
-                (const sp<IBinder>&, const gui::DisplayModeSpecs&), (override));
-    MOCK_METHOD(binder::Status, getDesiredDisplayModeSpecs,
-                (const sp<IBinder>&, gui::DisplayModeSpecs*), (override));
-    MOCK_METHOD(binder::Status, getDisplayBrightnessSupport, (const sp<IBinder>&, bool*),
-                (override));
-    MOCK_METHOD(binder::Status, setDisplayBrightness,
-                (const sp<IBinder>&, const gui::DisplayBrightness&), (override));
-    MOCK_METHOD(binder::Status, addHdrLayerInfoListener,
-                (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override));
-    MOCK_METHOD(binder::Status, removeHdrLayerInfoListener,
-                (const sp<IBinder>&, const sp<gui::IHdrLayerInfoListener>&), (override));
-    MOCK_METHOD(binder::Status, notifyPowerBoost, (int), (override));
-    MOCK_METHOD(binder::Status, setGlobalShadowSettings,
-                (const gui::Color&, const gui::Color&, float, float, float), (override));
-    MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
-                (const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
-    MOCK_METHOD(binder::Status, setGameModeFrameRateOverride, (int32_t, float), (override));
-    MOCK_METHOD(binder::Status, setGameDefaultFrameRateOverride, (int32_t, float), (override));
-    MOCK_METHOD(binder::Status, enableRefreshRateOverlay, (bool), (override));
-    MOCK_METHOD(binder::Status, setDebugFlash, (int), (override));
-    MOCK_METHOD(binder::Status, scheduleComposite, (), (override));
-    MOCK_METHOD(binder::Status, scheduleCommit, (), (override));
-    MOCK_METHOD(binder::Status, forceClientComposition, (bool), (override));
-    MOCK_METHOD(binder::Status, updateSmallAreaDetection,
-                (const std::vector<int32_t>&, const std::vector<float>&), (override));
-    MOCK_METHOD(binder::Status, setSmallAreaDetectionThreshold, (int32_t, float), (override));
-    MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
-    MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
-    MOCK_METHOD(binder::Status, addWindowInfosListener,
-                (const sp<gui::IWindowInfosListener>&, gui::WindowInfosListenerInfo*), (override));
-    MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
-                (override));
-    MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
-    MOCK_METHOD(binder::Status, getStalledTransactionInfo,
-                (int32_t, std::optional<gui::StalledTransactionInfo>*), (override));
-    MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override));
-};
-
-class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
-public:
-    MOCK_METHOD(binder::Status, createSurface,
-                (const std::string& name, int32_t flags, const sp<IBinder>& parent,
-                 const gui::LayerMetadata& metadata, gui::CreateSurfaceResult* outResult),
-                (override));
-
-    MOCK_METHOD(binder::Status, clearLayerFrameStats, (const sp<IBinder>& handle), (override));
-
-    MOCK_METHOD(binder::Status, getLayerFrameStats,
-                (const sp<IBinder>& handle, gui::FrameStats* outStats), (override));
-
-    MOCK_METHOD(binder::Status, mirrorSurface,
-                (const sp<IBinder>& mirrorFromHandle, gui::CreateSurfaceResult* outResult),
-                (override));
-
-    MOCK_METHOD(binder::Status, mirrorDisplay,
-                (int64_t displayId, gui::CreateSurfaceResult* outResult), (override));
-
-    MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override));
-};
-
-class FakeDisplayEventDispatcher : public DisplayEventDispatcher {
-public:
-    FakeDisplayEventDispatcher(const sp<Looper>& looper,
-                               gui::ISurfaceComposer::VsyncSource vsyncSource,
-                               gui::ISurfaceComposer::EventRegistration eventRegistration)
-          : DisplayEventDispatcher(looper, vsyncSource, eventRegistration){};
-
-    MOCK_METHOD4(dispatchVsync, void(nsecs_t, PhysicalDisplayId, uint32_t, VsyncEventData));
-    MOCK_METHOD3(dispatchHotplug, void(nsecs_t, PhysicalDisplayId, bool));
-    MOCK_METHOD2(dispatchHotplugConnectionError, void(nsecs_t, int32_t));
-    MOCK_METHOD4(dispatchModeChanged, void(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t));
-    MOCK_METHOD2(dispatchNullEvent, void(nsecs_t, PhysicalDisplayId));
-    MOCK_METHOD3(dispatchFrameRateOverrides,
-                 void(nsecs_t, PhysicalDisplayId, std::vector<FrameRateOverride>));
-    MOCK_METHOD3(dispatchHdcpLevelsChanged, void(PhysicalDisplayId, int32_t, int32_t));
-};
-
-} // namespace android
-
-namespace android::hardware {
-
-namespace graphics::bufferqueue::V1_0::utils {
-
-class FakeGraphicBufferProducerV1 : public HGraphicBufferProducer {
-public:
-    FakeGraphicBufferProducerV1() {
-        ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return 0; });
-        ON_CALL(*this, setAsyncMode).WillByDefault([]() { return 0; });
-        ON_CALL(*this, detachBuffer).WillByDefault([]() { return 0; });
-        ON_CALL(*this, cancelBuffer).WillByDefault([]() { return 0; });
-        ON_CALL(*this, disconnect).WillByDefault([]() { return 0; });
-        ON_CALL(*this, setSidebandStream).WillByDefault([]() { return 0; });
-        ON_CALL(*this, allowAllocation).WillByDefault([]() { return 0; });
-        ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return 0; });
-        ON_CALL(*this, setSharedBufferMode).WillByDefault([]() { return 0; });
-        ON_CALL(*this, setAutoRefresh).WillByDefault([]() { return 0; });
-        ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return 0; });
-        ON_CALL(*this, setLegacyBufferDrop).WillByDefault([]() { return 0; });
-    };
-    MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb));
-    MOCK_METHOD1(setMaxDequeuedBufferCount, Return<int32_t>(int32_t));
-    MOCK_METHOD1(setAsyncMode, Return<int32_t>(bool));
-    MOCK_METHOD6(dequeueBuffer,
-                 Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t,
-                              bool, dequeueBuffer_cb));
-    MOCK_METHOD1(detachBuffer, Return<int32_t>(int));
-    MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb));
-    MOCK_METHOD2(attachBuffer, Return<void>(const media::V1_0::AnwBuffer&, attachBuffer_cb));
-    MOCK_METHOD3(
-            queueBuffer,
-            Return<void>(
-                    int,
-                    const graphics::bufferqueue::V1_0::IGraphicBufferProducer::QueueBufferInput&,
-                    queueBuffer_cb));
-    MOCK_METHOD2(cancelBuffer, Return<int32_t>(int, const hidl_handle&));
-    MOCK_METHOD2(query, Return<void>(int32_t, query_cb));
-    MOCK_METHOD4(connect,
-                 Return<void>(const sp<graphics::bufferqueue::V1_0::IProducerListener>&, int32_t,
-                              bool, connect_cb));
-    MOCK_METHOD2(disconnect,
-                 Return<int32_t>(
-                         int, graphics::bufferqueue::V1_0::IGraphicBufferProducer::DisconnectMode));
-    MOCK_METHOD1(setSidebandStream, Return<int32_t>(const hidl_handle&));
-    MOCK_METHOD4(allocateBuffers,
-                 Return<void>(uint32_t, uint32_t, graphics::common::V1_0::PixelFormat, uint32_t));
-    MOCK_METHOD1(allowAllocation, Return<int32_t>(bool));
-    MOCK_METHOD1(setGenerationNumber, Return<int32_t>(uint32_t));
-    MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb));
-    MOCK_METHOD1(setSharedBufferMode, Return<int32_t>(bool));
-    MOCK_METHOD1(setAutoRefresh, Return<int32_t>(bool));
-    MOCK_METHOD1(setDequeueTimeout, Return<int32_t>(nsecs_t));
-    MOCK_METHOD1(setLegacyBufferDrop, Return<int32_t>(bool));
-    MOCK_METHOD1(getLastQueuedBuffer, Return<void>(getLastQueuedBuffer_cb));
-    MOCK_METHOD1(getFrameTimestamps, Return<void>(getFrameTimestamps_cb));
-    MOCK_METHOD1(getUniqueId, Return<void>(getUniqueId_cb));
-};
-
-}; // namespace graphics::bufferqueue::V1_0::utils
-
-namespace graphics::bufferqueue::V2_0::utils {
-
-class FakeGraphicBufferProducerV2 : public HGraphicBufferProducer {
-public:
-    FakeGraphicBufferProducerV2() {
-        ON_CALL(*this, setMaxDequeuedBufferCount).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, setAsyncMode).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, detachBuffer).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, cancelBuffer).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, disconnect).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, allocateBuffers).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, allowAllocation).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, setGenerationNumber).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, setDequeueTimeout).WillByDefault([]() { return Status::OK; });
-        ON_CALL(*this, getUniqueId).WillByDefault([]() { return 0; });
-    };
-    MOCK_METHOD2(requestBuffer, Return<void>(int, requestBuffer_cb));
-    MOCK_METHOD1(setMaxDequeuedBufferCount, Return<graphics::bufferqueue::V2_0::Status>(int));
-    MOCK_METHOD1(setAsyncMode, Return<graphics::bufferqueue::V2_0::Status>(bool));
-    MOCK_METHOD2(
-            dequeueBuffer,
-            Return<void>(
-                    const graphics::bufferqueue::V2_0::IGraphicBufferProducer::DequeueBufferInput&,
-                    dequeueBuffer_cb));
-    MOCK_METHOD1(detachBuffer, Return<graphics::bufferqueue::V2_0::Status>(int));
-    MOCK_METHOD1(detachNextBuffer, Return<void>(detachNextBuffer_cb));
-    MOCK_METHOD3(attachBuffer,
-                 Return<void>(const graphics::common::V1_2::HardwareBuffer&, uint32_t,
-                              attachBuffer_cb));
-    MOCK_METHOD3(
-            queueBuffer,
-            Return<void>(
-                    int,
-                    const graphics::bufferqueue::V2_0::IGraphicBufferProducer::QueueBufferInput&,
-                    queueBuffer_cb));
-    MOCK_METHOD2(cancelBuffer,
-                 Return<graphics::bufferqueue::V2_0::Status>(int, const hidl_handle&));
-    MOCK_METHOD2(query, Return<void>(int32_t, query_cb));
-    MOCK_METHOD4(connect,
-                 Return<void>(const sp<graphics::bufferqueue::V2_0::IProducerListener>&,
-                              graphics::bufferqueue::V2_0::ConnectionType, bool, connect_cb));
-    MOCK_METHOD1(disconnect,
-                 Return<graphics::bufferqueue::V2_0::Status>(
-                         graphics::bufferqueue::V2_0::ConnectionType));
-    MOCK_METHOD4(allocateBuffers,
-                 Return<graphics::bufferqueue::V2_0::Status>(uint32_t, uint32_t, uint32_t,
-                                                             uint64_t));
-    MOCK_METHOD1(allowAllocation, Return<graphics::bufferqueue::V2_0::Status>(bool));
-    MOCK_METHOD1(setGenerationNumber, Return<graphics::bufferqueue::V2_0::Status>(uint32_t));
-    MOCK_METHOD1(getConsumerName, Return<void>(getConsumerName_cb));
-    MOCK_METHOD1(setDequeueTimeout, Return<graphics::bufferqueue::V2_0::Status>(int64_t));
-    MOCK_METHOD0(getUniqueId, Return<uint64_t>());
-};
-
-}; // namespace graphics::bufferqueue::V2_0::utils
-}; // namespace android::hardware
diff --git a/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp b/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp
deleted file mode 100644
index 9f0f6ca..0000000
--- a/libs/gui/fuzzer/libgui_parcelable_fuzzer.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <gui/BufferQueueConsumer.h>
-#include <gui/BufferQueueCore.h>
-#include <gui/BufferQueueProducer.h>
-#include <gui/LayerMetadata.h>
-#include <gui/OccupancyTracker.h>
-#include <gui/StreamSplitter.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceControl.h>
-#include <gui/view/Surface.h>
-#include <libgui_fuzzer_utils.h>
-#include "android/view/LayerMetadataKey.h"
-
-using namespace android;
-
-constexpr int32_t kMaxBytes = 256;
-constexpr int32_t kMatrixSize = 4;
-constexpr int32_t kLayerMetadataKeyCount = 8;
-
-constexpr uint32_t kMetadataKey[] = {
-        (uint32_t)view::LayerMetadataKey::METADATA_OWNER_UID,
-        (uint32_t)view::LayerMetadataKey::METADATA_WINDOW_TYPE,
-        (uint32_t)view::LayerMetadataKey::METADATA_TASK_ID,
-        (uint32_t)view::LayerMetadataKey::METADATA_MOUSE_CURSOR,
-        (uint32_t)view::LayerMetadataKey::METADATA_ACCESSIBILITY_ID,
-        (uint32_t)view::LayerMetadataKey::METADATA_OWNER_PID,
-        (uint32_t)view::LayerMetadataKey::METADATA_DEQUEUE_TIME,
-        (uint32_t)view::LayerMetadataKey::METADATA_GAME_MODE,
-};
-
-class ParcelableFuzzer {
-public:
-    ParcelableFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
-    void process();
-
-private:
-    void invokeStreamSplitter();
-    void invokeOccupancyTracker();
-    void invokeLayerDebugInfo();
-    void invokeLayerMetadata();
-    void invokeViewSurface();
-
-    FuzzedDataProvider mFdp;
-};
-
-void ParcelableFuzzer::invokeViewSurface() {
-    view::Surface surface;
-    surface.name = String16((mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str());
-    Parcel parcel;
-    surface.writeToParcel(&parcel);
-    parcel.setDataPosition(0);
-    surface.readFromParcel(&parcel);
-    bool nameAlreadyWritten = mFdp.ConsumeBool();
-    surface.writeToParcel(&parcel, nameAlreadyWritten);
-    parcel.setDataPosition(0);
-    surface.readFromParcel(&parcel, mFdp.ConsumeBool());
-}
-
-void ParcelableFuzzer::invokeLayerMetadata() {
-    std::unordered_map<uint32_t, std::vector<uint8_t>> map;
-    for (size_t idx = 0; idx < kLayerMetadataKeyCount; ++idx) {
-        std::vector<uint8_t> data;
-        for (size_t idx1 = 0; idx1 < mFdp.ConsumeIntegral<uint32_t>(); ++idx1) {
-            data.push_back(mFdp.ConsumeIntegral<uint8_t>());
-        }
-        map[kMetadataKey[idx]] = data;
-    }
-    LayerMetadata metadata(map);
-    uint32_t key = mFdp.PickValueInArray(kMetadataKey);
-    metadata.setInt32(key, mFdp.ConsumeIntegral<int32_t>());
-    metadata.itemToString(key, (mFdp.ConsumeRandomLengthString(kMaxBytes)).c_str());
-
-    Parcel parcel;
-    metadata.writeToParcel(&parcel);
-    parcel.setDataPosition(0);
-    metadata.readFromParcel(&parcel);
-}
-
-void ParcelableFuzzer::invokeLayerDebugInfo() {
-    gui::LayerDebugInfo info;
-    info.mName = mFdp.ConsumeRandomLengthString(kMaxBytes);
-    info.mParentName = mFdp.ConsumeRandomLengthString(kMaxBytes);
-    info.mType = mFdp.ConsumeRandomLengthString(kMaxBytes);
-    info.mLayerStack = mFdp.ConsumeIntegral<uint32_t>();
-    info.mX = mFdp.ConsumeFloatingPoint<float>();
-    info.mY = mFdp.ConsumeFloatingPoint<float>();
-    info.mZ = mFdp.ConsumeIntegral<uint32_t>();
-    info.mWidth = mFdp.ConsumeIntegral<int32_t>();
-    info.mHeight = mFdp.ConsumeIntegral<int32_t>();
-    info.mActiveBufferWidth = mFdp.ConsumeIntegral<int32_t>();
-    info.mActiveBufferHeight = mFdp.ConsumeIntegral<int32_t>();
-    info.mActiveBufferStride = mFdp.ConsumeIntegral<int32_t>();
-    info.mActiveBufferFormat = mFdp.ConsumeIntegral<int32_t>();
-    info.mNumQueuedFrames = mFdp.ConsumeIntegral<int32_t>();
-
-    info.mFlags = mFdp.ConsumeIntegral<uint32_t>();
-    info.mPixelFormat = mFdp.ConsumeIntegral<int32_t>();
-    info.mTransparentRegion = Region(getRect(&mFdp));
-    info.mVisibleRegion = Region(getRect(&mFdp));
-    info.mSurfaceDamageRegion = Region(getRect(&mFdp));
-    info.mCrop = getRect(&mFdp);
-    info.mDataSpace = static_cast<android_dataspace>(mFdp.PickValueInArray(kDataspaces));
-    info.mColor = half4(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(),
-                        mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>());
-    for (size_t idx = 0; idx < kMatrixSize; ++idx) {
-        info.mMatrix[idx / 2][idx % 2] = mFdp.ConsumeFloatingPoint<float>();
-    }
-    info.mIsOpaque = mFdp.ConsumeBool();
-    info.mContentDirty = mFdp.ConsumeBool();
-    info.mStretchEffect.width = mFdp.ConsumeFloatingPoint<float>();
-    info.mStretchEffect.height = mFdp.ConsumeFloatingPoint<float>();
-    info.mStretchEffect.vectorX = mFdp.ConsumeFloatingPoint<float>();
-    info.mStretchEffect.vectorY = mFdp.ConsumeFloatingPoint<float>();
-    info.mStretchEffect.maxAmountX = mFdp.ConsumeFloatingPoint<float>();
-    info.mStretchEffect.maxAmountY = mFdp.ConsumeFloatingPoint<float>();
-    info.mStretchEffect.mappedChildBounds =
-            FloatRect(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(),
-                      mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>());
-
-    Parcel parcel;
-    info.writeToParcel(&parcel);
-    parcel.setDataPosition(0);
-    info.readFromParcel(&parcel);
-}
-
-void ParcelableFuzzer::invokeOccupancyTracker() {
-    nsecs_t totalTime = mFdp.ConsumeIntegral<uint32_t>();
-    size_t numFrames = mFdp.ConsumeIntegral<size_t>();
-    float occupancyAverage = mFdp.ConsumeFloatingPoint<float>();
-    OccupancyTracker::Segment segment(totalTime, numFrames, occupancyAverage,
-                                      mFdp.ConsumeBool() /*usedThirdBuffer*/);
-    Parcel parcel;
-    segment.writeToParcel(&parcel);
-    parcel.setDataPosition(0);
-    segment.readFromParcel(&parcel);
-}
-
-void ParcelableFuzzer::invokeStreamSplitter() {
-    sp<IGraphicBufferProducer> producer;
-    sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer);
-    sp<StreamSplitter> splitter;
-    StreamSplitter::createSplitter(consumer, &splitter);
-    splitter->addOutput(producer);
-    std::string name = mFdp.ConsumeRandomLengthString(kMaxBytes);
-    splitter->setName(String8(name.c_str()));
-}
-
-void ParcelableFuzzer::process() {
-    invokeStreamSplitter();
-    invokeOccupancyTracker();
-    invokeLayerDebugInfo();
-    invokeLayerMetadata();
-    invokeViewSurface();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    ParcelableFuzzer libGuiFuzzer(data, size);
-    libGuiFuzzer.process();
-    return 0;
-}
diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
deleted file mode 100644
index 4daa3be..0000000
--- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <aidl/android/hardware/power/Boost.h>
-#include <fuzzbinder/libbinder_driver.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <libgui_fuzzer_utils.h>
-#include "android-base/stringprintf.h"
-
-using namespace android;
-
-constexpr int32_t kRandomStringMaxBytes = 256;
-
-constexpr ui::ColorMode kColormodes[] = {ui::ColorMode::NATIVE,
-                                         ui::ColorMode::STANDARD_BT601_625,
-                                         ui::ColorMode::STANDARD_BT601_625_UNADJUSTED,
-                                         ui::ColorMode::STANDARD_BT601_525,
-                                         ui::ColorMode::STANDARD_BT601_525_UNADJUSTED,
-                                         ui::ColorMode::STANDARD_BT709,
-                                         ui::ColorMode::DCI_P3,
-                                         ui::ColorMode::SRGB,
-                                         ui::ColorMode::ADOBE_RGB,
-                                         ui::ColorMode::DISPLAY_P3,
-                                         ui::ColorMode::BT2020,
-                                         ui::ColorMode::BT2100_PQ,
-                                         ui::ColorMode::BT2100_HLG,
-                                         ui::ColorMode::DISPLAY_BT2020};
-
-constexpr aidl::android::hardware::power::Boost kBoost[] = {
-        aidl::android::hardware::power::Boost::INTERACTION,
-        aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT,
-        aidl::android::hardware::power::Boost::ML_ACC,
-        aidl::android::hardware::power::Boost::AUDIO_LAUNCH,
-        aidl::android::hardware::power::Boost::CAMERA_LAUNCH,
-        aidl::android::hardware::power::Boost::CAMERA_SHOT,
-};
-
-constexpr gui::TouchOcclusionMode kMode[] = {
-        gui::TouchOcclusionMode::BLOCK_UNTRUSTED,
-        gui::TouchOcclusionMode::USE_OPACITY,
-        gui::TouchOcclusionMode::ALLOW,
-};
-
-constexpr gui::WindowInfo::Flag kFlags[] = {
-        gui::WindowInfo::Flag::ALLOW_LOCK_WHILE_SCREEN_ON,
-        gui::WindowInfo::Flag::DIM_BEHIND,
-        gui::WindowInfo::Flag::BLUR_BEHIND,
-        gui::WindowInfo::Flag::NOT_FOCUSABLE,
-        gui::WindowInfo::Flag::NOT_TOUCHABLE,
-        gui::WindowInfo::Flag::NOT_TOUCH_MODAL,
-        gui::WindowInfo::Flag::TOUCHABLE_WHEN_WAKING,
-        gui::WindowInfo::Flag::KEEP_SCREEN_ON,
-        gui::WindowInfo::Flag::LAYOUT_IN_SCREEN,
-        gui::WindowInfo::Flag::LAYOUT_NO_LIMITS,
-        gui::WindowInfo::Flag::FULLSCREEN,
-        gui::WindowInfo::Flag::FORCE_NOT_FULLSCREEN,
-        gui::WindowInfo::Flag::DITHER,
-        gui::WindowInfo::Flag::SECURE,
-        gui::WindowInfo::Flag::SCALED,
-        gui::WindowInfo::Flag::IGNORE_CHEEK_PRESSES,
-        gui::WindowInfo::Flag::LAYOUT_INSET_DECOR,
-        gui::WindowInfo::Flag::ALT_FOCUSABLE_IM,
-        gui::WindowInfo::Flag::WATCH_OUTSIDE_TOUCH,
-        gui::WindowInfo::Flag::SHOW_WHEN_LOCKED,
-        gui::WindowInfo::Flag::SHOW_WALLPAPER,
-        gui::WindowInfo::Flag::TURN_SCREEN_ON,
-        gui::WindowInfo::Flag::DISMISS_KEYGUARD,
-        gui::WindowInfo::Flag::SPLIT_TOUCH,
-        gui::WindowInfo::Flag::HARDWARE_ACCELERATED,
-        gui::WindowInfo::Flag::LAYOUT_IN_OVERSCAN,
-        gui::WindowInfo::Flag::TRANSLUCENT_STATUS,
-        gui::WindowInfo::Flag::TRANSLUCENT_NAVIGATION,
-        gui::WindowInfo::Flag::LOCAL_FOCUS_MODE,
-        gui::WindowInfo::Flag::SLIPPERY,
-        gui::WindowInfo::Flag::LAYOUT_ATTACHED_IN_DECOR,
-        gui::WindowInfo::Flag::DRAWS_SYSTEM_BAR_BACKGROUNDS,
-};
-
-constexpr gui::WindowInfo::Type kType[] = {
-        gui::WindowInfo::Type::UNKNOWN,
-        gui::WindowInfo::Type::FIRST_APPLICATION_WINDOW,
-        gui::WindowInfo::Type::BASE_APPLICATION,
-        gui::WindowInfo::Type::APPLICATION,
-        gui::WindowInfo::Type::APPLICATION_STARTING,
-        gui::WindowInfo::Type::LAST_APPLICATION_WINDOW,
-        gui::WindowInfo::Type::FIRST_SUB_WINDOW,
-        gui::WindowInfo::Type::APPLICATION_PANEL,
-        gui::WindowInfo::Type::APPLICATION_MEDIA,
-        gui::WindowInfo::Type::APPLICATION_SUB_PANEL,
-        gui::WindowInfo::Type::APPLICATION_ATTACHED_DIALOG,
-        gui::WindowInfo::Type::APPLICATION_MEDIA_OVERLAY,
-};
-
-constexpr gui::WindowInfo::InputConfig kFeatures[] = {
-        gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL,
-        gui::WindowInfo::InputConfig::DISABLE_USER_ACTIVITY,
-        gui::WindowInfo::InputConfig::DROP_INPUT,
-        gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED,
-        gui::WindowInfo::InputConfig::SPY,
-        gui::WindowInfo::InputConfig::INTERCEPTS_STYLUS,
-};
-
-class SurfaceComposerClientFuzzer {
-public:
-    SurfaceComposerClientFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
-    void process();
-
-private:
-    void invokeSurfaceComposerClient();
-    void invokeSurfaceComposerClientBinder();
-    void invokeSurfaceComposerTransaction();
-    void getWindowInfo(gui::WindowInfo*);
-    sp<SurfaceControl> makeSurfaceControl();
-    BlurRegion getBlurRegion();
-    void fuzzOnPullAtom();
-    gui::DisplayModeSpecs getDisplayModeSpecs();
-
-    FuzzedDataProvider mFdp;
-};
-
-gui::DisplayModeSpecs SurfaceComposerClientFuzzer::getDisplayModeSpecs() {
-    const auto getRefreshRateRange = [&] {
-        gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range;
-        range.min = mFdp.ConsumeFloatingPoint<float>();
-        range.max = mFdp.ConsumeFloatingPoint<float>();
-        return range;
-    };
-
-    const auto getRefreshRateRanges = [&] {
-        gui::DisplayModeSpecs::RefreshRateRanges ranges;
-        ranges.physical = getRefreshRateRange();
-        ranges.render = getRefreshRateRange();
-        return ranges;
-    };
-
-    String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str());
-    sp<IBinder> displayToken =
-            SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/);
-    gui::DisplayModeSpecs specs;
-    specs.defaultMode = mFdp.ConsumeIntegral<int32_t>();
-    specs.allowGroupSwitching = mFdp.ConsumeBool();
-    specs.primaryRanges = getRefreshRateRanges();
-    specs.appRequestRanges = getRefreshRateRanges();
-    return specs;
-}
-
-BlurRegion SurfaceComposerClientFuzzer::getBlurRegion() {
-    int32_t left = mFdp.ConsumeIntegral<int32_t>();
-    int32_t right = mFdp.ConsumeIntegral<int32_t>();
-    int32_t top = mFdp.ConsumeIntegral<int32_t>();
-    int32_t bottom = mFdp.ConsumeIntegral<int32_t>();
-    uint32_t blurRadius = mFdp.ConsumeIntegral<uint32_t>();
-    float alpha = mFdp.ConsumeFloatingPoint<float>();
-    float cornerRadiusTL = mFdp.ConsumeFloatingPoint<float>();
-    float cornerRadiusTR = mFdp.ConsumeFloatingPoint<float>();
-    float cornerRadiusBL = mFdp.ConsumeFloatingPoint<float>();
-    float cornerRadiusBR = mFdp.ConsumeFloatingPoint<float>();
-    return BlurRegion{blurRadius,     cornerRadiusTL, cornerRadiusTR, cornerRadiusBL,
-                      cornerRadiusBR, alpha,          left,           top,
-                      right,          bottom};
-}
-
-void SurfaceComposerClientFuzzer::getWindowInfo(gui::WindowInfo* windowInfo) {
-    windowInfo->id = mFdp.ConsumeIntegral<int32_t>();
-    windowInfo->name = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes);
-    windowInfo->layoutParamsFlags = mFdp.PickValueInArray(kFlags);
-    windowInfo->layoutParamsType = mFdp.PickValueInArray(kType);
-    windowInfo->frame = Rect(mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<int32_t>(),
-                             mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<int32_t>());
-    windowInfo->surfaceInset = mFdp.ConsumeIntegral<int32_t>();
-    windowInfo->alpha = mFdp.ConsumeFloatingPointInRange<float>(0, 1);
-    ui::Transform transform(mFdp.PickValueInArray(kOrientation));
-    windowInfo->transform = transform;
-    windowInfo->touchableRegion = Region(getRect(&mFdp));
-    windowInfo->replaceTouchableRegionWithCrop = mFdp.ConsumeBool();
-    windowInfo->touchOcclusionMode = mFdp.PickValueInArray(kMode);
-    windowInfo->ownerPid = gui::Pid{mFdp.ConsumeIntegral<pid_t>()};
-    windowInfo->ownerUid = gui::Uid{mFdp.ConsumeIntegral<uid_t>()};
-    windowInfo->packageName = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes);
-    windowInfo->inputConfig = mFdp.PickValueInArray(kFeatures);
-}
-
-sp<SurfaceControl> SurfaceComposerClientFuzzer::makeSurfaceControl() {
-    sp<IBinder> handle;
-    const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient());
-    sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient);
-    sp<BnGraphicBufferProducer> producer;
-    uint32_t width = mFdp.ConsumeIntegral<uint32_t>();
-    uint32_t height = mFdp.ConsumeIntegral<uint32_t>();
-    uint32_t transformHint = mFdp.ConsumeIntegral<uint32_t>();
-    uint32_t flags = mFdp.ConsumeIntegral<uint32_t>();
-    int32_t format = mFdp.ConsumeIntegral<int32_t>();
-    int32_t layerId = mFdp.ConsumeIntegral<int32_t>();
-    std::string layerName = base::StringPrintf("#%d", layerId);
-    return new SurfaceControl(client, handle, layerId, layerName, width, height, format,
-                              transformHint, flags);
-}
-
-void SurfaceComposerClientFuzzer::invokeSurfaceComposerTransaction() {
-    sp<SurfaceControl> surface = makeSurfaceControl();
-
-    SurfaceComposerClient::Transaction transaction;
-    int32_t layer = mFdp.ConsumeIntegral<int32_t>();
-    transaction.setLayer(surface, layer);
-
-    sp<SurfaceControl> relativeSurface = makeSurfaceControl();
-    transaction.setRelativeLayer(surface, relativeSurface, layer);
-
-    Region transparentRegion(getRect(&mFdp));
-    transaction.setTransparentRegionHint(surface, transparentRegion);
-    transaction.setAlpha(surface, mFdp.ConsumeFloatingPoint<float>());
-
-    transaction.setCornerRadius(surface, mFdp.ConsumeFloatingPoint<float>());
-    transaction.setBackgroundBlurRadius(surface, mFdp.ConsumeFloatingPoint<float>());
-    std::vector<BlurRegion> regions;
-    uint32_t vectorSize = mFdp.ConsumeIntegralInRange<uint32_t>(0, 100);
-    regions.resize(vectorSize);
-    for (size_t idx = 0; idx < vectorSize; ++idx) {
-        regions.push_back(getBlurRegion());
-    }
-    transaction.setBlurRegions(surface, regions);
-
-    transaction.setLayerStack(surface, {mFdp.ConsumeIntegral<uint32_t>()});
-    half3 color = {mFdp.ConsumeIntegral<uint32_t>(), mFdp.ConsumeIntegral<uint32_t>(),
-                   mFdp.ConsumeIntegral<uint32_t>()};
-    transaction.setColor(surface, color);
-    transaction.setBackgroundColor(surface, color, mFdp.ConsumeFloatingPoint<float>(),
-                                   mFdp.PickValueInArray(kDataspaces));
-
-    transaction.setApi(surface, mFdp.ConsumeIntegral<int32_t>());
-    transaction.setFrameRateSelectionPriority(surface, mFdp.ConsumeIntegral<int32_t>());
-    transaction.setColorSpaceAgnostic(surface, mFdp.ConsumeBool() /*agnostic*/);
-
-    gui::WindowInfo windowInfo;
-    getWindowInfo(&windowInfo);
-    transaction.setInputWindowInfo(surface, windowInfo);
-    Parcel windowParcel;
-    windowInfo.writeToParcel(&windowParcel);
-    windowParcel.setDataPosition(0);
-    windowInfo.readFromParcel(&windowParcel);
-
-    windowInfo.addTouchableRegion(getRect(&mFdp));
-    int32_t pointX = mFdp.ConsumeIntegral<int32_t>();
-    int32_t pointY = mFdp.ConsumeIntegral<int32_t>();
-    windowInfo.touchableRegionContainsPoint(pointX, pointY);
-    windowInfo.frameContainsPoint(pointX, pointY);
-
-    Parcel transactionParcel;
-    transaction.writeToParcel(&transactionParcel);
-    transactionParcel.setDataPosition(0);
-    transaction.readFromParcel(&transactionParcel);
-    SurfaceComposerClient::Transaction::createFromParcel(&transactionParcel);
-}
-
-void SurfaceComposerClientFuzzer::fuzzOnPullAtom() {
-    std::string outData;
-    bool success;
-    SurfaceComposerClient::onPullAtom(mFdp.ConsumeIntegral<int32_t>(), &outData, &success);
-}
-
-void SurfaceComposerClientFuzzer::invokeSurfaceComposerClient() {
-    String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str());
-    sp<IBinder> displayToken =
-            SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/);
-    SurfaceComposerClient::setDesiredDisplayModeSpecs(displayToken, getDisplayModeSpecs());
-
-    ui::ColorMode colorMode = mFdp.PickValueInArray(kColormodes);
-    SurfaceComposerClient::setActiveColorMode(displayToken, colorMode);
-    SurfaceComposerClient::setAutoLowLatencyMode(displayToken, mFdp.ConsumeBool() /*on*/);
-    SurfaceComposerClient::setGameContentType(displayToken, mFdp.ConsumeBool() /*on*/);
-    SurfaceComposerClient::setDisplayPowerMode(displayToken, mFdp.ConsumeIntegral<int32_t>());
-    SurfaceComposerClient::doUncacheBufferTransaction(mFdp.ConsumeIntegral<uint64_t>());
-
-    SurfaceComposerClient::setDisplayBrightness(displayToken, getBrightness(&mFdp));
-    aidl::android::hardware::power::Boost boostId = mFdp.PickValueInArray(kBoost);
-    SurfaceComposerClient::notifyPowerBoost((int32_t)boostId);
-
-    String8 surfaceName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str());
-    sp<BBinder> handle(new BBinder());
-    sp<BnGraphicBufferProducer> producer;
-    sp<Surface> surfaceParent(
-            new Surface(producer, mFdp.ConsumeBool() /*controlledByApp*/, handle));
-
-    fuzzOnPullAtom();
-    SurfaceComposerClient::setDisplayContentSamplingEnabled(displayToken,
-                                                            mFdp.ConsumeBool() /*enable*/,
-                                                            mFdp.ConsumeIntegral<uint8_t>(),
-                                                            mFdp.ConsumeIntegral<uint64_t>());
-
-    sp<IBinder> stopLayerHandle;
-    sp<gui::IRegionSamplingListener> listener = sp<gui::IRegionSamplingListenerDefault>::make();
-    sp<gui::IRegionSamplingListenerDelegator> sampleListener =
-            new gui::IRegionSamplingListenerDelegator(listener);
-    SurfaceComposerClient::addRegionSamplingListener(getRect(&mFdp), stopLayerHandle,
-                                                     sampleListener);
-    sp<gui::IFpsListenerDefault> fpsListener;
-    SurfaceComposerClient::addFpsListener(mFdp.ConsumeIntegral<int32_t>(), fpsListener);
-}
-
-void SurfaceComposerClientFuzzer::invokeSurfaceComposerClientBinder() {
-    sp<FakeBnSurfaceComposerClient> client(new FakeBnSurfaceComposerClient());
-    fuzzService(client.get(), std::move(mFdp));
-}
-
-void SurfaceComposerClientFuzzer::process() {
-    invokeSurfaceComposerClient();
-    invokeSurfaceComposerTransaction();
-    invokeSurfaceComposerClientBinder();
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    SurfaceComposerClientFuzzer surfaceComposerClientFuzzer(data, size);
-    surfaceComposerClientFuzzer.process();
-    return 0;
-}
diff --git a/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp
deleted file mode 100644
index 6d5427b..0000000
--- a/libs/gui/fuzzer/libgui_surfaceComposer_fuzzer.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fuzzbinder/libbinder_driver.h>
-#include <fuzzer/FuzzedDataProvider.h>
-#include <libgui_fuzzer_utils.h>
-
-using namespace android;
-
-class SurfaceComposerFuzzer {
-public:
-    SurfaceComposerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
-    void process();
-
-private:
-    FuzzedDataProvider mFdp;
-};
-
-void SurfaceComposerFuzzer::process() {
-    sp<FakeBnSurfaceComposer> composer(new FakeBnSurfaceComposer());
-    fuzzService(composer.get(), std::move(mFdp));
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    SurfaceComposerFuzzer surfaceComposerFuzzer(data, size);
-    surfaceComposerFuzzer.process();
-    return 0;
-}
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/InputDevice.cpp b/libs/input/InputDevice.cpp
index d4dbc45..c348833 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -20,12 +20,14 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <ftl/enum.h>
 #include <gui/constants.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
 
+using android::base::GetProperty;
 using android::base::StringPrintf;
 
 namespace android {
@@ -96,21 +98,22 @@
 
     // Treblized input device config files will be located /product/usr, /system_ext/usr,
     // /odm/usr or /vendor/usr.
-    // These files may also be in the com.android.input.config APEX.
-    const char* rootsForPartition[]{
-            "/product",
-            "/system_ext",
-            "/odm",
-            "/vendor",
-            "/apex/com.android.input.config/etc",
-            getenv("ANDROID_ROOT"),
+    std::vector<std::string> pathPrefixes{
+            "/product/usr/",
+            "/system_ext/usr/",
+            "/odm/usr/",
+            "/vendor/usr/",
     };
-    for (size_t i = 0; i < size(rootsForPartition); i++) {
-        if (rootsForPartition[i] == nullptr) {
-            continue;
-        }
-        path = rootsForPartition[i];
-        path += "/usr/";
+    // These files may also be in the APEX pointed by input_device.config_file.apex sysprop.
+    if (auto apex = GetProperty("input_device.config_file.apex", ""); !apex.empty()) {
+        pathPrefixes.push_back("/apex/" + apex + "/etc/usr/");
+    }
+    // ANDROID_ROOT may not be set on host
+    if (auto android_root = getenv("ANDROID_ROOT"); android_root != nullptr) {
+        pathPrefixes.push_back(std::string(android_root) + "/usr/");
+    }
+    for (const auto& prefix : pathPrefixes) {
+        path = prefix;
         appendInputDeviceConfigurationFileRelativePath(path, name, type);
 #if DEBUG_PROBE
         ALOGD("Probing for system provided input device configuration file: path='%s'",
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/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8d43f3b..8858f0c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -54,6 +54,7 @@
 #include "InputDispatcher.h"
 #include "trace/InputTracer.h"
 #include "trace/InputTracingPerfettoBackend.h"
+#include "trace/ThreadedBackend.h"
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -86,6 +87,15 @@
     return input_flags::enable_input_event_tracing() && isUserdebugOrEng;
 }
 
+// Create the input tracing backend that writes to perfetto from a single thread.
+std::unique_ptr<trace::InputTracingBackendInterface> createInputTracingBackendIfEnabled() {
+    if (!isInputTracingEnabled()) {
+        return nullptr;
+    }
+    return std::make_unique<trace::impl::ThreadedBackend<trace::impl::PerfettoBackend>>(
+            trace::impl::PerfettoBackend());
+}
+
 template <class Entry>
 void ensureEventTraced(const Entry& entry) {
     if (!entry.traceTracker) {
@@ -365,17 +375,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 +399,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 +445,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;
 }
@@ -823,9 +842,7 @@
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
-      : InputDispatcher(policy,
-                        isInputTracingEnabled() ? std::make_unique<trace::impl::PerfettoBackend>()
-                                                : nullptr) {}
+      : InputDispatcher(policy, createInputTracingBackendIfEnabled()) {}
 
 InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
                                  std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
@@ -1303,6 +1320,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 +1648,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 +1719,8 @@
         }
         return;
     }
-    InputTarget target;
-    target.connection = connection;
     entry->dispatchInProgress = true;
-    dispatchEventLocked(currentTime, entry, {target});
+    dispatchEventLocked(currentTime, entry, {{connection}});
 
     dropReason = DropReason::NOT_DROPPED;
 }
@@ -1739,9 +1753,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 +2034,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 +2878,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 +2991,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 +3283,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 +3305,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 +3314,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 +3372,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 +3494,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 +3673,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 +4112,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 +4230,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/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index 8a855c2..be09013 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -19,12 +19,17 @@
 #include "InputTracer.h"
 
 #include <android-base/logging.h>
-#include <utils/AndroidThreads.h>
 
 namespace android::inputdispatcher::trace::impl {
 
 namespace {
 
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {
+    using V::operator()...;
+};
+
 TracedEvent createTracedEvent(const MotionEntry& e) {
     return TracedMotionEvent{e.id,
                              e.eventTime,
@@ -59,19 +64,9 @@
 // --- InputTracer ---
 
 InputTracer::InputTracer(std::unique_ptr<InputTracingBackendInterface> backend)
-      : mTracerThread(&InputTracer::threadLoop, this), mBackend(std::move(backend)) {}
-
-InputTracer::~InputTracer() {
-    {
-        std::scoped_lock lock(mLock);
-        mThreadExit = true;
-    }
-    mThreadWakeCondition.notify_all();
-    mTracerThread.join();
-}
+      : mBackend(std::move(backend)) {}
 
 std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) {
-    std::scoped_lock lock(mLock);
     TracedEvent traced;
 
     if (entry.type == EventEntry::Type::MOTION) {
@@ -89,7 +84,6 @@
 
 void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
                                        const InputTarget& target) {
-    std::scoped_lock lock(mLock);
     auto& cookieState = getState(cookie);
     if (!cookieState) {
         LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()";
@@ -98,127 +92,72 @@
 }
 
 void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
-    {
-        std::scoped_lock lock(mLock);
-        auto& cookieState = getState(cookie);
-        if (!cookieState) {
-            LOG(FATAL) << "Traced event was already logged. "
-                          "eventProcessingComplete() was likely called more than once.";
-        }
-        mTraceQueue.emplace_back(std::move(*cookieState));
-        cookieState.reset();
-    } // release lock
+    auto& cookieState = getState(cookie);
+    if (!cookieState) {
+        LOG(FATAL) << "Traced event was already logged. "
+                      "eventProcessingComplete() was likely called more than once.";
+    }
 
-    mThreadWakeCondition.notify_all();
+    std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
+                       [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
+               cookieState->event);
+    cookieState.reset();
 }
 
 void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
                                      const EventTrackerInterface* cookie) {
-    {
-        std::scoped_lock lock(mLock);
-        const EventEntry& entry = *dispatchEntry.eventEntry;
+    const EventEntry& entry = *dispatchEntry.eventEntry;
 
-        TracedEvent traced;
-        if (entry.type == EventEntry::Type::MOTION) {
-            const auto& motion = static_cast<const MotionEntry&>(entry);
-            traced = createTracedEvent(motion);
-        } else if (entry.type == EventEntry::Type::KEY) {
-            const auto& key = static_cast<const KeyEntry&>(entry);
-            traced = createTracedEvent(key);
-        } else {
-            LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
-        }
+    TracedEvent traced;
+    if (entry.type == EventEntry::Type::MOTION) {
+        const auto& motion = static_cast<const MotionEntry&>(entry);
+        traced = createTracedEvent(motion);
+    } else if (entry.type == EventEntry::Type::KEY) {
+        const auto& key = static_cast<const KeyEntry&>(entry);
+        traced = createTracedEvent(key);
+    } else {
+        LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
+    }
 
-        if (!cookie) {
-            // This event was not tracked as an inbound event, so trace it now.
-            mTraceQueue.emplace_back(traced);
-        }
+    if (!cookie) {
+        // This event was not tracked as an inbound event, so trace it now.
+        std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
+                           [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
+                   traced);
+    }
 
-        // The vsyncId only has meaning if the event is targeting a window.
-        const int32_t windowId = dispatchEntry.windowId.value_or(0);
-        const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;
+    // The vsyncId only has meaning if the event is targeting a window.
+    const int32_t windowId = dispatchEntry.windowId.value_or(0);
+    const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;
 
-        mDispatchTraceQueue.emplace_back(std::move(traced), dispatchEntry.deliveryTime,
-                                         dispatchEntry.resolvedFlags, dispatchEntry.targetUid,
-                                         vsyncId, windowId, dispatchEntry.transform,
-                                         dispatchEntry.rawTransform);
-    } // release lock
-
-    mThreadWakeCondition.notify_all();
+    mBackend->traceWindowDispatch({std::move(traced), dispatchEntry.deliveryTime,
+                                   dispatchEntry.resolvedFlags, dispatchEntry.targetUid, vsyncId,
+                                   windowId, dispatchEntry.transform, dispatchEntry.rawTransform,
+                                   /*hmac=*/{}});
 }
 
 std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) {
-    return static_cast<const EventTrackerImpl&>(cookie).mLockedState;
-}
-
-void InputTracer::threadLoop() {
-    androidSetThreadName("InputTracer");
-
-    std::vector<const EventState> eventsToTrace;
-    std::vector<const WindowDispatchArgs> dispatchEventsToTrace;
-
-    while (true) {
-        { // acquire lock
-            std::unique_lock lock(mLock);
-            base::ScopedLockAssertion assumeLocked(mLock);
-
-            // Wait until we need to process more events or exit.
-            mThreadWakeCondition.wait(lock, [&]() REQUIRES(mLock) {
-                return mThreadExit || !mTraceQueue.empty() || !mDispatchTraceQueue.empty();
-            });
-            if (mThreadExit) {
-                return;
-            }
-
-            mTraceQueue.swap(eventsToTrace);
-            mDispatchTraceQueue.swap(dispatchEventsToTrace);
-        } // release lock
-
-        // Trace the events into the backend without holding the lock to reduce the amount of
-        // work performed in the critical section.
-        writeEventsToBackend(eventsToTrace, dispatchEventsToTrace);
-        eventsToTrace.clear();
-        dispatchEventsToTrace.clear();
-    }
-}
-
-void InputTracer::writeEventsToBackend(
-        const std::vector<const EventState>& events,
-        const std::vector<const WindowDispatchArgs>& dispatchEvents) {
-    for (const auto& event : events) {
-        if (auto* motion = std::get_if<TracedMotionEvent>(&event.event); motion != nullptr) {
-            mBackend->traceMotionEvent(*motion);
-        } else {
-            mBackend->traceKeyEvent(std::get<TracedKeyEvent>(event.event));
-        }
-    }
-
-    for (const auto& dispatchArgs : dispatchEvents) {
-        mBackend->traceWindowDispatch(dispatchArgs);
-    }
+    return static_cast<const EventTrackerImpl&>(cookie).mState;
 }
 
 // --- InputTracer::EventTrackerImpl ---
 
 InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event)
-      : mTracer(tracer), mLockedState(event) {}
+      : mTracer(tracer), mState(event) {}
 
 InputTracer::EventTrackerImpl::~EventTrackerImpl() {
-    {
-        std::scoped_lock lock(mTracer.mLock);
-        if (!mLockedState) {
-            // This event has already been written to the trace as expected.
-            return;
-        }
-        // We're still holding on to the state, which means it hasn't yet been written to the trace.
-        // Write it to the trace now.
-        // TODO(b/210460522): Determine why/where the event is being destroyed before
-        //   eventProcessingComplete() is called.
-        mTracer.mTraceQueue.emplace_back(std::move(*mLockedState));
-        mLockedState.reset();
-    } // release lock
-
-    mTracer.mThreadWakeCondition.notify_all();
+    if (!mState) {
+        // This event has already been written to the trace as expected.
+        return;
+    }
+    // We're still holding on to the state, which means it hasn't yet been written to the trace.
+    // Write it to the trace now.
+    // TODO(b/210460522): Determine why/where the event is being destroyed before
+    //   eventProcessingComplete() is called.
+    std::visit(Visitor{[&](const TracedMotionEvent& e) { mTracer.mBackend->traceMotionEvent(e); },
+                       [&](const TracedKeyEvent& e) { mTracer.mBackend->traceKeyEvent(e); }},
+               mState->event);
+    mState.reset();
 }
 
 } // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index 9fe395d..c8b25c9 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -18,14 +18,7 @@
 
 #include "InputTracerInterface.h"
 
-#include <android-base/thread_annotations.h>
-#include <gui/WindowInfo.h>
-
 #include <memory>
-#include <mutex>
-#include <thread>
-#include <unordered_set>
-#include <vector>
 
 #include "../Entry.h"
 #include "InputTracingBackendInterface.h"
@@ -35,17 +28,16 @@
 /**
  * The tracer implementation for InputDispatcher.
  *
- * InputTracer is thread-safe, so it can be called from any thread. Upon construction, InputTracer
- * will start its own thread that it uses for write events into the tracing backend. That is the
- * one and only thread that will interact with the tracing backend, since the Perfetto backend
- * uses thread-local storage.
+ * InputTracer's responsibility is to keep track of events as they are processed by InputDispatcher,
+ * and to write the events to the tracing backend when enough information is collected. InputTracer
+ * is not thread-safe.
  *
  * See the documentation in InputTracerInterface for the API surface.
  */
 class InputTracer : public InputTracerInterface {
 public:
     explicit InputTracer(std::unique_ptr<InputTracingBackendInterface>);
-    ~InputTracer() override;
+    ~InputTracer() = default;
     InputTracer(const InputTracer&) = delete;
     InputTracer& operator=(const InputTracer&) = delete;
 
@@ -55,10 +47,6 @@
     void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override;
 
 private:
-    std::mutex mLock;
-    std::thread mTracerThread;
-    bool mThreadExit GUARDED_BY(mLock){false};
-    std::condition_variable mThreadWakeCondition;
     std::unique_ptr<InputTracingBackendInterface> mBackend;
 
     // The state of a tracked event.
@@ -67,14 +55,12 @@
         // TODO(b/210460522): Add additional args for tracking event sensitivity and
         //  dispatch target UIDs.
     };
-    std::vector<const EventState> mTraceQueue GUARDED_BY(mLock);
-    using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs;
-    std::vector<const WindowDispatchArgs> mDispatchTraceQueue GUARDED_BY(mLock);
 
-    // Provides thread-safe access to the state from an event tracker cookie.
-    std::optional<EventState>& getState(const EventTrackerInterface&) REQUIRES(mLock);
+    // Get the event state associated with a tracking cookie.
+    std::optional<EventState>& getState(const EventTrackerInterface&);
 
-    // Implementation of the event tracker cookie.
+    // Implementation of the event tracker cookie. The cookie holds the event state directly for
+    // convenience to avoid the overhead of tracking the state separately in InputTracer.
     class EventTrackerImpl : public EventTrackerInterface {
     public:
         explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry);
@@ -84,16 +70,10 @@
         InputTracer& mTracer;
         // This event tracker cookie will only hold the state as long as it has not been written
         // to the trace. The state is released when the event is written to the trace.
-        mutable std::optional<EventState> mLockedState;
+        mutable std::optional<EventState> mState;
 
-        // Only allow InputTracer access to the locked state through getTrackerState() to ensure
-        // that the InputTracer lock is held when this is accessed.
         friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&);
     };
-
-    void threadLoop();
-    void writeEventsToBackend(const std::vector<const EventState>& events,
-                              const std::vector<const WindowDispatchArgs>& dispatchEvents);
 };
 
 } // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index bc47817..b0eadfe 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -82,10 +82,10 @@
     virtual ~InputTracingBackendInterface() = default;
 
     /** Trace a KeyEvent. */
-    virtual void traceKeyEvent(const TracedKeyEvent&) const = 0;
+    virtual void traceKeyEvent(const TracedKeyEvent&) = 0;
 
     /** Trace a MotionEvent. */
-    virtual void traceMotionEvent(const TracedMotionEvent&) const = 0;
+    virtual void traceMotionEvent(const TracedMotionEvent&) = 0;
 
     /** Trace an event being sent to a window. */
     struct WindowDispatchArgs {
@@ -99,7 +99,7 @@
         ui::Transform rawTransform;
         std::array<uint8_t, 32> hmac;
     };
-    virtual void traceWindowDispatch(const WindowDispatchArgs&) const = 0;
+    virtual void traceWindowDispatch(const WindowDispatchArgs&) = 0;
 };
 
 } // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
index 4442ad8..46ad9e1 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -63,7 +63,7 @@
     });
 }
 
-void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) const {
+void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) {
     InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
         auto tracePacket = ctx.NewTracePacket();
         auto* inputEvent = tracePacket->set_android_input_event();
@@ -72,7 +72,7 @@
     });
 }
 
-void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) const {
+void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) {
     InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
         auto tracePacket = ctx.NewTracePacket();
         auto* inputEvent = tracePacket->set_android_input_event();
@@ -81,7 +81,7 @@
     });
 }
 
-void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) const {
+void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) {
     InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
         auto tracePacket = ctx.NewTracePacket();
         auto* inputEventProto = tracePacket->set_android_input_event();
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
index 2777cfe..fefcfb3 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
@@ -48,9 +48,9 @@
     PerfettoBackend();
     ~PerfettoBackend() override = default;
 
-    void traceKeyEvent(const TracedKeyEvent&) const override;
-    void traceMotionEvent(const TracedMotionEvent&) const override;
-    void traceWindowDispatch(const WindowDispatchArgs&) const override;
+    void traceKeyEvent(const TracedKeyEvent&) override;
+    void traceMotionEvent(const TracedMotionEvent&) override;
+    void traceWindowDispatch(const WindowDispatchArgs&) override;
 
     class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> {
     public:
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
new file mode 100644
index 0000000..25bc227
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "InputTracer"
+
+#include "ThreadedBackend.h"
+
+#include "InputTracingPerfettoBackend.h"
+
+#include <android-base/logging.h>
+
+namespace android::inputdispatcher::trace::impl {
+
+namespace {
+
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {
+    using V::operator()...;
+};
+
+} // namespace
+
+// --- ThreadedBackend ---
+
+template <typename Backend>
+ThreadedBackend<Backend>::ThreadedBackend(Backend&& innerBackend)
+      : mTracerThread(
+                "InputTracer", [this]() { threadLoop(); },
+                [this]() { mThreadWakeCondition.notify_all(); }),
+        mBackend(std::move(innerBackend)) {}
+
+template <typename Backend>
+ThreadedBackend<Backend>::~ThreadedBackend() {
+    {
+        std::scoped_lock lock(mLock);
+        mThreadExit = true;
+    }
+    mThreadWakeCondition.notify_all();
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event) {
+    std::scoped_lock lock(mLock);
+    mQueue.emplace_back(event);
+    mThreadWakeCondition.notify_all();
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event) {
+    std::scoped_lock lock(mLock);
+    mQueue.emplace_back(event);
+    mThreadWakeCondition.notify_all();
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) {
+    std::scoped_lock lock(mLock);
+    mQueue.emplace_back(dispatchArgs);
+    mThreadWakeCondition.notify_all();
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::threadLoop() {
+    std::vector<TraceEntry> entries;
+
+    { // acquire lock
+        std::unique_lock lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+
+        // Wait until we need to process more events or exit.
+        mThreadWakeCondition.wait(lock,
+                                  [&]() REQUIRES(mLock) { return mThreadExit || !mQueue.empty(); });
+        if (mThreadExit) {
+            return;
+        }
+
+        mQueue.swap(entries);
+    } // release lock
+
+    // Trace the events into the backend without holding the lock to reduce the amount of
+    // work performed in the critical section.
+    for (const auto& entry : entries) {
+        std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend.traceMotionEvent(e); },
+                           [&](const TracedKeyEvent& e) { mBackend.traceKeyEvent(e); },
+                           [&](const WindowDispatchArgs& args) {
+                               mBackend.traceWindowDispatch(args);
+                           }},
+                   entry);
+    }
+    entries.clear();
+}
+
+// Explicit template instantiation for the PerfettoBackend.
+template class ThreadedBackend<PerfettoBackend>;
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.h b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
new file mode 100644
index 0000000..5776cf9
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include "InputThread.h"
+#include "InputTracingPerfettoBackend.h"
+
+#include <android-base/thread_annotations.h>
+#include <mutex>
+#include <variant>
+#include <vector>
+
+namespace android::inputdispatcher::trace::impl {
+
+/**
+ * A wrapper around an InputTracingBackend implementation that writes to the inner tracing backend
+ * from a single new thread that it creates. The new tracing thread is started when the
+ * ThreadedBackend is created, and is stopped when it is destroyed. The ThreadedBackend is
+ * thread-safe.
+ */
+template <typename Backend>
+class ThreadedBackend : public InputTracingBackendInterface {
+public:
+    ThreadedBackend(Backend&& innerBackend);
+    ~ThreadedBackend() override;
+
+    void traceKeyEvent(const TracedKeyEvent&) override;
+    void traceMotionEvent(const TracedMotionEvent&) override;
+    void traceWindowDispatch(const WindowDispatchArgs&) override;
+
+private:
+    std::mutex mLock;
+    InputThread mTracerThread;
+    bool mThreadExit GUARDED_BY(mLock){false};
+    std::condition_variable mThreadWakeCondition;
+    Backend mBackend;
+    using TraceEntry = std::variant<TracedKeyEvent, TracedMotionEvent, WindowDispatchArgs>;
+    std::vector<TraceEntry> mQueue GUARDED_BY(mLock);
+
+    using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs;
+
+    void threadLoop();
+};
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 19788ce..764bb56 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -67,7 +67,7 @@
       : mDeviceId(deviceId),
         mReaderContext(readerContext),
         mPointerController(readerContext.getPointerController(deviceId)),
-        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);
 }
@@ -174,8 +174,7 @@
                                                    const Gesture& gesture) {
     float deltaX = gesture.details.move.dx;
     float deltaY = gesture.details.move.dy;
-    const auto [oldXCursorPosition, oldYCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [oldXCursorPosition, oldYCursorPosition] = mPointerController->getPosition();
     if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) {
         bool wasHoverCancelled = mIsHoverCancelled;
         // Gesture will be cancelled if it started before the user started typing and
@@ -213,8 +212,7 @@
     if (!down) {
         out += enterHover(when, readTime, oldXCursorPosition, oldYCursorPosition);
     }
-    const auto [newXCursorPosition, newYCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [newXCursorPosition, newYCursorPosition] = mPointerController->getPosition();
 
     PointerCoords coords;
     coords.clear();
@@ -238,8 +236,7 @@
     mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
     mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
 
-    const auto [xCursorPosition, yCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
 
     PointerCoords coords;
     coords.clear();
@@ -319,8 +316,7 @@
 
 std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
     std::list<NotifyArgs> out;
-    const auto [xCursorPosition, yCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
 
     PointerCoords coords;
     coords.clear();
@@ -355,8 +351,7 @@
                                                      const Gesture& gesture) {
     std::list<NotifyArgs> out;
     PointerCoords& coords = mFakeFingerCoords[0];
-    const auto [xCursorPosition, yCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
     if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
         out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
 
@@ -400,20 +395,54 @@
                 // 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] =
+                            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:
@@ -425,8 +454,7 @@
 
 std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
     std::list<NotifyArgs> out;
-    const auto [xCursorPosition, yCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
     NotifyMotionArgs args =
@@ -446,8 +474,7 @@
                                                                              float dx, float dy) {
     std::list<NotifyArgs> out = {};
 
-    const auto [xCursorPosition, yCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
         // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
         // three and then put a fourth finger down), the gesture library will treat it as two
@@ -511,8 +538,7 @@
     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
         return out;
     }
-    const auto [xCursorPosition, yCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
 
@@ -535,8 +561,7 @@
 
 [[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
                                                                   const Gesture& gesture) {
-    const auto [xCursorPosition, yCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
 
     // Pinch gesture phases are reported a little differently from others, in that the same details
     // struct is used for all phases of the gesture, just with different zoom_state values. When
@@ -594,8 +619,7 @@
 
 std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
     std::list<NotifyArgs> out;
-    const auto [xCursorPosition, yCursorPosition] =
-            mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+    const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
 
     mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
     out.push_back(makeMotionArgs(when, readTime,
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 07cc56c..c8f437e 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -105,7 +105,7 @@
     const int32_t mDeviceId;
     InputReaderContext& mReaderContext;
     std::shared_ptr<PointerControllerInterface> mPointerController;
-    const bool mEnablePointerChoreographer;
+    const bool mEnableFlingStop;
 
     std::optional<int32_t> mDisplayId;
     FloatRect mBoundsInLogicalDisplay{};
@@ -120,6 +120,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/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs
index 01165b5..09fbf40 100644
--- a/services/inputflinger/rust/slow_keys_filter.rs
+++ b/services/inputflinger/rust/slow_keys_filter.rs
@@ -28,6 +28,9 @@
 use std::collections::HashSet;
 use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
 
+// Policy flags from Input.h
+const POLICY_FLAG_DISABLE_KEY_REPEAT: i32 = 0x08000000;
+
 #[derive(Debug)]
 struct OngoingKeyDown {
     scancode: i32,
@@ -129,6 +132,12 @@
                     let mut pending_event = *event;
                     pending_event.downTime += slow_filter.slow_key_threshold_ns;
                     pending_event.eventTime = pending_event.downTime;
+                    // Currently a slow keys user ends up repeating the presses key quite often
+                    // since default repeat thresholds are very low, so blocking repeat for events
+                    // when slow keys is enabled.
+                    // TODO(b/322327461): Allow key repeat with slow keys, once repeat key rate and
+                    //  thresholds can be modified in the settings.
+                    pending_event.policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
                     slow_filter.pending_down_events.push(pending_event);
                 }
                 KeyEventAction::UP => {
@@ -200,7 +209,7 @@
 mod tests {
     use crate::input_filter::{test_callbacks::TestCallbacks, test_filter::TestFilter, Filter};
     use crate::input_filter_thread::test_thread::TestThread;
-    use crate::slow_keys_filter::SlowKeysFilter;
+    use crate::slow_keys_filter::{SlowKeysFilter, POLICY_FLAG_DISABLE_KEY_REPEAT};
     use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
     use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
         DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
@@ -285,6 +294,7 @@
                 action: KeyEventAction::DOWN,
                 downTime: 100,
                 eventTime: 100,
+                policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT,
                 ..BASE_KEY_EVENT
             }
         );
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp
index f4a06f7..4655ee8 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,33 +126,60 @@
                          });
     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 ---
 
-void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) const {
+void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) {
     {
         std::scoped_lock lock(mTrace->mLock);
-        mTrace->mTracedEvents.emplace(event.id);
+        mTrace->mTracedEvents.emplace(event.id, event);
     }
     mTrace->mEventTracedCondition.notify_all();
 }
 
-void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) const {
+void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) {
     {
         std::scoped_lock lock(mTrace->mLock);
-        mTrace->mTracedEvents.emplace(event.id);
+        mTrace->mTracedEvents.emplace(event.id, event);
     }
     mTrace->mEventTracedCondition.notify_all();
 }
 
-void FakeInputTracingBackend::traceWindowDispatch(const WindowDispatchArgs& args) const {
+void FakeInputTracingBackend::traceWindowDispatch(const WindowDispatchArgs& args) {
     {
         std::scoped_lock lock(mTrace->mLock);
         mTrace->mTracedWindowDispatches.push_back(args);
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h
index 40ca3a2..1b3613d 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*/>>
@@ -83,9 +83,9 @@
 private:
     std::shared_ptr<VerifyingTrace> mTrace;
 
-    void traceKeyEvent(const trace::TracedKeyEvent& entry) const override;
-    void traceMotionEvent(const trace::TracedMotionEvent& entry) const override;
-    void traceWindowDispatch(const WindowDispatchArgs& entry) const override;
+    void traceKeyEvent(const trace::TracedKeyEvent& entry) override;
+    void traceMotionEvent(const trace::TracedMotionEvent& entry) override;
+    void traceWindowDispatch(const WindowDispatchArgs& entry) override;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 31e1173..dc199e2 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -37,16 +37,22 @@
 }
 
 void FakePointerController::setPosition(float x, float y) {
+    if (!mEnabled) return;
+
     mX = x;
     mY = y;
 }
 
 FloatPoint FakePointerController::getPosition() const {
+    if (!mEnabled) {
+        return {0, 0};
+    }
+
     return {mX, mY};
 }
 
 int32_t FakePointerController::getDisplayId() const {
-    if (!mDisplayId) {
+    if (!mEnabled || !mDisplayId) {
         return ADISPLAY_ID_NONE;
     }
     return *mDisplayId;
@@ -64,6 +70,8 @@
 }
 
 void FakePointerController::setCustomPointerIcon(const SpriteIcon& icon) {
+    if (!mEnabled) return;
+
     ASSERT_FALSE(mCustomIconStyle.has_value()) << "Custom pointer icon was set more than once";
     mCustomIconStyle = icon.style;
 }
@@ -114,10 +122,14 @@
 }
 
 std::optional<FloatRect> FakePointerController::getBounds() const {
+    if (!mEnabled) return std::nullopt;
+
     return mHaveBounds ? std::make_optional<FloatRect>(mMinX, mMinY, mMaxX, mMaxY) : std::nullopt;
 }
 
 void FakePointerController::move(float deltaX, float deltaY) {
+    if (!mEnabled) return;
+
     mX += deltaX;
     if (mX < mMinX) mX = mMinX;
     if (mX > mMaxX) mX = mMaxX;
@@ -127,14 +139,20 @@
 }
 
 void FakePointerController::fade(Transition) {
+    if (!mEnabled) return;
+
     mIsPointerShown = false;
 }
 void FakePointerController::unfade(Transition) {
+    if (!mEnabled) return;
+
     mIsPointerShown = true;
 }
 
 void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
                                      int32_t displayId) {
+    if (!mEnabled) return;
+
     std::vector<int32_t> newSpots;
     // Add spots for fingers that are down.
     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
@@ -146,6 +164,8 @@
 }
 
 void FakePointerController::clearSpots() {
+    if (!mEnabled) return;
+
     mSpotsByDisplay.clear();
 }
 
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 061ae62..536b447 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -30,6 +30,9 @@
 
 class FakePointerController : public PointerControllerInterface {
 public:
+    FakePointerController() : FakePointerController(/*enabled=*/true) {}
+    FakePointerController(bool enabled) : mEnabled(enabled) {}
+
     virtual ~FakePointerController() {}
 
     void setBounds(float minX, float minY, float maxX, float maxY);
@@ -64,6 +67,7 @@
                   int32_t displayId) override;
     void clearSpots() override;
 
+    const bool mEnabled;
     bool mHaveBounds{false};
     float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
     float mX{0}, mY{0};
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index dd88165..337b52b 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -69,7 +69,8 @@
         mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, -500, 500, 0, 0, 20);
         mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, -500, 500, 0, 0, 20);
 
-        mFakePointerController = std::make_shared<FakePointerController>();
+        mFakePointerController = std::make_shared<FakePointerController>(
+                /*enabled=*/!input_flags::enable_pointer_choreographer());
         mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
         mFakePointerController->setPosition(POINTER_X, POINTER_Y);
         mFakePolicy->setPointerController(mFakePointerController);
@@ -1167,6 +1168,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 +2589,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/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 1c2f6cb..921e05d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1276,7 +1276,9 @@
     if (isProtected && supportsProtectedContent) {
         auto layers = getOutputLayersOrderedByZ();
         bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
-            return layer->getLayerFE().getCompositionState()->hasProtectedContent;
+            return layer->getLayerFE().getCompositionState()->hasProtectedContent &&
+                    (!FlagManager::getInstance().protected_if_client() ||
+                     layer->requiresClientComposition());
         });
         if (needsProtected != mRenderSurface->isProtected()) {
             mRenderSurface->setProtected(needsProtected);
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 62cfaf4..799c7ed 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -4051,6 +4051,7 @@
         Layer() {
             EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
             EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
+            EXPECT_CALL(mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
         }
 
         StrictMock<mock::OutputLayer> mOutputLayer;
@@ -4091,6 +4092,7 @@
 };
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
+    SET_FLAG_FOR_TEST(flags::protected_if_client, true);
     if (FlagManager::getInstance().display_protected()) {
         mOutput.mState.isProtected = true;
     } else {
@@ -4109,6 +4111,7 @@
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
+    SET_FLAG_FOR_TEST(flags::protected_if_client, true);
     if (FlagManager::getInstance().display_protected()) {
         mOutput.mState.isProtected = true;
     } else {
@@ -4135,6 +4138,7 @@
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
+    SET_FLAG_FOR_TEST(flags::protected_if_client, true);
     if (FlagManager::getInstance().display_protected()) {
         mOutput.mState.isProtected = true;
     } else {
@@ -4152,6 +4156,7 @@
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
+    SET_FLAG_FOR_TEST(flags::protected_if_client, true);
     if (FlagManager::getInstance().display_protected()) {
         mOutput.mState.isProtected = true;
     } else {
@@ -5096,5 +5101,79 @@
     mOutput->present(mRefreshArgs);
 }
 
+/*
+ * Output::updateProtectedContentState()
+ */
+
+struct OutputUpdateProtectedContentStateTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
+    };
+
+    OutputUpdateProtectedContentStateTest() {
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+        EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
+        EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+    }
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        sp<StrictMock<mock::LayerFE>> mLayerFE = sp<StrictMock<mock::LayerFE>>::make();
+        LayerFECompositionState mLayerFEState;
+    };
+
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+    Layer mLayer1;
+    Layer mLayer2;
+};
+
+TEST_F(OutputUpdateProtectedContentStateTest, ifProtectedContentLayerComposeByHWC) {
+    SET_FLAG_FOR_TEST(flags::protected_if_client, true);
+    if (FlagManager::getInstance().display_protected()) {
+        mOutput.mState.isProtected = true;
+    } else {
+        mOutput.mState.isSecure = true;
+    }
+    mLayer1.mLayerFEState.hasProtectedContent = false;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(mLayer1.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mLayer2.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    mOutput.updateProtectedContentState();
+}
+
+TEST_F(OutputUpdateProtectedContentStateTest, ifProtectedContentLayerComposeByClient) {
+    SET_FLAG_FOR_TEST(flags::protected_if_client, true);
+    if (FlagManager::getInstance().display_protected()) {
+        mOutput.mState.isProtected = true;
+    } else {
+        mOutput.mState.isSecure = true;
+    }
+    mLayer1.mLayerFEState.hasProtectedContent = false;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, setProtected(true));
+    EXPECT_CALL(mLayer1.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mLayer2.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    mOutput.updateProtectedContentState();
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index cfa0339..776bcd3 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -92,7 +92,7 @@
         mMaxVirtualDisplayDimension(static_cast<size_t>(sysprop::max_virtual_display_dimension(0))),
         mUpdateDeviceProductInfoOnHotplugReconnect(
                 sysprop::update_device_product_info_on_hotplug_reconnect(false)),
-        mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, false)) {}
+        mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, true)) {}
 
 HWComposer::HWComposer(const std::string& composerServiceName)
       : HWComposer(Hwc2::Composer::create(composerServiceName)) {}
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/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index a145e59..ff88d71 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -17,7 +17,6 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <binder/IPCThreadState.h>
-#include <gui/DisplayEventReceiver.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <utils/threads.h>
@@ -66,7 +65,7 @@
     {
         std::lock_guard lock(mVsync.mutex);
         mVsync.lastCallbackTime = expectedVsyncTime;
-        mVsync.scheduledFrameTime.reset();
+        mVsync.scheduledFrameTimeOpt.reset();
     }
 
     const auto vsyncId = VsyncId{mVsync.tokenManager->generateTokenForPredictions(
@@ -122,7 +121,7 @@
                                                             std::placeholders::_3),
                                                   "sf");
     if (reschedule) {
-        mVsync.scheduledFrameTime =
+        mVsync.scheduledFrameTimeOpt =
                 mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
                                                .readyDuration = 0,
                                                .lastVsync = mVsync.lastCallbackTime.ns()});
@@ -140,7 +139,7 @@
     ATRACE_CALL();
     std::lock_guard lock(mVsync.mutex);
     mVsync.workDuration = workDuration;
-    mVsync.scheduledFrameTime =
+    mVsync.scheduledFrameTimeOpt =
             mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(),
                                          .readyDuration = 0,
                                          .lastVsync = mVsync.lastCallbackTime.ns()});
@@ -193,22 +192,20 @@
     ATRACE_CALL();
 
     std::lock_guard lock(mVsync.mutex);
-    mVsync.scheduledFrameTime =
+    mVsync.scheduledFrameTimeOpt =
             mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
                                            .readyDuration = 0,
                                            .lastVsync = mVsync.lastCallbackTime.ns()});
 }
 
-auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
+std::optional<scheduler::ScheduleResult> MessageQueue::getScheduledFrameResult() const {
     if (mHandler->isFramePending()) {
-        return Clock::now();
+        return scheduler::ScheduleResult{TimePoint::now(), mHandler->getExpectedVsyncTime()};
     }
-
     std::lock_guard lock(mVsync.mutex);
-    if (const auto time = mVsync.scheduledFrameTime) {
-        return Clock::time_point(std::chrono::nanoseconds(*time));
+    if (const auto scheduledFrameTimeline = mVsync.scheduledFrameTimeOpt) {
+        return scheduledFrameTimeline;
     }
-
     return std::nullopt;
 }
 
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index edb424b..c5fc371 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -76,8 +76,7 @@
     virtual void scheduleConfigure() = 0;
     virtual void scheduleFrame() = 0;
 
-    using Clock = std::chrono::steady_clock;
-    virtual std::optional<Clock::time_point> getScheduledFrameTime() const = 0;
+    virtual std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const = 0;
 };
 
 namespace impl {
@@ -95,7 +94,9 @@
         explicit Handler(MessageQueue& queue) : mQueue(queue) {}
         void handleMessage(const Message& message) override;
 
-        bool isFramePending() const;
+        virtual TimePoint getExpectedVsyncTime() const { return mExpectedVsyncTime.load(); }
+
+        virtual bool isFramePending() const;
 
         virtual void dispatchFrame(VsyncId, TimePoint expectedVsyncTime);
     };
@@ -124,7 +125,7 @@
         TracedOrdinal<std::chrono::nanoseconds> workDuration
                 GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
         TimePoint lastCallbackTime GUARDED_BY(mutex);
-        std::optional<nsecs_t> scheduledFrameTime GUARDED_BY(mutex);
+        std::optional<scheduler::ScheduleResult> scheduledFrameTimeOpt GUARDED_BY(mutex);
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
@@ -150,7 +151,7 @@
     void scheduleConfigure() override;
     void scheduleFrame() override;
 
-    std::optional<Clock::time_point> getScheduledFrameTime() const override;
+    std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const override;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6979f03..3f91682 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,
@@ -162,9 +154,13 @@
     if (isNew) {
         onHardwareVsyncRequest(displayId, false);
     }
+
+    dispatchHotplug(displayId, Hotplug::Connected);
 }
 
 void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+    dispatchHotplug(displayId, Hotplug::Disconnected);
+
     demotePacesetterDisplay();
 
     std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
@@ -344,40 +340,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 +379,51 @@
     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::dispatchHotplug(PhysicalDisplayId displayId, Hotplug hotplug) {
+    if (hasEventThreads()) {
+        const bool connected = hotplug == Hotplug::Connected;
+        eventThreadFor(Cycle::Render).onHotplugReceived(displayId, connected);
+        eventThreadFor(Cycle::LastComposite).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::dispatchHotplugError(int32_t errorCode) {
+    if (hasEventThreads()) {
+        eventThreadFor(Cycle::Render).onHotplugConnectionError(errorCode);
+        eventThreadFor(Cycle::LastComposite).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 +450,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 +491,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 +961,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 9f29e9f..09f75fd 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;
 
@@ -133,9 +109,9 @@
 
     void initVsync(frametimeline::TokenManager&, std::chrono::nanoseconds workDuration);
 
-    using Impl::getScheduledFrameTime;
     using Impl::setDuration;
 
+    using Impl::getScheduledFrameResult;
     using Impl::scheduleConfigure;
     using Impl::scheduleFrame;
 
@@ -154,36 +130,34 @@
         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);
+    enum class Hotplug { Connected, Disconnected };
+    void dispatchHotplug(PhysicalDisplayId, Hotplug);
 
-    void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock);
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&);
+    void dispatchHotplugError(int32_t errorCode);
+
+    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 +262,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 +343,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 +441,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 +559,7 @@
         ftl::Optional<FrameRateMode> modeOpt;
 
         struct ModeChangedParams {
-            ConnectionHandle handle;
+            Cycle cycle;
             FrameRateMode mode;
         };
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index ed8f8fe..0c43ffb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -21,11 +21,15 @@
 #include <string>
 
 #include <ftl/mixins.h>
+#include <scheduler/Time.h>
 #include <utils/Timers.h>
 
 namespace android::scheduler {
 
-using ScheduleResult = std::optional<nsecs_t>;
+struct ScheduleResult {
+    TimePoint callbackTime;
+    TimePoint vsyncTime;
+};
 
 enum class CancelResult { Cancelled, TooLate, Error };
 
@@ -124,10 +128,12 @@
      *
      * \param [in] token           The callback to schedule.
      * \param [in] scheduleTiming  The timing information for this schedule call
-     * \return                     The expected callback time if a callback was scheduled.
+     * \return                     The expected callback time if a callback was scheduled,
+     *                             along with VSYNC time for the callback scheduled.
      *                             std::nullopt if the callback is not registered.
      */
-    virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
+    virtual std::optional<ScheduleResult> schedule(CallbackToken token,
+                                                   ScheduleTiming scheduleTiming) = 0;
 
     /*
      * Update the timing information for a scheduled callback.
@@ -135,10 +141,12 @@
      *
      * \param [in] token           The callback to schedule.
      * \param [in] scheduleTiming  The timing information for this schedule call
-     * \return                     The expected callback time if a callback was scheduled.
+     * \return                     The expected callback time if a callback was scheduled,
+     *                             along with VSYNC time for the callback scheduled.
      *                             std::nullopt if the callback is not registered.
      */
-    virtual ScheduleResult update(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
+    virtual std::optional<ScheduleResult> update(CallbackToken token,
+                                                 ScheduleTiming scheduleTiming) = 0;
 
     /* Cancels a scheduled callback, if possible.
      *
@@ -168,10 +176,10 @@
     VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
 
     // See documentation for VSyncDispatch::schedule.
-    ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
+    std::optional<ScheduleResult> schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
 
     // See documentation for VSyncDispatch::update.
-    ScheduleResult update(VSyncDispatch::ScheduleTiming scheduleTiming);
+    std::optional<ScheduleResult> update(VSyncDispatch::ScheduleTiming scheduleTiming);
 
     // See documentation for VSyncDispatch::cancel.
     CancelResult cancel();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index b92fa24..84ccf8e 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -38,9 +38,10 @@
 
 namespace {
 
-nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
-                                const VSyncDispatch::ScheduleTiming& timing) {
-    return nextVsyncTime - timing.readyDuration - timing.workDuration;
+ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime,
+                                       const VSyncDispatch::ScheduleTiming& timing) {
+    return {TimePoint::fromNs(nextVsyncTime - timing.readyDuration - timing.workDuration),
+            TimePoint::fromNs(nextVsyncTime)};
 }
 
 } // namespace
@@ -115,14 +116,15 @@
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mScheduleTiming = timing;
     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
-    return nextWakeupTime;
+    return ScheduleResult{TimePoint::fromNs(nextWakeupTime), TimePoint::fromNs(nextVsyncTime)};
 }
 
-nsecs_t VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
+ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
         VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) {
     mWorkloadUpdateInfo = timing;
     const auto armedInfo = update(tracker, now, timing, mArmedInfo);
-    return armedInfo.mActualWakeupTime;
+    return {TimePoint::fromNs(armedInfo.mActualWakeupTime),
+            TimePoint::fromNs(armedInfo.mActualVsyncTime)};
 }
 
 bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
@@ -383,14 +385,14 @@
     }
 }
 
-ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
-                                                 ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::schedule(CallbackToken token,
+                                                                ScheduleTiming scheduleTiming) {
     std::lock_guard lock(mMutex);
     return scheduleLocked(token, scheduleTiming);
 }
 
-ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token,
-                                                       ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::scheduleLocked(
+        CallbackToken token, ScheduleTiming scheduleTiming) {
     auto it = mCallbacks.find(token);
     if (it == mCallbacks.end()) {
         return {};
@@ -405,10 +407,7 @@
         return callback->addPendingWorkloadUpdate(*mTracker, now, scheduleTiming);
     }
 
-    const ScheduleResult result = callback->schedule(scheduleTiming, *mTracker, now);
-    if (!result.has_value()) {
-        return {};
-    }
+    const auto result = callback->schedule(scheduleTiming, *mTracker, now);
 
     if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
         rearmTimerSkippingUpdateFor(now, it);
@@ -417,7 +416,8 @@
     return result;
 }
 
-ScheduleResult VSyncDispatchTimerQueue::update(CallbackToken token, ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::update(CallbackToken token,
+                                                              ScheduleTiming scheduleTiming) {
     std::lock_guard lock(mMutex);
     const auto it = mCallbacks.find(token);
     if (it == mCallbacks.end()) {
@@ -494,14 +494,16 @@
     if (mToken) mDispatch->unregisterCallback(*mToken);
 }
 
-ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncCallbackRegistration::schedule(
+        VSyncDispatch::ScheduleTiming scheduleTiming) {
     if (!mToken) {
         return std::nullopt;
     }
     return mDispatch->schedule(*mToken, scheduleTiming);
 }
 
-ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncCallbackRegistration::update(
+        VSyncDispatch::ScheduleTiming scheduleTiming) {
     if (!mToken) {
         return std::nullopt;
     }
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index b5ddd25..252c09c 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -69,7 +69,8 @@
 
     // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
     // call to update()
-    nsecs_t addPendingWorkloadUpdate(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming);
+    ScheduleResult addPendingWorkloadUpdate(VSyncTracker&, nsecs_t now,
+                                            VSyncDispatch::ScheduleTiming);
 
     // Checks if there is a pending update to the workload, returning true if so.
     bool hasPendingWorkloadUpdate() const;
@@ -128,8 +129,8 @@
 
     CallbackToken registerCallback(Callback, std::string callbackName) final;
     void unregisterCallback(CallbackToken) final;
-    ScheduleResult schedule(CallbackToken, ScheduleTiming) final;
-    ScheduleResult update(CallbackToken, ScheduleTiming) final;
+    std::optional<ScheduleResult> schedule(CallbackToken, ScheduleTiming) final;
+    std::optional<ScheduleResult> update(CallbackToken, ScheduleTiming) final;
     CancelResult cancel(CallbackToken) final;
     void dump(std::string&) const final;
 
@@ -147,7 +148,7 @@
     void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::const_iterator skipUpdate)
             REQUIRES(mMutex);
     void cancelTimer() REQUIRES(mMutex);
-    ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
+    std::optional<ScheduleResult> scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
 
     std::mutex mutable mMutex;
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index edba50b..cf5f55d 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);
 
@@ -893,7 +886,6 @@
     // the DisplayDevice, hence the above commit of the primary display. Remove that special case by
     // initializing the Scheduler after configureLocked, once decoupled from DisplayDevice.
     initScheduler(display);
-    dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
 
     mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) {
         auto snapshot = perfetto::protos::LayersSnapshotProto{};
@@ -1763,7 +1755,7 @@
     }
 
     display->overrideHdrTypes(hdrTypes);
-    dispatchDisplayHotplugEvent(display->getPhysicalId(), true /* connected */);
+    mScheduler->dispatchHotplug(display->getPhysicalId(), scheduler::Scheduler::Hotplug::Connected);
     return NO_ERROR;
 }
 
@@ -2092,12 +2084,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) {
@@ -2136,10 +2127,9 @@
         vsyncPeriod.has_value()) {
         // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32
         if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) {
-            const int32_t hotplugErrorCode = static_cast<int32_t>(-timestamp);
-            ALOGD("SurfaceFlinger got hotplugErrorCode=%d for display %" PRIu64, hotplugErrorCode,
-                  hwcDisplayId);
-            mScheduler->onHotplugConnectionError(mAppConnectionHandle, hotplugErrorCode);
+            const auto errorCode = static_cast<int32_t>(-timestamp);
+            ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId);
+            mScheduler->dispatchHotplugError(errorCode);
             return;
         }
 
@@ -2148,8 +2138,7 @@
             // one byte is good enough to encode android.hardware.drm.HdcpLevel
             const int32_t maxLevel = (value >> 8) & 0xFF;
             const int32_t connectedLevel = value & 0xFF;
-            ALOGD("SurfaceFlinger got HDCP level changed: connected=%d, max=%d for "
-                  "display=%" PRIu64,
+            ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for display %" PRIu64, __func__,
                   connectedLevel, maxLevel, hwcDisplayId);
             updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
             return;
@@ -2188,9 +2177,10 @@
     }
 
     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));
+        const auto errorCode = static_cast<int32_t>(event);
+        ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId);
+        mScheduler->dispatchHotplugError(errorCode);
     }
 }
 
@@ -2759,7 +2749,11 @@
     // TODO(b/255601557) Update frameInterval per display
     refreshArgs.frameInterval =
             mScheduler->getNextFrameInterval(pacesetterId, pacesetterTarget.expectedPresentTime());
-    refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
+    const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+    const auto scheduledFrameTimeOpt = scheduledFrameResultOpt
+            ? std::optional{scheduledFrameResultOpt->callbackTime}
+            : std::nullopt;
+    refreshArgs.scheduledFrameTime = scheduledFrameTimeOpt;
     refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
     // Store the present time just before calling to the composition engine so we could notify
     // the scheduler.
@@ -3480,9 +3474,8 @@
     if (!activeMode) {
         ALOGE("Failed to hotplug display %s", to_string(displayId).c_str());
         if (FlagManager::getInstance().hotplug2()) {
-            mScheduler->onHotplugConnectionError(mAppConnectionHandle,
-                                                 static_cast<int32_t>(
-                                                         DisplayHotplugEvent::ERROR_UNKNOWN));
+            mScheduler->dispatchHotplugError(
+                    static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
         }
         getHwComposer().disconnectDisplay(displayId);
         return nullptr;
@@ -3532,11 +3525,6 @@
     return "Connecting";
 }
 
-void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected);
-    mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
-}
-
 void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId,
                                                     const scheduler::FrameRateMode& mode) {
     // TODO(b/255635821): Merge code paths and move to Scheduler.
@@ -3544,7 +3532,7 @@
             ? &scheduler::Scheduler::onPrimaryDisplayModeChanged
             : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged;
 
-    ((*mScheduler).*onDisplayModeChanged)(mAppConnectionHandle, mode);
+    ((*mScheduler).*onDisplayModeChanged)(scheduler::Cycle::Render, mode);
 }
 
 sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
@@ -3571,10 +3559,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};
 
@@ -3725,16 +3713,11 @@
                                                  displaySurface, producer);
 
     if (mScheduler && !display->isVirtual()) {
-        const auto displayId = display->getPhysicalId();
-        {
-            // TODO(b/241285876): Annotate `processDisplayAdded` instead.
-            ftl::FakeGuard guard(kMainThreadContext);
+        // TODO(b/241285876): Annotate `processDisplayAdded` instead.
+        ftl::FakeGuard guard(kMainThreadContext);
 
-            // For hotplug reconnect, renew the registration since display modes have been reloaded.
-            mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector());
-        }
-
-        dispatchDisplayHotplugEvent(displayId, true);
+        // For hotplug reconnect, renew the registration since display modes have been reloaded.
+        mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
     }
 
     if (display->isVirtual()) {
@@ -3773,7 +3756,6 @@
         if (display->isVirtual()) {
             releaseVirtualDisplay(display->getVirtualId());
         } else {
-            dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
             mScheduler->unregisterDisplay(display->getPhysicalId());
         }
     }
@@ -4196,7 +4178,7 @@
         return getDefaultDisplayDeviceLocked()->getPhysicalId();
     }();
 
-    mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+    mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
 }
 
 void SurfaceFlinger::notifyCpuLoadUp() {
@@ -4258,7 +4240,8 @@
 
     auto hintStatus = data.hintStatus.load();
     if (!expectedPresentWithinTimeout) {
-        if (hintStatus != NotifyExpectedPresentHintStatus::Sent ||
+        if ((hintStatus != NotifyExpectedPresentHintStatus::Sent &&
+             hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnTx) ||
             (timeoutOpt && timeoutOpt->ns() == 0)) {
             // Send the hint immediately if timeout, as the hint gets
             // delayed otherwise, as the frame is scheduled close
@@ -4267,11 +4250,16 @@
                         .compare_exchange_strong(hintStatus,
                                                  NotifyExpectedPresentHintStatus::ScheduleOnTx)) {
                 scheduleNotifyExpectedPresentHint(displayId);
+                return;
             }
-            return;
         }
     }
 
+    if (hintStatus == NotifyExpectedPresentHintStatus::Sent &&
+        data.hintStatus.compare_exchange_strong(hintStatus,
+                                                NotifyExpectedPresentHintStatus::ScheduleOnTx)) {
+        return;
+    }
     if (hintStatus != NotifyExpectedPresentHintStatus::Start) {
         return;
     }
@@ -4279,7 +4267,8 @@
     mScheduler->scheduleFrame();
 }
 
-void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId) {
+void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId,
+                                                       VsyncId vsyncId) {
     auto itr = mNotifyExpectedPresentMap.find(displayId);
     if (itr == mNotifyExpectedPresentMap.end()) {
         return;
@@ -4288,10 +4277,30 @@
     const char* const whence = __func__;
     const auto sendHint = [=, this]() {
         auto& data = mNotifyExpectedPresentMap.at(displayId);
-        data.hintStatus.store(NotifyExpectedPresentHintStatus::Sent);
-        const auto status =
-                getHwComposer().notifyExpectedPresent(displayId, data.lastExpectedPresentTimestamp,
-                                                      data.lastFrameInterval);
+        TimePoint expectedPresentTime = data.lastExpectedPresentTimestamp;
+        if (ftl::to_underlying(vsyncId) != FrameTimelineInfo::INVALID_VSYNC_ID) {
+            const auto predictionOpt = mFrameTimeline->getTokenManager()->getPredictionsForToken(
+                    ftl::to_underlying(vsyncId));
+            const auto expectedPresentTimeOnPredictor = TimePoint::fromNs(
+                    predictionOpt ? predictionOpt->presentTime : expectedPresentTime.ns());
+            const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+            const auto expectedPresentTimeOnScheduler = scheduledFrameResultOpt.has_value()
+                    ? scheduledFrameResultOpt->vsyncTime
+                    : TimePoint::fromNs(0);
+            expectedPresentTime =
+                    std::max(expectedPresentTimeOnPredictor, expectedPresentTimeOnScheduler);
+        }
+
+        if (expectedPresentTime < TimePoint::now()) {
+            expectedPresentTime =
+                    mScheduler->getVsyncSchedule()->vsyncDeadlineAfter(TimePoint::now());
+            if (mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration >
+                mScheduler->getVsyncSchedule(displayId)->period()) {
+                expectedPresentTime += mScheduler->getVsyncSchedule(displayId)->period();
+            }
+        }
+        const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime,
+                                                                  data.lastFrameInterval);
         if (status != NO_ERROR) {
             ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence,
                   displayId.value);
@@ -4309,7 +4318,11 @@
             }
         }));
     }
-    sendHint();
+    auto scheduleHintOnPresent = NotifyExpectedPresentHintStatus::ScheduleOnPresent;
+    if (itr->second.hintStatus.compare_exchange_strong(scheduleHintOnPresent,
+                                                       NotifyExpectedPresentHintStatus::Sent)) {
+        sendHint();
+    }
 }
 
 void SurfaceFlinger::sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) {
@@ -4361,24 +4374,25 @@
     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);
+
+    // Dispatch after EventThread creation, since registerDisplay above skipped dispatch.
+    mScheduler->dispatchHotplug(display->getPhysicalId(), scheduler::Scheduler::Hotplug::Connected);
 
     mScheduler->initVsync(*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
 
@@ -4386,6 +4400,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() {
@@ -5112,6 +5129,11 @@
 
     const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
     mTransactionHandler.queueTransaction(std::move(state));
+    for (const auto& [displayId, data] : mNotifyExpectedPresentMap) {
+        if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) {
+            scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId});
+        }
+    }
     setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
     return NO_ERROR;
 }
@@ -6322,7 +6344,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 {
@@ -7094,14 +7116,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
@@ -7333,7 +7356,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
@@ -7691,7 +7714,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
@@ -8473,10 +8496,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);
@@ -8664,7 +8687,7 @@
     }();
 
     mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
-    mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+    mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
     return NO_ERROR;
 }
 
@@ -8820,7 +8843,11 @@
         return;
     }
 
-    mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
+    const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+    const auto scheduleFrameTimeOpt = scheduledFrameResultOpt
+            ? std::optional{scheduledFrameResultOpt->callbackTime}
+            : std::nullopt;
+    mRegionSamplingThread->onCompositionComplete(scheduleFrameTimeOpt);
 }
 
 void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
@@ -8926,7 +8953,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 be05797..678be54 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1050,7 +1050,6 @@
                                const DisplayDeviceState& drawingState)
             REQUIRES(mStateLock, kMainThreadContext);
 
-    void dispatchDisplayHotplugEvent(PhysicalDisplayId, bool connected);
     void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&)
             REQUIRES(mStateLock);
 
@@ -1356,12 +1355,7 @@
 
     const std::string mHwcServiceName;
 
-    /*
-     * Scheduler
-     */
     std::unique_ptr<scheduler::Scheduler> mScheduler;
-    scheduler::ConnectionHandle mAppConnectionHandle;
-    scheduler::ConnectionHandle mSfConnectionHandle;
 
     scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
 
@@ -1500,7 +1494,9 @@
     std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap;
     void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) override
             REQUIRES(kMainThreadContext);
-    void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId);
+    void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId,
+                                           VsyncId vsyncId = VsyncId{
+                                                   FrameTimelineInfo::INVALID_VSYNC_ID});
     void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
                                          TimePoint expectedPresentTime, Fps frameInterval,
                                          std::optional<Period> timeoutOpt);
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/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index f7adc0e..b7f06a9 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -131,6 +131,7 @@
     DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
     DUMP_READ_ONLY_FLAG(restore_blur_step);
     DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
+    DUMP_READ_ONLY_FLAG(protected_if_client);
 #undef DUMP_READ_ONLY_FLAG
 #undef DUMP_SERVER_FLAG
 #undef DUMP_FLAG_INTERVAL
@@ -210,6 +211,7 @@
 FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
 FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
 FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "")
+FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
 
 /// Trunk stable server flags ///
 FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 18f623f..241c814 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -71,6 +71,7 @@
     bool renderable_buffer_usage() const;
     bool restore_blur_step() const;
     bool dont_skip_on_early_ro() const;
+    bool protected_if_client() const;
 
 protected:
     // overridden for unit tests
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/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index f5ec1ee..c8f4218 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -189,6 +189,13 @@
   namespace: "core_graphics"
   description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
   bug: "273702768"
+}
+
+flag {
+  name: "protected_if_client"
+  namespace: "core_graphics"
+  description: "Only set the RenderSurface to protected if protected layers are in client composition."
+  bug: "307674749"
   is_fixed_read_only: true
   metadata {
     purpose: PURPOSE_BUGFIX
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/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index d5ec654..3eabe1f 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -117,14 +117,16 @@
         mThread->onVsync(expectedPresentationTime, timestamp, deadlineTimestamp);
     }
 
+    static constexpr scheduler::ScheduleResult kScheduleResult{TimePoint::fromNs(0),
+                                                               TimePoint::fromNs(0)};
     AsyncCallRecorderWithCannedReturn<
             scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
                                           scheduler::VSyncDispatch::ScheduleTiming)>
-            mVSyncCallbackScheduleRecorder{0};
+            mVSyncCallbackScheduleRecorder{kScheduleResult};
     AsyncCallRecorderWithCannedReturn<
             scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
                                           scheduler::VSyncDispatch::ScheduleTiming)>
-            mVSyncCallbackUpdateRecorder{0};
+            mVSyncCallbackUpdateRecorder{kScheduleResult};
     AsyncCallRecorderWithCannedReturn<
             scheduler::VSyncDispatch::CallbackToken (*)(scheduler::VSyncDispatch::Callback,
                                                         std::string)>
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index f5661fc..71f9f88 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -25,6 +25,7 @@
 #include "FrameTimeline.h"
 #include "Scheduler/MessageQueue.h"
 #include "mock/MockVSyncDispatch.h"
+#include "utils/Timers.h"
 
 namespace android {
 
@@ -49,6 +50,8 @@
         using MessageQueue::Handler::Handler;
 
         MOCK_METHOD(void, dispatchFrame, (VsyncId, TimePoint), (override));
+        MOCK_METHOD(bool, isFramePending, (), (const, override));
+        MOCK_METHOD(TimePoint, getExpectedVsyncTime, (), (const override));
     };
 
     explicit TestableMessageQueue(sp<MockHandler> handler)
@@ -94,13 +97,17 @@
     const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
                                                                  .readyDuration = 0,
                                                                  .lastVsync = 0};
-    EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
+    EXPECT_FALSE(mEventQueue.getScheduledFrameResult());
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    const auto timePoint = TimePoint::fromNs(1234);
+    const auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
-    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
-    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+    const auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+    EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
 }
 
 TEST_F(MessageQueueTest, commitTwice) {
@@ -109,17 +116,25 @@
                                                                  .readyDuration = 0,
                                                                  .lastVsync = 0};
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    auto timePoint = TimePoint::fromNs(1234);
+    auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
-    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
-    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+    auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+    EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
+    timePoint = TimePoint::fromNs(4567);
+    scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
-    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
-    EXPECT_EQ(4567, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+    scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_EQ(4567, scheduledFrameResult->callbackTime.ns());
+    EXPECT_EQ(4567, scheduledFrameResult->vsyncTime.ns());
 }
 
 TEST_F(MessageQueueTest, commitTwiceWithCallback) {
@@ -128,11 +143,15 @@
                                                                  .readyDuration = 0,
                                                                  .lastVsync = 0};
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    const auto timePoint = TimePoint::fromNs(1234);
+    auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
-    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
-    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+    auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+    EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
 
     constexpr TimePoint kStartTime = TimePoint::fromNs(100);
     constexpr TimePoint kEndTime = kStartTime + kDuration;
@@ -148,14 +167,15 @@
     EXPECT_NO_FATAL_FAILURE(
             mEventQueue.vsyncCallback(kPresentTime.ns(), kStartTime.ns(), kEndTime.ns()));
 
-    EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
+    EXPECT_FALSE(mEventQueue.getScheduledFrameResult());
 
     const auto timingAfterCallback =
             scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
                                                      .readyDuration = 0,
                                                      .lastVsync = kPresentTime.ns()};
-
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
+    scheduleResult = scheduler::ScheduleResult{TimePoint::fromNs(0), TimePoint::fromNs(0)};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback))
+            .WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 }
 
@@ -167,9 +187,24 @@
                                                      .readyDuration = 0,
                                                      .lastVsync = 0};
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+    const auto scheduleResult =
+            scheduler::ScheduleResult{TimePoint::fromNs(0), TimePoint::fromNs(0)};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 }
 
+TEST_F(MessageQueueTest, scheduleResultWhenFrameIsPending) {
+    const auto timePoint = TimePoint::now();
+    EXPECT_CALL(*mEventQueue.mHandler, isFramePending()).WillOnce(Return(true));
+    EXPECT_CALL(*mEventQueue.mHandler, getExpectedVsyncTime()).WillRepeatedly(Return(timePoint));
+
+    const auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_NEAR(static_cast<double>(TimePoint::now().ns()),
+                static_cast<double>(scheduledFrameResult->callbackTime.ns()), ms2ns(1));
+    EXPECT_EQ(timePoint, scheduledFrameResult->vsyncTime);
+}
+
 } // namespace
 } // namespace android
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/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 91b9018..20a3315 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -41,6 +41,33 @@
     }
 
 protected:
+    void setTransactionState() {
+        ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
+        TransactionInfo transaction;
+        mFlinger.setTransactionState(FrameTimelineInfo{}, transaction.states, transaction.displays,
+                                     transaction.flags, transaction.applyToken,
+                                     transaction.inputWindowCommands,
+                                     TimePoint::now().ns() + s2ns(1), transaction.isAutoTimestamp,
+                                     transaction.unCachedBuffers,
+                                     /*HasListenerCallbacks=*/false, transaction.callbacks,
+                                     transaction.id, transaction.mergedTransactionIds);
+    }
+
+    struct TransactionInfo {
+        Vector<ComposerState> states;
+        Vector<DisplayState> displays;
+        uint32_t flags = 0;
+        sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+        InputWindowCommands inputWindowCommands;
+        int64_t desiredPresentTime = 0;
+        bool isAutoTimestamp = false;
+        FrameTimelineInfo frameTimelineInfo{};
+        std::vector<client_cache_t> unCachedBuffers;
+        uint64_t id = static_cast<uint64_t>(-1);
+        std::vector<uint64_t> mergedTransactionIds;
+        std::vector<ListenerCallbacks> callbacks;
+    };
+
     struct Compositor final : ICompositor {
         explicit Compositor(PhysicalDisplayId displayId, TestableSurfaceFlinger& surfaceFlinger)
               : displayId(displayId), surfaceFlinger(surfaceFlinger) {}
@@ -102,7 +129,7 @@
 };
 
 TEST_F(NotifyExpectedPresentTest, noNotifyExpectedPresentHintCall_absentTimeout) {
-    auto expectedPresentTime = systemTime() + ms2ns(10);
+    auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
     ASSERT_NO_FATAL_FAILURE(
             mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId,
                                                   TimePoint::fromNs(expectedPresentTime),
@@ -120,7 +147,7 @@
 }
 
 TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentHint_zeroTimeout) {
-    auto expectedPresentTime = systemTime() + ms2ns(10);
+    auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
     {
         // Very first ExpectedPresent after idle, no previous timestamp.
         EXPECT_CALL(*mComposer,
@@ -139,6 +166,10 @@
     {
         mCompositor->committed = false;
         expectedPresentTime += kFrameInterval60HzNs;
+        EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+                            mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+                    nextAnticipatedVSyncTimeFrom(_, _))
+                .WillRepeatedly(Return(expectedPresentTime));
         EXPECT_CALL(*mComposer,
                     notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
                 .WillOnce(Return(Error::NONE));
@@ -154,6 +185,10 @@
     }
     {
         expectedPresentTime += kFrameInterval60HzNs;
+        EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+                            mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+                    nextAnticipatedVSyncTimeFrom(_, _))
+                .WillRepeatedly(Return(expectedPresentTime));
         EXPECT_CALL(*mComposer,
                     notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
                 .WillOnce(Return(Error::NONE));
@@ -168,9 +203,8 @@
         ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
     }
 }
-
 TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) {
-    auto expectedPresentTime = systemTime() + ms2ns(10);
+    auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
     {
         // Very first ExpectedPresent after idle, no previous timestamp
         mCompositor->committed = false;
@@ -185,6 +219,27 @@
         ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
     }
     {
+        EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+        expectedPresentTime += 2 * kFrameInterval5HzNs;
+        mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod,
+                                                 TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+                                                 kTimeoutNs);
+        EXPECT_TRUE(
+                mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime));
+        ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
+        mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42});
+        ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
+        {
+            EXPECT_CALL(*mComposer,
+                        notifyExpectedPresent(kHwcDisplayId, expectedPresentTime,
+                                              kFrameInterval60HzNs))
+                    .WillOnce(Return(Error::NONE));
+            // Hint sent with the setTransactionState
+            setTransactionState();
+            ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
+        }
+    }
+    {
         // ExpectedPresentTime is after the timeoutNs
         mCompositor->committed = true;
         expectedPresentTime += 2 * kFrameInterval5HzNs;
@@ -194,7 +249,7 @@
                                                  kTimeoutNs);
         EXPECT_TRUE(
                 mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime));
-        ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
+        ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
         mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42});
         // Present happens notifyExpectedPresentHintStatus is Start
         ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId));
@@ -259,7 +314,7 @@
 }
 
 TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) {
-    const auto now = systemTime();
+    const auto now = TimePoint::now().ns();
     auto expectedPresentTime = now;
     static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
 
@@ -298,6 +353,10 @@
                                                  TimePoint::fromNs(expectedPresentTime),
                                                  Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs);
 
+        EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+                            mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+                    nextAnticipatedVSyncTimeFrom(_, _))
+                .WillRepeatedly(Return(expectedPresentTime));
         if (callNotifyExpectedPresentHint) {
             mCompositor->committed = false;
             ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId))
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/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index eb4e84e..9b70d92 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -247,7 +247,8 @@
                 mDispatch->schedule(cb,
                                     {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
         EXPECT_TRUE(result.has_value());
-        EXPECT_EQ(900, *result);
+        EXPECT_EQ(900, result->callbackTime.ns());
+        EXPECT_EQ(1000, result->vsyncTime.ns());
     }
 }
 
@@ -260,7 +261,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
 
@@ -279,12 +281,14 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     result =
             mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(700, *result);
+    EXPECT_EQ(700, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
 
@@ -332,7 +336,8 @@
                                              .readyDuration = 0,
                                              .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod, *result);
+    EXPECT_EQ(mPeriod, result->callbackTime.ns());
+    EXPECT_EQ(workDuration + mPeriod, result->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -344,7 +349,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod - 100, *result);
+    EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+    EXPECT_EQ(mPeriod, result->vsyncTime.ns());
     EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled);
 }
 
@@ -357,7 +363,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod - 100, *result);
+    EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+    EXPECT_EQ(mPeriod, result->vsyncTime.ns());
     mMockClock.advanceBy(950);
     EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate);
 }
@@ -371,7 +378,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod - 100, *result);
+    EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+    EXPECT_EQ(mPeriod, result->vsyncTime.ns());
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -392,7 +400,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod - 100, *result);
+    EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+    EXPECT_EQ(mPeriod, result->vsyncTime.ns());
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -625,19 +634,22 @@
                                                    .readyDuration = 0,
                                                    .lastVsync = timestamp - mVsyncMoveThreshold});
                 EXPECT_TRUE(result.has_value());
-                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+                EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
                 result = mDispatch->schedule(tmp,
                                              {.workDuration = 400,
                                               .readyDuration = 0,
                                               .lastVsync = timestamp});
                 EXPECT_TRUE(result.has_value());
-                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+                EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
                 result = mDispatch->schedule(tmp,
                                              {.workDuration = 400,
                                               .readyDuration = 0,
                                               .lastVsync = timestamp + mVsyncMoveThreshold});
                 EXPECT_TRUE(result.has_value());
-                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+                EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
                 lastTarget = timestamp;
             },
             "oo");
@@ -726,10 +738,12 @@
     auto result =
             mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     result = mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 }
 
 // b/1450138150
@@ -741,12 +755,14 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(400);
 
     result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1200, *result);
+    EXPECT_EQ(1200, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -763,12 +779,14 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(400);
 
     result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(400, *result);
+    EXPECT_EQ(400, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -784,11 +802,13 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(400);
     result = mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(602, *result);
+    EXPECT_EQ(602, result->callbackTime.ns());
+    EXPECT_EQ(1002, result->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
@@ -796,12 +816,14 @@
     auto result =
             mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     advanceToNextCallback();
     result =
             mDispatch->schedule(cb0, {.workDuration = 1100, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -812,12 +834,14 @@
     auto result =
             mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     advanceToNextCallback();
     result =
             mDispatch->schedule(cb0, {.workDuration = 1900, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1100, *result);
+    EXPECT_EQ(1100, result->callbackTime.ns());
+    EXPECT_EQ(3000, result->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
@@ -829,11 +853,13 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     advanceToNextCallback();
 }
@@ -849,11 +875,13 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(0, *result);
+    EXPECT_EQ(0, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
 }
@@ -899,14 +927,16 @@
     auto result =
             mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
     result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1900, *result);
+    EXPECT_EQ(1900, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -927,14 +957,16 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
     result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1630, *result);
+    EXPECT_EQ(1630, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -954,14 +986,16 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
     result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(80);
 
     ASSERT_EQ(1, cb.mCalls.size());
@@ -982,10 +1016,12 @@
     auto result =
             mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1900, *result);
+    EXPECT_EQ(1900, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -1009,10 +1045,12 @@
     auto result =
             mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1900, *result);
+    EXPECT_EQ(1900, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -1045,10 +1083,12 @@
     auto result =
             mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     result = mDispatch->schedule(cb2, {.workDuration = 390, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(610, *result);
+    EXPECT_EQ(610, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(700);
@@ -1072,7 +1112,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 70, .readyDuration = 30, .lastVsync = intended});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1138,12 +1179,14 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(300);
 
     result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1200, *result);
+    EXPECT_EQ(1200, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1159,12 +1202,14 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(300);
 
     result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(300, *result);
+    EXPECT_EQ(300, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1197,9 +1242,11 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    const auto scheduleResult =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -1220,9 +1267,11 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994},
-                               *mStubTracker.get(), now)
-                        .has_value());
+    const auto scheduleResult =
+            entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994},
+                           *mStubTracker, now);
+    EXPECT_EQ(9500, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(10000, scheduleResult.vsyncTime.ns());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(9500));
@@ -1243,9 +1292,11 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    const auto scheduleResult =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -1275,17 +1326,19 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    entry.update(*mStubTracker.get(), 0);
+    entry.update(*mStubTracker, 0);
     EXPECT_FALSE(entry.wakeupTime());
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    const auto scheduleResult =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
     auto wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(wakeup, Eq(900));
 
-    entry.update(*mStubTracker.get(), 0);
+    entry.update(*mStubTracker, 0);
     wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(920));
@@ -1294,9 +1347,10 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    EXPECT_EQ(900,
+              entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                             *mStubTracker.get(), 0)
+                      .callbackTime.ns());
     entry.update(*mStubTracker.get(), 0);
 
     auto const wakeup = entry.wakeupTime();
@@ -1307,26 +1361,32 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    auto scheduleResult =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+
+    EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
     entry.executing(); // 1000 is executing
     // had 1000 not been executing, this could have been scheduled for time 800.
-    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
+                                    *mStubTracker, 0);
+    EXPECT_EQ(1800, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(2000, scheduleResult.vsyncTime.ns());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    scheduleResult = entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
+                                    *mStubTracker, 0);
+    EXPECT_EQ(1950, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(2000, scheduleResult.vsyncTime.ns());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001},
+                                    *mStubTracker, 0);
+    EXPECT_EQ(1800, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(2000, scheduleResult.vsyncTime.ns());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 }
@@ -1349,32 +1409,48 @@
             .InSequence(seq)
             .WillOnce(Return(2000));
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    auto scheduleResult =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResult.vsyncTime.ns());
 
     entry.executing(); // 1000 is executing
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    scheduleResult = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
+                                    *mStubTracker, 0);
+    EXPECT_EQ(1800, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(2000, scheduleResult.vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
-    VSyncDispatchTimerQueueEntry entry(
-            "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
-    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
-    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
-    EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    VSyncDispatchTimerQueueEntry entry("test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_EQ(900,
+              entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                             *mStubTracker, 0)
+                      .callbackTime.ns());
+    EXPECT_EQ(800,
+              entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
+                             *mStubTracker, 0)
+                      .callbackTime.ns());
+    EXPECT_EQ(950,
+              entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
+                             *mStubTracker, 0)
+                      .callbackTime.ns());
+    {
+        SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true);
+        EXPECT_EQ(0,
+                  entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
+                                 *mStubTracker, 0)
+                          .callbackTime.ns());
+    }
+    {
+        SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false);
+        EXPECT_EQ(800,
+                  entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
+                                 *mStubTracker, 0)
+                          .callbackTime.ns());
+    }
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdateAndDontSkip) {
@@ -1389,7 +1465,7 @@
                                     .readyDuration = 0,
                                     .lastVsync = 400});
     EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
-    entry.update(*mStubTracker.get(), 0);
+    entry.update(*mStubTracker, 0);
     EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
     EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
 }
@@ -1409,9 +1485,11 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    const auto scheduleResult =
+            entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500},
+                           *mStubTracker, 0);
+    EXPECT_EQ(900, scheduleResult.callbackTime.ns());
+    EXPECT_EQ(mPeriod, scheduleResult.vsyncTime.ns());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
index dc32ff9..1dc2ef4 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
@@ -29,8 +29,10 @@
 
     MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
     MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
-    MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
-    MOCK_METHOD(scheduler::ScheduleResult, update, (CallbackToken, ScheduleTiming), (override));
+    MOCK_METHOD(std::optional<scheduler::ScheduleResult>, schedule, (CallbackToken, ScheduleTiming),
+                (override));
+    MOCK_METHOD(std::optional<scheduler::ScheduleResult>, update, (CallbackToken, ScheduleTiming),
+                (override));
     MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override));
     MOCK_METHOD(void, dump, (std::string&), (const, override));
 };
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",
-    ]
+    ],
 }
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 63ecaec..f10ba44 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -56,75 +56,6 @@
 
 // -------------------------------------------------------------------------------------------------
 
-const constexpr char* STATUS_T_ERROR_MESSAGE_PREFIX = "status_t = ";
-const constexpr char* STATUS_V_1_0_ERROR_MESSAGE_PREFIX =
-        "android::hardware::vibrator::V1_0::Status = ";
-
-template <typename T>
-HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) {
-    switch (status) {
-        case V1_0::Status::OK:
-            return HalResult<T>::ok(data);
-        case V1_0::Status::UNSUPPORTED_OPERATION:
-            return HalResult<T>::unsupported();
-        default:
-            return HalResult<T>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
-    }
-}
-
-template <typename T>
-template <typename R>
-HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) {
-    return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description());
-}
-
-template <typename T>
-template <typename R>
-HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) {
-    return ret.isOk() ? HalResult<T>::fromStatus(status, data)
-                      : HalResult<T>::failed(ret.description());
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<void> HalResult<void>::fromStatus(status_t status) {
-    if (status == android::OK) {
-        return HalResult<void>::ok();
-    }
-    return HalResult<void>::failed(STATUS_T_ERROR_MESSAGE_PREFIX + statusToString(status));
-}
-
-HalResult<void> HalResult<void>::fromStatus(binder::Status status) {
-    if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
-        status.transactionError() == android::UNKNOWN_TRANSACTION) {
-        // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
-        // the same as the operation being unsupported by this HAL. Should not retry.
-        return HalResult<void>::unsupported();
-    }
-    if (status.isOk()) {
-        return HalResult<void>::ok();
-    }
-    return HalResult<void>::failed(std::string(status.toString8().c_str()));
-}
-
-HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) {
-    switch (status) {
-        case V1_0::Status::OK:
-            return HalResult<void>::ok();
-        case V1_0::Status::UNSUPPORTED_OPERATION:
-            return HalResult<void>::unsupported();
-        default:
-            return HalResult<void>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status));
-    }
-}
-
-template <typename R>
-HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) {
-    return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
-}
-
-// -------------------------------------------------------------------------------------------------
-
 Info HalWrapper::getInfo() {
     getCapabilities();
     getPrimitiveDurations();
@@ -269,7 +200,7 @@
 // -------------------------------------------------------------------------------------------------
 
 HalResult<void> AidlHalWrapper::ping() {
-    return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
+    return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder());
 }
 
 void AidlHalWrapper::tryReconnect() {
@@ -291,7 +222,7 @@
             static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK);
     auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
 
-    auto ret = HalResult<void>::fromStatus(getHal()->on(timeout.count(), cb));
+    auto ret = HalResultFactory::fromStatus(getHal()->on(timeout.count(), cb));
     if (!supportsCallback && ret.isOk()) {
         mCallbackScheduler->schedule(completionCallback, timeout);
     }
@@ -300,23 +231,23 @@
 }
 
 HalResult<void> AidlHalWrapper::off() {
-    return HalResult<void>::fromStatus(getHal()->off());
+    return HalResultFactory::fromStatus(getHal()->off());
 }
 
 HalResult<void> AidlHalWrapper::setAmplitude(float amplitude) {
-    return HalResult<void>::fromStatus(getHal()->setAmplitude(amplitude));
+    return HalResultFactory::fromStatus(getHal()->setAmplitude(amplitude));
 }
 
 HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) {
-    return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled));
+    return HalResultFactory::fromStatus(getHal()->setExternalControl(enabled));
 }
 
 HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
-    return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength));
+    return HalResultFactory::fromStatus(getHal()->alwaysOnEnable(id, effect, strength));
 }
 
 HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) {
-    return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id));
+    return HalResultFactory::fromStatus(getHal()->alwaysOnDisable(id));
 }
 
 HalResult<milliseconds> AidlHalWrapper::performEffect(
@@ -330,7 +261,7 @@
     auto result = getHal()->perform(effect, strength, cb, &lengthMs);
     milliseconds length = milliseconds(lengthMs);
 
-    auto ret = HalResult<milliseconds>::fromStatus(result, length);
+    auto ret = HalResultFactory::fromStatus<milliseconds>(result, length);
     if (!supportsCallback && ret.isOk()) {
         mCallbackScheduler->schedule(completionCallback, length);
     }
@@ -357,38 +288,40 @@
         duration += milliseconds(effect.delayMs);
     }
 
-    return HalResult<milliseconds>::fromStatus(getHal()->compose(primitives, cb), duration);
+    return HalResultFactory::fromStatus<milliseconds>(getHal()->compose(primitives, cb), duration);
 }
 
 HalResult<void> AidlHalWrapper::performPwleEffect(const std::vector<PrimitivePwle>& primitives,
                                                   const std::function<void()>& completionCallback) {
     // This method should always support callbacks, so no need to double check.
     auto cb = new HalCallbackWrapper(completionCallback);
-    return HalResult<void>::fromStatus(getHal()->composePwle(primitives, cb));
+    return HalResultFactory::fromStatus(getHal()->composePwle(primitives, cb));
 }
 
 HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
     int32_t capabilities = 0;
     auto result = getHal()->getCapabilities(&capabilities);
-    return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities));
+    return HalResultFactory::fromStatus<Capabilities>(result,
+                                                      static_cast<Capabilities>(capabilities));
 }
 
 HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() {
     std::vector<Effect> supportedEffects;
     auto result = getHal()->getSupportedEffects(&supportedEffects);
-    return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects);
+    return HalResultFactory::fromStatus<std::vector<Effect>>(result, supportedEffects);
 }
 
 HalResult<std::vector<Braking>> AidlHalWrapper::getSupportedBrakingInternal() {
     std::vector<Braking> supportedBraking;
     auto result = getHal()->getSupportedBraking(&supportedBraking);
-    return HalResult<std::vector<Braking>>::fromStatus(result, supportedBraking);
+    return HalResultFactory::fromStatus<std::vector<Braking>>(result, supportedBraking);
 }
 
 HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() {
     std::vector<CompositePrimitive> supportedPrimitives;
     auto result = getHal()->getSupportedPrimitives(&supportedPrimitives);
-    return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
+    return HalResultFactory::fromStatus<std::vector<CompositePrimitive>>(result,
+                                                                         supportedPrimitives);
 }
 
 HalResult<std::vector<milliseconds>> AidlHalWrapper::getPrimitiveDurationsInternal(
@@ -408,7 +341,7 @@
         }
         int32_t duration = 0;
         auto result = getHal()->getPrimitiveDuration(primitive, &duration);
-        auto halResult = HalResult<int32_t>::fromStatus(result, duration);
+        auto halResult = HalResultFactory::fromStatus<int32_t>(result, duration);
         if (halResult.isUnsupported()) {
             // Should not happen, supported primitives should always support requesting duration.
             ALOGE("Supported primitive %zu returned unsupported for getPrimitiveDuration",
@@ -427,55 +360,55 @@
 HalResult<milliseconds> AidlHalWrapper::getPrimitiveDelayMaxInternal() {
     int32_t delay = 0;
     auto result = getHal()->getCompositionDelayMax(&delay);
-    return HalResult<milliseconds>::fromStatus(result, milliseconds(delay));
+    return HalResultFactory::fromStatus<milliseconds>(result, milliseconds(delay));
 }
 
 HalResult<milliseconds> AidlHalWrapper::getPrimitiveDurationMaxInternal() {
     int32_t delay = 0;
     auto result = getHal()->getPwlePrimitiveDurationMax(&delay);
-    return HalResult<milliseconds>::fromStatus(result, milliseconds(delay));
+    return HalResultFactory::fromStatus<milliseconds>(result, milliseconds(delay));
 }
 
 HalResult<int32_t> AidlHalWrapper::getCompositionSizeMaxInternal() {
     int32_t size = 0;
     auto result = getHal()->getCompositionSizeMax(&size);
-    return HalResult<int32_t>::fromStatus(result, size);
+    return HalResultFactory::fromStatus<int32_t>(result, size);
 }
 
 HalResult<int32_t> AidlHalWrapper::getPwleSizeMaxInternal() {
     int32_t size = 0;
     auto result = getHal()->getPwleCompositionSizeMax(&size);
-    return HalResult<int32_t>::fromStatus(result, size);
+    return HalResultFactory::fromStatus<int32_t>(result, size);
 }
 
 HalResult<float> AidlHalWrapper::getMinFrequencyInternal() {
     float minFrequency = 0;
     auto result = getHal()->getFrequencyMinimum(&minFrequency);
-    return HalResult<float>::fromStatus(result, minFrequency);
+    return HalResultFactory::fromStatus<float>(result, minFrequency);
 }
 
 HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
     float f0 = 0;
     auto result = getHal()->getResonantFrequency(&f0);
-    return HalResult<float>::fromStatus(result, f0);
+    return HalResultFactory::fromStatus<float>(result, f0);
 }
 
 HalResult<float> AidlHalWrapper::getFrequencyResolutionInternal() {
     float frequencyResolution = 0;
     auto result = getHal()->getFrequencyResolution(&frequencyResolution);
-    return HalResult<float>::fromStatus(result, frequencyResolution);
+    return HalResultFactory::fromStatus<float>(result, frequencyResolution);
 }
 
 HalResult<float> AidlHalWrapper::getQFactorInternal() {
     float qFactor = 0;
     auto result = getHal()->getQFactor(&qFactor);
-    return HalResult<float>::fromStatus(result, qFactor);
+    return HalResultFactory::fromStatus<float>(result, qFactor);
 }
 
 HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() {
     std::vector<float> amplitudes;
     auto result = getHal()->getBandwidthAmplitudeMap(&amplitudes);
-    return HalResult<std::vector<float>>::fromStatus(result, amplitudes);
+    return HalResultFactory::fromStatus<std::vector<float>>(result, amplitudes);
 }
 
 sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
@@ -488,7 +421,7 @@
 template <typename I>
 HalResult<void> HidlHalWrapper<I>::ping() {
     auto result = getHal()->ping();
-    return HalResult<void>::fromReturn(result);
+    return HalResultFactory::fromReturn(result);
 }
 
 template <typename I>
@@ -504,7 +437,7 @@
 HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
                                       const std::function<void()>& completionCallback) {
     auto result = getHal()->on(timeout.count());
-    auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+    auto ret = HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
     if (ret.isOk()) {
         mCallbackScheduler->schedule(completionCallback, timeout);
     }
@@ -514,14 +447,14 @@
 template <typename I>
 HalResult<void> HidlHalWrapper<I>::off() {
     auto result = getHal()->off();
-    return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+    return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
 }
 
 template <typename I>
 HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) {
     uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max());
     auto result = getHal()->setAmplitude(amp);
-    return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+    return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
 }
 
 template <typename I>
@@ -547,7 +480,7 @@
     hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
     Capabilities capabilities =
             result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
-    return HalResult<Capabilities>::fromReturn(result, capabilities);
+    return HalResultFactory::fromReturn<Capabilities>(result, capabilities);
 }
 
 template <typename I>
@@ -566,7 +499,7 @@
     auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
     milliseconds length = milliseconds(lengthMs);
 
-    auto ret = HalResult<milliseconds>::fromReturn(result, status, length);
+    auto ret = HalResultFactory::fromReturn<milliseconds>(result, status, length);
     if (ret.isOk()) {
         mCallbackScheduler->schedule(completionCallback, length);
     }
@@ -638,7 +571,7 @@
 
 HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
     auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
-    return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
+    return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
 }
 
 HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
@@ -671,7 +604,7 @@
     sp<V1_3::IVibrator> hal = getHal();
     auto amplitudeResult = hal->supportsAmplitudeControl();
     if (!amplitudeResult.isOk()) {
-        return HalResult<Capabilities>::fromReturn(amplitudeResult, capabilities);
+        return HalResultFactory::fromReturn<Capabilities>(amplitudeResult, capabilities);
     }
 
     auto externalControlResult = hal->supportsExternalControl();
@@ -686,7 +619,7 @@
         }
     }
 
-    return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities);
+    return HalResultFactory::fromReturn<Capabilities>(externalControlResult, capabilities);
 }
 
 // -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index 0df0bfa..aa5b7fc 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -46,8 +46,6 @@
 HalResult<T> ManagerHalController::processHalResult(HalResult<T> result, const char* functionName) {
     if (result.isFailed()) {
         ALOGE("VibratorManager HAL %s failed: %s", functionName, result.errorMessage());
-        std::lock_guard<std::mutex> lock(mConnectedHalMutex);
-        mConnectedHal->tryReconnect();
     }
     return result;
 }
@@ -70,12 +68,16 @@
         hal = mConnectedHal;
     }
 
-    HalResult<T> ret = processHalResult(halFn(hal), functionName);
-    for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) {
-        ret = processHalResult(halFn(hal), functionName);
+    HalResult<T> result = processHalResult(halFn(hal), functionName);
+    for (int i = 0; i < MAX_RETRIES && result.shouldRetry(); i++) {
+        {
+            std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+            mConnectedHal->tryReconnect();
+        }
+        result = processHalResult(halFn(hal), functionName);
     }
 
-    return ret;
+    return result;
 }
 
 // -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 6e660e7..1341266 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -55,8 +55,8 @@
         return HalResult<std::shared_ptr<HalController>>::ok(mController);
     }
     // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
-    return HalResult<std::shared_ptr<HalController>>::failed(MISSING_VIBRATOR_MESSAGE_PREFIX +
-                                                             std::to_string(id));
+    return HalResult<std::shared_ptr<HalController>>::failed(
+            (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str());
 }
 
 HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
@@ -75,10 +75,10 @@
 
 std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
         int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) {
-    std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=]() {
+    std::function<HalResult<sp<Aidl::IVibrator>>()> reconnectFn = [=, this]() {
         sp<Aidl::IVibrator> vibrator;
         auto result = this->getHal()->getVibrator(vibratorId, &vibrator);
-        return HalResult<sp<Aidl::IVibrator>>::fromStatus(result, vibrator);
+        return HalResultFactory::fromStatus<sp<Aidl::IVibrator>>(result, vibrator);
     };
     auto result = reconnectFn();
     if (!result.isOk()) {
@@ -88,12 +88,12 @@
     if (!vibrator) {
         return nullptr;
     }
-    return std::move(std::make_unique<AidlHalWrapper>(std::move(callbackScheduler),
-                                                      std::move(vibrator), reconnectFn));
+    return std::make_unique<AidlHalWrapper>(std::move(callbackScheduler), std::move(vibrator),
+                                            reconnectFn);
 }
 
 HalResult<void> AidlManagerHalWrapper::ping() {
-    return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder());
+    return HalResultFactory::fromStatus(IInterface::asBinder(getHal())->pingBinder());
 }
 
 void AidlManagerHalWrapper::tryReconnect() {
@@ -112,8 +112,8 @@
     }
     int32_t cap = 0;
     auto result = getHal()->getCapabilities(&cap);
-    auto ret = HalResult<ManagerCapabilities>::fromStatus(result,
-                                                          static_cast<ManagerCapabilities>(cap));
+    auto capabilities = static_cast<ManagerCapabilities>(cap);
+    auto ret = HalResultFactory::fromStatus<ManagerCapabilities>(result, capabilities);
     if (ret.isOk()) {
         // Cache copy of returned value.
         mCapabilities.emplace(ret.value());
@@ -129,7 +129,7 @@
     }
     std::vector<int32_t> ids;
     auto result = getHal()->getVibratorIds(&ids);
-    auto ret = HalResult<std::vector<int32_t>>::fromStatus(result, ids);
+    auto ret = HalResultFactory::fromStatus<std::vector<int32_t>>(result, ids);
     if (ret.isOk()) {
         // Cache copy of returned value and the individual controllers.
         mVibratorIds.emplace(ret.value());
@@ -152,12 +152,12 @@
     if (it != mVibrators.end()) {
         return HalResult<std::shared_ptr<HalController>>::ok(it->second);
     }
-    return HalResult<std::shared_ptr<HalController>>::failed(MISSING_VIBRATOR_MESSAGE_PREFIX +
-                                                             std::to_string(id));
+    return HalResult<std::shared_ptr<HalController>>::failed(
+            (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str());
 }
 
 HalResult<void> AidlManagerHalWrapper::prepareSynced(const std::vector<int32_t>& ids) {
-    auto ret = HalResult<void>::fromStatus(getHal()->prepareSynced(ids));
+    auto ret = HalResultFactory::fromStatus(getHal()->prepareSynced(ids));
     if (ret.isOk()) {
         // Force reload of all vibrator controllers that were prepared for a sync operation here.
         // This will trigger calls to getVibrator(id) on each controller, so they can use the
@@ -179,11 +179,11 @@
     bool supportsCallback = capabilities.isOk() &&
             static_cast<int32_t>(capabilities.value() & ManagerCapabilities::TRIGGER_CALLBACK);
     auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr;
-    return HalResult<void>::fromStatus(getHal()->triggerSynced(cb));
+    return HalResultFactory::fromStatus(getHal()->triggerSynced(cb));
 }
 
 HalResult<void> AidlManagerHalWrapper::cancelSynced() {
-    auto ret = HalResult<void>::fromStatus(getHal()->cancelSynced());
+    auto ret = HalResultFactory::fromStatus(getHal()->cancelSynced());
     if (ret.isOk()) {
         // Force reload of all vibrator controllers that were prepared for a sync operation before.
         // This will trigger calls to getVibrator(id) on each controller, so they can use the
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 6b73d17..f97442d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -64,7 +64,29 @@
      */
     Info getInfo() {
         static Info sDefaultInfo = InfoCache().get();
-        return apply<Info>([](HalWrapper* hal) { return hal->getInfo(); }, sDefaultInfo, "getInfo");
+        if (!init()) {
+            ALOGV("Skipped getInfo because Vibrator HAL is not available");
+            return sDefaultInfo;
+        }
+        std::shared_ptr<HalWrapper> hal;
+        {
+            std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+            hal = mConnectedHal;
+        }
+
+        for (int i = 0; i < MAX_RETRIES; i++) {
+            Info result = hal.get()->getInfo();
+            result.logFailures();
+            if (result.shouldRetry()) {
+                tryReconnect();
+            } else {
+                return result;
+            }
+        }
+
+        Info result = hal.get()->getInfo();
+        result.logFailures();
+        return result;
     }
 
     /* Calls given HAL function, applying automatic retries to reconnect with the HAL when the
@@ -72,7 +94,7 @@
      */
     template <typename T>
     HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, const char* functionName) {
-        return apply(halFn, HalResult<T>::unsupported(), functionName);
+        return doWithRetry<T>(halFn, HalResult<T>::unsupported(), functionName);
     }
 
 private:
@@ -90,7 +112,8 @@
      * function name is for logging purposes.
      */
     template <typename T>
-    T apply(const HalFunction<T>& halFn, T defaultValue, const char* functionName) {
+    HalResult<T> doWithRetry(const HalFunction<HalResult<T>>& halFn, HalResult<T> defaultValue,
+                             const char* functionName) {
         if (!init()) {
             ALOGV("Skipped %s because Vibrator HAL is not available", functionName);
             return defaultValue;
@@ -101,16 +124,22 @@
             hal = mConnectedHal;
         }
 
-        for (int i = 0; i < MAX_RETRIES; i++) {
-            T result = halFn(hal.get());
-            if (result.isFailedLogged(functionName)) {
-                tryReconnect();
-            } else {
-                return result;
-            }
+        HalResult<T> result = doOnce(hal.get(), halFn, functionName);
+        for (int i = 0; i < MAX_RETRIES && result.shouldRetry(); i++) {
+            tryReconnect();
+            result = doOnce(hal.get(), halFn, functionName);
         }
+        return result;
+    }
 
-        return halFn(hal.get());
+    template <typename T>
+    HalResult<T> doOnce(HalWrapper* hal, const HalFunction<HalResult<T>>& halFn,
+                        const char* functionName) {
+        HalResult<T> result = halFn(hal);
+        if (result.isFailed()) {
+            ALOGE("Vibrator HAL %s failed: %s", functionName, result.errorMessage());
+        }
+        return result;
     }
 };
 
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index d2cc9ad..39c4eb4 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -31,101 +31,154 @@
 
 // -------------------------------------------------------------------------------------------------
 
+// Base class to represent a generic result of a call to the Vibrator HAL wrapper.
+class BaseHalResult {
+public:
+    bool isOk() const { return mStatus == SUCCESS; }
+    bool isFailed() const { return mStatus == FAILED; }
+    bool isUnsupported() const { return mStatus == UNSUPPORTED; }
+    bool shouldRetry() const { return isFailed() && mDeadObject; }
+    const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+protected:
+    enum Status { SUCCESS, UNSUPPORTED, FAILED };
+    Status mStatus;
+    std::string mErrorMessage;
+    bool mDeadObject;
+
+    explicit BaseHalResult(Status status, const char* errorMessage = "", bool deadObject = false)
+          : mStatus(status), mErrorMessage(errorMessage), mDeadObject(deadObject) {}
+    virtual ~BaseHalResult() = default;
+};
+
 // Result of a call to the Vibrator HAL wrapper, holding data if successful.
 template <typename T>
-class HalResult {
+class HalResult : public BaseHalResult {
 public:
     static HalResult<T> ok(T value) { return HalResult(value); }
-    static HalResult<T> failed(std::string msg) {
-        return HalResult(std::move(msg), /* unsupported= */ false);
+    static HalResult<T> unsupported() { return HalResult(Status::UNSUPPORTED); }
+    static HalResult<T> failed(const char* msg) { return HalResult(Status::FAILED, msg); }
+    static HalResult<T> transactionFailed(const char* msg) {
+        return HalResult(Status::FAILED, msg, /* deadObject= */ true);
     }
-    static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
 
+    // This will throw std::bad_optional_access if this result is not ok.
+    const T& value() const { return mValue.value(); }
+    const T valueOr(T&& defaultValue) const { return mValue.value_or(defaultValue); }
+
+private:
+    std::optional<T> mValue;
+
+    explicit HalResult(T value)
+          : BaseHalResult(Status::SUCCESS), mValue(std::make_optional(value)) {}
+    explicit HalResult(Status status, const char* errorMessage = "", bool deadObject = false)
+          : BaseHalResult(status, errorMessage, deadObject), mValue() {}
+};
+
+// Empty result of a call to the Vibrator HAL wrapper.
+template <>
+class HalResult<void> : public BaseHalResult {
+public:
+    static HalResult<void> ok() { return HalResult(Status::SUCCESS); }
+    static HalResult<void> unsupported() { return HalResult(Status::UNSUPPORTED); }
+    static HalResult<void> failed(const char* msg) { return HalResult(Status::FAILED, msg); }
+    static HalResult<void> transactionFailed(const char* msg) {
+        return HalResult(Status::FAILED, msg, /* deadObject= */ true);
+    }
+
+private:
+    explicit HalResult(Status status, const char* errorMessage = "", bool deadObject = false)
+          : BaseHalResult(status, errorMessage, deadObject) {}
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Factory functions that convert failed HIDL/AIDL results into HalResult instances.
+// Implementation of static template functions needs to be in this header file for the linker.
+class HalResultFactory {
+public:
+    template <typename T>
     static HalResult<T> fromStatus(binder::Status status, T data) {
+        return status.isOk() ? HalResult<T>::ok(data) : fromFailedStatus<T>(status);
+    }
+
+    template <typename T>
+    static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data) {
+        return (status == hardware::vibrator::V1_0::Status::OK) ? HalResult<T>::ok(data)
+                                                                : fromFailedStatus<T>(status);
+    }
+
+    template <typename T, typename R>
+    static HalResult<T> fromReturn(hardware::Return<R>& ret, T data) {
+        return ret.isOk() ? HalResult<T>::ok(data) : fromFailedReturn<T, R>(ret);
+    }
+
+    template <typename T, typename R>
+    static HalResult<T> fromReturn(hardware::Return<R>& ret,
+                                   hardware::vibrator::V1_0::Status status, T data) {
+        return ret.isOk() ? fromStatus<T>(status, data) : fromFailedReturn<T, R>(ret);
+    }
+
+    static HalResult<void> fromStatus(status_t status) {
+        return (status == android::OK) ? HalResult<void>::ok() : fromFailedStatus<void>(status);
+    }
+
+    static HalResult<void> fromStatus(binder::Status status) {
+        return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(status);
+    }
+
+    static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status) {
+        return (status == hardware::vibrator::V1_0::Status::OK) ? HalResult<void>::ok()
+                                                                : fromFailedStatus<void>(status);
+    }
+
+    template <typename R>
+    static HalResult<void> fromReturn(hardware::Return<R>& ret) {
+        return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(ret);
+    }
+
+private:
+    template <typename T>
+    static HalResult<T> fromFailedStatus(status_t status) {
+        auto msg = "status_t = " + statusToString(status);
+        return (status == android::DEAD_OBJECT) ? HalResult<T>::transactionFailed(msg.c_str())
+                                                : HalResult<T>::failed(msg.c_str());
+    }
+
+    template <typename T>
+    static HalResult<T> fromFailedStatus(binder::Status status) {
         if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
             status.transactionError() == android::UNKNOWN_TRANSACTION) {
             // UNKNOWN_TRANSACTION means the HAL implementation is an older version, so this is
             // the same as the operation being unsupported by this HAL. Should not retry.
             return HalResult<T>::unsupported();
         }
-        if (status.isOk()) {
-            return HalResult<T>::ok(data);
+        if (status.exceptionCode() == binder::Status::EX_TRANSACTION_FAILED) {
+            return HalResult<T>::transactionFailed(status.toString8().c_str());
         }
         return HalResult<T>::failed(status.toString8().c_str());
     }
-    static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data);
 
-    template <typename R>
-    static HalResult<T> fromReturn(hardware::Return<R>& ret, T data);
-
-    template <typename R>
-    static HalResult<T> fromReturn(hardware::Return<R>& ret,
-                                   hardware::vibrator::V1_0::Status status, T data);
-
-    // This will throw std::bad_optional_access if this result is not ok.
-    const T& value() const { return mValue.value(); }
-    const T valueOr(T&& defaultValue) const { return mValue.value_or(defaultValue); }
-    bool isOk() const { return !mUnsupported && mValue.has_value(); }
-    bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
-    bool isUnsupported() const { return mUnsupported; }
-    const char* errorMessage() const { return mErrorMessage.c_str(); }
-    bool isFailedLogged(const char* functionNameForLogging) const {
-        if (isFailed()) {
-            ALOGE("Vibrator HAL %s failed: %s", functionNameForLogging, errorMessage());
-            return true;
+    template <typename T>
+    static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status status) {
+        switch (status) {
+            case hardware::vibrator::V1_0::Status::UNSUPPORTED_OPERATION:
+                return HalResult<T>::unsupported();
+            default:
+                auto msg = "android::hardware::vibrator::V1_0::Status = " + toString(status);
+                return HalResult<T>::failed(msg.c_str());
         }
-        return false;
     }
 
-private:
-    std::optional<T> mValue;
-    std::string mErrorMessage;
-    bool mUnsupported;
-
-    explicit HalResult(T value)
-          : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {}
-    explicit HalResult(std::string errorMessage, bool unsupported)
-          : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
-};
-
-// Empty result of a call to the Vibrator HAL wrapper.
-template <>
-class HalResult<void> {
-public:
-    static HalResult<void> ok() { return HalResult(); }
-    static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
-    static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
-
-    static HalResult<void> fromStatus(status_t status);
-    static HalResult<void> fromStatus(binder::Status status);
-    static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status);
-
-    template <typename R>
-    static HalResult<void> fromReturn(hardware::Return<R>& ret);
-
-    bool isOk() const { return !mUnsupported && !mFailed; }
-    bool isFailed() const { return !mUnsupported && mFailed; }
-    bool isUnsupported() const { return mUnsupported; }
-    const char* errorMessage() const { return mErrorMessage.c_str(); }
-    bool isFailedLogged(const char* functionNameForLogging) const {
-        if (isFailed()) {
-            ALOGE("Vibrator HAL %s failed: %s", functionNameForLogging, errorMessage());
-            return true;
-        }
-        return false;
+    template <typename T, typename R>
+    static HalResult<T> fromFailedReturn(hardware::Return<R>& ret) {
+        return ret.isDeadObject() ? HalResult<T>::transactionFailed(ret.description().c_str())
+                                  : HalResult<T>::failed(ret.description().c_str());
     }
-
-private:
-    std::string mErrorMessage;
-    bool mFailed;
-    bool mUnsupported;
-
-    explicit HalResult(bool unsupported = false)
-          : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
-    explicit HalResult(std::string errorMessage)
-          : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
 };
 
+// -------------------------------------------------------------------------------------------------
+
 class HalCallbackWrapper : public hardware::vibrator::BnVibratorCallback {
 public:
     HalCallbackWrapper(std::function<void()> completionCallback)
@@ -192,21 +245,44 @@
     const HalResult<float> qFactor;
     const HalResult<std::vector<float>> maxAmplitudes;
 
-    bool isFailedLogged(const char*) const {
-        return capabilities.isFailedLogged("getCapabilities") ||
-                supportedEffects.isFailedLogged("getSupportedEffects") ||
-                supportedBraking.isFailedLogged("getSupportedBraking") ||
-                supportedPrimitives.isFailedLogged("getSupportedPrimitives") ||
-                primitiveDurations.isFailedLogged("getPrimitiveDuration") ||
-                primitiveDelayMax.isFailedLogged("getPrimitiveDelayMax") ||
-                pwlePrimitiveDurationMax.isFailedLogged("getPwlePrimitiveDurationMax") ||
-                compositionSizeMax.isFailedLogged("getCompositionSizeMax") ||
-                pwleSizeMax.isFailedLogged("getPwleSizeMax") ||
-                minFrequency.isFailedLogged("getMinFrequency") ||
-                resonantFrequency.isFailedLogged("getResonantFrequency") ||
-                frequencyResolution.isFailedLogged("getFrequencyResolution") ||
-                qFactor.isFailedLogged("getQFactor") ||
-                maxAmplitudes.isFailedLogged("getMaxAmplitudes");
+    void logFailures() const {
+        logFailure<Capabilities>(capabilities, "getCapabilities");
+        logFailure<std::vector<hardware::vibrator::Effect>>(supportedEffects,
+                                                            "getSupportedEffects");
+        logFailure<std::vector<hardware::vibrator::Braking>>(supportedBraking,
+                                                             "getSupportedBraking");
+        logFailure<std::vector<hardware::vibrator::CompositePrimitive>>(supportedPrimitives,
+                                                                        "getSupportedPrimitives");
+        logFailure<std::vector<std::chrono::milliseconds>>(primitiveDurations,
+                                                           "getPrimitiveDuration");
+        logFailure<std::chrono::milliseconds>(primitiveDelayMax, "getPrimitiveDelayMax");
+        logFailure<std::chrono::milliseconds>(pwlePrimitiveDurationMax,
+                                              "getPwlePrimitiveDurationMax");
+        logFailure<int32_t>(compositionSizeMax, "getCompositionSizeMax");
+        logFailure<int32_t>(pwleSizeMax, "getPwleSizeMax");
+        logFailure<float>(minFrequency, "getMinFrequency");
+        logFailure<float>(resonantFrequency, "getResonantFrequency");
+        logFailure<float>(frequencyResolution, "getFrequencyResolution");
+        logFailure<float>(qFactor, "getQFactor");
+        logFailure<std::vector<float>>(maxAmplitudes, "getMaxAmplitudes");
+    }
+
+    bool shouldRetry() const {
+        return capabilities.shouldRetry() || supportedEffects.shouldRetry() ||
+                supportedBraking.shouldRetry() || supportedPrimitives.shouldRetry() ||
+                primitiveDurations.shouldRetry() || primitiveDelayMax.shouldRetry() ||
+                pwlePrimitiveDurationMax.shouldRetry() || compositionSizeMax.shouldRetry() ||
+                pwleSizeMax.shouldRetry() || minFrequency.shouldRetry() ||
+                resonantFrequency.shouldRetry() || frequencyResolution.shouldRetry() ||
+                qFactor.shouldRetry() || maxAmplitudes.shouldRetry();
+    }
+
+private:
+    template <typename T>
+    void logFailure(HalResult<T> result, const char* functionName) const {
+        if (result.isFailed()) {
+            ALOGE("Vibrator HAL %s failed: %s", functionName, result.errorMessage());
+        }
     }
 };
 
@@ -230,27 +306,29 @@
     }
 
 private:
+    // Create a transaction failed results as default so we can retry on the first time we get them.
     static const constexpr char* MSG = "never loaded";
-    HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::failed(MSG);
+    HalResult<Capabilities> mCapabilities = HalResult<Capabilities>::transactionFailed(MSG);
     HalResult<std::vector<hardware::vibrator::Effect>> mSupportedEffects =
-            HalResult<std::vector<hardware::vibrator::Effect>>::failed(MSG);
+            HalResult<std::vector<hardware::vibrator::Effect>>::transactionFailed(MSG);
     HalResult<std::vector<hardware::vibrator::Braking>> mSupportedBraking =
-            HalResult<std::vector<hardware::vibrator::Braking>>::failed(MSG);
+            HalResult<std::vector<hardware::vibrator::Braking>>::transactionFailed(MSG);
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives =
-            HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::failed(MSG);
+            HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::transactionFailed(MSG);
     HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations =
-            HalResult<std::vector<std::chrono::milliseconds>>::failed(MSG);
+            HalResult<std::vector<std::chrono::milliseconds>>::transactionFailed(MSG);
     HalResult<std::chrono::milliseconds> mPrimitiveDelayMax =
-            HalResult<std::chrono::milliseconds>::failed(MSG);
+            HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
     HalResult<std::chrono::milliseconds> mPwlePrimitiveDurationMax =
-            HalResult<std::chrono::milliseconds>::failed(MSG);
-    HalResult<int32_t> mCompositionSizeMax = HalResult<int>::failed(MSG);
-    HalResult<int32_t> mPwleSizeMax = HalResult<int>::failed(MSG);
-    HalResult<float> mMinFrequency = HalResult<float>::failed(MSG);
-    HalResult<float> mResonantFrequency = HalResult<float>::failed(MSG);
-    HalResult<float> mFrequencyResolution = HalResult<float>::failed(MSG);
-    HalResult<float> mQFactor = HalResult<float>::failed(MSG);
-    HalResult<std::vector<float>> mMaxAmplitudes = HalResult<std::vector<float>>::failed(MSG);
+            HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
+    HalResult<int32_t> mCompositionSizeMax = HalResult<int>::transactionFailed(MSG);
+    HalResult<int32_t> mPwleSizeMax = HalResult<int>::transactionFailed(MSG);
+    HalResult<float> mMinFrequency = HalResult<float>::transactionFailed(MSG);
+    HalResult<float> mResonantFrequency = HalResult<float>::transactionFailed(MSG);
+    HalResult<float> mFrequencyResolution = HalResult<float>::transactionFailed(MSG);
+    HalResult<float> mQFactor = HalResult<float>::transactionFailed(MSG);
+    HalResult<std::vector<float>> mMaxAmplitudes =
+            HalResult<std::vector<float>>::transactionFailed(MSG);
 
     friend class HalWrapper;
 };
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 8e77bc5..9b95d74 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -107,17 +107,38 @@
     ASSERT_EQ(1, mConnectCounter);
 }
 
-TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnAnyFailure) {
+TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnTransactionFailure) {
     EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
     EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal())
             .Times(Exactly(2))
-            .WillOnce(Return(vibrator::HalResult<vibrator::Capabilities>::failed("message")))
+            .WillOnce(Return(vibrator::HalResult<vibrator::Capabilities>::transactionFailed("msg")))
             .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::ok(
                     vibrator::Capabilities::ON_CALLBACK)));
 
     auto result = mController->getInfo();
-    ASSERT_FALSE(result.capabilities.isFailed());
+    ASSERT_TRUE(result.capabilities.isOk());
+    ASSERT_EQ(1, mConnectCounter);
+}
 
+TEST_F(VibratorHalControllerTest, TestGetInfoDoesNotRetryOnOperationFailure) {
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0));
+    EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal())
+            .Times(Exactly(1))
+            .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::failed("msg")));
+
+    auto result = mController->getInfo();
+    ASSERT_TRUE(result.capabilities.isFailed());
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestGetInfoDoesNotRetryOnUnsupported) {
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0));
+    EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal())
+            .Times(Exactly(1))
+            .WillRepeatedly(Return(vibrator::HalResult<vibrator::Capabilities>::unsupported()));
+
+    auto result = mController->getInfo();
+    ASSERT_TRUE(result.capabilities.isUnsupported());
     ASSERT_EQ(1, mConnectCounter);
 }
 
@@ -128,48 +149,54 @@
 
     auto result = mController->doWithRetry<void>(ON_FN, "on");
     ASSERT_TRUE(result.isOk());
-
     ASSERT_EQ(1, mConnectCounter);
 }
 
-TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoesNotResetHalConnection) {
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0));
     EXPECT_CALL(*mMockHal.get(), off())
             .Times(Exactly(1))
             .WillRepeatedly(Return(vibrator::HalResult<void>::unsupported()));
 
-    ASSERT_EQ(0, mConnectCounter);
     auto result = mController->doWithRetry<void>(OFF_FN, "off");
     ASSERT_TRUE(result.isUnsupported());
     ASSERT_EQ(1, mConnectCounter);
 }
 
-TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) {
+TEST_F(VibratorHalControllerTest, TestOperationFailedApiResultDoesNotResetHalConnection) {
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0));
     EXPECT_CALL(*mMockHal.get(), on(_, _))
-            .Times(Exactly(2))
+            .Times(Exactly(1))
             .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
-    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
-
-    ASSERT_EQ(0, mConnectCounter);
 
     auto result = mController->doWithRetry<void>(ON_FN, "on");
     ASSERT_TRUE(result.isFailed());
     ASSERT_EQ(1, mConnectCounter);
 }
 
-TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) {
+TEST_F(VibratorHalControllerTest, TestTransactionFailedApiResultResetsHalConnection) {
+    EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
+    EXPECT_CALL(*mMockHal.get(), on(_, _))
+            .Times(Exactly(2))
+            .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message")));
+
+    auto result = mController->doWithRetry<void>(ON_FN, "on");
+    ASSERT_TRUE(result.isFailed());
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorHalControllerTest, TestTransactionFailedApiResultReturnsSuccessAfterRetries) {
     {
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+                .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message")));
         EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
     }
 
-    ASSERT_EQ(0, mConnectCounter);
-
     auto result = mController->doWithRetry<void>(PING_FN, "ping");
     ASSERT_TRUE(result.isOk());
     ASSERT_EQ(1, mConnectCounter);
@@ -221,11 +248,11 @@
                 });
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+                .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message")));
         EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+                .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message")));
     }
 
     std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
index e5fbbae..11a8b66 100644
--- a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp
@@ -98,8 +98,10 @@
                 .WillRepeatedly(Return(voidResult));
 
         if (cardinality > 1) {
-            // One reconnection call after each failure.
-            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * cardinality));
+            // One reconnection for each retry.
+            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * (cardinality - 1)));
+        } else {
+            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0));
         }
     }
 };
@@ -141,14 +143,12 @@
     ASSERT_EQ(1, mConnectCounter);
 }
 
-TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) {
+TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoesNotResetHalConnection) {
     setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::unsupported(),
                        vibrator::HalResult<vibrator::ManagerCapabilities>::unsupported(),
                        vibrator::HalResult<std::vector<int32_t>>::unsupported(),
                        vibrator::HalResult<std::shared_ptr<HalController>>::unsupported());
 
-    ASSERT_EQ(0, mConnectCounter);
-
     ASSERT_TRUE(mController->ping().isUnsupported());
     ASSERT_TRUE(mController->getCapabilities().isUnsupported());
     ASSERT_TRUE(mController->getVibratorIds().isUnsupported());
@@ -160,13 +160,28 @@
     ASSERT_EQ(1, mConnectCounter);
 }
 
-TEST_F(VibratorManagerHalControllerTest, TestFailedApiResultResetsHalConnection) {
-    setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::failed("message"),
-                       vibrator::HalResult<vibrator::ManagerCapabilities>::failed("message"),
-                       vibrator::HalResult<std::vector<int32_t>>::failed("message"),
-                       vibrator::HalResult<std::shared_ptr<HalController>>::failed("message"));
+TEST_F(VibratorManagerHalControllerTest, TestOperationFailedApiResultDoesNotResetHalConnection) {
+    setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::failed("msg"),
+                       vibrator::HalResult<vibrator::ManagerCapabilities>::failed("msg"),
+                       vibrator::HalResult<std::vector<int32_t>>::failed("msg"),
+                       vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg"));
 
-    ASSERT_EQ(0, mConnectCounter);
+    ASSERT_TRUE(mController->ping().isFailed());
+    ASSERT_TRUE(mController->getCapabilities().isFailed());
+    ASSERT_TRUE(mController->getVibratorIds().isFailed());
+    ASSERT_TRUE(mController->getVibrator(VIBRATOR_ID).isFailed());
+    ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed());
+    ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed());
+    ASSERT_TRUE(mController->cancelSynced().isFailed());
+
+    ASSERT_EQ(1, mConnectCounter);
+}
+
+TEST_F(VibratorManagerHalControllerTest, TestTransactionFailedApiResultResetsHalConnection) {
+    setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::transactionFailed("m"),
+                       vibrator::HalResult<vibrator::ManagerCapabilities>::transactionFailed("m"),
+                       vibrator::HalResult<std::vector<int32_t>>::transactionFailed("m"),
+                       vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m"));
 
     ASSERT_TRUE(mController->ping().isFailed());
     ASSERT_TRUE(mController->getCapabilities().isFailed());
@@ -184,14 +199,13 @@
         InSequence seq;
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(1))
-                .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message")));
+                .WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message")));
         EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1));
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(1))
                 .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
     }
 
-    ASSERT_EQ(0, mConnectCounter);
     ASSERT_TRUE(mController->ping().isOk());
     ASSERT_EQ(1, mConnectCounter);
 }
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index 1593cb1..dffc281 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -254,13 +254,14 @@
     EXPECT_CALL(*mMockHal.get(), getVibrator(Eq(kVibratorId), _))
             .Times(Exactly(3))
             .WillOnce(DoAll(SetArgPointee<1>(nullptr),
-                            Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))))
+                            Return(Status::fromExceptionCode(
+                                    Status::Exception::EX_TRANSACTION_FAILED))))
             .WillRepeatedly(DoAll(SetArgPointee<1>(mMockVibrator), Return(Status())));
 
     EXPECT_CALL(*mMockVibrator.get(), off())
             .Times(Exactly(3))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_TRANSACTION_FAILED)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_TRANSACTION_FAILED)))
             .WillRepeatedly(Return(Status()));
 
     // Get vibrator controller is successful even if first getVibrator.