Merge "Add layer name to layer_state_t and SurfaceControl"
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 01f7d30..c163095 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -35,9 +35,12 @@
 GLHelper::~GLHelper() {
 }
 
-bool GLHelper::setUp(const ShaderDesc* shaderDescs, size_t numShaders) {
+bool GLHelper::setUp(const sp<IBinder>& displayToken, const ShaderDesc* shaderDescs,
+                     size_t numShaders) {
     bool result;
 
+    mDisplayToken = displayToken;
+
     mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     if (mDisplay == EGL_NO_DISPLAY) {
         fprintf(stderr, "eglGetDisplay error: %#x\n", eglGetError());
@@ -221,14 +224,8 @@
 }
 
 bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) {
-    const sp<IBinder> dpy = mSurfaceComposerClient->getInternalDisplayToken();
-    if (dpy == nullptr) {
-        fprintf(stderr, "SurfaceComposer::getInternalDisplayToken failed.\n");
-        return false;
-    }
-
     ui::DisplayMode mode;
-    status_t err = mSurfaceComposerClient->getActiveDisplayMode(dpy, &mode);
+    status_t err = mSurfaceComposerClient->getActiveDisplayMode(mDisplayToken, &mode);
     if (err != NO_ERROR) {
         fprintf(stderr, "SurfaceComposer::getActiveDisplayMode failed: %#x\n", err);
         return false;
diff --git a/cmds/flatland/GLHelper.h b/cmds/flatland/GLHelper.h
index d09463a..5194f50 100644
--- a/cmds/flatland/GLHelper.h
+++ b/cmds/flatland/GLHelper.h
@@ -44,7 +44,7 @@
 
     ~GLHelper();
 
-    bool setUp(const ShaderDesc* shaderDescs, size_t numShaders);
+    bool setUp(const sp<IBinder>& displayToken, const ShaderDesc* shaderDescs, size_t numShaders);
 
     void tearDown();
 
@@ -87,6 +87,8 @@
     size_t mNumShaders;
 
     GLuint mDitherTexture;
+
+    sp<IBinder> mDisplayToken;
 };
 
 } // namespace android
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
index 7ceb397..6d14d56 100644
--- a/cmds/flatland/Main.cpp
+++ b/cmds/flatland/Main.cpp
@@ -20,6 +20,7 @@
 #include <gui/SurfaceControl.h>
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
 #include <ui/Fence.h>
 #include <utils/Trace.h>
 
@@ -34,9 +35,10 @@
 
 using namespace ::android;
 
-static uint32_t g_SleepBetweenSamplesMs = 0;
-static bool     g_PresentToWindow       = false;
-static size_t   g_BenchmarkNameLen      = 0;
+static uint32_t    g_SleepBetweenSamplesMs = 0;
+static bool        g_PresentToWindow       = false;
+static size_t      g_BenchmarkNameLen      = 0;
+static sp<IBinder> g_DisplayToken          = nullptr;
 
 struct BenchmarkDesc {
     // The name of the test.
@@ -393,7 +395,7 @@
         uint32_t h = mDesc.runHeights[mInstance];
 
         mGLHelper = new GLHelper();
-        result = mGLHelper->setUp(shaders, NELEMS(shaders));
+        result = mGLHelper->setUp(g_DisplayToken, shaders, NELEMS(shaders));
         if (!result) {
             return false;
         }
@@ -718,13 +720,17 @@
 }
 
 // Print the command usage help to stderr.
-static void showHelp(const char *cmd) {
-    fprintf(stderr, "usage: %s [options]\n", cmd);
-    fprintf(stderr, "options include:\n"
-                    "  -s N            sleep for N ms between samples\n"
-                    "  -d              display the test frame to a window\n"
-                    "  --help          print this helpful message and exit\n"
-            );
+static void showHelp(const char* cmd) {
+  fprintf(stderr, "usage: %s [options]\n", cmd);
+  fprintf(
+      stderr,
+      "options include:\n"
+      "  -s N            sleep for N ms between samples\n"
+      "  -d              display the test frame to a window\n"
+      "  -i display-id   specify a display ID to use for multi-display device\n"
+      "                  see \"dumpsys SurfaceFlinger --display-id\" for valid "
+      "display IDs\n"
+      "  --help          print this helpful message and exit\n");
 }
 
 int main(int argc, char** argv) {
@@ -733,6 +739,14 @@
         exit(0);
     }
 
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    if (ids.empty()) {
+        fprintf(stderr, "Failed to get ID for any displays.\n");
+        exit(3);
+    }
+
+    std::optional<PhysicalDisplayId> displayId;
+
     for (;;) {
         int ret;
         int option_index = 0;
@@ -741,7 +755,7 @@
             {     0,               0, 0,  0 }
         };
 
-        ret = getopt_long(argc, argv, "ds:",
+        ret = getopt_long(argc, argv, "ds:i:",
                           long_options, &option_index);
 
         if (ret < 0) {
@@ -757,6 +771,14 @@
                 g_SleepBetweenSamplesMs = atoi(optarg);
             break;
 
+            case 'i':
+                displayId = DisplayId::fromValue<PhysicalDisplayId>(atoll(optarg));
+                if (!displayId) {
+                    fprintf(stderr, "Invalid display ID: %s.\n", optarg);
+                    exit(4);
+                }
+            break;
+
             case 0:
                 if (strcmp(long_options[option_index].name, "help")) {
                     showHelp(argv[0]);
@@ -770,6 +792,22 @@
         }
     }
 
+    if (!displayId) { // no display id is specified
+        if (ids.size() == 1) {
+            displayId = ids.front();
+        } else {
+            fprintf(stderr, "Please specify a display ID for multi-display device.\n");
+            showHelp(argv[0]);
+            exit(5);
+        }
+    }
+
+    g_DisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+    if (g_DisplayToken == nullptr) {
+        fprintf(stderr, "SurfaceComposer::getPhysicalDisplayToken failed.\n");
+        exit(6);
+    }
+
     g_BenchmarkNameLen = maxBenchmarkNameLen();
 
     printf(" cmdline:");
@@ -782,4 +820,6 @@
         fprintf(stderr, "exiting due to error.\n");
         return 1;
     }
+
+    return 0;
 }
diff --git a/cmds/surfacereplayer/Android.bp b/cmds/surfacereplayer/Android.bp
deleted file mode 100644
index 34fc8b1..0000000
--- a/cmds/surfacereplayer/Android.bp
+++ /dev/null
@@ -1,13 +0,0 @@
-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"],
-}
-
-subdirs = [
-    "proto",
-    "replayer",
-]
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS
deleted file mode 100644
index 32bcc83..0000000
--- a/cmds/surfacereplayer/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp
deleted file mode 100644
index 23b54ee..0000000
--- a/cmds/surfacereplayer/proto/Android.bp
+++ /dev/null
@@ -1,23 +0,0 @@
-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_library_static {
-    name: "libtrace_proto",
-    srcs: [
-        "src/trace.proto",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    proto: {
-        type: "lite",
-        export_proto_headers: true,
-    },
-}
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
deleted file mode 100644
index a177027..0000000
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ /dev/null
@@ -1,225 +0,0 @@
-syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
-package android.surfaceflinger;
-
-message Trace {
-    repeated Increment increment = 1;
-}
-
-message Increment {
-    required int64 time_stamp = 1;
-
-    oneof increment {
-        Transaction        transaction          = 2;
-        SurfaceCreation    surface_creation     = 3;
-        SurfaceDeletion    surface_deletion     = 4;
-        BufferUpdate       buffer_update        = 5;
-        VSyncEvent         vsync_event          = 6;
-        DisplayCreation    display_creation     = 7;
-        DisplayDeletion    display_deletion     = 8;
-        PowerModeUpdate    power_mode_update    = 9;
-    }
-}
-
-message Transaction {
-    repeated SurfaceChange surface_change = 1;
-    repeated DisplayChange display_change = 2;
-
-    required bool   synchronous      = 3;
-    required bool   animation        = 4;
-    optional Origin origin           = 5;
-    optional uint64 id               = 6;
-}
-
-message SurfaceChange {
-    required int32 id = 1;
-    reserved 7;
-    oneof SurfaceChange {
-        PositionChange              position                = 2;
-        SizeChange                  size                    = 3;
-        AlphaChange                 alpha                   = 4;
-        LayerChange                 layer                   = 5;
-        CropChange                  crop                    = 6;
-        MatrixChange                matrix                  = 8;
-        TransparentRegionHintChange transparent_region_hint = 10;
-        LayerStackChange            layer_stack             = 11;
-        HiddenFlagChange            hidden_flag             = 12;
-        OpaqueFlagChange            opaque_flag             = 13;
-        SecureFlagChange            secure_flag             = 14;
-        CornerRadiusChange          corner_radius           = 16;
-        ReparentChange              reparent                = 17;
-        RelativeParentChange        relative_parent         = 18;
-        BackgroundBlurRadiusChange  background_blur_radius  = 20;
-        ShadowRadiusChange          shadow_radius           = 21;
-        BlurRegionsChange           blur_regions            = 22;
-        TrustedOverlayChange        trusted_overlay         = 23;
-    }
-}
-
-message PositionChange {
-    required float x = 1;
-    required float y = 2;
-}
-
-message SizeChange {
-    required uint32 w = 1;
-    required uint32 h = 2;
-}
-
-message AlphaChange {
-    required float alpha = 1;
-}
-
-message CornerRadiusChange {
-    required float corner_radius = 1;
-}
-
-message BackgroundBlurRadiusChange {
-    required float background_blur_radius = 1;
-}
-
-message LayerChange {
-    required uint32 layer = 1;
-}
-
-message CropChange {
-    required Rectangle rectangle = 1;
-}
-
-message MatrixChange {
-    required float dsdx = 1;
-    required float dtdx = 2;
-    required float dsdy = 3;
-    required float dtdy = 4;
-}
-
-message TransparentRegionHintChange {
-    repeated Rectangle region = 1;
-}
-
-message LayerStackChange {
-    required uint32 layer_stack = 1;
-}
-
-message DisplayFlagsChange {
-    required uint32 flags = 1;
-}
-
-message HiddenFlagChange {
-    required bool hidden_flag = 1;
-}
-
-message OpaqueFlagChange {
-    required bool opaque_flag = 1;
-}
-
-message SecureFlagChange {
-    required bool secure_flag = 1;
-}
-
-message DisplayChange {
-    required int32 id = 1;
-
-    oneof DisplayChange {
-        DispSurfaceChange surface     = 2;
-        LayerStackChange  layer_stack = 3;
-        SizeChange        size        = 4;
-        ProjectionChange  projection  = 5;
-        DisplayFlagsChange flags      = 6;
-    }
-}
-
-message DispSurfaceChange {
-    required uint64 buffer_queue_id   = 1;
-    required string buffer_queue_name = 2;
-}
-
-message ProjectionChange {
-    required int32     orientation = 1;
-    required Rectangle viewport    = 2;
-    required Rectangle frame       = 3;
-}
-
-message Rectangle {
-    required int32 left   = 1;
-    required int32 top    = 2;
-    required int32 right  = 3;
-    required int32 bottom = 4;
-}
-
-message SurfaceCreation {
-    required int32  id   = 1;
-    required string name = 2;
-    required uint32 w    = 3;
-    required uint32 h    = 4;
-}
-
-message SurfaceDeletion {
-    required int32 id = 1;
-}
-
-message BufferUpdate {
-    required int32  id           = 1;
-    required uint32 w            = 2;
-    required uint32 h            = 3;
-    required uint64 frame_number = 4;
-}
-
-message VSyncEvent {
-    required int64 when = 1;
-}
-
-message DisplayCreation {
-    required int32     id                = 1;
-    required string    name              = 2;
-    optional uint64    display_id        = 3;
-    required bool      is_secure         = 4;
-}
-
-message DisplayDeletion {
-    required int32 id = 1;
-}
-
-message PowerModeUpdate {
-    required int32  id   = 1;
-    required int32  mode = 2;
-}
-
-message ReparentChange {
-    required int32 parent_id = 1;
-}
-
-message RelativeParentChange {
-    required int32 relative_parent_id = 1;
-    required int32 z = 2;
-}
-
-message ShadowRadiusChange {
-    required float radius = 1;
-}
-
-message TrustedOverlayChange {
-    required float is_trusted_overlay = 1;
-}
-
-message BlurRegionsChange {
-    repeated BlurRegionChange blur_regions = 1;
-}
-
-message BlurRegionChange {
-    required uint32 blur_radius = 1;
-    required float corner_radius_tl = 2;
-    required float corner_radius_tr = 3;
-    required float corner_radius_bl = 4;
-    required float corner_radius_br = 5;
-    required float alpha = 6;
-    required int32 left = 7;
-    required int32 top = 8;
-    required int32 right = 9;
-    required int32 bottom = 10;
-}
-
-message Origin {
-    required int32 pid = 1;
-    required int32 uid = 2;
-}
diff --git a/cmds/surfacereplayer/replayer/Android.bp b/cmds/surfacereplayer/replayer/Android.bp
deleted file mode 100644
index 3985230..0000000
--- a/cmds/surfacereplayer/replayer/Android.bp
+++ /dev/null
@@ -1,71 +0,0 @@
-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_library_shared {
-    name: "libsurfacereplayer",
-    srcs: [
-        "BufferQueueScheduler.cpp",
-        "Event.cpp",
-        "Replayer.cpp",
-    ],
-    cppflags: [
-        "-Werror",
-        "-Wno-unused-parameter",
-        "-Wno-format",
-	"-Wno-c++98-compat-pedantic",
-	"-Wno-float-conversion",
-	"-Wno-disabled-macro-expansion",
-	"-Wno-float-equal",
-	"-Wno-sign-conversion",
-	"-Wno-padded",
-    ],
-    static_libs: [
-        "libtrace_proto",
-    ],
-    shared_libs: [
-        "libEGL",
-        "libGLESv2",
-        "libbinder",
-        "liblog",
-        "libcutils",
-        "libgui",
-        "libui",
-        "libutils",
-        "libprotobuf-cpp-lite",
-        "libbase",
-        "libnativewindow",
-    ],
-    export_include_dirs: [
-        ".",
-    ],
-}
-
-cc_binary {
-    name: "surfacereplayer",
-    srcs: [
-        "Main.cpp",
-    ],
-    shared_libs: [
-        "libprotobuf-cpp-lite",
-        "libsurfacereplayer",
-        "libutils",
-        "libgui",
-    ],
-    static_libs: [
-        "libtrace_proto",
-    ],
-    cppflags: [
-        "-Werror",
-        "-Wno-unused-parameter",
-	"-Wno-c++98-compat-pedantic",
-	"-Wno-float-conversion",
-	"-Wno-disabled-macro-expansion",
-	"-Wno-float-equal",
-    ],
-}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
deleted file mode 100644
index 77de8dc..0000000
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2016 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 "BufferQueueScheduler"
-
-#include "BufferQueueScheduler.h"
-
-#include <android/native_window.h>
-#include <gui/Surface.h>
-
-using namespace android;
-
-BufferQueueScheduler::BufferQueueScheduler(
-        const sp<SurfaceControl>& surfaceControl, const HSV& color, int id)
-      : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {}
-
-void BufferQueueScheduler::startScheduling() {
-    ALOGV("Starting Scheduler for %d Layer", mSurfaceId);
-    std::unique_lock<std::mutex> lock(mMutex);
-    if (mSurfaceControl == nullptr) {
-        mCondition.wait(lock, [&] { return (mSurfaceControl != nullptr); });
-    }
-
-    while (mContinueScheduling) {
-        while (true) {
-            if (mBufferEvents.empty()) {
-                break;
-            }
-
-            BufferEvent event = mBufferEvents.front();
-            lock.unlock();
-
-            bufferUpdate(event.dimensions);
-            fillSurface(event.event);
-            mColor.modulate();
-            lock.lock();
-            mBufferEvents.pop();
-        }
-        mCondition.wait(lock);
-    }
-}
-
-void BufferQueueScheduler::addEvent(const BufferEvent& event) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mBufferEvents.push(event);
-    mCondition.notify_one();
-}
-
-void BufferQueueScheduler::stopScheduling() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mContinueScheduling = false;
-    mCondition.notify_one();
-}
-
-void BufferQueueScheduler::setSurfaceControl(
-        const sp<SurfaceControl>& surfaceControl, const HSV& color) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mSurfaceControl = surfaceControl;
-    mColor = color;
-    mCondition.notify_one();
-}
-
-void BufferQueueScheduler::bufferUpdate(const Dimensions& dimensions) {
-    sp<Surface> s = mSurfaceControl->getSurface();
-    s->setBuffersDimensions(dimensions.width, dimensions.height);
-}
-
-void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = mSurfaceControl->getSurface();
-
-    status_t status = s->lock(&outBuffer, nullptr);
-
-    if (status != NO_ERROR) {
-        ALOGE("fillSurface: failed to lock buffer, (%d)", status);
-        return;
-    }
-
-    auto color = mColor.getRGB();
-
-    auto img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
-            pixel[0] = color.r;
-            pixel[1] = color.g;
-            pixel[2] = color.b;
-            pixel[3] = LAYER_ALPHA;
-        }
-    }
-
-    event->readyToExecute();
-
-    status = s->unlockAndPost();
-
-    ALOGE_IF(status != NO_ERROR, "fillSurface: failed to unlock and post buffer, (%d)", status);
-}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
deleted file mode 100644
index cb20fcc..0000000
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
-#define ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
-
-#include "Color.h"
-#include "Event.h"
-
-#include <gui/SurfaceControl.h>
-
-#include <utils/StrongPointer.h>
-
-#include <atomic>
-#include <condition_variable>
-#include <mutex>
-#include <queue>
-#include <utility>
-
-namespace android {
-
-auto constexpr LAYER_ALPHA = 190;
-
-struct Dimensions {
-    Dimensions() = default;
-    Dimensions(int w, int h) : width(w), height(h) {}
-
-    int width = 0;
-    int height = 0;
-};
-
-struct BufferEvent {
-    BufferEvent() = default;
-    BufferEvent(std::shared_ptr<Event> e, Dimensions d) : event(e), dimensions(d) {}
-
-    std::shared_ptr<Event> event;
-    Dimensions dimensions;
-};
-
-class BufferQueueScheduler {
-  public:
-    BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id);
-
-    void startScheduling();
-    void addEvent(const BufferEvent&);
-    void stopScheduling();
-
-    void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color);
-
-  private:
-    void bufferUpdate(const Dimensions& dimensions);
-
-    // Lock and fill the surface, block until the event is signaled by the main loop,
-    // then unlock and post the buffer.
-    void fillSurface(const std::shared_ptr<Event>& event);
-
-    sp<SurfaceControl> mSurfaceControl;
-    HSV mColor;
-    const int mSurfaceId;
-
-    bool mContinueScheduling;
-
-    std::queue<BufferEvent> mBufferEvents;
-    std::mutex mMutex;
-    std::condition_variable mCondition;
-};
-
-}  // namespace android
-#endif
diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h
deleted file mode 100644
index ce644be..0000000
--- a/cmds/surfacereplayer/replayer/Color.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_COLOR_H
-#define ANDROID_SURFACEREPLAYER_COLOR_H
-
-#include <cmath>
-#include <cstdlib>
-
-namespace android {
-
-constexpr double modulateFactor = .0001;
-constexpr double modulateLimit = .80;
-
-struct RGB {
-    RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {}
-
-    uint8_t r = 0;
-    uint8_t g = 0;
-    uint8_t b = 0;
-};
-
-struct HSV {
-    HSV() = default;
-    HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {}
-
-    double h = 0;
-    double s = 0;
-    double v = 0;
-
-    RGB getRGB() const;
-
-    bool modulateUp = false;
-
-    void modulate();
-};
-
-void inline HSV::modulate() {
-    if(modulateUp) {
-        v += modulateFactor;
-    } else {
-        v -= modulateFactor;
-    }
-
-    if(v <= modulateLimit || v >= 1) {
-        modulateUp = !modulateUp;
-    }
-}
-
-inline RGB HSV::getRGB() const {
-    using namespace std;
-    double r = 0, g = 0, b = 0;
-
-    if (s == 0) {
-        r = v;
-        g = v;
-        b = v;
-    } else {
-        auto tempHue = static_cast<int>(h) % 360;
-        tempHue = tempHue / 60;
-
-        int i = static_cast<int>(trunc(tempHue));
-        double f = h - i;
-
-        double x = v * (1.0 - s);
-        double y = v * (1.0 - (s * f));
-        double z = v * (1.0 - (s * (1.0 - f)));
-
-        switch (i) {
-            case 0:
-                r = v;
-                g = z;
-                b = x;
-                break;
-
-            case 1:
-                r = y;
-                g = v;
-                b = x;
-                break;
-
-            case 2:
-                r = x;
-                g = v;
-                b = z;
-                break;
-
-            case 3:
-                r = x;
-                g = y;
-                b = v;
-                break;
-
-            case 4:
-                r = z;
-                g = x;
-                b = v;
-                break;
-
-            default:
-                r = v;
-                g = x;
-                b = y;
-                break;
-        }
-    }
-
-    return RGB(round(r * 255), round(g * 255), round(b * 255));
-}
-}
-#endif
diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp
deleted file mode 100644
index 64db5f0..0000000
--- a/cmds/surfacereplayer/replayer/Event.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2016 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 "Event.h"
-
-using namespace android;
-using Increment = surfaceflinger::Increment;
-
-Event::Event(Increment::IncrementCase type) : mIncrementType(type) {}
-
-void Event::readyToExecute() {
-    changeState(Event::EventState::Waiting);
-    waitUntil(Event::EventState::Signaled);
-    changeState(Event::EventState::Running);
-}
-
-void Event::complete() {
-    waitUntil(Event::EventState::Waiting);
-    changeState(Event::EventState::Signaled);
-    waitUntil(Event::EventState::Running);
-}
-
-void Event::waitUntil(Event::EventState state) {
-    std::unique_lock<std::mutex> lock(mLock);
-    mCond.wait(lock, [this, state] { return (mState == state); });
-}
-
-void Event::changeState(Event::EventState state) {
-    std::unique_lock<std::mutex> lock(mLock);
-    mState = state;
-    lock.unlock();
-
-    mCond.notify_one();
-}
-
-Increment::IncrementCase Event::getIncrementType() {
-    return mIncrementType;
-}
diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h
deleted file mode 100644
index 09a7c24..0000000
--- a/cmds/surfacereplayer/replayer/Event.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_EVENT_H
-#define ANDROID_SURFACEREPLAYER_EVENT_H
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <condition_variable>
-#include <mutex>
-
-namespace android {
-
-using Increment = surfaceflinger::Increment;
-
-class Event {
-  public:
-    Event(Increment::IncrementCase);
-
-    enum class EventState {
-        SettingUp,  // Completing as much time-independent work as possible
-        Waiting,    // Waiting for signal from main thread to finish execution
-        Signaled,   // Signaled by main thread, about to immediately switch to Running
-        Running     // Finishing execution of rest of work
-    };
-
-    void readyToExecute();
-    void complete();
-
-    Increment::IncrementCase getIncrementType();
-
-  private:
-    void waitUntil(EventState state);
-    void changeState(EventState state);
-
-    std::mutex mLock;
-    std::condition_variable mCond;
-
-    EventState mState = EventState::SettingUp;
-
-    Increment::IncrementCase mIncrementType;
-};
-}
-#endif
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp
deleted file mode 100644
index fbfcacf..0000000
--- a/cmds/surfacereplayer/replayer/Main.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2016 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.
- */
-
-/*
- * Replayer - Main.cpp
- *
- * 1. Get flags from command line
- * 2. Commit actions or settings based on the flags
- * 3. Initalize a replayer object with the filename passed in
- * 4. Replay
- * 5. Exit successfully or print error statement
- */
-
-#include <Replayer.h>
-
-#include <csignal>
-#include <iostream>
-#include <stdlib.h>
-#include <unistd.h>
-
-using namespace android;
-
-void printHelpMenu() {
-    std::cout << "SurfaceReplayer options:\n";
-    std::cout << "Usage: surfacereplayer [OPTIONS...] <TRACE FILE>\n";
-    std::cout << "  File path must be absolute" << std::endl << std::endl;
-
-    std::cout << "  -m  Stops the replayer at the start of the trace and switches ";
-                 "to manual replay\n";
-
-    std::cout << "\n  -t [Number of Threads]  Specifies the number of threads to be used while "
-                 "replaying (default is " << android::DEFAULT_THREADS << ")\n";
-
-    std::cout << "\n  -s [Timestamp]  Specify at what timestamp should the replayer switch "
-                 "to manual replay\n";
-
-    std::cout << "  -n  Ignore timestamps and run through trace as fast as possible\n";
-
-    std::cout << "  -l  Indefinitely loop the replayer\n";
-
-    std::cout << "  -h  Display help menu\n";
-
-    std::cout << std::endl;
-}
-
-int main(int argc, char** argv) {
-    std::string filename;
-    bool loop = false;
-    bool wait = true;
-    bool pauseBeginning = false;
-    int numThreads = DEFAULT_THREADS;
-    long stopHere = -1;
-
-    int opt = 0;
-    while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) {
-        switch (opt) {
-            case 'm':
-                pauseBeginning = true;
-                break;
-            case 't':
-                numThreads = atoi(optarg);
-                break;
-            case 's':
-                stopHere = atol(optarg);
-                break;
-            case 'n':
-                wait = false;
-                break;
-            case 'l':
-                loop = true;
-                break;
-            case 'h':
-            case '?':
-                printHelpMenu();
-                exit(0);
-            default:
-                std::cerr << "Invalid argument...exiting" << std::endl;
-                printHelpMenu();
-                exit(0);
-        }
-    }
-
-    char** input = argv + optind;
-    if (input[0] == nullptr) {
-        std::cerr << "No trace file provided...exiting" << std::endl;
-        abort();
-    }
-    filename.assign(input[0]);
-
-    status_t status = NO_ERROR;
-    do {
-        android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere);
-        status = r.replay();
-    } while(loop);
-
-    if (status == NO_ERROR) {
-        std::cout << "Successfully finished replaying trace" << std::endl;
-    } else {
-        std::cerr << "Trace replayer returned error: " << status << std::endl;
-    }
-
-    return 0;
-}
diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md
deleted file mode 100644
index 893f0dc..0000000
--- a/cmds/surfacereplayer/replayer/README.md
+++ /dev/null
@@ -1,262 +0,0 @@
-SurfaceReplayer Documentation
-===================
-
-[go/SurfaceReplayer](go/SurfaceReplayer)
-
-SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by
-[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays
-
-* Creation and deletion of surfaces/displays
-* Alterations to the surfaces/displays called Transactions
-* Buffer Updates to surfaces
-* VSync events
-
-At their specified times to be as close to the original trace.
-
-Usage
---------
-
-###Creating a trace
-
-SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to
-utilize it. To allow it to write to the device, run
-
-`setenforce 0`
-
-To start recording a trace, run
-
-`service call SurfaceFlinger 1020 i32 1`
-
-To stop recording, run
-
-`service call SurfaceFlinger 1020 i32 0`
-
-The default location for the trace is `/data/SurfaceTrace.dat`
-
-###Executable
-
-To replay a specific trace, execute
-
-`/data/local/tmp/surfacereplayer /absolute/path/to/trace`
-
-inside the android shell. This will replay the full trace and then exit. Running this command
-outside of the shell by prepending `adb shell` will not allow for manual control and will not turn
-off VSync injections if it interrupted in any way other than fully replaying the trace
-
-The replay will not fill surfaces with their contents during the capture. Rather they are given a
-random color which will be the same every time the trace is replayed. Surfaces modulate their color
-at buffer updates.
-
-**Options:**
-
-- -m    pause the replayer at the start of the trace for manual replay
-- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3)
-- -s [Timestamp] switches to manual replay at specified timestamp
-- -n    Ignore timestamps and run through trace as fast as possible
-- -l    Indefinitely loop the replayer
-- -h    displays help menu
-
-**Manual Replay:**
-When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled
-by the user. Pressing CTRL-C again will exit the replayer.
-
-Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to
-input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter
-without inputting a command repeats the previous command.
-
-- n  - steps the replayer to the next VSync event
-- ni - steps the replayer to the next increment
-- c  - continues normal replaying
-- c [milliseconds] - continue until specified number of milliseconds have passed
-- s [timestamp]    - continue and stop at specified timestamp
-- l  - list out timestamp of current increment
-- h  - displays help menu
-
-###Shared Library
-
-To use the shared library include these shared libraries
-
-`libsurfacereplayer`
-`libprotobuf-cpp-full`
-`libutils`
-
-And the static library
-
-`libtrace_proto`
-
-Include the replayer header at the top of your file
-
-`#include <replayer/Replayer.h>`
-
-There are two constructors for the replayer
-
-`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)`
-`Replayer(Trace& trace, ... ditto ...)`
-
-The first constructor takes in the filepath where the trace is located and loads in the trace
-object internally.
-- replayManually - **True**: if the replayer will immediately switch to manual replay at the start
-- numThreads - Number of worker threads the replayer will use.
-- wait - **False**: Replayer ignores waits in between increments
-- stopHere - Time stamp of where the replayer should run to then switch to manual replay
-
-The second constructor includes all of the same parameters but takes in a preloaded trace object.
-To use add
-
-`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>`
-
-To your file
-
-After initializing the Replayer call
-
-    replayer.replay();
-
-And the trace will start replaying. Once the trace is finished replaying, the function will return.
-The layers that are visible at the end of the trace will remain on screen until the program
-terminates.
-
-
-**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was
-never executed. This can be fixed by executing
-
-`service call SurfaceFlinger 23 i32 0`
-
-in the android shell
-
-Code Breakdown
--------------
-
-The Replayer is composed of 5 components.
-
-- The data format of the trace (Trace.proto)
-- The Replayer object (Replayer.cpp)
-- The synchronization mechanism to signal threads within the Replayer (Event.cpp)
-- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp)
-- The Main executable (Main.cpp)
-
-### Traces
-
-Traces are represented as a protobuf message located in surfacereplayer/proto/src.
-
-**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger).
-**Increments** contain the time stamp of when it occurred and a *oneof* which can be a
-
- - Transaction
- - SurfaceCreation
- - SurfaceDeletion
- - DisplayCreation
- - DisplayDeleteion
- - BufferUpdate
- - VSyncEvent
- - PowerModeUpdate
-
-**Transactions** contain whether the transaction was synchronous or animated and *repeated*
-**SurfaceChanges** and **DisplayChanges**
-
-- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as
-position, alpha, hidden, size, etc.
-- **DisplayChanges** contain the id of the display being manipulated and can be changes such as
-size, layer stack, projection, etc.
-
-**Surface/Display Creation** contain the id of the surface/display and the name of the
-surface/display
-
-**Surface/Display Deletion** contain the id of the surface/display to be deleted
-
-**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the
-buffer, and the frame number.
-
-**VSyncEvents** contain when the VSync event has occurred.
-
-**PowerModeUpdates** contain the id of the display being updated and what mode it is being
-changed to.
-
-To output the contents of a trace in a readable format, execute
-
-`**aprotoc** --decode=Trace \
--I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \
-$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \
- < **YourTraceFile.dat** > **YourOutputName.txt**`
-
-
-###Replayer
-
-Fundamentally the replayer loads a trace and iterates through each increment, waiting the required
-amount of time until the increment should be executed, then executing the increment. The first
-increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and
-goes from there.
-
-Increments from the trace are played asynchronously rather than one by one, being dispatched by
-the main thread, queued up in a thread pool and completed when the main thread deems they are
-ready to finish execution.
-
-When an increment is dispatched, it completes as much work as it can before it has to be
-synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action
-(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event
-object. The main thread holds a queue of these Event objects and completes the
-corresponding Event base on its time stamp. After completing an increment, the main thread will
-dispatch another increment and continue.
-
-The main thread's execution flow is outlined below
-
-    initReplay() //queue up the initial increments
-    while(!pendingIncrements.empty()) { //while increments remaining
-        event = pendingIncrement.pop();
-        wait(event.time_stamp(); //waitUntil it is time to complete this increment
-
-        event.complete() //signal to let event finish
-        if(increments remaing()) {
-            dispatchEvent() //queue up another increment
-        }
-    }
-
-A worker thread's flow looks like so
-
-    //dispatched!
-    Execute non-time sensitive work here
-    ...
-    event.readyToExecute() //time sensitive point...waiting for Main Thread
-    ...
-    Finish execution
-
-
-### Event
-
-An Event is a simple synchronization mechanism used to facilitate communication between the main
-and worker threads. Every time an increment is dispatched, an Event object is also created.
-
-An Event can be in 4 different states:
-
-- **SettingUp** - The worker is in the process of completing all non-time sensitive work
-- **Waiting** - The worker is waiting on the main thread to signal it.
-- **Signaled** - The worker has just been signaled by the main thread
-- **Running** - The worker is running again and finishing the rest of its work.
-
-When the main thread wants to finish the execution of a worker, the worker can either still be
-**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the
-main thread will **Signal** it to complete. The worker thread changes itself to the **Running**
-state once **Signaled**. This last step exists in order to communicate back to the main thread that
-the worker thread has actually started completing its execution, rather than being preempted right
-after signalling. Once this happens, the main thread schedules the next worker. This makes sure
-there is a constant amount of workers running at one time.
-
-This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the
-worker and main thread respectively.
-
-### BufferQueueScheduler
-
-During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a
-buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential
-**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it,
-which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by
-handling when **BufferUpdates** should be scheduled, making sure that they don't overlap.
-
-When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a
-**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one
-every time an Event is completed.
-
-### Main
-
-The main exectuable reads in the command line arguments. Creates the Replayer using those
-arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit
-gracefully, if there are then it will report the error and then exit.
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
deleted file mode 100644
index d62522a..0000000
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_H
-#define ANDROID_SURFACEREPLAYER_H
-
-#include "BufferQueueScheduler.h"
-#include "Color.h"
-#include "Event.h"
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceControl.h>
-
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-
-#include <stdatomic.h>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <thread>
-#include <unordered_map>
-#include <utility>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
-const auto RAND_COLOR_SEED = 700;
-const auto DEFAULT_THREADS = 3;
-
-typedef int32_t layer_id;
-typedef int32_t display_id;
-
-typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
-typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
-
-class Replayer {
-  public:
-    Replayer(const std::string& filename, bool replayManually = false,
-            int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
-    Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
-            bool wait = true, nsecs_t stopHere = -1);
-
-    status_t replay();
-
-  private:
-    status_t initReplay();
-
-    void waitForConsoleCommmand();
-    static void stopAutoReplayHandler(int signal);
-
-    status_t dispatchEvent(int index);
-
-    status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
-    status_t createSurfaceControl(const SurfaceCreation& create,
-            const std::shared_ptr<Event>& event);
-    status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
-    void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
-    void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
-    void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
-
-    status_t doSurfaceTransaction(SurfaceComposerClient::Transaction& transaction,
-            const SurfaceChanges& surfaceChange);
-    void doDisplayTransaction(SurfaceComposerClient::Transaction& transaction,
-            const DisplayChanges& displayChange);
-
-    void setPosition(SurfaceComposerClient::Transaction& t,
-            layer_id id, const PositionChange& pc);
-    void setSize(SurfaceComposerClient::Transaction& t,
-            layer_id id, const SizeChange& sc);
-    void setAlpha(SurfaceComposerClient::Transaction& t,
-            layer_id id, const AlphaChange& ac);
-    void setLayer(SurfaceComposerClient::Transaction& t,
-            layer_id id, const LayerChange& lc);
-    void setCrop(SurfaceComposerClient::Transaction& t,
-            layer_id id, const CropChange& cc);
-    void setCornerRadius(SurfaceComposerClient::Transaction& t,
-            layer_id id, const CornerRadiusChange& cc);
-    void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
-            layer_id id, const BackgroundBlurRadiusChange& cc);
-    void setBlurRegions(SurfaceComposerClient::Transaction& t,
-            layer_id id, const BlurRegionsChange& cc);
-    void setMatrix(SurfaceComposerClient::Transaction& t,
-            layer_id id, const MatrixChange& mc);
-    void setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
-            layer_id id, const TransparentRegionHintChange& trgc);
-    void setLayerStack(SurfaceComposerClient::Transaction& t,
-            layer_id id, const LayerStackChange& lsc);
-    void setHiddenFlag(SurfaceComposerClient::Transaction& t,
-            layer_id id, const HiddenFlagChange& hfc);
-    void setOpaqueFlag(SurfaceComposerClient::Transaction& t,
-            layer_id id, const OpaqueFlagChange& ofc);
-    void setSecureFlag(SurfaceComposerClient::Transaction& t,
-            layer_id id, const SecureFlagChange& sfc);
-    void setReparentChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const ReparentChange& c);
-    void setRelativeParentChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const RelativeParentChange& c);
-    void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const ShadowRadiusChange& c);
-    void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
-            layer_id id, const BlurRegionsChange& c);
-
-    void setDisplaySurface(SurfaceComposerClient::Transaction& t,
-            display_id id, const DispSurfaceChange& dsc);
-    void setDisplayLayerStack(SurfaceComposerClient::Transaction& t,
-            display_id id, const LayerStackChange& lsc);
-    void setDisplaySize(SurfaceComposerClient::Transaction& t,
-            display_id id, const SizeChange& sc);
-    void setDisplayProjection(SurfaceComposerClient::Transaction& t,
-            display_id id, const ProjectionChange& pc);
-
-    void waitUntilTimestamp(int64_t timestamp);
-    status_t loadSurfaceComposerClient();
-
-    Trace mTrace;
-    bool mLoaded = false;
-    int32_t mIncrementIndex = 0;
-    int64_t mCurrentTime = 0;
-    int32_t mNumThreads = DEFAULT_THREADS;
-
-    Increment mCurrentIncrement;
-
-    std::string mLastInput;
-
-    static atomic_bool sReplayingManually;
-    bool mWaitingForNextVSync;
-    bool mWaitForTimeStamps;
-    nsecs_t mStopTimeStamp;
-    bool mHasStopped;
-
-    std::mutex mLayerLock;
-    std::condition_variable mLayerCond;
-    std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
-    std::unordered_map<layer_id, HSV> mColors;
-
-    std::mutex mPendingLayersLock;
-    std::vector<layer_id> mLayersPendingRemoval;
-
-    std::mutex mBufferQueueSchedulerLock;
-    std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
-
-    std::mutex mDisplayLock;
-    std::condition_variable mDisplayCond;
-    std::unordered_map<display_id, sp<IBinder>> mDisplays;
-
-    sp<SurfaceComposerClient> mComposerClient;
-    std::queue<std::shared_ptr<Event>> mPendingIncrements;
-};
-
-}  // namespace android
-#endif
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
deleted file mode 100644
index 58bfbf3..0000000
--- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
+++ /dev/null
@@ -1,285 +0,0 @@
-#!/usr/bin/python
-from subprocess import call
-import os
-proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/"
-call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"])
-
-from trace_pb2 import *
-
-trace = Trace()
-
-def main():
-    global trace
-    while(1):
-        option = main_menu()
-
-        if option == 0:
-            break
-
-        increment = trace.increment.add()
-        increment.time_stamp  = int(input("Time stamp of action: "))
-
-        if option == 1:
-           transaction(increment)
-        elif option == 2:
-            surface_create(increment)
-        elif option == 3:
-            surface_delete(increment)
-        elif option == 4:
-            display_create(increment)
-        elif option == 5:
-            display_delete(increment)
-        elif option == 6:
-            buffer_update(increment)
-        elif option == 7:
-            vsync_event(increment)
-        elif option == 8:
-            power_mode_update(increment)
-
-    seralizeTrace()
-
-def seralizeTrace():
-    with open("trace.dat", 'wb') as f:
-        f.write(trace.SerializeToString())
-
-
-def main_menu():
-    print ("")
-    print ("What would you like to do?")
-    print ("1. Add transaction")
-    print ("2. Add surface creation")
-    print ("3. Add surface deletion")
-    print ("4. Add display creation")
-    print ("5. Add display deletion")
-    print ("6. Add buffer update")
-    print ("7. Add VSync event")
-    print ("8. Add power mode update")
-    print ("0. Finish and serialize")
-    print ("")
-
-    return int(input("> "))
-
-def transaction_menu():
-    print ("")
-    print ("What kind of transaction?")
-    print ("1. Position Change")
-    print ("2. Size Change")
-    print ("3. Alpha Change")
-    print ("4. Layer Change")
-    print ("5. Crop Change")
-    print ("6. Final Crop Change")
-    print ("7. Matrix Change")
-    print ("9. Transparent Region Hint Change")
-    print ("10. Layer Stack Change")
-    print ("11. Hidden Flag Change")
-    print ("12. Opaque Flag Change")
-    print ("13. Secure Flag Change")
-    print ("14. Deferred Transaction Change")
-    print ("15. Display - Surface Change")
-    print ("16. Display - Layer Stack Change")
-    print ("17. Display - Size Change")
-    print ("18. Display - Projection Change")
-    print ("0. Finished adding Changes to this transaction")
-    print ("")
-
-    return int(input("> "))
-
-def transaction(increment):
-    global trace
-
-    increment.transaction.synchronous \
-            = bool(input("Is transaction synchronous (True/False): "))
-    increment.transaction.animation \
-            = bool(input("Is transaction animated (True/False): "))
-
-    while(1):
-        option = transaction_menu()
-
-        if option == 0:
-            break
-
-        change = None
-        if option <= 14:
-            change = increment.transaction.surface_change.add()
-        elif option >= 15 and option <= 18:
-            change = increment.transaction.display_change.add()
-
-        change.id = int(input("ID of layer/display to undergo a change: "))
-
-        if option == 1:
-            change.position.x, change.position.y = position()
-        elif option == 2:
-            change.size.w, change.size.h = size()
-        elif option == 3:
-            change.alpha.alpha = alpha()
-        elif option == 4:
-            change.layer.layer = layer()
-        elif option == 5:
-            change.crop.rectangle.left,  change.crop.rectangle.top, \
-            change.crop.rectangle.right, change.crop.rectangle.bottom = crop()
-        elif option == 6:
-            change.final_crop.rectangle.left, \
-            change.final_crop.rectangle.top,  \
-            change.final_crop.rectangle.right,\
-            change.final_crop.rectangle.bottom = final_crop()
-        elif option == 7:
-            change.matrix.dsdx,\
-            change.matrix.dtdx,\
-            change.matrix.dsdy,\
-            change.matrix.dtdy = layer()
-        elif option == 9:
-            for rect in transparent_region_hint():
-                new = increment.transparent_region_hint.region.add()
-                new.left = rect[0]
-                new.top = rect[1]
-                new.right = rect[2]
-                new.bottom = rect[3]
-        elif option == 10:
-            change.layer_stack.layer_stack = layer_stack()
-        elif option == 11:
-            change.hidden_flag.hidden_flag = hidden_flag()
-        elif option == 12:
-            change.opaque_flag.opaque_flag = opaque_flag()
-        elif option == 13:
-            change.secure_flag.secure_flag = secure_flag()
-        elif option == 14:
-            change.deferred_transaction.layer_id, \
-            change.deferred_transaction.frame_number = deferred_transaction()
-        elif option == 15:
-            change.surface.buffer_queue_id, \
-            change.surface.buffer_queue_name = surface()
-        elif option == 16:
-            change.layer_stack.layer_stack = layer_stack()
-        elif option == 17:
-            change.size.w, change.size.h = size()
-        elif option == 18:
-            projection(change)
-
-def surface_create(increment):
-    increment.surface_creation.id = int(input("Enter id: "))
-    n = str(raw_input("Enter name: "))
-    increment.surface_creation.name = n
-    increment.surface_creation.w = input("Enter w: ")
-    increment.surface_creation.h = input("Enter h: ")
-
-def surface_delete(increment):
-    increment.surface_deletion.id = int(input("Enter id: "))
-
-def display_create(increment):
-    increment.display_creation.id = int(input("Enter id: "))
-    increment.display_creation.name = str(raw_input("Enter name: "))
-    increment.display_creation.display_id = int(input("Enter display ID: "))
-    increment.display_creation.is_secure = bool(input("Enter if secure: "))
-
-def display_delete(increment):
-    increment.surface_deletion.id = int(input("Enter id: "))
-
-def buffer_update(increment):
-    increment.buffer_update.id = int(input("Enter id: "))
-    increment.buffer_update.w = int(input("Enter w: "))
-    increment.buffer_update.h = int(input("Enter h: "))
-    increment.buffer_update.frame_number = int(input("Enter frame_number: "))
-
-def vsync_event(increment):
-    increment.vsync_event.when = int(input("Enter when: "))
-
-def power_mode_update(increment):
-    increment.power_mode_update.id = int(input("Enter id: "))
-    increment.power_mode_update.mode = int(input("Enter mode: "))
-
-def position():
-    x = input("Enter x: ")
-    y = input("Enter y: ")
-
-    return float(x), float(y)
-
-def size():
-    w = input("Enter w: ")
-    h = input("Enter h: ")
-
-    return int(w), int(h)
-
-def alpha():
-    alpha = input("Enter alpha: ")
-
-    return float(alpha)
-
-def layer():
-    layer = input("Enter layer: ")
-
-    return int(layer)
-
-def crop():
-    return rectangle()
-
-def final_crop():
-    return rectangle()
-
-def matrix():
-    dsdx = input("Enter dsdx: ")
-    dtdx = input("Enter dtdx: ")
-    dsdy = input("Enter dsdy: ")
-    dtdy = input("Enter dtdy: ")
-
-    return float(dsdx)
-
-def transparent_region_hint():
-    num = input("Enter number of rectangles in region: ")
-
-    return [rectangle() in range(x)]
-
-def layer_stack():
-    layer_stack = input("Enter layer stack: ")
-
-    return int(layer_stack)
-
-def hidden_flag():
-    flag = input("Enter hidden flag state (True/False): ")
-
-    return bool(flag)
-
-def opaque_flag():
-    flag = input("Enter opaque flag state (True/False): ")
-
-    return bool(flag)
-
-def secure_flag():
-    flag = input("Enter secure flag state (True/False): ")
-
-    return bool(flag)
-
-def deferred_transaction():
-    layer_id = input("Enter layer_id: ")
-    frame_number = input("Enter frame_number: ")
-
-    return int(layer_id), int(frame_number)
-
-def surface():
-    id = input("Enter id: ")
-    name = raw_input("Enter name: ")
-
-    return int(id), str(name)
-
-def projection(change):
-    change.projection.orientation = input("Enter orientation: ")
-    print("Enter rectangle for viewport")
-    change.projection.viewport.left, \
-    change.projection.viewport.top,  \
-    change.projection.viewport.right,\
-    change.projection.viewport.bottom = rectangle()
-    print("Enter rectangle for frame")
-    change.projection.frame.left, \
-    change.projection.frame.top,  \
-    change.projection.frame.right,\
-    change.projection.frame.bottom = rectangle()
-
-def rectangle():
-    left = input("Enter left: ")
-    top = input("Enter top: ")
-    right = input("Enter right: ")
-    bottom = input("Enter bottom: ")
-
-    return int(left), int(top), int(right), int(bottom)
-
-if __name__ == "__main__":
-    main()
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 15bd5c3..ebc74fb 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -23,8 +23,10 @@
 #include <sys/eventfd.h>
 #include <sys/uio.h>
 
+#include <atomic>
 #include <chrono>
 #include <deque>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <tuple>
diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp
index 24ce2bb..77e401f 100644
--- a/libs/binder/OS.cpp
+++ b/libs/binder/OS.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/file.h>
 #include <binder/RpcTransportRaw.h>
+#include <log/log.h>
 #include <string.h>
 
 using android::base::ErrnoError;
@@ -25,6 +26,9 @@
 
 namespace android {
 
+// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
+constexpr size_t kMaxFdsPerMsg = 253;
+
 Result<void> setNonBlocking(android::base::borrowed_fd fd) {
     int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
     if (flags == -1) {
@@ -63,4 +67,99 @@
     return RpcTransportCtxFactoryRaw::make();
 }
 
+int sendMessageOnSocket(
+        const RpcTransportFd& socket, iovec* iovs, int niovs,
+        const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+    if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
+        if (ancillaryFds->size() > kMaxFdsPerMsg) {
+            errno = EINVAL;
+            return -1;
+        }
+
+        // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
+        // use memcpy.
+        int fds[kMaxFdsPerMsg];
+        for (size_t i = 0; i < ancillaryFds->size(); i++) {
+            fds[i] = std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i));
+        }
+        const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
+
+        alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
+
+        msghdr msg{
+                .msg_iov = iovs,
+                .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+                .msg_control = msgControlBuf,
+                .msg_controllen = sizeof(msgControlBuf),
+        };
+
+        cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
+        memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
+
+        msg.msg_controllen = CMSG_SPACE(fdsByteSize);
+        return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
+    }
+
+    msghdr msg{
+            .msg_iov = iovs,
+            // posix uses int, glibc uses size_t.  niovs is a
+            // non-negative int and can be cast to either.
+            .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+    };
+    return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+}
+
+int receiveMessageFromSocket(
+        const RpcTransportFd& socket, iovec* iovs, int niovs,
+        std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+    if (ancillaryFds != nullptr) {
+        int fdBuffer[kMaxFdsPerMsg];
+        alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
+
+        msghdr msg{
+                .msg_iov = iovs,
+                .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+                .msg_control = msgControlBuf,
+                .msg_controllen = sizeof(msgControlBuf),
+        };
+        ssize_t processSize = TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+        if (processSize < 0) {
+            return -1;
+        }
+
+        for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+                // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
+                // application devs to memcpy the data to ensure memory alignment.
+                size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
+                LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // validity check
+                memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
+                size_t fdCount = dataLen / sizeof(int);
+                ancillaryFds->reserve(ancillaryFds->size() + fdCount);
+                for (size_t i = 0; i < fdCount; i++) {
+                    ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i]));
+                }
+                break;
+            }
+        }
+
+        if (msg.msg_flags & MSG_CTRUNC) {
+            errno = EPIPE;
+            return -1;
+        }
+        return processSize;
+    }
+    msghdr msg{
+            .msg_iov = iovs,
+            // posix uses int, glibc uses size_t.  niovs is a
+            // non-negative int and can be cast to either.
+            .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+    };
+
+    return TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+}
+
 } // namespace android
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index 5ab8bab..0d38968 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -33,4 +33,12 @@
 
 std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory();
 
+int sendMessageOnSocket(
+        const RpcTransportFd& socket, iovec* iovs, int niovs,
+        const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
+
+int receiveMessageFromSocket(
+        const RpcTransportFd& socket, iovec* iovs, int niovs,
+        std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
+
 } // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 8333298..07d0a65 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1439,7 +1439,8 @@
             case RpcSession::FileDescriptorTransportMode::NONE: {
                 return FDS_NOT_ALLOWED;
             }
-            case RpcSession::FileDescriptorTransportMode::UNIX: {
+            case RpcSession::FileDescriptorTransportMode::UNIX:
+            case RpcSession::FileDescriptorTransportMode::TRUSTY: {
                 if (rpcFields->mFds == nullptr) {
                     rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
                 }
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index e581d0b..83d0de7 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -37,6 +37,7 @@
 #include "OS.h"
 #include "RpcSocketAddress.h"
 #include "RpcState.h"
+#include "RpcTransportUtils.h"
 #include "RpcWireFormat.h"
 #include "Utils.h"
 
@@ -61,6 +62,10 @@
     return sp<RpcServer>::make(std::move(ctx));
 }
 
+status_t RpcServer::setupUnixDomainSocketBootstrapServer(unique_fd bootstrapFd) {
+    return setupExternalServer(std::move(bootstrapFd), &RpcServer::recvmsgSocketConnection);
+}
+
 status_t RpcServer::setupUnixDomainServer(const char* path) {
     return setupSocketServer(UnixSocketAddress(path));
 }
@@ -177,11 +182,50 @@
     rpcJoinIfSingleThreaded(*mJoinThread);
 }
 
+status_t RpcServer::acceptSocketConnection(const RpcServer& server, RpcTransportFd* out) {
+    RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY(
+            accept4(server.mServer.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK))));
+    if (clientSocket.fd < 0) {
+        int savedErrno = errno;
+        ALOGE("Could not accept4 socket: %s", strerror(savedErrno));
+        return -savedErrno;
+    }
+
+    *out = std::move(clientSocket);
+    return OK;
+}
+
+status_t RpcServer::recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out) {
+    int zero = 0;
+    iovec iov{&zero, sizeof(zero)};
+    std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+
+    if (receiveMessageFromSocket(server.mServer, &iov, 1, &fds) < 0) {
+        int savedErrno = errno;
+        ALOGE("Failed recvmsg: %s", strerror(savedErrno));
+        return -savedErrno;
+    }
+    if (fds.size() != 1) {
+        ALOGE("Expected exactly one fd from recvmsg, got %zu", fds.size());
+        return -EINVAL;
+    }
+
+    unique_fd fd(std::move(std::get<unique_fd>(fds.back())));
+    if (auto res = setNonBlocking(fd); !res.ok()) {
+        ALOGE("Failed setNonBlocking: %s", res.error().message().c_str());
+        return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
+    }
+
+    *out = RpcTransportFd(std::move(fd));
+    return OK;
+}
+
 void RpcServer::join() {
 
     {
         RpcMutexLockGuard _l(mLock);
         LOG_ALWAYS_FATAL_IF(!mServer.fd.ok(), "RpcServer must be setup to join.");
+        LOG_ALWAYS_FATAL_IF(mAcceptFn == nullptr, "RpcServer must have an accept() function");
         LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
         mJoinThreadRunning = true;
         mShutdownTrigger = FdTrigger::make();
@@ -192,20 +236,19 @@
     while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) {
         std::array<uint8_t, kRpcAddressSize> addr;
         static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small");
-
         socklen_t addrLen = addr.size();
-        RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY(
-                accept4(mServer.fd.get(), reinterpret_cast<sockaddr*>(addr.data()), &addrLen,
-                        SOCK_CLOEXEC | SOCK_NONBLOCK))));
 
-        LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(sockaddr_storage)),
-                            "Truncated address");
-
-        if (clientSocket.fd < 0) {
-            ALOGE("Could not accept4 socket: %s", strerror(errno));
+        RpcTransportFd clientSocket;
+        if (mAcceptFn(*this, &clientSocket) != OK) {
             continue;
         }
-        LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+        if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
+                        &addrLen)) {
+            ALOGE("Could not getpeername socket: %s", strerror(errno));
+            continue;
+        }
+
+        LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
 
         {
             RpcMutexLockGuard _l(mLock);
@@ -550,16 +593,23 @@
     return std::move(mServer.fd);
 }
 
-status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
+status_t RpcServer::setupExternalServer(
+        base::unique_fd serverFd,
+        std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn) {
     RpcMutexLockGuard _l(mLock);
     if (mServer.fd.ok()) {
         ALOGE("Each RpcServer can only have one server.");
         return INVALID_OPERATION;
     }
     mServer = std::move(serverFd);
+    mAcceptFn = std::move(acceptFn);
     return OK;
 }
 
+status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
+    return setupExternalServer(std::move(serverFd), &RpcServer::acceptSocketConnection);
+}
+
 bool RpcServer::hasActiveRequests() {
     RpcMutexLockGuard _l(mLock);
     for (const auto& [_, session] : mSessions) {
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 49843e5..ff50c16 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -41,6 +41,7 @@
 #include "OS.h"
 #include "RpcSocketAddress.h"
 #include "RpcState.h"
+#include "RpcTransportUtils.h"
 #include "RpcWireFormat.h"
 #include "Utils.h"
 
@@ -147,6 +148,34 @@
     return setupSocketClient(UnixSocketAddress(path));
 }
 
+status_t RpcSession::setupUnixDomainSocketBootstrapClient(unique_fd bootstrapFd) {
+    mBootstrapTransport =
+            mCtx->newTransport(RpcTransportFd(std::move(bootstrapFd)), mShutdownTrigger.get());
+    return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) {
+        int socks[2];
+        if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, socks) < 0) {
+            int savedErrno = errno;
+            ALOGE("Failed socketpair: %s", strerror(savedErrno));
+            return -savedErrno;
+        }
+        unique_fd clientFd(socks[0]), serverFd(socks[1]);
+
+        int zero = 0;
+        iovec iov{&zero, sizeof(zero)};
+        std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+        fds.push_back(std::move(serverFd));
+
+        status_t status = mBootstrapTransport->interruptableWriteFully(mShutdownTrigger.get(), &iov,
+                                                                       1, std::nullopt, &fds);
+        if (status != OK) {
+            ALOGE("Failed to send fd over bootstrap transport: %s", strerror(-status));
+            return status;
+        }
+
+        return initAndAddConnection(RpcTransportFd(std::move(clientFd)), sessionId, incoming);
+    });
+}
+
 status_t RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
     return setupSocketClient(VsockSocketAddress(cid, port));
 }
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 7067328..b27f102 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -56,6 +56,7 @@
         case RpcSession::FileDescriptorTransportMode::NONE:
             return false;
         case RpcSession::FileDescriptorTransportMode::UNIX:
+        case RpcSession::FileDescriptorTransportMode::TRUSTY:
             return true;
     }
 }
@@ -1207,6 +1208,20 @@
                                              rpcFields->mFds->size(), kMaxFdsPerMsg);
                     return BAD_VALUE;
                 }
+                break;
+            }
+            case RpcSession::FileDescriptorTransportMode::TRUSTY: {
+                // Keep this in sync with trusty_ipc.h!!!
+                // We could import that file here on Trusty, but it's not
+                // available on Android
+                constexpr size_t kMaxFdsPerMsg = 8;
+                if (rpcFields->mFds->size() > kMaxFdsPerMsg) {
+                    *errorMsg = StringPrintf("Too many file descriptors in Parcel for Trusty "
+                                             "IPC connection: %zu (max is %zu)",
+                                             rpcFields->mFds->size(), kMaxFdsPerMsg);
+                    return BAD_VALUE;
+                }
+                break;
             }
         }
     }
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index 65e8fac..1912d14 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -23,6 +23,7 @@
 #include <binder/RpcTransportRaw.h>
 
 #include "FdTrigger.h"
+#include "OS.h"
 #include "RpcState.h"
 #include "RpcTransportUtils.h"
 
@@ -30,9 +31,6 @@
 
 namespace {
 
-// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
-constexpr size_t kMaxFdsPerMsg = 253;
-
 // RpcTransport with TLS disabled.
 class RpcTransportRaw : public RpcTransport {
 public:
@@ -63,57 +61,9 @@
             override {
         bool sentFds = false;
         auto send = [&](iovec* iovs, int niovs) -> ssize_t {
-            if (ancillaryFds != nullptr && !ancillaryFds->empty() && !sentFds) {
-                if (ancillaryFds->size() > kMaxFdsPerMsg) {
-                    // This shouldn't happen because we check the FD count in RpcState.
-                    ALOGE("Saw too many file descriptors in RpcTransportCtxRaw: %zu (max is %zu). "
-                          "Aborting session.",
-                          ancillaryFds->size(), kMaxFdsPerMsg);
-                    errno = EINVAL;
-                    return -1;
-                }
-
-                // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
-                // use memcpy.
-                int fds[kMaxFdsPerMsg];
-                for (size_t i = 0; i < ancillaryFds->size(); i++) {
-                    fds[i] = std::visit([](const auto& fd) { return fd.get(); },
-                                        ancillaryFds->at(i));
-                }
-                const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
-
-                alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
-
-                msghdr msg{
-                        .msg_iov = iovs,
-                        .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
-                        .msg_control = msgControlBuf,
-                        .msg_controllen = sizeof(msgControlBuf),
-                };
-
-                cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
-                cmsg->cmsg_level = SOL_SOCKET;
-                cmsg->cmsg_type = SCM_RIGHTS;
-                cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
-                memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
-
-                msg.msg_controllen = CMSG_SPACE(fdsByteSize);
-
-                ssize_t processedSize = TEMP_FAILURE_RETRY(
-                        sendmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
-                if (processedSize > 0) {
-                    sentFds = true;
-                }
-                return processedSize;
-            }
-
-            msghdr msg{
-                    .msg_iov = iovs,
-                    // posix uses int, glibc uses size_t.  niovs is a
-                    // non-negative int and can be cast to either.
-                    .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
-            };
-            return TEMP_FAILURE_RETRY(sendmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL));
+            int ret = sendMessageOnSocket(mSocket, iovs, niovs, sentFds ? nullptr : ancillaryFds);
+            sentFds |= ret > 0;
+            return ret;
         };
         return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT,
                                         altPoll);
@@ -124,54 +74,7 @@
             const std::optional<android::base::function_ref<status_t()>>& altPoll,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
         auto recv = [&](iovec* iovs, int niovs) -> ssize_t {
-            if (ancillaryFds != nullptr) {
-                int fdBuffer[kMaxFdsPerMsg];
-                alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
-
-                msghdr msg{
-                        .msg_iov = iovs,
-                        .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
-                        .msg_control = msgControlBuf,
-                        .msg_controllen = sizeof(msgControlBuf),
-                };
-                ssize_t processSize =
-                        TEMP_FAILURE_RETRY(recvmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL));
-                if (processSize < 0) {
-                    return -1;
-                }
-
-                for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;
-                     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-                    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
-                        // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
-                        // application devs to memcpy the data to ensure memory alignment.
-                        size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
-                        LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // sanity check
-                        memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
-                        size_t fdCount = dataLen / sizeof(int);
-                        ancillaryFds->reserve(ancillaryFds->size() + fdCount);
-                        for (size_t i = 0; i < fdCount; i++) {
-                            ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i]));
-                        }
-                        break;
-                    }
-                }
-
-                if (msg.msg_flags & MSG_CTRUNC) {
-                    ALOGE("msg was truncated. Aborting session.");
-                    errno = EPIPE;
-                    return -1;
-                }
-
-                return processSize;
-            }
-            msghdr msg{
-                    .msg_iov = iovs,
-                    // posix uses int, glibc uses size_t.  niovs is a
-                    // non-negative int and can be cast to either.
-                    .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
-            };
-            return TEMP_FAILURE_RETRY(recvmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL));
+            return receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds);
         };
         return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN,
                                         altPoll);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index c91d56c..342e4a3 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -58,13 +58,10 @@
       "name": "CtsOsTestCases",
       "options": [
         {
-          "exclude-annotation": "android.platform.test.annotations.LargeTest"
+          "include-filter": "android.os.cts.BinderTest"
         },
         {
-          "exclude-filter": "android.os.cts.BuildTest#testSdkInt"
-        },
-        {
-          "exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage"
+          "include-filter": "android.os.cts.ParcelTest"
         }
       ]
     },
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 2c99334..81ae26a3 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -50,6 +50,17 @@
             std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
     /**
+     * Creates an RPC server that bootstraps sessions using an existing
+     * Unix domain socket pair.
+     *
+     * Callers should create a pair of SOCK_STREAM Unix domain sockets, pass
+     * one to RpcServer::setupUnixDomainSocketBootstrapServer and the other
+     * to RpcSession::setupUnixDomainSocketBootstrapClient. Multiple client
+     * session can be created from the client end of the pair.
+     */
+    [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(base::unique_fd serverFd);
+
+    /**
      * This represents a session for responses, e.g.:
      *
      *     process A serves binder a
@@ -202,11 +213,18 @@
     void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
     void onSessionIncomingThreadEnded() override;
 
+    status_t setupExternalServer(
+            base::unique_fd serverFd,
+            std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn);
+
     static constexpr size_t kRpcAddressSize = 128;
     static void establishConnection(
             sp<RpcServer>&& server, RpcTransportFd clientFd,
             std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen,
             std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn);
+    static status_t acceptSocketConnection(const RpcServer& server, RpcTransportFd* out);
+    static status_t recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out);
+
     [[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address);
 
     const std::unique_ptr<RpcTransportCtx> mCtx;
@@ -228,6 +246,7 @@
     std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
     std::unique_ptr<FdTrigger> mShutdownTrigger;
     RpcConditionVariable mShutdownCv;
+    std::function<status_t(const RpcServer& server, RpcTransportFd* out)> mAcceptFn;
 };
 
 } // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index a25ba98..5e5d4ea 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -100,6 +100,8 @@
         NONE = 0,
         // Send file descriptors via unix domain socket ancillary data.
         UNIX = 1,
+        // Send file descriptors as Trusty IPC handles.
+        TRUSTY = 2,
     };
 
     /**
@@ -115,6 +117,11 @@
     [[nodiscard]] status_t setupUnixDomainClient(const char* path);
 
     /**
+     * Connects to an RPC server over a nameless Unix domain socket pair.
+     */
+    [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(base::unique_fd bootstrap);
+
+    /**
      * Connects to an RPC server at the CVD & port.
      */
     [[nodiscard]] status_t setupVsockClient(unsigned int cvd, unsigned int port);
@@ -366,6 +373,8 @@
 
     RpcConditionVariable mAvailableConnectionCv; // for mWaitingThreads
 
+    std::unique_ptr<RpcTransport> mBootstrapTransport;
+
     struct ThreadState {
         size_t mWaitingThreads = 0;
         // hint index into clients, ++ when sending an async transaction
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 7ea9be7..fccc0af 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -349,7 +349,7 @@
     /**
      * See AIBinder_Weak_promote.
      */
-    SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); }
+    SpAIBinder promote() const { return SpAIBinder(AIBinder_Weak_promote(get())); }
 };
 
 namespace internal {
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
index dfe12a1..74a7157 100644
--- a/libs/binder/ndk/include_platform/android/binder_libbinder.h
+++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)) || defined(__TRUSTY__)
 
 #include <android/binder_ibinder.h>
 #include <android/binder_parcel.h>
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 6d29238..01b9472 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -670,6 +670,26 @@
     EXPECT_EQ(42, pparcel->readInt32());
 }
 
+TEST(NdkBinder, GetAndVerifyScopedAIBinder_Weak) {
+    for (const ndk::SpAIBinder& binder :
+         {// remote
+          ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)),
+          // local
+          ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
+        // get a const ScopedAIBinder_Weak and verify promote
+        EXPECT_NE(binder.get(), nullptr);
+        const ndk::ScopedAIBinder_Weak wkAIBinder =
+                ndk::ScopedAIBinder_Weak(AIBinder_Weak_new(binder.get()));
+        EXPECT_EQ(wkAIBinder.promote().get(), binder.get());
+        // get another ScopedAIBinder_Weak and verify
+        ndk::ScopedAIBinder_Weak wkAIBinder2 =
+                ndk::ScopedAIBinder_Weak(AIBinder_Weak_new(binder.get()));
+        EXPECT_FALSE(AIBinder_Weak_lt(wkAIBinder.get(), wkAIBinder2.get()));
+        EXPECT_FALSE(AIBinder_Weak_lt(wkAIBinder2.get(), wkAIBinder.get()));
+        EXPECT_EQ(wkAIBinder2.promote(), wkAIBinder.promote());
+    }
+}
+
 class MyResultReceiver : public BnResultReceiver {
    public:
     Mutex mMutex;
diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl
index 34d74be..4cdeac4 100644
--- a/libs/binder/tests/BinderRpcTestServerConfig.aidl
+++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl
@@ -21,5 +21,6 @@
     int rpcSecurity;
     int serverVersion;
     int vsockPort;
+    int unixBootstrapFd; // Inherited from parent
     @utf8InCpp String addr;
 }
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 6e1c8ac..25b524f 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -115,6 +115,7 @@
     BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
     BINDER_LIB_TEST_GETPID,
     BINDER_LIB_TEST_ECHO_VECTOR,
+    BINDER_LIB_TEST_GET_NON_BLOCKING_FD,
     BINDER_LIB_TEST_REJECT_OBJECTS,
     BINDER_LIB_TEST_CAN_GET_SID,
     BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
@@ -1158,6 +1159,21 @@
     EXPECT_EQ(readValue, testValue);
 }
 
+TEST_F(BinderLibTest, FileDescriptorRemainsNonBlocking) {
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+
+    Parcel reply;
+    EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_NON_BLOCKING_FD, {} /*data*/, &reply),
+                StatusEq(NO_ERROR));
+    base::unique_fd fd;
+    EXPECT_THAT(reply.readUniqueFileDescriptor(&fd), StatusEq(OK));
+
+    const int result = fcntl(fd.get(), F_GETFL);
+    ASSERT_NE(result, -1);
+    EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
 // see ProcessState.cpp BINDER_VM_SIZE = 1MB.
 // This value is not exposed, but some code in the framework relies on being able to use
 // buffers near the cap size.
@@ -1801,6 +1817,28 @@
                 reply->writeUint64Vector(vector);
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_GET_NON_BLOCKING_FD: {
+                std::array<int, 2> sockets;
+                const bool created = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets.data()) == 0;
+                if (!created) {
+                    ALOGE("Could not create socket pair");
+                    return UNKNOWN_ERROR;
+                }
+
+                const int result = fcntl(sockets[0], F_SETFL, O_NONBLOCK);
+                if (result != 0) {
+                    ALOGE("Could not make socket non-blocking: %s", strerror(errno));
+                    return UNKNOWN_ERROR;
+                }
+                base::unique_fd out(sockets[0]);
+                status_t writeResult = reply->writeUniqueFileDescriptor(out);
+                if (writeResult != NO_ERROR) {
+                    ALOGE("Could not write unique_fd");
+                    return writeResult;
+                }
+                close(sockets[1]); // we don't need the other side of the fd
+                return NO_ERROR;
+            }
             case BINDER_LIB_TEST_REJECT_OBJECTS: {
                 return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
             }
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 2b70492..7294305 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -181,10 +181,20 @@
             wp<RpcSession> weakSession = session;
             session = nullptr;
 
-            sp<RpcSession> strongSession = weakSession.promote();
-            EXPECT_EQ(nullptr, strongSession)
-                    << (debugBacktrace(host.getPid()), debugBacktrace(getpid()), "Leaked sess: ")
-                    << strongSession->getStrongCount();
+            // b/244325464 - 'getStrongCount' is printing '1' on failure here, which indicates the
+            // the object should not actually be promotable. By looping, we distinguish a race here
+            // from a bug causing the object to not be promotable.
+            for (size_t i = 0; i < 3; i++) {
+                sp<RpcSession> strongSession = weakSession.promote();
+                EXPECT_EQ(nullptr, strongSession)
+                        << (debugBacktrace(host.getPid()), debugBacktrace(getpid()),
+                            "Leaked sess: ")
+                        << strongSession->getStrongCount() << " checked time " << i;
+
+                if (strongSession != nullptr) {
+                    sleep(1);
+                }
+            }
         }
     }
 };
@@ -244,6 +254,25 @@
     return serverFd;
 }
 
+static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) {
+    base::unique_fd sockClient, sockServer;
+    if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) {
+        int savedErrno = errno;
+        LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+    }
+
+    int zero = 0;
+    iovec iov{&zero, sizeof(zero)};
+    std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+    fds.emplace_back(std::move(sockServer));
+
+    if (sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) {
+        int savedErrno = errno;
+        LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno);
+    }
+    return std::move(sockClient);
+}
+
 using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd,
                               android::base::borrowed_fd readEnd);
 
@@ -264,7 +293,14 @@
     // Whether the test params support sending FDs in parcels.
     bool supportsFdTransport() const {
         return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
-                (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX);
+                (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
+                 socketType() == SocketType::UNIX_BOOTSTRAP);
+    }
+
+    void SetUp() override {
+        if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) {
+            GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport";
+        }
     }
 
     static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
@@ -298,6 +334,14 @@
                                             singleThreaded ? "_single_threaded" : "",
                                             noKernel ? "_no_kernel" : "");
 
+        base::unique_fd bootstrapClientFd, bootstrapServerFd;
+        // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec.
+        // This is because we cannot pass ParcelFileDescriptor over a pipe.
+        if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &bootstrapServerFd)) {
+            int savedErrno = errno;
+            LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+        }
+
         auto ret = ProcessSession{
                 .host = Process([=](android::base::borrowed_fd writeEnd,
                                     android::base::borrowed_fd readEnd) {
@@ -315,6 +359,7 @@
         serverConfig.serverVersion = serverVersion;
         serverConfig.vsockPort = allocateVsockPort();
         serverConfig.addr = allocateSocketAddress();
+        serverConfig.unixBootstrapFd = bootstrapServerFd.get();
         for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
             serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
                     static_cast<int32_t>(mode));
@@ -364,6 +409,10 @@
                 case SocketType::UNIX:
                     status = session->setupUnixDomainClient(serverConfig.addr.c_str());
                     break;
+                case SocketType::UNIX_BOOTSTRAP:
+                    status = session->setupUnixDomainSocketBootstrapClient(
+                            base::unique_fd(dup(bootstrapClientFd.get())));
+                    break;
                 case SocketType::VSOCK:
                     status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
                     break;
@@ -430,7 +479,8 @@
     }
 
     SocketType type = std::get<0>(GetParam());
-    if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) {
+    if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
+        type == SocketType::UNIX_BOOTSTRAP) {
         // we can't get port numbers for unix sockets
         return;
     }
@@ -1566,7 +1616,7 @@
 }
 
 static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
-    std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET};
+    std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET};
 
     if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED);
 
@@ -1767,6 +1817,8 @@
     // A server that handles client socket connections.
     class Server {
     public:
+        using AcceptConnection = std::function<base::unique_fd(Server*)>;
+
         explicit Server() {}
         Server(Server&&) = default;
         ~Server() { shutdownAndWait(); }
@@ -1791,6 +1843,21 @@
                         return connectTo(UnixSocketAddress(addr.c_str()));
                     };
                 } break;
+                case SocketType::UNIX_BOOTSTRAP: {
+                    base::unique_fd bootstrapFdClient, bootstrapFdServer;
+                    if (!base::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) {
+                        return AssertionFailure() << "Socketpair() failed";
+                    }
+                    auto status = rpcServer->setupUnixDomainSocketBootstrapServer(
+                            std::move(bootstrapFdServer));
+                    if (status != OK) {
+                        return AssertionFailure() << "setupUnixDomainSocketBootstrapServer: "
+                                                  << statusToString(status);
+                    }
+                    mBootstrapSocket = RpcTransportFd(std::move(bootstrapFdClient));
+                    mAcceptConnection = &Server::recvmsgServerConnection;
+                    mConnectToServer = [this] { return connectToUnixBootstrap(mBootstrapSocket); };
+                } break;
                 case SocketType::VSOCK: {
                     auto port = allocateVsockPort();
                     auto status = rpcServer->setupVsockServer(port);
@@ -1838,14 +1905,33 @@
             LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
             mThread = std::make_unique<std::thread>(&Server::run, this);
         }
+
+        base::unique_fd acceptServerConnection() {
+            return base::unique_fd(TEMP_FAILURE_RETRY(
+                    accept4(mFd.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK)));
+        }
+
+        base::unique_fd recvmsgServerConnection() {
+            std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+            int buf;
+            iovec iov{&buf, sizeof(buf)};
+
+            if (receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
+                int savedErrno = errno;
+                LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno);
+            }
+            if (fds.size() != 1) {
+                LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size();
+            }
+            return std::move(std::get<base::unique_fd>(fds[0]));
+        }
+
         void run() {
             LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
 
             std::vector<std::thread> threads;
             while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) {
-                base::unique_fd acceptedFd(
-                        TEMP_FAILURE_RETRY(accept4(mFd.fd.get(), nullptr, nullptr /*length*/,
-                                                   SOCK_CLOEXEC | SOCK_NONBLOCK)));
+                base::unique_fd acceptedFd = mAcceptConnection(this);
                 threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd));
             }
 
@@ -1872,8 +1958,9 @@
     private:
         std::unique_ptr<std::thread> mThread;
         ConnectToServer mConnectToServer;
+        AcceptConnection mAcceptConnection = &Server::acceptServerConnection;
         std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
-        RpcTransportFd mFd;
+        RpcTransportFd mFd, mBootstrapSocket;
         std::unique_ptr<RpcTransportCtx> mCtx;
         std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
                 std::make_shared<RpcCertificateVerifierSimple>();
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index fddf5f3..823bbf6 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -49,6 +49,7 @@
 
 #include "../BuildFlags.h"
 #include "../FdTrigger.h"
+#include "../OS.h"               // for testing UnixBootstrap clients
 #include "../RpcSocketAddress.h" // for testing preconnected clients
 #include "../RpcState.h"         // for debugging
 #include "../vm_sockets.h"       // for VMADDR_*
@@ -67,15 +68,19 @@
 enum class SocketType {
     PRECONNECTED,
     UNIX,
+    UNIX_BOOTSTRAP,
     VSOCK,
     INET,
 };
+
 static inline std::string PrintToString(SocketType socketType) {
     switch (socketType) {
         case SocketType::PRECONNECTED:
             return "preconnected_uds";
         case SocketType::UNIX:
             return "unix_domain_socket";
+        case SocketType::UNIX_BOOTSTRAP:
+            return "unix_domain_socket_bootstrap";
         case SocketType::VSOCK:
             return "vm_socket";
         case SocketType::INET:
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 31eb5da..a922b21 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -42,6 +42,7 @@
     server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
 
     unsigned int outPort = 0;
+    base::unique_fd unixBootstrapFd(serverConfig.unixBootstrapFd);
 
     switch (socketType) {
         case SocketType::PRECONNECTED:
@@ -50,6 +51,9 @@
             CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str()))
                     << serverConfig.addr;
             break;
+        case SocketType::UNIX_BOOTSTRAP:
+            CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(unixBootstrapFd)));
+            break;
         case SocketType::VSOCK:
             CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
             break;
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 0210237..3904e1d 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -84,6 +84,7 @@
         },
     },
     srcs: [
+        "random_binder.cpp",
         "random_fd.cpp",
         "random_parcel.cpp",
         "libbinder_driver.cpp",
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h
new file mode 100644
index 0000000..8fc9263
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IBinder.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+// Get a random binder object for use in fuzzing.
+//
+// May return nullptr.
+sp<IBinder> getRandomBinder(FuzzedDataProvider* provider);
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/random_binder.cpp b/libs/binder/tests/parcel_fuzzer/random_binder.cpp
new file mode 100644
index 0000000..8a1fecb
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/random_binder.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <fuzzbinder/random_binder.h>
+
+#include <fuzzbinder/random_parcel.h>
+
+#include <android-base/logging.h>
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+class RandomBinder : public BBinder {
+public:
+    RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes)
+          : mDescriptor(descriptor),
+            mBytes(std::move(bytes)),
+            mProvider(mBytes.data(), mBytes.size()) {}
+    const String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override {
+        (void)code;
+        (void)data;
+        (void)reply;
+        (void)flags; // note - for maximum coverage even ignore if oneway
+
+        if (mProvider.ConsumeBool()) {
+            return mProvider.ConsumeIntegral<status_t>();
+        }
+
+        if (reply == nullptr) return OK;
+
+        // TODO: things we could do to increase state space
+        // - also pull FDs and binders from 'data'
+        //     (optionally combine these into random parcel 'options')
+        // - also pull FDs and binders from random parcel 'options'
+        RandomParcelOptions options;
+
+        // random output
+        std::vector<uint8_t> subData = mProvider.ConsumeBytes<uint8_t>(
+                mProvider.ConsumeIntegralInRange<size_t>(0, mProvider.remaining_bytes()));
+        fillRandomParcel(reply, FuzzedDataProvider(subData.data(), subData.size()), &options);
+
+        return OK;
+    }
+
+private:
+    String16 mDescriptor;
+
+    // note may not all be used
+    std::vector<uint8_t> mBytes;
+    FuzzedDataProvider mProvider;
+};
+
+sp<IBinder> getRandomBinder(FuzzedDataProvider* provider) {
+    auto makeFunc = provider->PickValueInArray<const std::function<sp<IBinder>()>>({
+            [&]() {
+                // descriptor is the length of a class name, e.g.
+                // "some.package.Foo"
+                std::string str = provider->ConsumeRandomLengthString(100 /*max length*/);
+
+                // arbitrarily consume remaining data to create a binder that can return
+                // random results - coverage guided fuzzer should ensure all of the remaining
+                // data isn't always used
+                std::vector<uint8_t> bytes = provider->ConsumeBytes<uint8_t>(
+                        provider->ConsumeIntegralInRange<size_t>(0, provider->remaining_bytes()));
+
+                return new RandomBinder(String16(str.c_str()), std::move(bytes));
+            },
+            []() {
+                // this is the easiest remote binder to get ahold of, and it
+                // should be able to handle anything thrown at it, and
+                // essentially every process can talk to it, so it's a good
+                // candidate for checking usage of an actual BpBinder
+                return IInterface::asBinder(defaultServiceManager());
+            },
+            [&]() -> sp<IBinder> { return nullptr; },
+    });
+    return makeFunc();
+}
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 51cb768..edc695f 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -17,23 +17,14 @@
 #include <fuzzbinder/random_parcel.h>
 
 #include <android-base/logging.h>
-#include <binder/IServiceManager.h>
 #include <binder/RpcSession.h>
 #include <binder/RpcTransportRaw.h>
+#include <fuzzbinder/random_binder.h>
 #include <fuzzbinder/random_fd.h>
 #include <utils/String16.h>
 
 namespace android {
 
-class NamedBinder : public BBinder {
-public:
-    NamedBinder(const String16& descriptor) : mDescriptor(descriptor) {}
-    const String16& getInterfaceDescriptor() const override { return mDescriptor; }
-
-private:
-    String16 mDescriptor;
-};
-
 static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) {
     std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes());
     CHECK(OK == p->write(data.data(), data.size()));
@@ -45,6 +36,11 @@
     if (provider.ConsumeBool()) {
         auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
         CHECK_EQ(OK, session->addNullDebuggingClient());
+        // Set the protocol version so that we don't crash if the session
+        // actually gets used. This isn't cheating because the version should
+        // always be set if the session init succeeded and we aren't testing the
+        // session init here (it is bypassed by addNullDebuggingClient).
+        session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION);
         p->markForRpc(session);
 
         if (options->writeHeader) {
@@ -89,32 +85,16 @@
                 },
                 // write binder
                 [&]() {
-                    auto makeFunc = provider.PickValueInArray<const std::function<sp<IBinder>()>>({
-                            [&]() {
-                                // descriptor is the length of a class name, e.g.
-                                // "some.package.Foo"
-                                std::string str =
-                                        provider.ConsumeRandomLengthString(100 /*max length*/);
-                                return new NamedBinder(String16(str.c_str()));
-                            },
-                            []() {
-                                // this is the easiest remote binder to get ahold of, and it
-                                // should be able to handle anything thrown at it, and
-                                // essentially every process can talk to it, so it's a good
-                                // candidate for checking usage of an actual BpBinder
-                                return IInterface::asBinder(defaultServiceManager());
-                            },
-                            [&]() -> sp<IBinder> {
-                                if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
-                                    return options->extraBinders.at(
-                                            provider.ConsumeIntegralInRange<
-                                                    size_t>(0, options->extraBinders.size() - 1));
-                                } else {
-                                    return nullptr;
-                                }
-                            },
-                    });
-                    sp<IBinder> binder = makeFunc();
+                    sp<IBinder> binder;
+                    if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
+                        binder = options->extraBinders.at(
+                                provider.ConsumeIntegralInRange<size_t>(0,
+                                                                        options->extraBinders
+                                                                                        .size() -
+                                                                                1));
+                    } else {
+                        binder = getRandomBinder(&provider);
+                    }
                     CHECK(OK == p->writeStrongBinder(binder));
                 },
         });
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 46346bb..397ff41 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -16,6 +16,7 @@
 
 #if defined(TRUSTY_USERSPACE)
 #include <openssl/rand.h>
+#include <trusty_ipc.h>
 #else
 #include <lib/rand/rand.h>
 #endif
@@ -23,6 +24,7 @@
 #include <binder/RpcTransportTipcTrusty.h>
 
 #include "../OS.h"
+#include "TrustyStatus.h"
 
 using android::base::Result;
 
@@ -43,13 +45,32 @@
 #endif // TRUSTY_USERSPACE
 }
 
-status_t dupFileDescriptor(int /*oldFd*/, int* /*newFd*/) {
-    // TODO: implement separately
-    return INVALID_OPERATION;
+status_t dupFileDescriptor(int oldFd, int* newFd) {
+    int res = dup(oldFd);
+    if (res < 0) {
+        return statusFromTrusty(res);
+    }
+
+    *newFd = res;
+    return OK;
 }
 
 std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() {
     return RpcTransportCtxFactoryTipcTrusty::make();
 }
 
+int sendMessageOnSocket(
+        const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
+        const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) {
+    errno = ENOTSUP;
+    return -1;
+}
+
+int receiveMessageFromSocket(
+        const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
+        std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) {
+    errno = ENOTSUP;
+    return -1;
+}
+
 } // namespace android
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index 0b67b9f..58bfe71 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "RpcTransportTipcTrusty"
 
+#include <inttypes.h>
 #include <trusty_ipc.h>
 
 #include <binder/RpcSession.h>
@@ -47,7 +48,7 @@
     status_t interruptableWriteFully(
             FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
             const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/,
-            const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/)
+            const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
             override {
         if (niovs < 0) {
             return BAD_VALUE;
@@ -58,12 +59,32 @@
             size += iovs[i].iov_len;
         }
 
+        handle_t msgHandles[IPC_MAX_MSG_HANDLES];
         ipc_msg_t msg{
                 .num_iov = static_cast<uint32_t>(niovs),
                 .iov = iovs,
-                .num_handles = 0, // TODO: add ancillaryFds
+                .num_handles = 0,
                 .handles = nullptr,
         };
+
+        if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
+            if (ancillaryFds->size() > IPC_MAX_MSG_HANDLES) {
+                // This shouldn't happen because we check the FD count in RpcState.
+                ALOGE("Saw too many file descriptors in RpcTransportCtxTipcTrusty: "
+                      "%zu (max is %u). Aborting session.",
+                      ancillaryFds->size(), IPC_MAX_MSG_HANDLES);
+                return BAD_VALUE;
+            }
+
+            for (size_t i = 0; i < ancillaryFds->size(); i++) {
+                msgHandles[i] =
+                        std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i));
+            }
+
+            msg.num_handles = ancillaryFds->size();
+            msg.handles = msgHandles;
+        }
+
         ssize_t rc = send_msg(mSocket.fd.get(), &msg);
         if (rc == ERR_NOT_ENOUGH_BUFFER) {
             // Peer is blocked, wait until it unblocks.
@@ -97,8 +118,7 @@
     status_t interruptableReadFully(
             FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
             const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/,
-            std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/)
-            override {
+            std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
         if (niovs < 0) {
             return BAD_VALUE;
         }
@@ -124,11 +144,16 @@
                 return status;
             }
 
+            LOG_ALWAYS_FATAL_IF(mMessageInfo.num_handles > IPC_MAX_MSG_HANDLES,
+                                "Received too many handles %" PRIu32, mMessageInfo.num_handles);
+            bool haveHandles = mMessageInfo.num_handles != 0;
+            handle_t msgHandles[IPC_MAX_MSG_HANDLES];
+
             ipc_msg_t msg{
                     .num_iov = static_cast<uint32_t>(niovs),
                     .iov = iovs,
-                    .num_handles = 0, // TODO: support ancillaryFds
-                    .handles = nullptr,
+                    .num_handles = mMessageInfo.num_handles,
+                    .handles = haveHandles ? msgHandles : 0,
             };
             ssize_t rc = read_msg(mSocket.fd.get(), mMessageInfo.id, mMessageOffset, &msg);
             if (rc < 0) {
@@ -141,6 +166,28 @@
                                 "Message offset exceeds length %zu/%zu", mMessageOffset,
                                 mMessageInfo.len);
 
+            if (haveHandles) {
+                if (ancillaryFds != nullptr) {
+                    ancillaryFds->reserve(ancillaryFds->size() + mMessageInfo.num_handles);
+                    for (size_t i = 0; i < mMessageInfo.num_handles; i++) {
+                        ancillaryFds->emplace_back(base::unique_fd(msgHandles[i]));
+                    }
+
+                    // Clear the saved number of handles so we don't accidentally
+                    // read them multiple times
+                    mMessageInfo.num_handles = 0;
+                    haveHandles = false;
+                } else {
+                    ALOGE("Received unexpected handles %" PRIu32, mMessageInfo.num_handles);
+                    // It should be safe to continue here. We could abort, but then
+                    // peers could DoS us by sending messages with handles in them.
+                    // Close the handles since we are ignoring them.
+                    for (size_t i = 0; i < mMessageInfo.num_handles; i++) {
+                        ::close(msgHandles[i]);
+                    }
+                }
+            }
+
             // Release the message if all of it has been read
             if (mMessageOffset == mMessageInfo.len) {
                 releaseMessage();
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index cc31c95..7d9dd8c 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -60,6 +60,10 @@
             std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
     void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); }
+    void setSupportedFileDescriptorTransportModes(
+            const std::vector<RpcSession::FileDescriptorTransportMode>& modes) {
+        mRpcServer->setSupportedFileDescriptorTransportModes(modes);
+    }
     void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); }
     void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); }
     void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) {
diff --git a/libs/binder/trusty/ndk/include/sys/cdefs.h b/libs/binder/trusty/ndk/include/sys/cdefs.h
new file mode 100644
index 0000000..6a48d2b
--- /dev/null
+++ b/libs/binder/trusty/ndk/include/sys/cdefs.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <lk/compiler.h>
+
+/* Alias the bionic macros to the ones from lk/compiler.h */
+#define __BEGIN_DECLS __BEGIN_CDECLS
+#define __END_DECLS __END_CDECLS
+
+#define __INTRODUCED_IN(x) /* nothing on Trusty */
diff --git a/libs/binder/trusty/ndk/rules.mk b/libs/binder/trusty/ndk/rules.mk
new file mode 100644
index 0000000..03fd006
--- /dev/null
+++ b/libs/binder/trusty/ndk/rules.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+LIBBINDER_NDK_DIR := frameworks/native/libs/binder/ndk
+
+MODULE_SRCS := \
+	$(LIBBINDER_NDK_DIR)/ibinder.cpp \
+	$(LIBBINDER_NDK_DIR)/libbinder.cpp \
+	$(LIBBINDER_NDK_DIR)/parcel.cpp \
+	$(LIBBINDER_NDK_DIR)/status.cpp \
+
+MODULE_EXPORT_INCLUDES += \
+	$(LOCAL_DIR)/include \
+	$(LIBBINDER_NDK_DIR)/include_cpp \
+	$(LIBBINDER_NDK_DIR)/include_ndk \
+	$(LIBBINDER_NDK_DIR)/include_platform \
+
+MODULE_LIBRARY_DEPS += \
+	trusty/user/base/lib/libstdc++-trusty \
+	frameworks/native/libs/binder/trusty \
+
+include make/library.mk
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d3144bb..23a2181 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -217,7 +217,6 @@
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
-        "TransactionTracing.cpp",
         "VsyncEventData.cpp",
         "view/Surface.cpp",
         "WindowInfosListenerReporter.cpp",
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index af64b3b..4c887ec 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -19,7 +19,6 @@
 
 #include <android/gui/IDisplayEventConnection.h>
 #include <android/gui/IRegionSamplingListener.h>
-#include <android/gui/ITransactionTraceListener.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 4d5978c..2759c58 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -634,7 +634,7 @@
 }
 
 bool layer_state_t::hasValidBuffer() const {
-    return bufferData && (bufferData->buffer || bufferData->cachedBuffer.isValid());
+    return bufferData && (bufferData->hasBuffer() || bufferData->cachedBuffer.isValid());
 }
 
 status_t layer_state_t::matrix22_t::write(Parcel& output) const {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 4b4d46a..c4fb1cf 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -56,6 +56,12 @@
 
 namespace {
 
+enum {
+    // moved from nativewindow/include/system/window.h, to be removed
+    NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
+    NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
+};
+
 bool isInterceptorRegistrationOp(int op) {
     return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR ||
             op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR ||
@@ -348,34 +354,25 @@
     return NO_ERROR;
 }
 
+// Deprecated(b/242763577): to be removed, this method should not be used
+// The reason this method still exists here is to support compiled vndk
+// Surface support should not be tied to the display
+// Return true since most displays should have this support
 status_t Surface::getWideColorSupport(bool* supported) {
     ATRACE_CALL();
 
-    const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
-    if (display == nullptr) {
-        return NAME_NOT_FOUND;
-    }
-
-    *supported = false;
-    binder::Status status = composerServiceAIDL()->isWideColorDisplay(display, supported);
-    return statusTFromBinderStatus(status);
+    *supported = true;
+    return NO_ERROR;
 }
 
+// Deprecated(b/242763577): to be removed, this method should not be used
+// The reason this method still exists here is to support compiled vndk
+// Surface support should not be tied to the display
+// Return true since most displays should have this support
 status_t Surface::getHdrSupport(bool* supported) {
     ATRACE_CALL();
 
-    const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
-    if (display == nullptr) {
-        return NAME_NOT_FOUND;
-    }
-
-    gui::DynamicDisplayInfo info;
-    if (binder::Status status = composerServiceAIDL()->getDynamicDisplayInfo(display, &info);
-        !status.isOk()) {
-        return statusTFromBinderStatus(status);
-    }
-
-    *supported = !info.hdrCapabilities.supportedHdrTypes.empty();
+    *supported = true;
     return NO_ERROR;
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a239008..694bc5a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1130,11 +1130,6 @@
     return physicalDisplayIds;
 }
 
-std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() {
-    ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance();
-    return instance.getInternalDisplayId();
-}
-
 sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId displayId) {
     sp<IBinder> display = nullptr;
     binder::Status status =
@@ -1143,11 +1138,6 @@
     return status.isOk() ? display : nullptr;
 }
 
-sp<IBinder> SurfaceComposerClient::getInternalDisplayToken() {
-    ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance();
-    return instance.getInternalDisplayToken();
-}
-
 void SurfaceComposerClient::Transaction::setAnimationTransaction() {
     mAnimation = true;
 }
@@ -2755,8 +2745,7 @@
             ComposerServiceAIDL::getComposerService()->getDisplayDecorationSupport(displayToken,
                                                                                    &gsupport);
     std::optional<DisplayDecorationSupport> support;
-    // TODO (b/241277093): Remove `false && ` once b/241278870 is fixed.
-    if (false && status.isOk() && gsupport.has_value()) {
+    if (status.isOk() && gsupport.has_value()) {
         support.emplace(DisplayDecorationSupport{
           .format =
                 static_cast<aidl::android::hardware::graphics::common::PixelFormat>(
diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp
deleted file mode 100644
index 59450fb..0000000
--- a/libs/gui/TransactionTracing.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2020 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/TransactionTracing.h"
-#include "android/gui/ISurfaceComposer.h"
-
-#include <private/gui/ComposerServiceAIDL.h>
-
-namespace android {
-
-sp<TransactionTraceListener> TransactionTraceListener::sInstance = nullptr;
-std::mutex TransactionTraceListener::sMutex;
-
-TransactionTraceListener::TransactionTraceListener() {}
-
-sp<TransactionTraceListener> TransactionTraceListener::getInstance() {
-    const std::lock_guard<std::mutex> lock(sMutex);
-
-    if (sInstance == nullptr) {
-        sInstance = new TransactionTraceListener;
-
-        sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-        sf->addTransactionTraceListener(sInstance);
-    }
-
-    return sInstance;
-}
-
-binder::Status TransactionTraceListener::onToggled(bool enabled) {
-    ALOGD("TransactionTraceListener: onToggled listener called");
-    mTracingEnabled = enabled;
-
-    return binder::Status::ok();
-}
-
-bool TransactionTraceListener::isTracingEnabled() {
-    return mTracingEnabled;
-}
-
-} // namespace android
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 3c220fc..ac1442b 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -36,7 +36,6 @@
 import android.gui.IRegionSamplingListener;
 import android.gui.IScreenCaptureListener;
 import android.gui.ISurfaceComposerClient;
-import android.gui.ITransactionTraceListener;
 import android.gui.ITunnelModeEnabledListener;
 import android.gui.IWindowInfosListener;
 import android.gui.LayerCaptureArgs;
@@ -453,11 +452,6 @@
     void setOverrideFrameRate(int uid, float frameRate);
 
     /**
-     * Adds a TransactionTraceListener to listen for transaction tracing state updates.
-     */
-    void addTransactionTraceListener(ITransactionTraceListener listener);
-
-    /**
      * Gets priority of the RenderEngine in SurfaceFlinger.
      */
     int getGpuContextPriority();
diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
deleted file mode 100644
index 5cd12fd..0000000
--- a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
+++ /dev/null
@@ -1,6 +0,0 @@
-package android.gui;
-
-/** @hide */
-interface ITransactionTraceListener {
-   void onToggled(boolean enabled);
-}
\ No newline at end of file
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index d43c197..433b044 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -148,8 +148,6 @@
     MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
                 (const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
     MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
-    MOCK_METHOD(binder::Status, addTransactionTraceListener,
-                (const sp<gui::ITransactionTraceListener>&), (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>&),
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d46c2e7..e91d754 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -23,7 +23,6 @@
 #include <android/gui/IHdrLayerInfoListener.h>
 #include <android/gui/IRegionSamplingListener.h>
 #include <android/gui/IScreenCaptureListener.h>
-#include <android/gui/ITransactionTraceListener.h>
 #include <android/gui/ITunnelModeEnabledListener.h>
 #include <android/gui/IWindowInfosListener.h>
 #include <binder/IBinder.h>
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 634acb6..1f19f4e 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -187,8 +187,8 @@
             nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
             nsecs_t* outReleaseTime);
 
-    status_t getWideColorSupport(bool* supported);
-    status_t getHdrSupport(bool* supported);
+    status_t getWideColorSupport(bool* supported) __attribute__((__deprecated__));
+    status_t getHdrSupport(bool* supported) __attribute__((__deprecated__));
 
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 242eceb..04d3ab2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -354,11 +354,9 @@
 
     //! Get stable IDs for connected physical displays
     static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
-    static std::optional<PhysicalDisplayId> getInternalDisplayId();
 
     //! Get token for a physical display given its stable ID
     static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
-    static sp<IBinder> getInternalDisplayToken();
 
     static status_t enableVSyncInjections(bool enable);
 
diff --git a/libs/gui/include/gui/TransactionTracing.h b/libs/gui/include/gui/TransactionTracing.h
deleted file mode 100644
index 9efba47..0000000
--- a/libs/gui/include/gui/TransactionTracing.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android/gui/BnTransactionTraceListener.h>
-#include <utils/Mutex.h>
-
-namespace android {
-
-class TransactionTraceListener : public gui::BnTransactionTraceListener {
-    static std::mutex sMutex;
-    static sp<TransactionTraceListener> sInstance;
-
-    TransactionTraceListener();
-
-public:
-    static sp<TransactionTraceListener> getInstance();
-
-    binder::Status onToggled(bool enabled) override;
-
-    bool isTracingEnabled();
-
-private:
-    bool mTracingEnabled = false;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/fake/BufferData.h b/libs/gui/include/gui/fake/BufferData.h
new file mode 100644
index 0000000..725d11c
--- /dev/null
+++ b/libs/gui/include/gui/fake/BufferData.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/LayerState.h>
+
+namespace android::fake {
+
+// Class which exposes buffer properties from BufferData without holding on to an actual buffer
+class BufferData : public android::BufferData {
+public:
+    BufferData(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
+               uint64_t outUsage)
+          : mBufferId(bufferId),
+            mWidth(width),
+            mHeight(height),
+            mPixelFormat(pixelFormat),
+            mOutUsage(outUsage) {}
+    bool hasBuffer() const override { return mBufferId != 0; }
+    bool hasSameBuffer(const android::BufferData& other) const override {
+        return getId() == other.getId() && frameNumber == other.frameNumber;
+    }
+    uint32_t getWidth() const override { return mWidth; }
+    uint32_t getHeight() const override { return mHeight; }
+    uint64_t getId() const override { return mBufferId; }
+    PixelFormat getPixelFormat() const override { return mPixelFormat; }
+    uint64_t getUsage() const override { return mOutUsage; }
+
+private:
+    uint64_t mBufferId;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    int32_t mPixelFormat;
+    uint64_t mOutUsage;
+};
+
+} // namespace android::fake
diff --git a/libs/gui/include/private/gui/ComposerServiceAIDL.h b/libs/gui/include/private/gui/ComposerServiceAIDL.h
index 2963583..6352a58 100644
--- a/libs/gui/include/private/gui/ComposerServiceAIDL.h
+++ b/libs/gui/include/private/gui/ComposerServiceAIDL.h
@@ -51,28 +51,6 @@
     // Get a connection to the Composer Service.  This will block until
     // a connection is established. Returns null if permission is denied.
     static sp<gui::ISurfaceComposer> getComposerService();
-
-    // the following two methods are moved from ISurfaceComposer.h
-    // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
-    std::optional<PhysicalDisplayId> getInternalDisplayId() const {
-        std::vector<int64_t> displayIds;
-        binder::Status status = mComposerService->getPhysicalDisplayIds(&displayIds);
-        return (!status.isOk() || displayIds.empty())
-                ? std::nullopt
-                : DisplayId::fromValue<PhysicalDisplayId>(
-                          static_cast<uint64_t>(displayIds.front()));
-    }
-
-    // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
-    sp<IBinder> getInternalDisplayToken() const {
-        const auto displayId = getInternalDisplayId();
-        if (!displayId) return nullptr;
-        sp<IBinder> display;
-        binder::Status status =
-                mComposerService->getPhysicalDisplayToken(static_cast<int64_t>(displayId->value),
-                                                          &display);
-        return status.isOk() ? display : nullptr;
-    }
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index c4c2fa5..cf2593d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -188,7 +188,10 @@
     void SetUp() {
         mComposer = ComposerService::getComposerService();
         mClient = new SurfaceComposerClient();
-        mDisplayToken = mClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        // display 0 is picked as this test is not much display depedent
+        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_NE(nullptr, mDisplayToken.get());
         Transaction t;
         t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index b647aab..0a2750a 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -32,7 +32,10 @@
     void SetUp() {
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(OK, mComposerClient->initCheck());
-        mDisplayToken = mComposerClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        // display 0 is picked for now, can extend to support all displays if needed
+        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_TRUE(mDisplayToken);
     }
 
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 2637f59..3344e0b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -360,8 +360,10 @@
     void SetUp() {
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        const auto display = mComposerClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        // display 0 is picked for now, can extend to support all displays if needed
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_NE(display, nullptr);
 
         ui::DisplayMode mode;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index b9358e7..d5cc55f 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -263,7 +263,10 @@
     sp<ANativeWindow> anw(mSurface);
 
     // Verify the screenshot works with no protected buffers.
-    const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    // display 0 is picked for now, can extend to support all displays if needed
+    const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ASSERT_FALSE(display == nullptr);
 
     DisplayCaptureArgs captureArgs;
@@ -974,11 +977,6 @@
         return binder::Status::ok();
     }
 
-    binder::Status addTransactionTraceListener(
-            const sp<gui::ITransactionTraceListener>& /*listener*/) override {
-        return binder::Status::ok();
-    }
-
     binder::Status getGpuContextPriority(int32_t* /*outPriority*/) override {
         return binder::Status::ok();
     }
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 76b85d6..60328e4 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -136,6 +136,7 @@
     }
 
     std::vector<DisplayConfigImpl> modesPerDisplay[size];
+    ui::DisplayConnectionType displayConnectionTypes[size];
     int numModes = 0;
     for (int i = 0; i < size; ++i) {
         const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
@@ -145,6 +146,7 @@
             status != OK) {
             return status;
         }
+        displayConnectionTypes[i] = staticInfo.connectionType;
 
         ui::DynamicDisplayInfo dynamicInfo;
         if (const status_t status =
@@ -168,8 +170,6 @@
         }
     }
 
-    const std::optional<PhysicalDisplayId> internalId =
-            SurfaceComposerClient::getInternalDisplayId();
     ui::Dataspace defaultDataspace;
     ui::PixelFormat defaultPixelFormat;
     ui::Dataspace wcgDataspace;
@@ -201,8 +201,9 @@
 
     for (size_t i = 0; i < size; ++i) {
         const PhysicalDisplayId id = ids[i];
-        const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
-                                                     : ADisplayType::DISPLAY_TYPE_EXTERNAL;
+        const ADisplayType type = (displayConnectionTypes[i] == ui::DisplayConnectionType::Internal)
+                ? ADisplayType::DISPLAY_TYPE_INTERNAL
+                : ADisplayType::DISPLAY_TYPE_EXTERNAL;
         const std::vector<DisplayConfigImpl>& configs = modesPerDisplay[i];
         memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
 
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 79f49e1..c7745e6 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -235,8 +235,8 @@
     NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS         = 25,
     NATIVE_WINDOW_GET_COMPOSITOR_TIMING           = 26,
     NATIVE_WINDOW_GET_FRAME_TIMESTAMPS            = 27,
-    NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT          = 28,
-    NATIVE_WINDOW_GET_HDR_SUPPORT                 = 29,
+    /* 28, removed: NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT */
+    /* 29, removed: NATIVE_WINDOW_GET_HDR_SUPPORT */
     NATIVE_WINDOW_SET_USAGE64                     = ANATIVEWINDOW_PERFORM_SET_USAGE64,
     NATIVE_WINDOW_GET_CONSUMER_USAGE64            = 31,
     NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA  = 32,
@@ -988,15 +988,34 @@
             outDequeueReadyTime, outReleaseTime);
 }
 
-static inline int native_window_get_wide_color_support(
-    struct ANativeWindow* window, bool* outSupport) {
-    return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT,
-            outSupport);
+/* deprecated. Always returns 0 and outSupport holds true. Don't call. */
+static inline int native_window_get_wide_color_support (
+    struct ANativeWindow* window __UNUSED, bool* outSupport) __deprecated;
+
+/*
+   Deprecated(b/242763577): to be removed, this method should not be used
+   Surface support should not be tied to the display
+   Return true since most displays should have this support
+*/
+static inline int native_window_get_wide_color_support (
+    struct ANativeWindow* window __UNUSED, bool* outSupport) {
+    *outSupport = true;
+    return 0;
 }
 
-static inline int native_window_get_hdr_support(struct ANativeWindow* window,
+/* deprecated. Always returns 0 and outSupport holds true. Don't call. */
+static inline int native_window_get_hdr_support(struct ANativeWindow* window __UNUSED,
+                                                bool* outSupport) __deprecated;
+
+/*
+   Deprecated(b/242763577): to be removed, this method should not be used
+   Surface support should not be tied to the display
+   Return true since most displays should have this support
+*/
+static inline int native_window_get_hdr_support(struct ANativeWindow* window __UNUSED,
                                                 bool* outSupport) {
-    return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
+    *outSupport = true;
+    return 0;
 }
 
 static inline int native_window_get_consumer_usage(struct ANativeWindow* window,
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 739f3fa..d44eb46 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -80,16 +80,26 @@
     std::once_flag once;
     std::call_once(once, []() {
         auto surfaceComposerClient = SurfaceComposerClient::getDefault();
-        auto displayToken = surfaceComposerClient->getInternalDisplayToken();
-        ui::DisplayMode displayMode;
-        if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
-            LOG_ALWAYS_FATAL("Failed to get active display mode!");
+        auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        LOG_ALWAYS_FATAL_IF(ids.empty(), "Failed to get any display!");
+        ui::Size resolution = ui::kEmptySize;
+        // find the largest display resolution
+        for (auto id : ids) {
+            auto displayToken = surfaceComposerClient->getPhysicalDisplayToken(id);
+            ui::DisplayMode displayMode;
+            if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
+                LOG_ALWAYS_FATAL("Failed to get active display mode!");
+            }
+            auto tw = displayMode.resolution.width;
+            auto th = displayMode.resolution.height;
+            LOG_ALWAYS_FATAL_IF(tw <= 0 || th <= 0, "Invalid display size!");
+            if (resolution.width * resolution.height <
+                displayMode.resolution.width * displayMode.resolution.height) {
+                resolution = displayMode.resolution;
+            }
         }
-        auto w = displayMode.resolution.width;
-        auto h = displayMode.resolution.height;
-        LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!");
-        width = static_cast<uint32_t>(w);
-        height = static_cast<uint32_t>(h);
+        width = static_cast<uint32_t>(resolution.width);
+        height = static_cast<uint32_t>(resolution.height);
     });
     return std::pair<uint32_t, uint32_t>(width, height);
 }
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index fd4522e..e94b565 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -36,7 +36,14 @@
         return;
     }
 
-    const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    if (ids.empty()) {
+        fprintf(stderr, "Failed to get ID for any displays.\n");
+        return;
+    }
+
+    // display 0 is picked for now, can extend to support all displays if needed
+    const auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     if (displayToken == nullptr) {
         fprintf(stderr, "ERROR: no display\n");
         return;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 0972e22..d33b298 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -31,6 +31,11 @@
 
 namespace android {
 
+std::list<NotifyArgs>& operator+=(std::list<NotifyArgs>& keep, std::list<NotifyArgs>&& consume) {
+    keep.splice(keep.end(), consume);
+    return keep;
+}
+
 // --- InputListenerInterface ---
 
 // Helper to std::visit with lambdas.
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index 1f37774..b192ad7 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -225,4 +225,27 @@
         int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
       : id(id), eventTime(eventTime), request(request) {}
 
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {};
+// explicit deduction guide (not needed as of C++20)
+template <typename... V>
+Visitor(V...) -> Visitor<V...>;
+
+const char* toString(const NotifyArgs& args) {
+    Visitor toStringVisitor{
+            [&](const NotifyConfigurationChangedArgs&) { return "NotifyConfigurationChangedArgs"; },
+            [&](const NotifyKeyArgs&) { return "NotifyKeyArgs"; },
+            [&](const NotifyMotionArgs&) { return "NotifyMotionArgs"; },
+            [&](const NotifySensorArgs&) { return "NotifySensorArgs"; },
+            [&](const NotifySwitchArgs&) { return "NotifySwitchArgs"; },
+            [&](const NotifyDeviceResetArgs&) { return "NotifyDeviceResetArgs"; },
+            [&](const NotifyPointerCaptureChangedArgs&) {
+                return "NotifyPointerCaptureChangedArgs";
+            },
+            [&](const NotifyVibratorStateArgs&) { return "NotifyVibratorStateArgs"; },
+    };
+    return std::visit(toStringVisitor, args);
+}
+
 } // namespace android
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 8647bcb..1bb1968 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -25,6 +25,8 @@
 
 namespace android {
 
+std::list<NotifyArgs>& operator+=(std::list<NotifyArgs>& keep, std::list<NotifyArgs>&& consume);
+
 /*
  * The interface used by the InputReader to notify the InputListener about input events.
  */
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index 611b1aa..f28dbf3 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -213,4 +213,6 @@
                                 NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
                                 NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>;
 
+const char* toString(const NotifyArgs& args);
+
 } // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 6b9b9f1..5291776 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -65,7 +65,8 @@
     return enabled;
 }
 
-void InputDevice::setEnabled(bool enabled, nsecs_t when) {
+std::list<NotifyArgs> InputDevice::setEnabled(bool enabled, nsecs_t when) {
+    std::list<NotifyArgs> out;
     if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
         ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
               "but the corresponding viewport is not found",
@@ -74,7 +75,7 @@
     }
 
     if (isEnabled() == enabled) {
-        return;
+        return out;
     }
 
     // When resetting some devices, the driver needs to be queried to ensure that a proper reset is
@@ -82,13 +83,14 @@
     // but before disabling the device. See MultiTouchMotionAccumulator::reset for more information.
     if (enabled) {
         for_each_subdevice([](auto& context) { context.enableDevice(); });
-        reset(when);
+        out += reset(when);
     } else {
-        reset(when);
+        out += reset(when);
         for_each_subdevice([](auto& context) { context.disableDevice(); });
     }
     // Must change generation to flag this device as changed
     bumpGeneration();
+    return out;
 }
 
 void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
@@ -241,8 +243,9 @@
     mDevices.erase(eventHubId);
 }
 
-void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
-                            uint32_t changes) {
+std::list<NotifyArgs> InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                             uint32_t changes) {
+    std::list<NotifyArgs> out;
     mSources = 0;
     mClasses = ftl::Flags<InputDeviceClass>(0);
     mControllerNumber = 0;
@@ -313,7 +316,7 @@
         if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
             auto it = config->disabledDevices.find(mId);
             bool enabled = it == config->disabledDevices.end();
-            setEnabled(enabled, when);
+            out += setEnabled(enabled, when);
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
@@ -366,37 +369,42 @@
                 // For first-time configuration, only allow device to be disabled after mappers have
                 // finished configuring. This is because we need to read some of the properties from
                 // the device's open fd.
-                setEnabled(enabled, when);
+                out += setEnabled(enabled, when);
             }
         }
 
-        for_each_mapper([this, when, config, changes](InputMapper& mapper) {
-            mapper.configure(when, config, changes);
+        for_each_mapper([this, when, &config, changes, &out](InputMapper& mapper) {
+            out += mapper.configure(when, config, changes);
             mSources |= mapper.getSources();
         });
 
         // If a device is just plugged but it might be disabled, we need to update some info like
         // axis range of touch from each InputMapper first, then disable it.
         if (!changes) {
-            setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
+            out += setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(),
+                              when);
         }
     }
+    return out;
 }
 
-void InputDevice::reset(nsecs_t when) {
-    for_each_mapper([when](InputMapper& mapper) { mapper.reset(when); });
+std::list<NotifyArgs> InputDevice::reset(nsecs_t when) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.reset(when); });
 
     mContext->updateGlobalMetaState();
 
-    notifyReset(when);
+    out.push_back(notifyReset(when));
+    return out;
 }
 
-void InputDevice::process(const RawEvent* rawEvents, size_t count) {
+std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) {
     // Process all of the events in order for each mapper.
     // We cannot simply ask each mapper to process them in bulk because mappers may
     // have side-effects that must be interleaved.  For example, joystick movement events and
     // gamepad button presses are handled by different mappers but they should be dispatched
     // in the order received.
+    std::list<NotifyArgs> out;
     for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
         if (DEBUG_RAW_EVENTS) {
             ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
@@ -418,22 +426,27 @@
         } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
             ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
             mDropUntilNextSync = true;
-            reset(rawEvent->when);
+            out += reset(rawEvent->when);
         } else {
-            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
-                mapper.process(rawEvent);
+            for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
+                out += mapper.process(rawEvent);
             });
         }
         --count;
     }
+    return out;
 }
 
-void InputDevice::timeoutExpired(nsecs_t when) {
-    for_each_mapper([when](InputMapper& mapper) { mapper.timeoutExpired(when); });
+std::list<NotifyArgs> InputDevice::timeoutExpired(nsecs_t when) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.timeoutExpired(when); });
+    return out;
 }
 
-void InputDevice::updateExternalStylusState(const StylusState& state) {
-    for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); });
+std::list<NotifyArgs> InputDevice::updateExternalStylusState(const StylusState& state) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.updateExternalStylusState(state); });
+    return out;
 }
 
 InputDeviceInfo InputDevice::getDeviceInfo() {
@@ -511,14 +524,17 @@
     return *result;
 }
 
-void InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {
-    for_each_mapper([sequence, repeat, token](InputMapper& mapper) {
-        mapper.vibrate(sequence, repeat, token);
-    });
+std::list<NotifyArgs> InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat,
+                                           int32_t token) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.vibrate(sequence, repeat, token); });
+    return out;
 }
 
-void InputDevice::cancelVibrate(int32_t token) {
-    for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); });
+std::list<NotifyArgs> InputDevice::cancelVibrate(int32_t token) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.cancelVibrate(token); });
+    return out;
 }
 
 bool InputDevice::isVibrating() {
@@ -561,8 +577,10 @@
     for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); });
 }
 
-void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
-    for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); });
+std::list<NotifyArgs> InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
+    for_each_mapper([&](InputMapper& mapper) { out += mapper.cancelTouch(when, readTime); });
+    return out;
 }
 
 bool InputDevice::setLightColor(int32_t lightId, int32_t color) {
@@ -601,9 +619,8 @@
     mGeneration = mContext->bumpGeneration();
 }
 
-void InputDevice::notifyReset(nsecs_t when) {
-    NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
-    mContext->getListener().notifyDeviceReset(&args);
+NotifyDeviceResetArgs InputDevice::notifyReset(nsecs_t when) {
+    return NotifyDeviceResetArgs(mContext->getNextId(), when, mId);
 }
 
 std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 8650876..428e999 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -127,7 +127,7 @@
         mReaderIsAliveCondition.notify_all();
 
         if (!events.empty()) {
-            processEventsLocked(events.data(), events.size());
+            notifyAll(processEventsLocked(events.data(), events.size()));
         }
 
         if (mNextTimeout != LLONG_MAX) {
@@ -137,7 +137,7 @@
                     ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
                 }
                 mNextTimeout = LLONG_MAX;
-                timeoutExpiredLocked(now);
+                notifyAll(timeoutExpiredLocked(now));
             }
         }
 
@@ -162,7 +162,8 @@
     mQueuedListener.flush();
 }
 
-void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
+std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
+    std::list<NotifyArgs> out;
     for (const RawEvent* rawEvent = rawEvents; count;) {
         int32_t type = rawEvent->type;
         size_t batchSize = 1;
@@ -178,7 +179,7 @@
             if (DEBUG_RAW_EVENTS) {
                 ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
             }
-            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
+            out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
         } else {
             switch (rawEvent->type) {
                 case EventHubInterface::DEVICE_ADDED:
@@ -198,6 +199,7 @@
         count -= batchSize;
         rawEvent += batchSize;
     }
+    return out;
 }
 
 void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
@@ -208,8 +210,9 @@
 
     InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
     std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
-    device->configure(when, &mConfig, 0);
-    device->reset(when);
+
+    notifyAll(device->configure(when, &mConfig, 0));
+    notifyAll(device->reset(when));
 
     if (device->isIgnored()) {
         ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
@@ -282,10 +285,12 @@
         notifyExternalStylusPresenceChangedLocked();
     }
 
+    std::list<NotifyArgs> resetEvents;
     if (device->hasEventHubDevices()) {
-        device->configure(when, &mConfig, 0);
+        resetEvents += device->configure(when, &mConfig, 0);
     }
-    device->reset(when);
+    resetEvents += device->reset(when);
+    notifyAll(std::move(resetEvents));
 }
 
 std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
@@ -308,21 +313,22 @@
     return device;
 }
 
-void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
-                                               size_t count) {
+std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId,
+                                                                const RawEvent* rawEvents,
+                                                                size_t count) {
     auto deviceIt = mDevices.find(eventHubId);
     if (deviceIt == mDevices.end()) {
         ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
-        return;
+        return {};
     }
 
     std::shared_ptr<InputDevice>& device = deviceIt->second;
     if (device->isIgnored()) {
         // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
-        return;
+        return {};
     }
 
-    device->process(rawEvents, count);
+    return device->process(rawEvents, count);
 }
 
 InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) const {
@@ -336,13 +342,15 @@
     return nullptr;
 }
 
-void InputReader::timeoutExpiredLocked(nsecs_t when) {
+std::list<NotifyArgs> InputReader::timeoutExpiredLocked(nsecs_t when) {
+    std::list<NotifyArgs> out;
     for (auto& devicePair : mDevices) {
         std::shared_ptr<InputDevice>& device = devicePair.second;
         if (!device->isIgnored()) {
-            device->timeoutExpired(when);
+            out += device->timeoutExpired(when);
         }
     }
+    return out;
 }
 
 int32_t InputReader::nextInputDeviceIdLocked() {
@@ -377,7 +385,7 @@
     } else {
         for (auto& devicePair : mDevices) {
             std::shared_ptr<InputDevice>& device = devicePair.second;
-            device->configure(now, &mConfig, changes);
+            notifyAll(device->configure(now, &mConfig, changes));
         }
     }
 
@@ -394,6 +402,12 @@
     }
 }
 
+void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) {
+    for (const NotifyArgs& args : argsList) {
+        mQueuedListener.notify(args);
+    }
+}
+
 void InputReader::updateGlobalMetaStateLocked() {
     mGlobalMetaState = 0;
 
@@ -432,11 +446,13 @@
     }
 }
 
-void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
+std::list<NotifyArgs> InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
+    std::list<NotifyArgs> out;
     for (auto& devicePair : mDevices) {
         std::shared_ptr<InputDevice>& device = devicePair.second;
-        device->updateExternalStylusState(state);
+        out += device->updateExternalStylusState(state);
     }
+    return out;
 }
 
 void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
@@ -642,7 +658,7 @@
 
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (device) {
-        device->vibrate(sequence, repeat, token);
+        notifyAll(device->vibrate(sequence, repeat, token));
     }
 }
 
@@ -651,7 +667,7 @@
 
     InputDevice* device = findInputDeviceLocked(deviceId);
     if (device) {
-        device->cancelVibrate(token);
+        notifyAll(device->cancelVibrate(token));
     }
 }
 
@@ -1015,18 +1031,15 @@
     mReader->getExternalStylusDevicesLocked(outDevices);
 }
 
-void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
-    mReader->dispatchExternalStylusStateLocked(state);
+std::list<NotifyArgs> InputReader::ContextImpl::dispatchExternalStylusState(
+        const StylusState& state) {
+    return mReader->dispatchExternalStylusStateLocked(state);
 }
 
 InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
     return mReader->mPolicy.get();
 }
 
-InputListenerInterface& InputReader::ContextImpl::getListener() {
-    return mReader->mQueuedListener;
-}
-
 EventHubInterface* InputReader::ContextImpl::getEventHub() {
     return mReader->mEventHub.get();
 }
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 4ae9ae9..afb1bed 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -29,6 +29,7 @@
 #include "EventHub.h"
 #include "InputReaderBase.h"
 #include "InputReaderContext.h"
+#include "NotifyArgs.h"
 
 namespace android {
 
@@ -69,16 +70,18 @@
     inline bool isIgnored() { return !getMapperCount(); }
 
     bool isEnabled();
-    void setEnabled(bool enabled, nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> setEnabled(bool enabled, nsecs_t when);
 
     void dump(std::string& dump, const std::string& eventHubDevStr);
     void addEventHubDevice(int32_t eventHubId, bool populateMappers = true);
     void removeEventHubDevice(int32_t eventHubId);
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    void reset(nsecs_t when);
-    void process(const RawEvent* rawEvents, size_t count);
-    void timeoutExpired(nsecs_t when);
-    void updateExternalStylusState(const StylusState& state);
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes);
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvents, size_t count);
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
 
     InputDeviceInfo getDeviceInfo();
     int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
@@ -87,11 +90,12 @@
     int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const;
     bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
                                uint8_t* outFlags);
-    void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
-    void cancelVibrate(int32_t token);
+    [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
+                                                int32_t token);
+    [[nodiscard]] std::list<NotifyArgs> cancelVibrate(int32_t token);
     bool isVibrating();
     std::vector<int32_t> getVibratorIds();
-    void cancelTouch(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime);
     bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
                       std::chrono::microseconds maxBatchReportLatency);
     void disableSensor(InputDeviceSensorType sensorType);
@@ -109,7 +113,7 @@
 
     void bumpGeneration();
 
-    void notifyReset(nsecs_t when);
+    [[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
 
     inline const PropertyMap& getConfiguration() { return mConfiguration; }
     inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
@@ -395,7 +399,9 @@
     inline std::optional<DisplayViewport> getAssociatedViewport() const {
         return mDevice.getAssociatedViewport();
     }
-    inline void cancelTouch(nsecs_t when, nsecs_t readTime) { mDevice.cancelTouch(when, readTime); }
+    [[nodiscard]] inline std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) {
+        return mDevice.cancelTouch(when, readTime);
+    }
     inline void bumpGeneration() { mDevice.bumpGeneration(); }
     inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
 
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 012d43f..de268cf 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -142,10 +142,9 @@
         int32_t bumpGeneration() NO_THREAD_SAFETY_ANALYSIS override;
         void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices)
                 REQUIRES(mReader->mLock) override;
-        void dispatchExternalStylusState(const StylusState& outState)
+        [[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusState(const StylusState& outState)
                 REQUIRES(mReader->mLock) override;
         InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override;
-        InputListenerInterface& getListener() REQUIRES(mReader->mLock) override;
         EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override;
         int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
         void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
@@ -181,13 +180,15 @@
             mDeviceToEventHubIdsMap GUARDED_BY(mLock);
 
     // low-level input event decoding and device management
-    void processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock);
+    [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
+            REQUIRES(mLock);
 
     void addDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock);
     void removeDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock);
-    void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count)
-            REQUIRES(mLock);
-    void timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
+    [[nodiscard]] std::list<NotifyArgs> processEventsForDeviceLocked(int32_t eventHubId,
+                                                                     const RawEvent* rawEvents,
+                                                                     size_t count) REQUIRES(mLock);
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
 
     void handleConfigurationChangedLocked(nsecs_t when) REQUIRES(mLock);
 
@@ -201,7 +202,8 @@
 
     void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock);
     void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock);
-    void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock);
+    [[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusStateLocked(const StylusState& state)
+            REQUIRES(mLock);
 
     // The PointerController that is shared among all the input devices that need it.
     std::weak_ptr<PointerControllerInterface> mPointerController;
@@ -228,6 +230,8 @@
     uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
     void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
 
+    void notifyAll(std::list<NotifyArgs>&& argsList);
+
     PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
 
     // state queries
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index f2f156c..0beace1 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <input/InputDevice.h>
+#include "NotifyArgs.h"
 
 #include <vector>
 
@@ -51,10 +52,10 @@
     virtual int32_t bumpGeneration() = 0;
 
     virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0;
-    virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
+    [[nodiscard]] virtual std::list<NotifyArgs> dispatchExternalStylusState(
+            const StylusState& outState) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
-    virtual InputListenerInterface& getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 
     virtual int32_t getNextId() = 0;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index d6d324b..c691ca9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -126,9 +126,10 @@
     dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
 }
 
-void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                  uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> CursorInputMapper::configure(nsecs_t when,
+                                                   const InputReaderConfiguration* config,
+                                                   uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
         mCursorScrollAccumulator.configure(getDeviceContext());
@@ -187,8 +188,7 @@
         }
         bumpGeneration();
         if (changes) {
-            NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
-            getListener().notifyDeviceReset(&args);
+            out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
         }
     }
 
@@ -241,6 +241,7 @@
 
         bumpGeneration();
     }
+    return out;
 }
 
 void CursorInputMapper::configureParameters() {
@@ -272,7 +273,7 @@
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
 }
 
-void CursorInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> CursorInputMapper::reset(nsecs_t when) {
     mButtonState = 0;
     mDownTime = 0;
 
@@ -284,23 +285,26 @@
     mCursorMotionAccumulator.reset(getDeviceContext());
     mCursorScrollAccumulator.reset(getDeviceContext());
 
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
-void CursorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     mCursorButtonAccumulator.process(rawEvent);
     mCursorMotionAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when, rawEvent->readTime);
+        out += sync(rawEvent->when, rawEvent->readTime);
     }
+    return out;
 }
 
-void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
     if (!mDisplayId) {
         // Ignore events when there is no target display configured.
-        return;
+        return out;
     }
 
     int32_t lastButtonState = mButtonState;
@@ -391,8 +395,9 @@
     }
 
     // Synthesize key down from buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
-                         mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState);
+    out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+                                mSource, *mDisplayId, policyFlags, lastButtonState,
+                                currentButtonState);
 
     // Send motion event.
     if (downChanged || moved || scrolled || buttonsChanged) {
@@ -412,40 +417,38 @@
             while (!released.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
-                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
-                                             getDeviceId(), mSource, *mDisplayId, policyFlags,
-                                             AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
-                                             metaState, buttonState, MotionClassification::NONE,
-                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                             &pointerCoords, mXPrecision, mYPrecision,
-                                             xCursorPosition, yCursorPosition, downTime,
-                                             /* videoFrames */ {});
-                getListener().notifyMotion(&releaseArgs);
+                out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime,
+                                               getDeviceId(), mSource, *mDisplayId, policyFlags,
+                                               AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+                                               metaState, buttonState, MotionClassification::NONE,
+                                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                               &pointerCoords, mXPrecision, mYPrecision,
+                                               xCursorPosition, yCursorPosition, downTime,
+                                               /* videoFrames */ {}));
             }
         }
 
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState,
-                              currentButtonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
-                              mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
-                              /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, *mDisplayId, policyFlags, motionEventAction, 0, 0,
+                                       metaState, currentButtonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                       yCursorPosition, downTime,
+                                       /* videoFrames */ {}));
 
         if (buttonsPressed) {
             BitSet32 pressed(buttonsPressed);
             while (!pressed.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
-                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                           mSource, *mDisplayId, policyFlags,
-                                           AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
-                                           metaState, buttonState, MotionClassification::NONE,
-                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                           &pointerCoords, mXPrecision, mYPrecision,
-                                           xCursorPosition, yCursorPosition, downTime,
-                                           /* videoFrames */ {});
-                getListener().notifyMotion(&pressArgs);
+                out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime,
+                                               getDeviceId(), mSource, *mDisplayId, policyFlags,
+                                               AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
+                                               metaState, buttonState, MotionClassification::NONE,
+                                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                               &pointerCoords, mXPrecision, mYPrecision,
+                                               xCursorPosition, yCursorPosition, downTime,
+                                               /* videoFrames */ {}));
             }
         }
 
@@ -453,14 +456,14 @@
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
-            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                       mSource, *mDisplayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                                       currentButtonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
-                                       yCursorPosition, downTime, /* videoFrames */ {});
-            getListener().notifyMotion(&hoverArgs);
+            out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, *mDisplayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                                           currentButtonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                           &pointerCoords, mXPrecision, mYPrecision,
+                                           xCursorPosition, yCursorPosition, downTime,
+                                           /* videoFrames */ {}));
         }
 
         // Send scroll events.
@@ -468,23 +471,25 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                        mSource, *mDisplayId, policyFlags,
-                                        AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                        currentButtonState, MotionClassification::NONE,
-                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
-                                        yCursorPosition, downTime, /* videoFrames */ {});
-            getListener().notifyMotion(&scrollArgs);
+            out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, *mDisplayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+                                           currentButtonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                           &pointerCoords, mXPrecision, mYPrecision,
+                                           xCursorPosition, yCursorPosition, downTime,
+                                           /* videoFrames */ {}));
         }
     }
 
     // Synthesize key up from buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
-                         *mDisplayId, policyFlags, lastButtonState, currentButtonState);
+    out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(),
+                                mSource, *mDisplayId, policyFlags, lastButtonState,
+                                currentButtonState);
 
     mCursorMotionAccumulator.finishSync();
     mCursorScrollAccumulator.finishSync();
+    return out;
 }
 
 int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index a0229a7..6a4275e 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -58,10 +58,11 @@
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
 
@@ -124,7 +125,7 @@
     void configureParameters();
     void dumpParameters(std::string& dump);
 
-    void sync(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 6b5d37f..0404c9a 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -44,28 +44,32 @@
     dumpStylusState(dump, mStylusState);
 }
 
-void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                          uint32_t changes) {
+std::list<NotifyArgs> ExternalStylusInputMapper::configure(nsecs_t when,
+                                                           const InputReaderConfiguration* config,
+                                                           uint32_t changes) {
     getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
     mTouchButtonAccumulator.configure(getDeviceContext());
+    return {};
 }
 
-void ExternalStylusInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) {
     mSingleTouchMotionAccumulator.reset(getDeviceContext());
     mTouchButtonAccumulator.reset(getDeviceContext());
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
-void ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     mSingleTouchMotionAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
+        out += sync(rawEvent->when);
     }
+    return out;
 }
 
-void ExternalStylusInputMapper::sync(nsecs_t when) {
+std::list<NotifyArgs> ExternalStylusInputMapper::sync(nsecs_t when) {
     mStylusState.clear();
 
     mStylusState.when = when;
@@ -86,7 +90,7 @@
 
     mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
 
-    getContext()->dispatchExternalStylusState(mStylusState);
+    return getContext()->dispatchExternalStylusState(mStylusState);
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 03d9909..b6c9055 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -29,13 +29,14 @@
     explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext);
     virtual ~ExternalStylusInputMapper() = default;
 
-    virtual uint32_t getSources() const override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    uint32_t getSources() const override;
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void dump(std::string& dump) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
@@ -44,7 +45,7 @@
 
     StylusState mStylusState;
 
-    void sync(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 75cebf3..844afe0 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -32,12 +32,18 @@
 
 void InputMapper::dump(std::string& dump) {}
 
-void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                            uint32_t changes) {}
+std::list<NotifyArgs> InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                             uint32_t changes) {
+    return {};
+}
 
-void InputMapper::reset(nsecs_t when) {}
+std::list<NotifyArgs> InputMapper::reset(nsecs_t when) {
+    return {};
+}
 
-void InputMapper::timeoutExpired(nsecs_t when) {}
+std::list<NotifyArgs> InputMapper::timeoutExpired(nsecs_t when) {
+    return {};
+}
 
 int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
     return AKEY_STATE_UNKNOWN;
@@ -60,9 +66,14 @@
     return false;
 }
 
-void InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {}
+std::list<NotifyArgs> InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
+                                           int32_t token) {
+    return {};
+}
 
-void InputMapper::cancelVibrate(int32_t token) {}
+std::list<NotifyArgs> InputMapper::cancelVibrate(int32_t token) {
+    return {};
+}
 
 bool InputMapper::isVibrating() {
     return false;
@@ -72,7 +83,9 @@
     return {};
 }
 
-void InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {}
+std::list<NotifyArgs> InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+    return {};
+}
 
 bool InputMapper::enableSensor(InputDeviceSensorType sensorType,
                                std::chrono::microseconds samplingPeriod,
@@ -92,7 +105,9 @@
     return false;
 }
 
-void InputMapper::updateExternalStylusState(const StylusState& state) {}
+std::list<NotifyArgs> InputMapper::updateExternalStylusState(const StylusState& state) {
+    return {};
+}
 
 status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
     return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo);
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 5567cac..104305b 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -20,6 +20,7 @@
 #include "InputDevice.h"
 #include "InputListener.h"
 #include "InputReaderContext.h"
+#include "NotifyArgs.h"
 #include "StylusState.h"
 #include "VibrationElement.h"
 
@@ -48,15 +49,16 @@
     inline const std::string getDeviceName() const { return mDeviceContext.getName(); }
     inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
     inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
-    inline InputListenerInterface& getListener() { return getContext()->getListener(); }
 
     virtual uint32_t getSources() const = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
     virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent) = 0;
-    virtual void timeoutExpired(nsecs_t when);
+    [[nodiscard]] virtual std::list<NotifyArgs> configure(nsecs_t when,
+                                                          const InputReaderConfiguration* config,
+                                                          uint32_t changes);
+    [[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when);
+    [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0;
+    [[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when);
 
     virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
@@ -65,11 +67,12 @@
 
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
                                        uint8_t* outFlags);
-    virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
-    virtual void cancelVibrate(int32_t token);
+    [[nodiscard]] virtual std::list<NotifyArgs> vibrate(const VibrationSequence& sequence,
+                                                        ssize_t repeat, int32_t token);
+    [[nodiscard]] virtual std::list<NotifyArgs> cancelVibrate(int32_t token);
     virtual bool isVibrating();
     virtual std::vector<int32_t> getVibratorIds();
-    virtual void cancelTouch(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] virtual std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime);
     virtual bool enableSensor(InputDeviceSensorType sensorType,
                               std::chrono::microseconds samplingPeriod,
                               std::chrono::microseconds maxBatchReportLatency);
@@ -91,7 +94,7 @@
      */
     virtual bool updateMetaState(int32_t keyCode);
 
-    virtual void updateExternalStylusState(const StylusState& state);
+    [[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
 
     virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
     virtual void updateLedState(bool reset) {}
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 7d30d0c..42b8012 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -103,9 +103,10 @@
     }
 }
 
-void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                    uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> JoystickInputMapper::configure(nsecs_t when,
+                                                     const InputReaderConfiguration* config,
+                                                     uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
         // Collect all axes.
@@ -164,6 +165,7 @@
             it++;
         }
     }
+    return out;
 }
 
 JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo,
@@ -246,17 +248,18 @@
     }
 }
 
-void JoystickInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> JoystickInputMapper::reset(nsecs_t when) {
     // Recenter all axes.
     for (std::pair<const int32_t, Axis>& pair : mAxes) {
         Axis& axis = pair.second;
         axis.resetValue();
     }
 
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
-void JoystickInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     switch (rawEvent->type) {
         case EV_ABS: {
             auto it = mAxes.find(rawEvent->code);
@@ -298,16 +301,18 @@
         case EV_SYN:
             switch (rawEvent->code) {
                 case SYN_REPORT:
-                    sync(rawEvent->when, rawEvent->readTime, false /*force*/);
+                    out += sync(rawEvent->when, rawEvent->readTime, false /*force*/);
                     break;
             }
             break;
     }
+    return out;
 }
 
-void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
+std::list<NotifyArgs> JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
+    std::list<NotifyArgs> out;
     if (!filterAxes(force)) {
-        return;
+        return out;
     }
 
     int32_t metaState = getContext()->getGlobalMetaState();
@@ -340,13 +345,14 @@
         displayId = getDeviceContext().getAssociatedViewport()->displayId;
     }
 
-    NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
-                          AINPUT_SOURCE_JOYSTICK, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE,
-                          0, 0, metaState, buttonState, MotionClassification::NONE,
-                          AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
-    getListener().notifyMotion(&args);
+    out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                   AINPUT_SOURCE_JOYSTICK, displayId, policyFlags,
+                                   AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                   &pointerProperties, &pointerCoords, 0, 0,
+                                   AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                   AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+    return out;
 }
 
 void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index e002397..72b8a52 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -28,10 +28,11 @@
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 private:
     struct Axis {
@@ -91,7 +92,7 @@
     // Axes indexed by raw ABS_* axis index.
     std::unordered_map<int32_t, Axis> mAxes;
 
-    void sync(nsecs_t when, nsecs_t readTime, bool force);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime, bool force);
 
     bool haveAxis(int32_t axisId);
     void pruneAxes(bool ignoreExplicitlyMappedAxes);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 9bb6273..8704d1b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -143,9 +143,10 @@
     return std::nullopt;
 }
 
-void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                    uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> KeyboardInputMapper::configure(nsecs_t when,
+                                                     const InputReaderConfiguration* config,
+                                                     uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
         // Configure basic parameters.
@@ -155,6 +156,7 @@
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         mViewport = findViewport(when, config);
     }
+    return out;
 }
 
 static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
@@ -194,16 +196,18 @@
     dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat));
 }
 
-void KeyboardInputMapper::reset(nsecs_t when) {
-    cancelAllDownKeys(when);
+std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) {
+    std::list<NotifyArgs> out = cancelAllDownKeys(when);
     mCurrentHidUsage = 0;
 
     resetLedState();
 
-    InputMapper::reset(when);
+    out += InputMapper::reset(when);
+    return out;
 }
 
-void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     switch (rawEvent->type) {
         case EV_KEY: {
             int32_t scanCode = rawEvent->code;
@@ -211,8 +215,8 @@
             mCurrentHidUsage = 0;
 
             if (isKeyboardOrGamepadKey(scanCode)) {
-                processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
-                           usageCode);
+                out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
+                                  scanCode, usageCode);
             }
             break;
         }
@@ -228,6 +232,7 @@
             }
         }
     }
+    return out;
 }
 
 bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
@@ -265,8 +270,9 @@
     return false;
 }
 
-void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
-                                     int32_t usageCode) {
+std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down,
+                                                      int32_t scanCode, int32_t usageCode) {
+    std::list<NotifyArgs> out;
     int32_t keyCode;
     int32_t keyMetaState;
     uint32_t policyFlags;
@@ -295,10 +301,10 @@
             // key down
             if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
                 getContext()->shouldDropVirtualKey(when, keyCode, scanCode)) {
-                return;
+                return out;
             }
             if (policyFlags & POLICY_FLAG_GESTURE) {
-                getDeviceContext().cancelTouch(when, readTime);
+                out += getDeviceContext().cancelTouch(when, readTime);
             }
 
             KeyDown keyDown;
@@ -320,7 +326,7 @@
             ALOGI("Dropping key up from device %s because the key was not down.  "
                   "keyCode=%d, scanCode=%d",
                   getDeviceName().c_str(), keyCode, scanCode);
-            return;
+            return out;
         }
     }
 
@@ -347,11 +353,12 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                       getDisplayId(), policyFlags,
-                       down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
-    getListener().notifyKey(&args);
+    out.push_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                                getDisplayId(), policyFlags,
+                                down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+                                AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
+                                downTime));
+    return out;
 }
 
 ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
@@ -470,19 +477,20 @@
     return std::nullopt;
 }
 
-void KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) {
+std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) {
+    std::list<NotifyArgs> out;
     size_t n = mKeyDowns.size();
     for (size_t i = 0; i < n; i++) {
-        NotifyKeyArgs args(getContext()->getNextId(), when, systemTime(SYSTEM_TIME_MONOTONIC),
-                           getDeviceId(), mSource, getDisplayId(), 0 /*policyFlags*/,
-                           AKEY_EVENT_ACTION_UP,
-                           AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
-                           mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
-                           mKeyDowns[i].downTime);
-        getListener().notifyKey(&args);
+        out.push_back(NotifyKeyArgs(getContext()->getNextId(), when,
+                                    systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
+                                    getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP,
+                                    AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+                                    mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
+                                    mKeyDowns[i].downTime));
     }
     mKeyDowns.clear();
     mMetaState = AMETA_NONE;
+    return out;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 2136d25..8d72ee9 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -25,24 +25,25 @@
     KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
     virtual ~KeyboardInputMapper();
 
-    virtual uint32_t getSources() const override;
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    uint32_t getSources() const override;
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void dump(std::string& dump) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
-    virtual bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
-                                       uint8_t* outFlags) override;
-    virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
+    int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+    bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
+                               uint8_t* outFlags) override;
+    int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
 
-    virtual int32_t getMetaState() override;
-    virtual bool updateMetaState(int32_t keyCode) override;
-    virtual std::optional<int32_t> getAssociatedDisplayId() override;
-    virtual void updateLedState(bool reset);
+    int32_t getMetaState() override;
+    bool updateMetaState(int32_t keyCode) override;
+    std::optional<int32_t> getAssociatedDisplayId() override;
+    void updateLedState(bool reset) override;
 
 private:
     // The current viewport.
@@ -86,7 +87,8 @@
     bool isKeyboardOrGamepadKey(int32_t scanCode);
     bool isMediaKey(int32_t keyCode);
 
-    void processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode);
+    [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
+                                                   int32_t scanCode, int32_t usageCode);
 
     bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
 
@@ -97,7 +99,7 @@
     void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
     std::optional<DisplayViewport> findViewport(nsecs_t when,
                                                 const InputReaderConfiguration* config);
-    void cancelAllDownKeys(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 047f068..1d53eab 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -191,20 +191,21 @@
 
 MultiTouchInputMapper::~MultiTouchInputMapper() {}
 
-void MultiTouchInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
     // The evdev multi-touch protocol does not allow userspace applications to query the initial or
     // current state of the pointers at any time. This means if we clear our accumulated state when
     // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
     // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
     // rebuilding the state from scratch, we work around this kernel API limitation by never
     // fully clearing any state specific to the multi-touch protocol.
-    TouchInputMapper::reset(when);
+    return TouchInputMapper::reset(when);
 }
 
-void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
-    TouchInputMapper::process(rawEvent);
+std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
 
     mMultiTouchMotionAccumulator.process(rawEvent);
+    return out;
 }
 
 std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 75cde8e..047e62d 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -93,8 +93,8 @@
     explicit MultiTouchInputMapper(InputDeviceContext& deviceContext);
     ~MultiTouchInputMapper() override;
 
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 protected:
     void syncTouch(nsecs_t when, RawState* outState) override;
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 05973f7..29a1bda 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -60,9 +60,10 @@
                          toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
 }
 
-void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                         uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> RotaryEncoderInputMapper::configure(nsecs_t when,
+                                                          const InputReaderConfiguration* config,
+                                                          uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
     if (!changes) {
         mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
     }
@@ -75,23 +76,27 @@
             mOrientation = DISPLAY_ORIENTATION_0;
         }
     }
+    return out;
 }
 
-void RotaryEncoderInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::reset(nsecs_t when) {
     mRotaryEncoderScrollAccumulator.reset(getDeviceContext());
 
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
-void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     mRotaryEncoderScrollAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when, rawEvent->readTime);
+        out += sync(rawEvent->when, rawEvent->readTime);
     }
+    return out;
 }
 
-void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
     PointerCoords pointerCoords;
     pointerCoords.clear();
 
@@ -121,16 +126,17 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
-        NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
-                                    mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0,
-                                    0, metaState, /* buttonState */ 0, MotionClassification::NONE,
-                                    AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                    &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                    AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
-        getListener().notifyMotion(&scrollArgs);
+        out.push_back(
+                NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                                 displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+                                 metaState, /* buttonState */ 0, MotionClassification::NONE,
+                                 AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                 &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                 AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
     }
 
     mRotaryEncoderScrollAccumulator.finishSync();
+    return out;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 42e2421..f4352e7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -29,10 +29,11 @@
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     virtual void dump(std::string& dump) override;
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
-                           uint32_t changes) override;
-    virtual void reset(nsecs_t when) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 private:
     CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
@@ -41,7 +42,7 @@
     float mScalingFactor;
     int32_t mOrientation;
 
-    void sync(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 573f99c..d81022f 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -122,9 +122,10 @@
     }
 }
 
-void SensorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                  uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> SensorInputMapper::configure(nsecs_t when,
+                                                   const InputReaderConfiguration* config,
+                                                   uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
         mDeviceEnabled = true;
@@ -158,6 +159,7 @@
             }
         }
     }
+    return out;
 }
 
 SensorInputMapper::Axis SensorInputMapper::createAxis(const AxisInfo& axisInfo,
@@ -185,7 +187,7 @@
     return Axis(rawAxisInfo, axisInfo, scale, offset, min, max, flat, fuzz, resolution, filter);
 }
 
-void SensorInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> SensorInputMapper::reset(nsecs_t when) {
     // Recenter all axes.
     for (std::pair<const int32_t, Axis>& pair : mAxes) {
         Axis& axis = pair.second;
@@ -193,7 +195,7 @@
     }
     mHardwareTimestamp = 0;
     mPrevMscTime = 0;
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
 SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType,
@@ -256,7 +258,8 @@
     mPrevMscTime = static_cast<uint32_t>(mscTime);
 }
 
-void SensorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SensorInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     switch (rawEvent->type) {
         case EV_ABS: {
             auto it = mAxes.find(rawEvent->code);
@@ -274,7 +277,7 @@
                         Axis& axis = pair.second;
                         axis.currentValue = axis.newValue;
                     }
-                    sync(rawEvent->when, false /*force*/);
+                    out += sync(rawEvent->when, false /*force*/);
                     break;
             }
             break;
@@ -287,6 +290,7 @@
                     break;
             }
     }
+    return out;
 }
 
 bool SensorInputMapper::setSensorEnabled(InputDeviceSensorType sensorType, bool enabled) {
@@ -375,7 +379,8 @@
     }
 }
 
-void SensorInputMapper::sync(nsecs_t when, bool force) {
+std::list<NotifyArgs> SensorInputMapper::sync(nsecs_t when, bool force) {
+    std::list<NotifyArgs> out;
     for (auto& [sensorType, sensor] : mSensors) {
         // Skip if sensor not enabled
         if (!sensor.enabled) {
@@ -405,17 +410,17 @@
             // Convert to Android unit
             convertFromLinuxToAndroid(values, sensorType);
             // Notify dispatcher for sensor event
-            NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(),
-                                  AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy,
-                                  sensor.accuracy !=
-                                          sensor.sensorInfo.accuracy /* accuracyChanged */,
-                                  timestamp /* hwTimestamp */, values);
-
-            getListener().notifySensor(&args);
+            out.push_back(NotifySensorArgs(getContext()->getNextId(), when, getDeviceId(),
+                                           AINPUT_SOURCE_SENSOR, sensorType,
+                                           sensor.sensorInfo.accuracy,
+                                           sensor.accuracy !=
+                                                   sensor.sensorInfo.accuracy /* accuracyChanged */,
+                                           timestamp /* hwTimestamp */, values));
             sensor.lastSampleTimeNs = timestamp;
             sensor.accuracy = sensor.sensorInfo.accuracy;
         }
     }
+    return out;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 38d4c3c..457567b 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -30,9 +30,11 @@
     uint32_t getSources() const override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
     bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
                       std::chrono::microseconds maxBatchReportLatency) override;
     void disableSensor(InputDeviceSensorType sensorType) override;
@@ -116,7 +118,7 @@
     // Sensor list
     std::unordered_map<InputDeviceSensorType, Sensor> mSensors;
 
-    void sync(nsecs_t when, bool force);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, bool force);
 
     template <typename T>
     bool tryGetProperty(std::string keyName, T& outValue);
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index 4fff9be..13ad224 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -23,16 +23,17 @@
 
 SingleTouchInputMapper::~SingleTouchInputMapper() {}
 
-void SingleTouchInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> SingleTouchInputMapper::reset(nsecs_t when) {
     mSingleTouchMotionAccumulator.reset(getDeviceContext());
 
-    TouchInputMapper::reset(when);
+    return TouchInputMapper::reset(when);
 }
 
-void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
-    TouchInputMapper::process(rawEvent);
+std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
 
     mSingleTouchMotionAccumulator.process(rawEvent);
+    return out;
 }
 
 void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index f54c195..662e6bc 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -26,8 +26,8 @@
     explicit SingleTouchInputMapper(InputDeviceContext& deviceContext);
     ~SingleTouchInputMapper() override;
 
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
 protected:
     void syncTouch(nsecs_t when, RawState* outState) override;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index ebb5de6..c9101ca 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -29,7 +29,8 @@
     return AINPUT_SOURCE_SWITCH;
 }
 
-void SwitchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent* rawEvent) {
+    std::list<NotifyArgs> out;
     switch (rawEvent->type) {
         case EV_SW:
             processSwitch(rawEvent->code, rawEvent->value);
@@ -37,9 +38,10 @@
 
         case EV_SYN:
             if (rawEvent->code == SYN_REPORT) {
-                sync(rawEvent->when);
+                out += sync(rawEvent->when);
             }
     }
+    return out;
 }
 
 void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
@@ -53,15 +55,16 @@
     }
 }
 
-void SwitchInputMapper::sync(nsecs_t when) {
+std::list<NotifyArgs> SwitchInputMapper::sync(nsecs_t when) {
+    std::list<NotifyArgs> out;
     if (mUpdatedSwitchMask) {
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
-                              updatedSwitchValues, mUpdatedSwitchMask);
-        getListener().notifySwitch(&args);
+        out.push_back(NotifySwitchArgs(getContext()->getNextId(), when, 0 /*policyFlags*/,
+                                       updatedSwitchValues, mUpdatedSwitchMask));
 
         mUpdatedSwitchMask = 0;
     }
+    return out;
 }
 
 int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index e0c949f..06d6504 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -26,7 +26,7 @@
     virtual ~SwitchInputMapper();
 
     virtual uint32_t getSources() const override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
     virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
     virtual void dump(std::string& dump) override;
@@ -36,7 +36,7 @@
     uint32_t mUpdatedSwitchMask;
 
     void processSwitch(int32_t switchCode, int32_t switchValue);
-    void sync(nsecs_t when);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 42d819b..5a7ba9a 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -71,30 +71,34 @@
              AMOTION_EVENT_BUTTON_TERTIARY);
 }
 
-static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when,
-                                nsecs_t readTime, int32_t deviceId, uint32_t source,
-                                int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
-                                int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKey(
+        InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+        int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+        int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+    std::list<NotifyArgs> out;
     if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
          (currentButtonState & buttonState)) ||
         (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
          !(currentButtonState & buttonState))) {
-        NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId,
-                           policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
-        context->getListener().notifyKey(&args);
+        out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
+                                    displayId, policyFlags, action, 0, keyCode, 0,
+                                    context->getGlobalMetaState(), when));
     }
+    return out;
 }
 
-static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when,
-                                 nsecs_t readTime, int32_t deviceId, uint32_t source,
-                                 int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
-                                 int32_t currentButtonState) {
-    synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
-                        lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK,
-                        AKEYCODE_BACK);
-    synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
-                        lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD,
-                        AKEYCODE_FORWARD);
+[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKeys(
+        InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+        int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+        int32_t lastButtonState, int32_t currentButtonState) {
+    std::list<NotifyArgs> out;
+    out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+                               policyFlags, lastButtonState, currentButtonState,
+                               AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
+    out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+                               policyFlags, lastButtonState, currentButtonState,
+                               AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
+    return out;
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 5d0f188..da58efd 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -346,9 +346,10 @@
     }
 }
 
-void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
-                                 uint32_t changes) {
-    InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> TouchInputMapper::configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) {
+    std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
 
     mConfig = *config;
 
@@ -395,14 +396,14 @@
 
     if (changes && resetNeeded) {
         // If the device needs to be reset, cancel any ongoing gestures and reset the state.
-        cancelTouch(when, when);
-        reset(when);
+        out += cancelTouch(when, when);
+        out += reset(when);
 
         // Send reset, unless this is the first time the device has been configured,
         // in which case the reader will call reset itself after all mappers are ready.
-        NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
-        getListener().notifyDeviceReset(&args);
+        out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
     }
+    return out;
 }
 
 void TouchInputMapper::resolveExternalStylusPresence() {
@@ -1438,7 +1439,7 @@
                                                                  mInputDeviceOrientation);
 }
 
-void TouchInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) {
     mCursorButtonAccumulator.reset(getDeviceContext());
     mCursorScrollAccumulator.reset(getDeviceContext());
     mTouchButtonAccumulator.reset(getDeviceContext());
@@ -1469,7 +1470,7 @@
         mPointerController->clearSpots();
     }
 
-    InputMapper::reset(when);
+    return InputMapper::reset(when);
 }
 
 void TouchInputMapper::resetExternalStylus() {
@@ -1484,17 +1485,20 @@
     mExternalStylusFusionTimeout = LLONG_MAX;
 }
 
-void TouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> TouchInputMapper::process(const RawEvent* rawEvent) {
     mCursorButtonAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
+    std::list<NotifyArgs> out;
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when, rawEvent->readTime);
+        out += sync(rawEvent->when, rawEvent->readTime);
     }
+    return out;
 }
 
-void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
     if (mDeviceMode == DeviceMode::DISABLED) {
         // Only save the last pending state when the device is disabled.
         mRawStatesPending.clear();
@@ -1543,16 +1547,18 @@
               next.rawPointerData.hoveringIdBits.value);
     }
 
-    processRawTouches(false /*timeout*/);
+    out += processRawTouches(false /*timeout*/);
+    return out;
 }
 
-void TouchInputMapper::processRawTouches(bool timeout) {
+std::list<NotifyArgs> TouchInputMapper::processRawTouches(bool timeout) {
+    std::list<NotifyArgs> out;
     if (mDeviceMode == DeviceMode::DISABLED) {
         // Drop all input if the device is disabled.
-        cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
+        out += cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
         mCurrentCookedState.clear();
         updateTouchSpots();
-        return;
+        return out;
     }
 
     // Drain any pending touch states. The invariant here is that the mCurrentRawState is always
@@ -1577,7 +1583,7 @@
             mCurrentRawState.when = mLastRawState.when;
             mCurrentRawState.readTime = mLastRawState.readTime;
         }
-        cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
+        out += cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
     }
     if (count != 0) {
         mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
@@ -1591,15 +1597,17 @@
             ALOGD_IF(DEBUG_STYLUS_FUSION,
                      "Timeout expired, synthesizing event with new stylus data");
             const nsecs_t readTime = when; // consider this synthetic event to be zero latency
-            cookAndDispatch(when, readTime);
+            out += cookAndDispatch(when, readTime);
         } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
             mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
             getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
         }
     }
+    return out;
 }
 
-void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
     // Always start with a clean state.
     mCurrentCookedState.clear();
 
@@ -1625,7 +1633,9 @@
 
     // Consume raw off-screen touches before cooking pointer data.
     // If touches are consumed, subsequent code will not receive any pointer data.
-    if (consumeRawTouches(when, readTime, policyFlags)) {
+    bool consumed;
+    out += consumeRawTouches(when, readTime, policyFlags, consumed /*byref*/);
+    if (consumed) {
         mCurrentRawState.rawPointerData.clear();
     }
 
@@ -1638,9 +1648,9 @@
     applyExternalStylusTouchState(when);
 
     // Synthesize key down from raw buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
-                         mSource, mViewport.displayId, policyFlags, mLastCookedState.buttonState,
-                         mCurrentCookedState.buttonState);
+    out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+                                mSource, mViewport.displayId, policyFlags,
+                                mLastCookedState.buttonState, mCurrentCookedState.buttonState);
 
     // Dispatch the touches either directly or by translation through a pointer on screen.
     if (mDeviceMode == DeviceMode::POINTER) {
@@ -1682,15 +1692,15 @@
             pointerUsage = PointerUsage::GESTURES;
         }
 
-        dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
+        out += dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
     } else {
         if (!mCurrentMotionAborted) {
             updateTouchSpots();
-            dispatchButtonRelease(when, readTime, policyFlags);
-            dispatchHoverExit(when, readTime, policyFlags);
-            dispatchTouches(when, readTime, policyFlags);
-            dispatchHoverEnterAndMove(when, readTime, policyFlags);
-            dispatchButtonPress(when, readTime, policyFlags);
+            out += dispatchButtonRelease(when, readTime, policyFlags);
+            out += dispatchHoverExit(when, readTime, policyFlags);
+            out += dispatchTouches(when, readTime, policyFlags);
+            out += dispatchHoverEnterAndMove(when, readTime, policyFlags);
+            out += dispatchButtonPress(when, readTime, policyFlags);
         }
 
         if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
@@ -1699,9 +1709,9 @@
     }
 
     // Synthesize key up from raw buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
-                         mViewport.displayId, policyFlags, mLastCookedState.buttonState,
-                         mCurrentCookedState.buttonState);
+    out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(),
+                                mSource, mViewport.displayId, policyFlags,
+                                mLastCookedState.buttonState, mCurrentCookedState.buttonState);
 
     // Clear some transient state.
     mCurrentRawState.rawVScroll = 0;
@@ -1710,6 +1720,7 @@
     // Copy current touch to last touch in preparation for the next cycle.
     mLastRawState.copyFrom(mCurrentRawState);
     mLastCookedState.copyFrom(mCurrentCookedState);
+    return out;
 }
 
 void TouchInputMapper::updateTouchSpots() {
@@ -1801,34 +1812,41 @@
     return false;
 }
 
-void TouchInputMapper::timeoutExpired(nsecs_t when) {
+std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) {
+    std::list<NotifyArgs> out;
     if (mDeviceMode == DeviceMode::POINTER) {
         if (mPointerUsage == PointerUsage::GESTURES) {
             // Since this is a synthetic event, we can consider its latency to be zero
             const nsecs_t readTime = when;
-            dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
+            out += dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
         }
     } else if (mDeviceMode == DeviceMode::DIRECT) {
         if (mExternalStylusFusionTimeout < when) {
-            processRawTouches(true /*timeout*/);
+            out += processRawTouches(true /*timeout*/);
         } else if (mExternalStylusFusionTimeout != LLONG_MAX) {
             getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
         }
     }
+    return out;
 }
 
-void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
+std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) {
+    std::list<NotifyArgs> out;
     mExternalStylusState.copyFrom(state);
     if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
         // We're either in the middle of a fused stream of data or we're waiting on data before
         // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
         // data.
         mExternalStylusDataPending = true;
-        processRawTouches(false /*timeout*/);
+        out += processRawTouches(false /*timeout*/);
     }
+    return out;
 }
 
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags, bool& outConsumed) {
+    outConsumed = false;
+    std::list<NotifyArgs> out;
     // Check for release of a virtual key.
     if (mCurrentVirtualKey.down) {
         if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
@@ -1838,10 +1856,12 @@
                 ALOGD_IF(DEBUG_VIRTUAL_KEYS,
                          "VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
                          mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-                dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
-                                   AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+                out.push_back(dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
+                                                 AKEY_EVENT_FLAG_FROM_SYSTEM |
+                                                         AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY));
             }
-            return true;
+            outConsumed = true;
+            return out;
         }
 
         if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1851,7 +1871,8 @@
             const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
             if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
                 // Pointer is still within the space of the virtual key.
-                return true;
+                outConsumed = true;
+                return out;
             }
         }
 
@@ -1863,9 +1884,10 @@
         if (!mCurrentVirtualKey.ignored) {
             ALOGD_IF(DEBUG_VIRTUAL_KEYS, "VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
                      mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-            dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
-                               AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
-                                       AKEY_EVENT_FLAG_CANCELED);
+            out.push_back(dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
+                                             AKEY_EVENT_FLAG_FROM_SYSTEM |
+                                                     AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
+                                                     AKEY_EVENT_FLAG_CANCELED));
         }
     }
 
@@ -1895,13 +1917,15 @@
                         ALOGD_IF(DEBUG_VIRTUAL_KEYS,
                                  "VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
                                  mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-                        dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN,
-                                           AKEY_EVENT_FLAG_FROM_SYSTEM |
-                                                   AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+                        out.push_back(dispatchVirtualKey(when, readTime, policyFlags,
+                                                         AKEY_EVENT_ACTION_DOWN,
+                                                         AKEY_EVENT_FLAG_FROM_SYSTEM |
+                                                                 AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY));
                     }
                 }
             }
-            return true;
+            outConsumed = true;
+            return out;
         }
     }
 
@@ -1923,44 +1947,50 @@
         !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
         getContext()->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
     }
-    return false;
+    return out;
 }
 
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                          int32_t keyEventAction, int32_t keyEventFlags) {
+NotifyKeyArgs TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime,
+                                                   uint32_t policyFlags, int32_t keyEventAction,
+                                                   int32_t keyEventFlags) {
     int32_t keyCode = mCurrentVirtualKey.keyCode;
     int32_t scanCode = mCurrentVirtualKey.scanCode;
     nsecs_t downTime = mCurrentVirtualKey.downTime;
     int32_t metaState = getContext()->getGlobalMetaState();
     policyFlags |= POLICY_FLAG_VIRTUAL;
 
-    NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
-                       AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
-                       keyEventFlags, keyCode, scanCode, metaState, downTime);
-    getListener().notifyKey(&args);
+    return NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                         AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
+                         keyEventFlags, keyCode, scanCode, metaState, downTime);
 }
 
-void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime,
+                                                     uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     if (mCurrentMotionAborted) {
         // Current motion event was already aborted.
-        return;
+        return out;
     }
     BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
     if (!currentIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = mCurrentCookedState.buttonState;
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
-                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mCurrentCookedState.cookedPointerData.pointerProperties,
-                       mCurrentCookedState.cookedPointerData.pointerCoords,
-                       mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                       MotionClassification::NONE);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState,
+                                     AMOTION_EVENT_EDGE_FLAG_NONE,
+                                     mCurrentCookedState.cookedPointerData.pointerProperties,
+                                     mCurrentCookedState.cookedPointerData.pointerCoords,
+                                     mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits,
+                                     -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
         mCurrentMotionAborted = true;
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime,
+                                                        uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
     BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
     int32_t metaState = getContext()->getGlobalMetaState();
@@ -1970,13 +2000,14 @@
         if (!currentIdBits.isEmpty()) {
             // No pointer id changes so this is a move event.
             // The listener takes care of batching moves so we don't have to deal with that here.
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
-                           metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
-                           mCurrentCookedState.cookedPointerData.pointerCoords,
-                           mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                           MotionClassification::NONE);
+            out.push_back(
+                    dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE,
+                                   0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                   mCurrentCookedState.cookedPointerData.pointerProperties,
+                                   mCurrentCookedState.cookedPointerData.pointerCoords,
+                                   mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits,
+                                   -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                   MotionClassification::NONE));
         }
     } else {
         // There may be pointers going up and pointers going down and pointers moving
@@ -2006,13 +2037,16 @@
             if (isCanceled) {
                 ALOGI("Canceling pointer %d for the palm event was detected.", upId);
             }
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
-                           isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
-                           mLastCookedState.cookedPointerData.pointerProperties,
-                           mLastCookedState.cookedPointerData.pointerCoords,
-                           mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                           MotionClassification::NONE);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_POINTER_UP, 0,
+                                         isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState,
+                                         buttonState, 0,
+                                         mLastCookedState.cookedPointerData.pointerProperties,
+                                         mLastCookedState.cookedPointerData.pointerCoords,
+                                         mLastCookedState.cookedPointerData.idToIndex,
+                                         dispatchedIdBits, upId, mOrientedXPrecision,
+                                         mOrientedYPrecision, mDownTime,
+                                         MotionClassification::NONE));
             dispatchedIdBits.clearBit(upId);
             mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
         }
@@ -2022,13 +2056,14 @@
         // events, they do not generally handle them except when presented in a move event.
         if (moveNeeded && !moveIdBits.isEmpty()) {
             ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
-                           metaState, buttonState, 0,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
-                           mCurrentCookedState.cookedPointerData.pointerCoords,
-                           mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                           MotionClassification::NONE);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,
+                                         mCurrentCookedState.cookedPointerData.pointerProperties,
+                                         mCurrentCookedState.cookedPointerData.pointerCoords,
+                                         mCurrentCookedState.cookedPointerData.idToIndex,
+                                         dispatchedIdBits, -1, mOrientedXPrecision,
+                                         mOrientedYPrecision, mDownTime,
+                                         MotionClassification::NONE));
         }
 
         // Dispatch pointer down events using the new pointer locations.
@@ -2041,62 +2076,75 @@
                 mDownTime = when;
             }
 
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
-                           0, 0, metaState, buttonState, 0,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
-                           mCurrentCookedState.cookedPointerData.pointerCoords,
-                           mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
-                           downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                           MotionClassification::NONE);
+            out.push_back(
+                    dispatchMotion(when, readTime, policyFlags, mSource,
+                                   AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState,
+                                   0, mCurrentCookedState.cookedPointerData.pointerProperties,
+                                   mCurrentCookedState.cookedPointerData.pointerCoords,
+                                   mCurrentCookedState.cookedPointerData.idToIndex,
+                                   dispatchedIdBits, downId, mOrientedXPrecision,
+                                   mOrientedYPrecision, mDownTime, MotionClassification::NONE));
         }
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     if (mSentHoverEnter &&
         (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() ||
          !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
         int32_t metaState = getContext()->getGlobalMetaState();
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
-                       metaState, mLastCookedState.buttonState, 0,
-                       mLastCookedState.cookedPointerData.pointerProperties,
-                       mLastCookedState.cookedPointerData.pointerCoords,
-                       mLastCookedState.cookedPointerData.idToIndex,
-                       mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision,
-                       mOrientedYPrecision, mDownTime, MotionClassification::NONE);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+                                     mLastCookedState.buttonState, 0,
+                                     mLastCookedState.cookedPointerData.pointerProperties,
+                                     mLastCookedState.cookedPointerData.pointerCoords,
+                                     mLastCookedState.cookedPointerData.idToIndex,
+                                     mLastCookedState.cookedPointerData.hoveringIdBits, -1,
+                                     mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
         mSentHoverEnter = false;
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
-                                                 uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+                                                                  uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() &&
         !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         if (!mSentHoverEnter) {
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
-                           0, 0, metaState, mCurrentRawState.buttonState, 0,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
-                           mCurrentCookedState.cookedPointerData.pointerCoords,
-                           mCurrentCookedState.cookedPointerData.idToIndex,
-                           mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                           MotionClassification::NONE);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
+                                         mCurrentRawState.buttonState, 0,
+                                         mCurrentCookedState.cookedPointerData.pointerProperties,
+                                         mCurrentCookedState.cookedPointerData.pointerCoords,
+                                         mCurrentCookedState.cookedPointerData.idToIndex,
+                                         mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+                                         mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                         MotionClassification::NONE));
             mSentHoverEnter = true;
         }
 
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                       metaState, mCurrentRawState.buttonState, 0,
-                       mCurrentCookedState.cookedPointerData.pointerProperties,
-                       mCurrentCookedState.cookedPointerData.pointerCoords,
-                       mCurrentCookedState.cookedPointerData.idToIndex,
-                       mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                       MotionClassification::NONE);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                                     mCurrentRawState.buttonState, 0,
+                                     mCurrentCookedState.cookedPointerData.pointerProperties,
+                                     mCurrentCookedState.cookedPointerData.pointerCoords,
+                                     mCurrentCookedState.cookedPointerData.idToIndex,
+                                     mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+                                     mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
     const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
     const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2104,17 +2152,21 @@
     while (!releasedButtons.isEmpty()) {
         int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
         buttonState &= ~actionButton;
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
-                       actionButton, 0, metaState, buttonState, 0,
-                       mCurrentCookedState.cookedPointerData.pointerProperties,
-                       mCurrentCookedState.cookedPointerData.pointerCoords,
-                       mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                       MotionClassification::NONE);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+                                     metaState, buttonState, 0,
+                                     mCurrentCookedState.cookedPointerData.pointerProperties,
+                                     mCurrentCookedState.cookedPointerData.pointerCoords,
+                                     mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+                                     mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
     }
+    return out;
 }
 
-void TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime,
+                                                            uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
     const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
     const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2122,14 +2174,16 @@
     while (!pressedButtons.isEmpty()) {
         int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
         buttonState |= actionButton;
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS,
-                       actionButton, 0, metaState, buttonState, 0,
-                       mCurrentCookedState.cookedPointerData.pointerProperties,
-                       mCurrentCookedState.cookedPointerData.pointerCoords,
-                       mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
-                       MotionClassification::NONE);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState,
+                                     buttonState, 0,
+                                     mCurrentCookedState.cookedPointerData.pointerProperties,
+                                     mCurrentCookedState.cookedPointerData.pointerCoords,
+                                     mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+                                     mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                                     MotionClassification::NONE));
     }
+    return out;
 }
 
 const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) {
@@ -2411,54 +2465,62 @@
     }
 }
 
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                            PointerUsage pointerUsage) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags,
+                                                             PointerUsage pointerUsage) {
+    std::list<NotifyArgs> out;
     if (pointerUsage != mPointerUsage) {
-        abortPointerUsage(when, readTime, policyFlags);
+        out += abortPointerUsage(when, readTime, policyFlags);
         mPointerUsage = pointerUsage;
     }
 
     switch (mPointerUsage) {
         case PointerUsage::GESTURES:
-            dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
+            out += dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
             break;
         case PointerUsage::STYLUS:
-            dispatchPointerStylus(when, readTime, policyFlags);
+            out += dispatchPointerStylus(when, readTime, policyFlags);
             break;
         case PointerUsage::MOUSE:
-            dispatchPointerMouse(when, readTime, policyFlags);
+            out += dispatchPointerMouse(when, readTime, policyFlags);
             break;
         case PointerUsage::NONE:
             break;
     }
+    return out;
 }
 
-void TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags) {
+    std::list<NotifyArgs> out;
     switch (mPointerUsage) {
         case PointerUsage::GESTURES:
-            abortPointerGestures(when, readTime, policyFlags);
+            out += abortPointerGestures(when, readTime, policyFlags);
             break;
         case PointerUsage::STYLUS:
-            abortPointerStylus(when, readTime, policyFlags);
+            out += abortPointerStylus(when, readTime, policyFlags);
             break;
         case PointerUsage::MOUSE:
-            abortPointerMouse(when, readTime, policyFlags);
+            out += abortPointerMouse(when, readTime, policyFlags);
             break;
         case PointerUsage::NONE:
             break;
     }
 
     mPointerUsage = PointerUsage::NONE;
+    return out;
 }
 
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                               bool isTimeout) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime,
+                                                                uint32_t policyFlags,
+                                                                bool isTimeout) {
+    std::list<NotifyArgs> out;
     // Update current gesture coordinates.
     bool cancelPreviousGesture, finishPreviousGesture;
     bool sendEvents =
             preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
     if (!sendEvents) {
-        return;
+        return {};
     }
     if (finishPreviousGesture) {
         cancelPreviousGesture = false;
@@ -2555,11 +2617,14 @@
     BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
     if (!dispatchedGestureIdBits.isEmpty()) {
         if (cancelPreviousGesture) {
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0,
-                           flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                           mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
-                           mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                           mPointerGesture.downTime, classification);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_CANCEL, 0, flags, metaState,
+                                         buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                         mPointerGesture.lastGestureProperties,
+                                         mPointerGesture.lastGestureCoords,
+                                         mPointerGesture.lastGestureIdToIndex,
+                                         dispatchedGestureIdBits, -1, 0, 0,
+                                         mPointerGesture.downTime, classification));
 
             dispatchedGestureIdBits.clear();
         } else {
@@ -2573,12 +2638,14 @@
             while (!upGestureIdBits.isEmpty()) {
                 uint32_t id = upGestureIdBits.clearFirstMarkedBit();
 
-                dispatchMotion(when, readTime, policyFlags, mSource,
-                               AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState,
-                               AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
-                               mPointerGesture.lastGestureCoords,
-                               mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
-                               0, mPointerGesture.downTime, classification);
+                out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                             AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState,
+                                             buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                             mPointerGesture.lastGestureProperties,
+                                             mPointerGesture.lastGestureCoords,
+                                             mPointerGesture.lastGestureIdToIndex,
+                                             dispatchedGestureIdBits, id, 0, 0,
+                                             mPointerGesture.downTime, classification));
 
                 dispatchedGestureIdBits.clearBit(id);
             }
@@ -2587,12 +2654,13 @@
 
     // Send motion events for all pointers that moved.
     if (moveNeeded) {
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags,
-                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mPointerGesture.currentGestureProperties,
-                       mPointerGesture.currentGestureCoords,
-                       mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                       mPointerGesture.downTime, classification);
+        out.push_back(
+                dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0,
+                               flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                               mPointerGesture.currentGestureProperties,
+                               mPointerGesture.currentGestureCoords,
+                               mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1,
+                               0, 0, mPointerGesture.downTime, classification));
     }
 
     // Send motion events for all pointers that went down.
@@ -2607,24 +2675,26 @@
                 mPointerGesture.downTime = when;
             }
 
-            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
-                           0, flags, metaState, buttonState, 0,
-                           mPointerGesture.currentGestureProperties,
-                           mPointerGesture.currentGestureCoords,
-                           mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
-                           0, mPointerGesture.downTime, classification);
+            out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                         AMOTION_EVENT_ACTION_POINTER_DOWN, 0, flags, metaState,
+                                         buttonState, 0, mPointerGesture.currentGestureProperties,
+                                         mPointerGesture.currentGestureCoords,
+                                         mPointerGesture.currentGestureIdToIndex,
+                                         dispatchedGestureIdBits, id, 0, 0,
+                                         mPointerGesture.downTime, classification));
         }
     }
 
     // Send motion events for hover.
     if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
-                       flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mPointerGesture.currentGestureProperties,
-                       mPointerGesture.currentGestureCoords,
-                       mPointerGesture.currentGestureIdToIndex,
-                       mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime,
-                       MotionClassification::NONE);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState,
+                                     buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                     mPointerGesture.currentGestureProperties,
+                                     mPointerGesture.currentGestureCoords,
+                                     mPointerGesture.currentGestureIdToIndex,
+                                     mPointerGesture.currentGestureIdBits, -1, 0, 0,
+                                     mPointerGesture.downTime, MotionClassification::NONE));
     } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) {
         // Synthesize a hover move event after all pointers go up to indicate that
         // the pointer is hovering again even if the user is not currently touching
@@ -2644,12 +2714,13 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
         const int32_t displayId = mPointerController->getDisplayId();
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags,
-                              metaState, buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
-                              0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState,
+                                       buttonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                       &pointerCoords, 0, 0, x, y, mPointerGesture.downTime,
+                                       /* videoFrames */ {}));
     }
 
     // Update state.
@@ -2668,22 +2739,28 @@
             mPointerGesture.lastGestureIdToIndex[id] = index;
         }
     }
+    return out;
 }
 
-void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags) {
     const MotionClassification classification =
             mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE
             ? MotionClassification::TWO_FINGER_SWIPE
             : MotionClassification::NONE;
+    std::list<NotifyArgs> out;
     // Cancel previously dispatches pointers.
     if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = mCurrentRawState.buttonState;
-        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
-                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
-                       mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
-                       0, 0, mPointerGesture.downTime, classification);
+        out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+                                     AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState,
+                                     AMOTION_EVENT_EDGE_FLAG_NONE,
+                                     mPointerGesture.lastGestureProperties,
+                                     mPointerGesture.lastGestureCoords,
+                                     mPointerGesture.lastGestureIdToIndex,
+                                     mPointerGesture.lastGestureIdBits, -1, 0, 0,
+                                     mPointerGesture.downTime, classification));
     }
 
     // Reset the current pointer gesture.
@@ -2695,6 +2772,7 @@
         mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
         mPointerController->clearSpots();
     }
+    return out;
 }
 
 bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
@@ -3411,7 +3489,8 @@
     mPointerController->move(deltaX, deltaY);
 }
 
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
@@ -3440,14 +3519,16 @@
         hovering = false;
     }
 
-    dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
+    return dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
 }
 
-void TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
-    abortPointerSimple(when, readTime, policyFlags);
+std::list<NotifyArgs> TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime,
+                                                           uint32_t policyFlags) {
+    return abortPointerSimple(when, readTime, policyFlags);
 }
 
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
@@ -3482,17 +3563,22 @@
         hovering = false;
     }
 
-    dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
+    return dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
 }
 
-void TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
-    abortPointerSimple(when, readTime, policyFlags);
+std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags) {
+    std::list<NotifyArgs> out = abortPointerSimple(when, readTime, policyFlags);
 
     mPointerVelocityControl.reset();
+
+    return out;
 }
 
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                             bool down, bool hovering) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags, bool down,
+                                                              bool hovering) {
+    std::list<NotifyArgs> out;
     int32_t metaState = getContext()->getGlobalMetaState();
 
     if (down || hovering) {
@@ -3512,28 +3598,29 @@
         mPointerSimple.down = false;
 
         // Send up.
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
-                              mLastRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0,
+                                       0, metaState, mLastRawState.buttonState,
+                                       MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                       yCursorPosition, mPointerSimple.downTime,
+                                       /* videoFrames */ {}));
     }
 
     if (mPointerSimple.hovering && !hovering) {
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
-                              metaState, mLastRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+                                       mLastRawState.buttonState, MotionClassification::NONE,
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                       yCursorPosition, mPointerSimple.downTime,
+                                       /* videoFrames */ {}));
     }
 
     if (down) {
@@ -3542,25 +3629,26 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                                  displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
-                                  metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener().notifyMotion(&args);
+            out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, displayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState,
+                                           mCurrentRawState.buttonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                           &mPointerSimple.currentProperties,
+                                           &mPointerSimple.currentCoords, mOrientedXPrecision,
+                                           mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                           mPointerSimple.downTime, /* videoFrames */ {}));
         }
 
         // Send move.
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                              mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &mPointerSimple.currentCoords, mOrientedXPrecision,
-                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE,
+                                       0, 0, metaState, mCurrentRawState.buttonState,
+                                       MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.currentProperties,
+                                       &mPointerSimple.currentCoords, mOrientedXPrecision,
+                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                       mPointerSimple.downTime, /* videoFrames */ {}));
     }
 
     if (hovering) {
@@ -3568,25 +3656,26 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                                  displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
-                                  metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
-            getListener().notifyMotion(&args);
+            out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, displayId, policyFlags,
+                                           AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
+                                           mCurrentRawState.buttonState, MotionClassification::NONE,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                           &mPointerSimple.currentProperties,
+                                           &mPointerSimple.currentCoords, mOrientedXPrecision,
+                                           mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                                           mPointerSimple.downTime, /* videoFrames */ {}));
         }
 
         // Send hover move.
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                              metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &mPointerSimple.currentCoords, mOrientedXPrecision,
-                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(
+                NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                                 displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+                                 metaState, mCurrentRawState.buttonState,
+                                 MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                 &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                 mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                 yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}));
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3601,14 +3690,14 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                              mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
-                              &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
-                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
-        getListener().notifyMotion(&args);
+        out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL,
+                                       0, 0, metaState, mCurrentRawState.buttonState,
+                                       MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                       &mPointerSimple.currentProperties, &pointerCoords,
+                                       mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                       yCursorPosition, mPointerSimple.downTime,
+                                       /* videoFrames */ {}));
     }
 
     // Save state.
@@ -3618,23 +3707,23 @@
     } else {
         mPointerSimple.reset();
     }
+    return out;
 }
 
-void TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime,
+                                                           uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
-    dispatchPointerSimple(when, readTime, policyFlags, false, false);
+    return dispatchPointerSimple(when, readTime, policyFlags, false, false);
 }
 
-void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                      uint32_t source, int32_t action, int32_t actionButton,
-                                      int32_t flags, int32_t metaState, int32_t buttonState,
-                                      int32_t edgeFlags, const PointerProperties* properties,
-                                      const PointerCoords* coords, const uint32_t* idToIndex,
-                                      BitSet32 idBits, int32_t changedId, float xPrecision,
-                                      float yPrecision, nsecs_t downTime,
-                                      MotionClassification classification) {
+NotifyMotionArgs TouchInputMapper::dispatchMotion(
+        nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
+        int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+        int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords,
+        const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
+        float yPrecision, nsecs_t downTime, MotionClassification classification) {
     PointerCoords pointerCoords[MAX_POINTERS];
     PointerProperties pointerProperties[MAX_POINTERS];
     uint32_t pointerCount = 0;
@@ -3680,12 +3769,11 @@
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
-    NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
-                          policyFlags, action, actionButton, flags, metaState, buttonState,
-                          classification, edgeFlags, pointerCount, pointerProperties, pointerCoords,
-                          xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
-                          std::move(frames));
-    getListener().notifyMotion(&args);
+    return NotifyMotionArgs(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
+                            policyFlags, action, actionButton, flags, metaState, buttonState,
+                            classification, edgeFlags, pointerCount, pointerProperties,
+                            pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+                            downTime, std::move(frames));
 }
 
 bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
@@ -3718,9 +3806,11 @@
     return changed;
 }
 
-void TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
-    abortPointerUsage(when, readTime, 0 /*policyFlags*/);
-    abortTouches(when, readTime, 0 /* policyFlags*/);
+std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+    std::list<NotifyArgs> out;
+    out += abortPointerUsage(when, readTime, 0 /*policyFlags*/);
+    out += abortTouches(when, readTime, 0 /* policyFlags*/);
+    return out;
 }
 
 // Transform input device coordinates to display panel coordinates.
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 76fdf5d..50f30c8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -140,18 +140,21 @@
     uint32_t getSources() const override;
     void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
     void dump(std::string& dump) override;
-    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
-    void reset(nsecs_t when) override;
-    void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+                                                  const InputReaderConfiguration* config,
+                                                  uint32_t changes) override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
     int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
     int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
     bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
                                uint8_t* outFlags) override;
 
-    void cancelTouch(nsecs_t when, nsecs_t readTime) override;
-    void timeoutExpired(nsecs_t when) override;
-    void updateExternalStylusState(const StylusState& state) override;
+    [[nodiscard]] std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) override;
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(
+            const StylusState& state) override;
     std::optional<int32_t> getAssociatedDisplayId() override;
 
 protected:
@@ -728,30 +731,42 @@
     void initializeOrientedRanges();
     void initializeSizeRanges();
 
-    void sync(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
 
-    bool consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void processRawTouches(bool timeout);
-    void cookAndDispatch(nsecs_t when, nsecs_t readTime);
-    void dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                            int32_t keyEventAction, int32_t keyEventFlags);
+    [[nodiscard]] std::list<NotifyArgs> consumeRawTouches(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags, bool& outConsumed);
+    [[nodiscard]] std::list<NotifyArgs> processRawTouches(bool timeout);
+    [[nodiscard]] std::list<NotifyArgs> cookAndDispatch(nsecs_t when, nsecs_t readTime);
+    [[nodiscard]] NotifyKeyArgs dispatchVirtualKey(nsecs_t when, nsecs_t readTime,
+                                                   uint32_t policyFlags, int32_t keyEventAction,
+                                                   int32_t keyEventFlags);
 
-    void dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchTouches(nsecs_t when, nsecs_t readTime,
+                                                        uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchHoverExit(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+                                                                  uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchButtonRelease(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchButtonPress(nsecs_t when, nsecs_t readTime,
+                                                            uint32_t policyFlags);
     const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
     void cookPointerData();
-    void abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> abortTouches(nsecs_t when, nsecs_t readTime,
+                                                     uint32_t policyFlags);
 
-    void dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                              PointerUsage pointerUsage);
-    void abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerUsage(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags,
+                                                             PointerUsage pointerUsage);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerUsage(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags);
 
-    void dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
-                                 bool isTimeout);
-    void abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerGestures(nsecs_t when, nsecs_t readTime,
+                                                                uint32_t policyFlags,
+                                                                bool isTimeout);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerGestures(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags);
     bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
                                 bool* outFinishPreviousGesture, bool isTimeout);
 
@@ -759,15 +774,21 @@
     // between the last and current events. Uses a relative motion.
     void moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId);
 
-    void dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerStylus(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerStylus(nsecs_t when, nsecs_t readTime,
+                                                           uint32_t policyFlags);
 
-    void dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
-    void abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerMouse(nsecs_t when, nsecs_t readTime,
+                                                             uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerMouse(nsecs_t when, nsecs_t readTime,
+                                                          uint32_t policyFlags);
 
-    void dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, bool down,
-                               bool hovering);
-    void abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    [[nodiscard]] std::list<NotifyArgs> dispatchPointerSimple(nsecs_t when, nsecs_t readTime,
+                                                              uint32_t policyFlags, bool down,
+                                                              bool hovering);
+    [[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime,
+                                                           uint32_t policyFlags);
 
     bool assignExternalStylusId(const RawState& state, bool timeout);
     void applyExternalStylusButtonState(nsecs_t when);
@@ -777,12 +798,12 @@
     // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
     // method will take care of setting the index and transmuting the action to DOWN or UP
     // it is the first / last pointer to go down / up.
-    void dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source,
-                        int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
-                        int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties,
-                        const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
-                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime,
-                        MotionClassification classification);
+    [[nodiscard]] NotifyMotionArgs dispatchMotion(
+            nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
+            int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+            int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords,
+            const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
+            float yPrecision, nsecs_t downTime, MotionClassification classification);
 
     // Updates pointer coords and properties for pointers with specified ids that have moved.
     // Returns true if any of them changed.
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 33db527..7645b12 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -35,16 +35,18 @@
     info->setVibrator(true);
 }
 
-void VibratorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent* rawEvent) {
     // TODO: Handle FF_STATUS, although it does not seem to be widely supported.
+    return {};
 }
 
-void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
-                                  int32_t token) {
+std::list<NotifyArgs> VibratorInputMapper::vibrate(const VibrationSequence& sequence,
+                                                   ssize_t repeat, int32_t token) {
     if (DEBUG_VIBRATOR) {
         ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
               sequence.toString().c_str(), repeat, token);
     }
+    std::list<NotifyArgs> out;
 
     mVibrating = true;
     mSequence = sequence;
@@ -53,19 +55,22 @@
     mIndex = -1;
 
     // Request InputReader to notify InputManagerService for vibration started.
-    NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true);
-    getListener().notifyVibratorState(&args);
-    nextStep();
+    out.push_back(
+            NotifyVibratorStateArgs(getContext()->getNextId(), systemTime(), getDeviceId(), true));
+    out += nextStep();
+    return out;
 }
 
-void VibratorInputMapper::cancelVibrate(int32_t token) {
+std::list<NotifyArgs> VibratorInputMapper::cancelVibrate(int32_t token) {
     if (DEBUG_VIBRATOR) {
         ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
     }
+    std::list<NotifyArgs> out;
 
     if (mVibrating && mToken == token) {
-        stopVibrating();
+        out.push_back(stopVibrating());
     }
+    return out;
 }
 
 bool VibratorInputMapper::isVibrating() {
@@ -76,26 +81,29 @@
     return getDeviceContext().getVibratorIds();
 }
 
-void VibratorInputMapper::timeoutExpired(nsecs_t when) {
+std::list<NotifyArgs> VibratorInputMapper::timeoutExpired(nsecs_t when) {
+    std::list<NotifyArgs> out;
     if (mVibrating) {
         if (when >= mNextStepTime) {
-            nextStep();
+            out += nextStep();
         } else {
             getContext()->requestTimeoutAtTime(mNextStepTime);
         }
     }
+    return out;
 }
 
-void VibratorInputMapper::nextStep() {
+std::list<NotifyArgs> VibratorInputMapper::nextStep() {
     if (DEBUG_VIBRATOR) {
         ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
     }
+    std::list<NotifyArgs> out;
     mIndex += 1;
     if (size_t(mIndex) >= mSequence.pattern.size()) {
         if (mRepeat < 0) {
             // We are done.
-            stopVibrating();
-            return;
+            out.push_back(stopVibrating());
+            return out;
         }
         mIndex = mRepeat;
     }
@@ -122,9 +130,10 @@
     if (DEBUG_VIBRATOR) {
         ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
     }
+    return out;
 }
 
-void VibratorInputMapper::stopVibrating() {
+NotifyVibratorStateArgs VibratorInputMapper::stopVibrating() {
     mVibrating = false;
     if (DEBUG_VIBRATOR) {
         ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
@@ -132,8 +141,7 @@
     getDeviceContext().cancelVibrate();
 
     // Request InputReader to notify InputManagerService for vibration complete.
-    NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false);
-    getListener().notifyVibratorState(&args);
+    return NotifyVibratorStateArgs(getContext()->getNextId(), systemTime(), getDeviceId(), false);
 }
 
 void VibratorInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 894c573..e98f63a 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -27,13 +27,14 @@
 
     virtual uint32_t getSources() const override;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
-    virtual void process(const RawEvent* rawEvent) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
 
-    virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) override;
-    virtual void cancelVibrate(int32_t token) override;
+    [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
+                                                int32_t token) override;
+    [[nodiscard]] std::list<NotifyArgs> cancelVibrate(int32_t token) override;
     virtual bool isVibrating() override;
     virtual std::vector<int32_t> getVibratorIds() override;
-    virtual void timeoutExpired(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
     virtual void dump(std::string& dump) override;
 
 private:
@@ -44,8 +45,8 @@
     ssize_t mIndex;
     nsecs_t mNextStepTime;
 
-    void nextStep();
-    void stopVibrating();
+    [[nodiscard]] std::list<NotifyArgs> nextStep();
+    [[nodiscard]] NotifyVibratorStateArgs stopVibrating();
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 76500c5..fcbb98f 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -40,12 +40,10 @@
         "BlockingQueue_test.cpp",
         "EventHub_test.cpp",
         "FocusResolver_test.cpp",
-        "IInputFlingerQuery.aidl",
         "InputProcessor_test.cpp",
         "InputProcessorConverter_test.cpp",
         "InputDispatcher_test.cpp",
         "InputReader_test.cpp",
-        "InputFlingerService_test.cpp",
         "LatencyTracker_test.cpp",
         "PreferStylusOverTouch_test.cpp",
         "TestInputListener.cpp",
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
deleted file mode 100644
index 5aeb21f..0000000
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2020, 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.
- */
-
-import android.InputChannel;
-import android.gui.FocusRequest;
-import android.gui.WindowInfo;
-
-/** @hide */
-interface IInputFlingerQuery
-{
-    /* Test interfaces */
-    void getInputChannels(out InputChannel[] channels);
-    void resetInputManager();
-}
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
deleted file mode 100644
index ca548be..0000000
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2020 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 <BnInputFlingerQuery.h>
-#include <IInputFlingerQuery.h>
-
-#include <android/os/BnInputFlinger.h>
-#include <android/os/IInputFlinger.h>
-
-#include <binder/Binder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-
-#include <input/Input.h>
-#include <input/InputTransport.h>
-
-#include <gtest/gtest.h>
-#include <inttypes.h>
-#include <linux/uinput.h>
-#include <log/log.h>
-#include <chrono>
-#include <thread>
-#include <unordered_map>
-
-#define TAG "InputFlingerServiceTest"
-
-using android::gui::FocusRequest;
-using android::os::BnInputFlinger;
-using android::os::IInputFlinger;
-
-using std::chrono_literals::operator""ms;
-using std::chrono_literals::operator""s;
-
-namespace android {
-
-static const String16 kTestServiceName = String16("InputFlingerService");
-static const String16 kQueryServiceName = String16("InputFlingerQueryService");
-
-// --- InputFlingerServiceTest ---
-class InputFlingerServiceTest : public testing::Test {
-public:
-    void SetUp() override;
-    void TearDown() override;
-
-protected:
-    void InitializeInputFlinger();
-
-    sp<IInputFlinger> mService;
-    sp<IInputFlingerQuery> mQuery;
-
-private:
-    std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
-    std::mutex mLock;
-};
-
-
-class TestInputManager : public BnInputFlinger {
-protected:
-    virtual ~TestInputManager(){};
-
-public:
-    TestInputManager(){};
-
-    binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
-
-    status_t dump(int fd, const Vector<String16>& args) override;
-
-    binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
-    binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
-    binder::Status setFocusedWindow(const FocusRequest&) override;
-
-    void reset();
-
-private:
-    mutable Mutex mLock;
-    std::vector<std::shared_ptr<InputChannel>> mInputChannels;
-};
-
-class TestInputQuery : public BnInputFlingerQuery {
-public:
-    TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
-    binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
-    binder::Status resetInputManager() override;
-
-private:
-    sp<android::TestInputManager> mManager;
-};
-
-binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
-    return mManager->getInputChannels(channels);
-}
-
-binder::Status TestInputQuery::resetInputManager() {
-    mManager->reset();
-    return binder::Status::ok();
-}
-
-binder::Status TestInputManager::createInputChannel(const std::string& name,
-                                                    InputChannel* outChannel) {
-    AutoMutex _l(mLock);
-    std::unique_ptr<InputChannel> serverChannel;
-    std::unique_ptr<InputChannel> clientChannel;
-    InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-
-    clientChannel->copyTo(*outChannel);
-
-    mInputChannels.emplace_back(std::move(serverChannel));
-
-    return binder::Status::ok();
-}
-
-binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
-    AutoMutex _l(mLock);
-
-    auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
-                           [&](std::shared_ptr<InputChannel>& c) {
-                               return c->getConnectionToken() == connectionToken;
-                           });
-    if (it != mInputChannels.end()) {
-        mInputChannels.erase(it);
-    }
-
-    return binder::Status::ok();
-}
-
-status_t TestInputManager::dump(int fd, const Vector<String16>& args) {
-    std::string dump;
-
-    dump += " InputFlinger dump\n";
-
-    ::write(fd, dump.c_str(), dump.size());
-    return NO_ERROR;
-}
-
-binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
-    channels->clear();
-    for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
-        channels->push_back(*channel);
-    }
-    return binder::Status::ok();
-}
-
-binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
-    return binder::Status::ok();
-}
-
-void TestInputManager::reset() {
-    mInputChannels.clear();
-}
-
-void InputFlingerServiceTest::SetUp() {
-    InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
-    InitializeInputFlinger();
-}
-
-void InputFlingerServiceTest::TearDown() {
-    mQuery->resetInputManager();
-}
-
-void InputFlingerServiceTest::InitializeInputFlinger() {
-    sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
-    ASSERT_TRUE(input != nullptr);
-    mService = interface_cast<IInputFlinger>(input);
-
-    input = defaultServiceManager()->waitForService(kQueryServiceName);
-    ASSERT_TRUE(input != nullptr);
-    mQuery = interface_cast<IInputFlingerQuery>(input);
-}
-
-/**
- *  Test InputFlinger service interface createInputChannel
- */
-TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) {
-    // Test that the unblocked file descriptor flag is kept across processes over binder
-    // transactions.
-
-    InputChannel channel;
-    ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
-
-    const base::unique_fd& fd = channel.getFd();
-    ASSERT_TRUE(fd.ok());
-
-    const int result = fcntl(fd, F_GETFL);
-    EXPECT_NE(result, -1);
-    EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
-}
-
-TEST_F(InputFlingerServiceTest, CreateInputChannel) {
-    InputChannel channel;
-    ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
-
-    std::vector<::android::InputChannel> channels;
-    mQuery->getInputChannels(&channels);
-    ASSERT_EQ(channels.size(), 1UL);
-    EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken());
-
-    mService->removeInputChannel(channel.getConnectionToken());
-    mQuery->getInputChannels(&channels);
-    EXPECT_EQ(channels.size(), 0UL);
-}
-
-} // namespace android
-
-int main(int argc, char** argv) {
-    pid_t forkPid = fork();
-
-    if (forkPid == 0) {
-        // Server process
-        android::sp<android::TestInputManager> manager =
-                android::sp<android::TestInputManager>::make();
-        android::sp<android::TestInputQuery> query =
-                android::sp<android::TestInputQuery>::make(manager);
-
-        android::defaultServiceManager()->addService(android::kTestServiceName, manager,
-                                                     false /*allowIsolated*/);
-        android::defaultServiceManager()->addService(android::kQueryServiceName, query,
-                                                     false /*allowIsolated*/);
-        android::ProcessState::self()->startThreadPool();
-        android::IPCThreadState::self()->joinThreadPool();
-    } else {
-        android::ProcessState::self()->startThreadPool();
-        ::testing::InitGoogleTest(&argc, argv);
-        int result = RUN_ALL_TESTS();
-        kill(forkPid, SIGKILL);
-        return result;
-    }
-    return 0;
-}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8972e9f..dded6a1 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1190,7 +1190,8 @@
         }
     }
 
-    void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override {
+    std::list<NotifyArgs> configure(nsecs_t, const InputReaderConfiguration* config,
+                                    uint32_t changes) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mConfigureWasCalled = true;
 
@@ -1201,19 +1202,22 @@
         }
 
         mStateChangedCondition.notify_all();
+        return {};
     }
 
-    void reset(nsecs_t) override {
+    std::list<NotifyArgs> reset(nsecs_t) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mResetWasCalled = true;
         mStateChangedCondition.notify_all();
+        return {};
     }
 
-    void process(const RawEvent* rawEvent) override {
+    std::list<NotifyArgs> process(const RawEvent* rawEvent) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mLastEvent = *rawEvent;
         mProcessWasCalled = true;
         mStateChangedCondition.notify_all();
+        return {};
     }
 
     int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
@@ -2736,7 +2740,7 @@
     // Configuration
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
     InputReaderConfiguration config;
-    mDevice->configure(ARBITRARY_TIME, &config, 0);
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
 
     ASSERT_EQ(InputDeviceCountryCode::INTERNATIONAL, mDevice->getDeviceInfo().getCountryCode());
 }
@@ -2748,10 +2752,10 @@
 TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
     // Configuration.
     InputReaderConfiguration config;
-    mDevice->configure(ARBITRARY_TIME, &config, 0);
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
 
     // Reset.
-    mDevice->reset(ARBITRARY_TIME);
+    unused += mDevice->reset(ARBITRARY_TIME);
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -2807,7 +2811,7 @@
     mapper2.setMetaState(AMETA_SHIFT_ON);
 
     InputReaderConfiguration config;
-    mDevice->configure(ARBITRARY_TIME, &config, 0);
+    std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
 
     std::string propertyValue;
     ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue))
@@ -2818,7 +2822,7 @@
     ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
 
     // Reset
-    mDevice->reset(ARBITRARY_TIME);
+    unused += mDevice->reset(ARBITRARY_TIME);
     ASSERT_NO_FATAL_FAILURE(mapper1.assertResetWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper2.assertResetWasCalled());
 
@@ -2874,7 +2878,7 @@
     // Event handling.
     RawEvent event;
     event.deviceId = EVENTHUB_ID;
-    mDevice->process(&event, 1);
+    unused += mDevice->process(&event, 1);
 
     ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled());
     ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
@@ -2887,7 +2891,8 @@
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
 
     // First Configuration.
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+    std::list<NotifyArgs> unused =
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
 
     // Device should be enabled by default.
     ASSERT_TRUE(mDevice->isEnabled());
@@ -2897,8 +2902,8 @@
     const std::string UNIQUE_ID = "local:1";
 
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     // Device should be disabled because it is associated with a specific display via
     // input port <-> display port association, but the corresponding display is not found
     ASSERT_FALSE(mDevice->isEnabled());
@@ -2907,19 +2912,19 @@
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
                                     ViewportType::INTERNAL);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled after set disable.
     mFakePolicy->addDisabledDevice(mDevice->getId());
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_ENABLED_STATE);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should still be disabled even found the associated display.
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 }
 
@@ -2927,47 +2932,49 @@
     // Device should be enabled by default.
     mFakePolicy->clearViewports();
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+    std::list<NotifyArgs> unused =
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled because it is associated with a specific display, but the
     // corresponding display is not found.
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should be enabled when a display is found.
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
                                     NO_PORT, ViewportType::INTERNAL);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_TRUE(mDevice->isEnabled());
 
     // Device should be disabled after set disable.
     mFakePolicy->addDisabledDevice(mDevice->getId());
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_ENABLED_STATE);
     ASSERT_FALSE(mDevice->isEnabled());
 
     // Device should still be disabled even found the associated display.
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_FALSE(mDevice->isEnabled());
 }
 
 TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) {
     mFakePolicy->clearViewports();
     mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+    std::list<NotifyArgs> unused =
+            mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
 
     mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
                                     NO_PORT, ViewportType::INTERNAL);
-    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
 }
 
@@ -3028,7 +3035,7 @@
         mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
     }
 
-    void configureDevice(uint32_t changes) {
+    std::list<NotifyArgs> configureDevice(uint32_t changes) {
         if (!changes ||
             (changes &
              (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
@@ -3036,9 +3043,14 @@
             mReader->requestRefreshConfiguration(changes);
             mReader->loopOnce();
         }
-        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+        std::list<NotifyArgs> out =
+                mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
         // Loop the reader to flush the input listener queue.
+        for (const NotifyArgs& args : out) {
+            mFakeListener->notify(args);
+        }
         mReader->loopOnce();
+        return out;
     }
 
     std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
@@ -3060,9 +3072,12 @@
     T& addMapperAndConfigure(Args... args) {
         T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
         configureDevice(0);
-        mDevice->reset(ARBITRARY_TIME);
-        mapper.reset(ARBITRARY_TIME);
+        std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
+        resetArgList += mapper.reset(ARBITRARY_TIME);
         // Loop the reader to flush the input listener queue.
+        for (const NotifyArgs& loopArgs : resetArgList) {
+            mFakeListener->notify(loopArgs);
+        }
         mReader->loopOnce();
         return mapper;
     }
@@ -3079,8 +3094,8 @@
         mFakePolicy->clearViewports();
     }
 
-    void process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, int32_t code,
-                 int32_t value) {
+    std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
+                                  int32_t code, int32_t value) {
         RawEvent event;
         event.when = when;
         event.readTime = readTime;
@@ -3088,9 +3103,13 @@
         event.type = type;
         event.code = code;
         event.value = value;
-        mapper.process(&event);
+        std::list<NotifyArgs> processArgList = mapper.process(&event);
+        for (const NotifyArgs& args : processArgList) {
+            mFakeListener->notify(args);
+        }
         // Loop the reader to flush the input listener queue.
         mReader->loopOnce();
+        return processArgList;
     }
 
     static void assertMotionRange(const InputDeviceInfo& info,
@@ -3166,14 +3185,17 @@
 
 TEST_F(SwitchInputMapperTest, Process) {
     SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+    std::list<NotifyArgs> out;
+    out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
+    ASSERT_TRUE(out.empty());
+    out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+    ASSERT_TRUE(out.empty());
+    out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+    ASSERT_TRUE(out.empty());
+    out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
 
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
-    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-
-    NotifySwitchArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
+    ASSERT_EQ(1u, out.size());
+    const NotifySwitchArgs& args = std::get<NotifySwitchArgs>(*out.begin());
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues);
     ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
@@ -3220,22 +3242,23 @@
 
     ASSERT_FALSE(mapper.isVibrating());
     // Start vibrating
-    mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN);
+    std::list<NotifyArgs> out = mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN);
     ASSERT_TRUE(mapper.isVibrating());
     // Verify vibrator state listener was notified.
     mReader->loopOnce();
-    NotifyVibratorStateArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args));
-    ASSERT_EQ(DEVICE_ID, args.deviceId);
-    ASSERT_TRUE(args.isOn);
+    ASSERT_EQ(1u, out.size());
+    const NotifyVibratorStateArgs& vibrateArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
+    ASSERT_EQ(DEVICE_ID, vibrateArgs.deviceId);
+    ASSERT_TRUE(vibrateArgs.isOn);
     // Stop vibrating
-    mapper.cancelVibrate(VIBRATION_TOKEN);
+    out = mapper.cancelVibrate(VIBRATION_TOKEN);
     ASSERT_FALSE(mapper.isVibrating());
     // Verify vibrator state listener was notified.
     mReader->loopOnce();
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args));
-    ASSERT_EQ(DEVICE_ID, args.deviceId);
-    ASSERT_FALSE(args.isOn);
+    ASSERT_EQ(1u, out.size());
+    const NotifyVibratorStateArgs& cancelArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
+    ASSERT_EQ(DEVICE_ID, cancelArgs.deviceId);
+    ASSERT_FALSE(cancelArgs.isOn);
 }
 
 // --- SensorInputMapperTest ---
@@ -3891,7 +3914,7 @@
                                                        AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
 
     // Meta state should be AMETA_NONE after reset
-    mapper.reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
     // Meta state should be AMETA_NONE with update, as device doesn't have the keys.
     mapper.updateMetaState(AKEYCODE_NUM_LOCK);
@@ -3943,8 +3966,10 @@
     KeyboardInputMapper& mapper2 =
             device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
-    device2->reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused =
+            device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               0 /*changes*/);
+    unused += device2->reset(ARBITRARY_TIME);
 
     // Prepared displays and associated info.
     constexpr uint8_t hdmi1 = 0;
@@ -3955,8 +3980,8 @@
     mFakePolicy->addInputPortAssociation(USB2, hdmi2);
 
     // No associated display viewport found, should disable the device.
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     ASSERT_FALSE(device2->isEnabled());
 
     // Prepare second display.
@@ -3966,8 +3991,8 @@
     setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
                                  SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
     // Default device will reconfigure above, need additional reconfiguration for another device.
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 
     // Device should be enabled after the associated display is found.
     ASSERT_TRUE(mDevice->isEnabled());
@@ -4051,8 +4076,10 @@
     KeyboardInputMapper& mapper2 =
             device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
-    device2->reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused =
+            device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               0 /*changes*/);
+    unused += device2->reset(ARBITRARY_TIME);
 
     ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
@@ -4110,8 +4137,10 @@
     KeyboardInputMapper& mapper2 =
             device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
                                                     AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
-    device2->reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused =
+            device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               0 /*changes*/);
+    unused += device2->reset(ARBITRARY_TIME);
 
     // Initial metastate is AMETA_NONE.
     ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
@@ -6850,7 +6879,7 @@
 
     // Reset the mapper. When the mapper is reset, we expect it to attempt to recreate the touch
     // state by reading the current axis values.
-    mapper.reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
 
     // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
     // the recreated touch state to generate a down event.
@@ -8859,8 +8888,10 @@
 
     // Setup the second touch screen device.
     MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
-    device2->reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused =
+            device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                               0 /*changes*/);
+    unused += device2->reset(ARBITRARY_TIME);
 
     // Setup PointerController.
     std::shared_ptr<FakePointerController> fakePointerController =
@@ -8879,9 +8910,9 @@
     prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
 
     // Default device will reconfigure above, need additional reconfiguration for another device.
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_DISPLAY_INFO |
-                               InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+    unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+                                         InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
 
     // Two fingers down at default display.
     int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -8911,8 +8942,8 @@
 
     // Disable the show touches configuration and ensure the spots are cleared.
     mFakePolicy->setShowTouches(false);
-    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
-                       InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+    unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                                 InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
 
     ASSERT_TRUE(fakePointerController->getSpots().empty());
 }
@@ -9500,7 +9531,7 @@
 
     // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
     // preserved. Resetting should not generate any events.
-    mapper.reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
@@ -9535,7 +9566,7 @@
 
     // Reset the mapper. When the mapper is reset, we expect it to restore the latest
     // raw state where no pointers are down.
-    mapper.reset(ARBITRARY_TIME);
+    std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // Send an empty sync frame. Since there are no pointers, no events are generated.
@@ -10149,12 +10180,12 @@
         mFakePolicy.clear();
     }
 
-    void configureDevice(uint32_t changes) {
+    std::list<NotifyArgs> configureDevice(uint32_t changes) {
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
             mReader->requestRefreshConfiguration(changes);
             mReader->loopOnce();
         }
-        mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+        return mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
     std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 57b382c..29093ef 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -147,7 +147,7 @@
 }
 
 template <class NotifyArgsType>
-void TestInputListener::notify(const NotifyArgsType* args) {
+void TestInputListener::addToQueue(const NotifyArgsType* args) {
     std::scoped_lock<std::mutex> lock(mLock);
 
     std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
@@ -156,35 +156,35 @@
 }
 
 void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-    notify<NotifyConfigurationChangedArgs>(args);
+    addToQueue<NotifyConfigurationChangedArgs>(args);
 }
 
 void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-    notify<NotifyDeviceResetArgs>(args);
+    addToQueue<NotifyDeviceResetArgs>(args);
 }
 
 void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
-    notify<NotifyKeyArgs>(args);
+    addToQueue<NotifyKeyArgs>(args);
 }
 
 void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
-    notify<NotifyMotionArgs>(args);
+    addToQueue<NotifyMotionArgs>(args);
 }
 
 void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
-    notify<NotifySwitchArgs>(args);
+    addToQueue<NotifySwitchArgs>(args);
 }
 
 void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
-    notify<NotifyPointerCaptureChangedArgs>(args);
+    addToQueue<NotifyPointerCaptureChangedArgs>(args);
 }
 
 void TestInputListener::notifySensor(const NotifySensorArgs* args) {
-    notify<NotifySensorArgs>(args);
+    addToQueue<NotifySensorArgs>(args);
 }
 
 void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
-    notify<NotifyVibratorStateArgs>(args);
+    addToQueue<NotifyVibratorStateArgs>(args);
 }
 
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index cad698f..4ad1c42 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -67,7 +67,7 @@
     void assertNotCalled(std::string message);
 
     template <class NotifyArgsType>
-    void notify(const NotifyArgsType* args);
+    void addToQueue(const NotifyArgsType* args);
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
 
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index 4b542aa..cc523e1 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -51,12 +51,14 @@
                 },
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
-                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
-                                     fdp->ConsumeIntegral<int32_t>());
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                             fdp->ConsumeIntegral<int32_t>());
                 },
                 [&]() -> void {
                     // Need to reconfigure with 0 or you risk a NPE.
-                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
                     InputDeviceInfo info;
                     mapper.populateDeviceInfo(&info);
                 },
@@ -68,23 +70,27 @@
                                               : fdp->ConsumeIntegral<int32_t>();
 
                     // Need to reconfigure with 0 or you risk a NPE.
-                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
                     RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
                                       fdp->ConsumeIntegral<nsecs_t>(),
                                       fdp->ConsumeIntegral<int32_t>(),
                                       type,
                                       code,
                                       fdp->ConsumeIntegral<int32_t>()};
-                    mapper.process(&rawEvent);
+                    unused += mapper.process(&rawEvent);
                 },
-                [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+                },
                 [&]() -> void {
                     mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
                                             fdp->ConsumeIntegral<int32_t>());
                 },
                 [&]() -> void {
                     // Need to reconfigure with 0 or you risk a NPE.
-                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
                     mapper.getAssociatedDisplayId();
                 },
         })();
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
index 62615d0..1e0764f 100644
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -27,7 +27,7 @@
 class FuzzContainer {
     std::shared_ptr<FuzzEventHub> mFuzzEventHub;
     sp<FuzzInputReaderPolicy> mFuzzPolicy;
-    std::unique_ptr<FuzzInputListener> mFuzzListener;
+    FuzzInputListener mFuzzListener;
     std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
     std::unique_ptr<InputDevice> mFuzzDevice;
     InputReaderConfiguration mPolicyConfig;
@@ -44,9 +44,8 @@
         // Create mocked objects.
         mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp);
         mFuzzPolicy = sp<FuzzInputReaderPolicy>::make(mFdp);
-        mFuzzListener = std::make_unique<FuzzInputListener>();
         mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy,
-                                                                *mFuzzListener, mFdp);
+                                                                mFuzzListener, mFdp);
 
         InputDeviceIdentifier identifier;
         identifier.name = deviceName;
@@ -60,8 +59,12 @@
 
     void configureDevice() {
         nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
-        mFuzzDevice->configure(arbitraryTime, &mPolicyConfig, 0);
-        mFuzzDevice->reset(arbitraryTime);
+        std::list<NotifyArgs> out;
+        out += mFuzzDevice->configure(arbitraryTime, &mPolicyConfig, 0);
+        out += mFuzzDevice->reset(arbitraryTime);
+        for (const NotifyArgs& args : out) {
+            mFuzzListener.notify(args);
+        }
     }
 
     void addProperty(std::string key, std::string value) {
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index c48a099..e880f55 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -63,10 +63,13 @@
                 },
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
-                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
-                                     fdp->ConsumeIntegral<uint32_t>());
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                             fdp->ConsumeIntegral<uint32_t>());
                 },
-                [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+                },
                 [&]() -> void {
                     int32_t type, code;
                     type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
@@ -79,7 +82,7 @@
                                       type,
                                       code,
                                       fdp->ConsumeIntegral<int32_t>()};
-                    mapper.process(&rawEvent);
+                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
                 },
                 [&]() -> void {
                     mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 03c2266..bd81761 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -332,7 +332,6 @@
 class FuzzInputReaderContext : public InputReaderContext {
     std::shared_ptr<EventHubInterface> mEventHub;
     sp<InputReaderPolicyInterface> mPolicy;
-    InputListenerInterface& mListener;
     std::shared_ptr<FuzzedDataProvider> mFdp;
 
 public:
@@ -340,7 +339,7 @@
                            const sp<InputReaderPolicyInterface>& policy,
                            InputListenerInterface& listener,
                            std::shared_ptr<FuzzedDataProvider> mFdp)
-          : mEventHub(eventHub), mPolicy(policy), mListener(listener), mFdp(mFdp) {}
+          : mEventHub(eventHub), mPolicy(policy), mFdp(mFdp) {}
     ~FuzzInputReaderContext() {}
     void updateGlobalMetaState() override {}
     int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
@@ -355,9 +354,10 @@
     void requestTimeoutAtTime(nsecs_t when) override {}
     int32_t bumpGeneration() override { return mFdp->ConsumeIntegral<int32_t>(); }
     void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {}
-    void dispatchExternalStylusState(const StylusState& outState) override {}
+    std::list<NotifyArgs> dispatchExternalStylusState(const StylusState& outState) override {
+        return {};
+    }
     InputReaderPolicyInterface* getPolicy() override { return mPolicy.get(); }
-    InputListenerInterface& getListener() override { return mListener; }
     EventHubInterface* getEventHub() override { return mEventHub.get(); }
     int32_t getNextId() override { return mFdp->ConsumeIntegral<int32_t>(); }
 
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 59b0642..99fd083 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -78,10 +78,13 @@
                 },
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
-                    mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
-                                     fdp->ConsumeIntegral<uint32_t>());
+                    std::list<NotifyArgs> unused =
+                            mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+                                             fdp->ConsumeIntegral<uint32_t>());
                 },
-                [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+                },
                 [&]() -> void {
                     int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
                                                       : fdp->ConsumeIntegral<int32_t>();
@@ -93,7 +96,7 @@
                                       type,
                                       code,
                                       fdp->ConsumeIntegral<int32_t>()};
-                    mapper.process(&rawEvent);
+                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
                 },
                 [&]() -> void {
                     mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
@@ -113,16 +116,20 @@
                                                  nullptr);
                 },
                 [&]() -> void {
-                    mapper.cancelTouch(fdp->ConsumeIntegral<nsecs_t>(),
-                                       fdp->ConsumeIntegral<nsecs_t>());
+                    std::list<NotifyArgs> unused =
+                            mapper.cancelTouch(fdp->ConsumeIntegral<nsecs_t>(),
+                                               fdp->ConsumeIntegral<nsecs_t>());
                 },
-                [&]() -> void { mapper.timeoutExpired(fdp->ConsumeIntegral<nsecs_t>()); },
+                [&]() -> void {
+                    std::list<NotifyArgs> unused =
+                            mapper.timeoutExpired(fdp->ConsumeIntegral<nsecs_t>());
+                },
                 [&]() -> void {
                     StylusState state{fdp->ConsumeIntegral<nsecs_t>(),
                                       fdp->ConsumeFloatingPoint<float>(),
                                       fdp->ConsumeIntegral<uint32_t>(),
                                       fdp->ConsumeIntegral<int32_t>()};
-                    mapper.updateExternalStylusState(state);
+                    std::list<NotifyArgs> unused = mapper.updateExternalStylusState(state);
                 },
                 [&]() -> void { mapper.getAssociatedDisplayId(); },
         })();
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index e76bd72..7416ce9 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -46,7 +46,7 @@
                                       type,
                                       code,
                                       fdp->ConsumeIntegral<int32_t>()};
-                    mapper.process(&rawEvent);
+                    std::list<NotifyArgs> unused = mapper.process(&rawEvent);
                 },
                 [&]() -> void {
                     mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b911ae7..8a76d7c 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -84,7 +84,6 @@
         "libserviceutils",
         "libshaders",
         "libtonemap",
-        "libtrace_proto",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
@@ -189,11 +188,11 @@
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceFlingerDefaultFactory.cpp",
-        "SurfaceInterceptor.cpp",
         "Tracing/LayerTracing.cpp",
         "Tracing/TransactionTracing.cpp",
         "Tracing/TransactionProtoParser.cpp",
         "TransactionCallbackInvoker.cpp",
+        "TransactionHandler.cpp",
         "TunnelModeEnabledReporter.cpp",
     ],
 }
@@ -224,7 +223,6 @@
     ],
     static_libs: [
         "libserviceutils",
-        "libtrace_proto",
     ],
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b5d2ad0..0ae8bf9 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -42,7 +42,6 @@
         "libmath",
         "librenderengine",
         "libtonemap",
-        "libtrace_proto",
         "libaidlcommonsupport",
         "libprocessgroup",
         "libcgrouprc",
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a57af09..c63d57f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -216,7 +216,6 @@
               to_string(getId()).c_str());
         return BAD_VALUE;
     }
-    mNumModeSwitchesInPolicy++;
     mUpcomingActiveMode = info;
     ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
     return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
@@ -498,27 +497,6 @@
     mDesiredActiveModeChanged = false;
 }
 
-status_t DisplayDevice::setRefreshRatePolicy(
-        const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
-    const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy();
-    const status_t setPolicyResult = overridePolicy
-            ? mRefreshRateConfigs->setOverridePolicy(policy)
-            : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
-
-    if (setPolicyResult == OK) {
-        const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0);
-
-        ALOGI("Display %s policy changed\n"
-              "Previous: {%s}\n"
-              "Current:  {%s}\n"
-              "%d mode changes were performed under the previous policy",
-              to_string(getId()).c_str(), oldPolicy.toString().c_str(),
-              policy ? policy->toString().c_str() : "null", numModeChanges);
-    }
-
-    return setPolicyResult;
-}
-
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
 
 }  // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index e155ca1..06a812b 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -236,10 +236,6 @@
 
     nsecs_t getVsyncPeriodFromHWC() const;
 
-    status_t setRefreshRatePolicy(
-            const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
-            bool overridePolicy);
-
     // release HWC resources (if any) for removable displays
     void disconnect();
 
@@ -287,8 +283,6 @@
     TracedOrdinal<bool> mDesiredActiveModeChanged
             GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
     ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
-
-    std::atomic_int mNumModeSwitchesInPolicy = 0;
 };
 
 struct DisplayDeviceState {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4ff86e5..8ace812 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -77,10 +77,6 @@
 class LayerDebugInfo;
 }
 
-namespace impl {
-class SurfaceInterceptor;
-}
-
 namespace frametimeline {
 class SurfaceFrame;
 } // namespace frametimeline
@@ -881,7 +877,9 @@
 
     bool mPendingHWCDestroy{false};
 
-    bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
+    bool backpressureEnabled() const {
+        return mDrawingState.flags & layer_state_t::eEnableBackpressure;
+    }
 
     bool setStretchEffect(const StretchEffect& effect);
     StretchEffect getStretchEffect() const;
@@ -914,8 +912,6 @@
                                         std::unordered_set<Layer*>& visited);
 
 protected:
-    friend class impl::SurfaceInterceptor;
-
     // For unit tests
     friend class TestableSurfaceFlinger;
     friend class FpsReporterTest;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index d3f53c1..a6cd47b 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -237,16 +237,13 @@
 
 EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
                          android::frametimeline::TokenManager* tokenManager,
-                         InterceptVSyncsCallback interceptVSyncsCallback,
                          ThrottleVsyncCallback throttleVsyncCallback,
                          GetVsyncPeriodFunction getVsyncPeriodFunction)
       : mVSyncSource(std::move(vsyncSource)),
         mTokenManager(tokenManager),
-        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
         mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
         mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)),
         mThreadName(mVSyncSource->getName()) {
-
     LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
             "getVsyncPeriodFunction must not be null");
 
@@ -443,21 +440,13 @@
             event = mPendingEvents.front();
             mPendingEvents.pop_front();
 
-            switch (event->header.type) {
-                case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
-                    if (event->hotplug.connected && !mVSyncState) {
-                        mVSyncState.emplace(event->header.displayId);
-                    } else if (!event->hotplug.connected && mVSyncState &&
-                               mVSyncState->displayId == event->header.displayId) {
-                        mVSyncState.reset();
-                    }
-                    break;
-
-                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
-                    if (mInterceptVSyncsCallback) {
-                        mInterceptVSyncsCallback(event->header.timestamp);
-                    }
-                    break;
+            if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
+                if (event->hotplug.connected && !mVSyncState) {
+                    mVSyncState.emplace(event->header.displayId);
+                } else if (!event->hotplug.connected && mVSyncState &&
+                           mVSyncState->displayId == event->header.displayId) {
+                    mVSyncState.reset();
+                }
             }
         }
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index d85d140..7a5a348 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -161,12 +161,11 @@
 
 class EventThread : public android::EventThread, private VSyncSource::Callback {
 public:
-    using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
     using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
     using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
 
-    EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
-                ThrottleVsyncCallback, GetVsyncPeriodFunction);
+    EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, ThrottleVsyncCallback,
+                GetVsyncPeriodFunction);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
@@ -225,7 +224,6 @@
     const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
     frametimeline::TokenManager* const mTokenManager;
 
-    const InterceptVSyncsCallback mInterceptVSyncsCallback;
     const ThrottleVsyncCallback mThrottleVsyncCallback;
     const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
     const char* const mThreadName;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index fb50588..30483a2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -28,6 +28,7 @@
 #include <android-base/stringprintf.h>
 #include <ftl/enum.h>
 #include <ftl/fake_guard.h>
+#include <ftl/match.h>
 #include <utils/Trace.h>
 
 #include "../SurfaceFlingerProperties.h"
@@ -117,6 +118,20 @@
     return false;
 }
 
+std::string toString(const RefreshRateConfigs::PolicyVariant& policy) {
+    using namespace std::string_literals;
+
+    return ftl::match(
+            policy,
+            [](const RefreshRateConfigs::DisplayManagerPolicy& policy) {
+                return "DisplayManagerPolicy"s + policy.toString();
+            },
+            [](const RefreshRateConfigs::OverridePolicy& policy) {
+                return "OverridePolicy"s + policy.toString();
+            },
+            [](RefreshRateConfigs::NoOverridePolicy) { return "NoOverridePolicy"s; });
+}
+
 } // namespace
 
 struct RefreshRateConfigs::RefreshRateScoreComparator {
@@ -299,7 +314,8 @@
     // Keep the display at max refresh rate for the duration of powering on the display.
     if (signals.powerOnImminent) {
         ALOGV("Power On Imminent");
-        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Descending),
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 GlobalSignals{.powerOnImminent = true}};
     }
 
@@ -359,7 +375,8 @@
     // selected a refresh rate to see if we should apply touch boost.
     if (signals.touch && !hasExplicitVoteLayers) {
         ALOGV("Touch Boost");
-        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 GlobalSignals{.touch = true}};
     }
 
@@ -371,20 +388,23 @@
 
     if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
         ALOGV("Idle");
-        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending),
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 GlobalSignals{.idle = true}};
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
         ALOGV("No layers with votes");
-        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 kNoSignals};
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
         ALOGV("All layers Min");
-        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending),
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 kNoSignals};
     }
 
@@ -545,13 +565,17 @@
                        return RefreshRateRanking{score.modeIt->second, score.overallScore};
                    });
 
+    const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
+        return score.overallScore == 0;
+    });
+
     if (primaryRangeIsSingleRate) {
         // If we never scored any layers, then choose the rate from the primary
         // range instead of picking a random score from the app range.
-        if (std::all_of(scores.begin(), scores.end(),
-                        [](RefreshRateScore score) { return score.overallScore == 0; })) {
+        if (noLayerScore) {
             ALOGV("Layers not scored");
-            return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+            return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                                  /*preferredDisplayModeOpt*/ std::nullopt),
                     kNoSignals};
         } else {
             return {rankedRefreshRates, kNoSignals};
@@ -573,7 +597,8 @@
     }();
 
     const auto& touchRefreshRates =
-            getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending);
+            getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                          /*preferredDisplayModeOpt*/ std::nullopt);
     using fps_approx_ops::operator<;
 
     if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
@@ -583,6 +608,15 @@
         return {touchRefreshRates, GlobalSignals{.touch = true}};
     }
 
+    // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
+    // current config
+    if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
+        const auto preferredDisplayMode = activeMode.getId();
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Ascending,
+                                              preferredDisplayMode),
+                kNoSignals};
+    }
+
     return {rankedRefreshRates, kNoSignals};
 }
 
@@ -750,15 +784,29 @@
 }
 
 std::vector<RefreshRateRanking> RefreshRateConfigs::getRefreshRatesByPolicyLocked(
-        std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
-    std::vector<RefreshRateRanking> rankings;
+        std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
+        std::optional<DisplayModeId> preferredDisplayModeOpt) const {
+    std::deque<RefreshRateRanking> rankings;
     const auto makeRanking = [&](const DisplayModeIterator it) REQUIRES(mLock) {
         const auto& mode = it->second;
-        const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
-        const float score = calculateRefreshRateScoreForFps(mode->getFps());
-        if (!anchorGroupOpt || mode->getGroup() == anchorGroupOpt) {
-            rankings.push_back(RefreshRateRanking{mode, inverseScore ? 1.0f / score : score});
+        if (anchorGroupOpt && mode->getGroup() != anchorGroupOpt) {
+            return;
         }
+
+        float score = calculateRefreshRateScoreForFps(mode->getFps());
+        const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
+        if (inverseScore) {
+            score = 1.0f / score;
+        }
+        if (preferredDisplayModeOpt) {
+            if (*preferredDisplayModeOpt == mode->getId()) {
+                rankings.push_front(RefreshRateRanking{mode, /*score*/ 1.0f});
+                return;
+            }
+            constexpr float kNonPreferredModePenalty = 0.95f;
+            score *= kNonPreferredModePenalty;
+        }
+        rankings.push_back(RefreshRateRanking{mode, score});
     };
 
     if (refreshRateOrder == RefreshRateOrder::Ascending) {
@@ -768,14 +816,15 @@
     }
 
     if (!rankings.empty() || !anchorGroupOpt) {
-        return rankings;
+        return {rankings.begin(), rankings.end()};
     }
 
     ALOGW("Can't find %s refresh rate by policy with the same mode group"
           " as the mode group %d",
           refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
 
-    return getRefreshRatesByPolicyLocked(/*anchorGroupOpt*/ std::nullopt, refreshRateOrder);
+    return getRefreshRatesByPolicyLocked(/*anchorGroupOpt*/ std::nullopt, refreshRateOrder,
+                                         preferredDisplayModeOpt);
 }
 
 DisplayModePtr RefreshRateConfigs::getActiveModePtr() const {
@@ -874,35 +923,60 @@
             policy.appRequestRange.max >= policy.primaryRange.max;
 }
 
-status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
-    std::lock_guard lock(mLock);
-    if (!isPolicyValidLocked(policy)) {
-        ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
-        return BAD_VALUE;
-    }
-    mGetRankedRefreshRatesCache.reset();
-    Policy previousPolicy = *getCurrentPolicyLocked();
-    mDisplayManagerPolicy = policy;
-    if (*getCurrentPolicyLocked() == previousPolicy) {
-        return CURRENT_POLICY_UNCHANGED;
-    }
-    constructAvailableRefreshRates();
-    return NO_ERROR;
-}
+auto RefreshRateConfigs::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
+    Policy oldPolicy;
+    {
+        std::lock_guard lock(mLock);
+        oldPolicy = *getCurrentPolicyLocked();
 
-status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
-    std::lock_guard lock(mLock);
-    if (policy && !isPolicyValidLocked(*policy)) {
-        return BAD_VALUE;
+        const bool valid = ftl::match(
+                policy,
+                [this](const auto& policy) {
+                    ftl::FakeGuard guard(mLock);
+                    if (!isPolicyValidLocked(policy)) {
+                        ALOGE("Invalid policy: %s", policy.toString().c_str());
+                        return false;
+                    }
+
+                    using T = std::decay_t<decltype(policy)>;
+
+                    if constexpr (std::is_same_v<T, DisplayManagerPolicy>) {
+                        mDisplayManagerPolicy = policy;
+                    } else {
+                        static_assert(std::is_same_v<T, OverridePolicy>);
+                        mOverridePolicy = policy;
+                    }
+                    return true;
+                },
+                [this](NoOverridePolicy) {
+                    ftl::FakeGuard guard(mLock);
+                    mOverridePolicy.reset();
+                    return true;
+                });
+
+        if (!valid) {
+            return SetPolicyResult::Invalid;
+        }
+
+        mGetRankedRefreshRatesCache.reset();
+
+        if (*getCurrentPolicyLocked() == oldPolicy) {
+            return SetPolicyResult::Unchanged;
+        }
+        constructAvailableRefreshRates();
     }
-    mGetRankedRefreshRatesCache.reset();
-    Policy previousPolicy = *getCurrentPolicyLocked();
-    mOverridePolicy = policy;
-    if (*getCurrentPolicyLocked() == previousPolicy) {
-        return CURRENT_POLICY_UNCHANGED;
-    }
-    constructAvailableRefreshRates();
-    return NO_ERROR;
+
+    const auto displayId = getActiveMode().getPhysicalDisplayId();
+    const unsigned numModeChanges = std::exchange(mNumModeSwitchesInPolicy, 0u);
+
+    ALOGI("Display %s policy changed\n"
+          "Previous: %s\n"
+          "Current:  %s\n"
+          "%u mode changes were performed under the previous policy",
+          to_string(displayId).c_str(), oldPolicy.toString().c_str(), toString(policy).c_str(),
+          numModeChanges);
+
+    return SetPolicyResult::Changed;
 }
 
 const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 8b89104..7219584 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -21,6 +21,7 @@
 #include <optional>
 #include <type_traits>
 #include <utility>
+#include <variant>
 
 #include <gui/DisplayEventReceiver.h>
 
@@ -67,8 +68,7 @@
     static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
             std::chrono::nanoseconds(800us).count();
 
-    struct Policy {
-    private:
+    class Policy {
         static constexpr int kAllowGroupSwitchingDefault = false;
 
     public:
@@ -118,23 +118,28 @@
         std::string toString() const;
     };
 
-    // Return code set*Policy() to indicate the current policy is unchanged.
-    static constexpr int CURRENT_POLICY_UNCHANGED = 1;
+    enum class SetPolicyResult { Invalid, Unchanged, Changed };
 
     // We maintain the display manager policy and the override policy separately. The override
     // policy is used by CTS tests to get a consistent device state for testing. While the override
     // policy is set, it takes precedence over the display manager policy. Once the override policy
     // is cleared, we revert to using the display manager policy.
+    struct DisplayManagerPolicy : Policy {
+        using Policy::Policy;
+    };
 
-    // Sets the display manager policy to choose refresh rates. The return value will be:
-    //   - A negative value if the policy is invalid or another error occurred.
-    //   - NO_ERROR if the policy was successfully updated, and the current policy is different from
-    //     what it was before the call.
-    //   - CURRENT_POLICY_UNCHANGED if the policy was successfully updated, but the current policy
-    //     is the same as it was before the call.
-    status_t setDisplayManagerPolicy(const Policy& policy) EXCLUDES(mLock);
-    // Sets the override policy. See setDisplayManagerPolicy() for the meaning of the return value.
-    status_t setOverridePolicy(const std::optional<Policy>& policy) EXCLUDES(mLock);
+    struct OverridePolicy : Policy {
+        using Policy::Policy;
+    };
+
+    struct NoOverridePolicy {};
+
+    using PolicyVariant = std::variant<DisplayManagerPolicy, OverridePolicy, NoOverridePolicy>;
+
+    SetPolicyResult setPolicy(const PolicyVariant&) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+
+    void onModeChangeInitiated() REQUIRES(kMainThreadContext) { mNumModeSwitchesInPolicy++; }
+
     // Gets the current policy, which will be the override policy if active, and the display manager
     // policy otherwise.
     Policy getCurrentPolicy() const EXCLUDES(mLock);
@@ -370,9 +375,9 @@
 
     // Returns the rankings in RefreshRateOrder. May change at runtime.
     // Only uses the primary range, not the app request range.
-    std::vector<RefreshRateRanking> getRefreshRatesByPolicyLocked(std::optional<int> anchorGroupOpt,
-                                                                  RefreshRateOrder) const
-            REQUIRES(mLock);
+    std::vector<RefreshRateRanking> getRefreshRatesByPolicyLocked(
+            std::optional<int> anchorGroupOpt, RefreshRateOrder,
+            std::optional<DisplayModeId> preferredDisplayModeOpt) const REQUIRES(mLock);
 
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
@@ -418,6 +423,8 @@
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
 
+    unsigned mNumModeSwitchesInPolicy GUARDED_BY(kMainThreadContext) = 0;
+
     mutable std::mutex mLock;
 
     // A sorted list of known frame rates that a Heuristic layer will choose
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6d68bac..72b6545 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -57,6 +57,39 @@
         }                                                            \
     } while (false)
 
+namespace {
+
+using android::Fps;
+using android::FpsApproxEqual;
+using android::FpsHash;
+using android::scheduler::AggregatedFpsScore;
+using android::scheduler::RefreshRateRankingsAndSignals;
+
+// Returns the aggregated score per Fps for the RefreshRateRankingsAndSignals sourced.
+auto getAggregatedScoresPerFps(
+        const std::vector<RefreshRateRankingsAndSignals>& refreshRateRankingsAndSignalsPerDisplay)
+        -> std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> {
+    std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> aggregatedScoresPerFps;
+
+    for (const auto& refreshRateRankingsAndSignal : refreshRateRankingsAndSignalsPerDisplay) {
+        const auto& refreshRateRankings = refreshRateRankingsAndSignal.refreshRateRankings;
+
+        std::for_each(refreshRateRankings.begin(), refreshRateRankings.end(), [&](const auto& it) {
+            const auto [score, result] =
+                    aggregatedScoresPerFps.try_emplace(it.displayModePtr->getFps(),
+                                                       AggregatedFpsScore{it.score,
+                                                                          /* numDisplays */ 1});
+            if (!result) { // update
+                score->second.totalScore += it.score;
+                score->second.numDisplays++;
+            }
+        });
+    }
+    return aggregatedScoresPerFps;
+}
+
+} // namespace
+
 namespace android::scheduler {
 
 Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
@@ -198,15 +231,14 @@
     };
 }
 
-ConnectionHandle Scheduler::createConnection(
-        const char* connectionName, frametimeline::TokenManager* tokenManager,
-        std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
-        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+ConnectionHandle Scheduler::createConnection(const char* connectionName,
+                                             frametimeline::TokenManager* tokenManager,
+                                             std::chrono::nanoseconds workDuration,
+                                             std::chrono::nanoseconds readyDuration) {
     auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
     auto throttleVsync = makeThrottleVsyncCallback();
     auto getVsyncPeriod = makeGetVsyncPeriodFunction();
     auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
-                                                           std::move(interceptCallback),
                                                            std::move(throttleVsync),
                                                            std::move(getVsyncPeriod));
     return createConnection(std::move(eventThread));
@@ -385,7 +417,6 @@
         auto eventThread =
                 std::make_unique<impl::EventThread>(std::move(vsyncSource),
                                                     /*tokenManager=*/nullptr,
-                                                    impl::EventThread::InterceptVSyncsCallback(),
                                                     impl::EventThread::ThrottleVsyncCallback(),
                                                     impl::EventThread::GetVsyncPeriodFunction());
 
@@ -662,6 +693,7 @@
 auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
     DisplayModePtr newMode;
     GlobalSignals consideredSignals;
+    std::vector<DisplayModeConfig> displayModeConfigs;
 
     bool refreshRateChanged = false;
     bool frameRateOverridesChanged;
@@ -674,9 +706,27 @@
         if (currentState == newState) return {};
         currentState = std::forward<T>(newState);
 
-        const auto [rankings, signals] = getRankedDisplayModes();
-        newMode = rankings.front().displayModePtr;
-        consideredSignals = signals;
+        displayModeConfigs = getBestDisplayModeConfigs();
+
+        // mPolicy holds the current mode, using the current mode we find out
+        // what display is currently being tracked through the policy and
+        // then find the DisplayModeConfig for that display. So that
+        // later we check if the policy mode has changed for the same display in policy.
+        // If mPolicy mode isn't available then we take the first display from the best display
+        // modes as the candidate for policy changes and frame rate overrides.
+        // TODO(b/240743786) Update the single display based assumptions and make mode changes
+        // and mPolicy per display.
+        const DisplayModeConfig& displayModeConfigForCurrentPolicy = mPolicy.mode
+                ? *std::find_if(displayModeConfigs.begin(), displayModeConfigs.end(),
+                                [&](const auto& displayModeConfig) REQUIRES(mPolicyLock) {
+                                    return displayModeConfig.displayModePtr
+                                                   ->getPhysicalDisplayId() ==
+                                            mPolicy.mode->getPhysicalDisplayId();
+                                })
+                : displayModeConfigs.front();
+
+        newMode = displayModeConfigForCurrentPolicy.displayModePtr;
+        consideredSignals = displayModeConfigForCurrentPolicy.signals;
         frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
 
         if (mPolicy.mode == newMode) {
@@ -691,9 +741,7 @@
         }
     }
     if (refreshRateChanged) {
-        mSchedulerCallback.requestDisplayMode(std::move(newMode),
-                                              consideredSignals.idle ? DisplayModeEvent::None
-                                                                     : DisplayModeEvent::Changed);
+        mSchedulerCallback.requestDisplayModes(std::move(displayModeConfigs));
     }
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -701,12 +749,68 @@
     return consideredSignals;
 }
 
-auto Scheduler::getRankedDisplayModes()
-        -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
+void Scheduler::registerDisplay(const sp<const DisplayDevice>& display) {
+    const bool ok = mDisplays.try_emplace(display->getPhysicalId(), display).second;
+    ALOGE_IF(!ok, "Duplicate display registered");
+}
+
+void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+    mDisplays.erase(displayId);
+}
+
+std::vector<DisplayModeConfig> Scheduler::getBestDisplayModeConfigs() const {
     ATRACE_CALL();
 
-    const auto configs = holdRefreshRateConfigs();
+    std::vector<RefreshRateRankingsAndSignals> refreshRateRankingsAndSignalsPerDisplay;
+    refreshRateRankingsAndSignalsPerDisplay.reserve(mDisplays.size());
 
+    const auto displayModeSelectionParams = getDisplayModeSelectionParams();
+
+    std::for_each(mDisplays.begin(), mDisplays.end(), [&](const auto& display) {
+        const auto& [refreshRateRankings, globalSignals] =
+                display.second->holdRefreshRateConfigs()
+                        ->getRankedRefreshRates(displayModeSelectionParams.layerRequirements,
+                                                displayModeSelectionParams.globalSignals);
+        refreshRateRankingsAndSignalsPerDisplay.emplace_back(
+                RefreshRateRankingsAndSignals{refreshRateRankings, globalSignals});
+    });
+
+    // FPS and their Aggregated score.
+    std::unordered_map<Fps, AggregatedFpsScore, FpsHash, FpsApproxEqual> aggregatedScoresPerFps =
+            getAggregatedScoresPerFps(refreshRateRankingsAndSignalsPerDisplay);
+
+    Fps chosenFps = std::max_element(aggregatedScoresPerFps.begin(), aggregatedScoresPerFps.end(),
+                                     [](const auto& max, const auto& current) {
+                                         return max.second.totalScore <= current.second.totalScore;
+                                     })
+                            ->first;
+
+    return getDisplayModeConfigsForTheChosenFps(chosenFps, refreshRateRankingsAndSignalsPerDisplay);
+}
+
+std::vector<DisplayModeConfig> Scheduler::getDisplayModeConfigsForTheChosenFps(
+        Fps chosenFps,
+        const std::vector<RefreshRateRankingsAndSignals>& refreshRateRankingsAndSignalsPerDisplay)
+        const {
+    std::vector<DisplayModeConfig> displayModeConfigs;
+    displayModeConfigs.reserve(mDisplays.size());
+    using fps_approx_ops::operator==;
+    std::for_each(refreshRateRankingsAndSignalsPerDisplay.begin(),
+                  refreshRateRankingsAndSignalsPerDisplay.end(),
+                  [&](const auto& refreshRateRankingsAndSignal) {
+                      for (const auto& ranking : refreshRateRankingsAndSignal.refreshRateRankings) {
+                          if (ranking.displayModePtr->getFps() == chosenFps) {
+                              displayModeConfigs.emplace_back(
+                                      DisplayModeConfig{refreshRateRankingsAndSignal.globalSignals,
+                                                        ranking.displayModePtr});
+                              break;
+                          }
+                      }
+                  });
+    return displayModeConfigs;
+}
+
+DisplayModeSelectionParams Scheduler::getDisplayModeSelectionParams() const {
     const bool powerOnImminent = mDisplayPowerTimer &&
             (mPolicy.displayPowerMode != hal::PowerMode::ON ||
              mPolicy.displayPowerTimer == TimerState::Reset);
@@ -715,7 +819,18 @@
                                 .idle = mPolicy.idleTimer == TimerState::Expired,
                                 .powerOnImminent = powerOnImminent};
 
-    return configs->getRankedRefreshRates(mPolicy.contentRequirements, signals);
+    return {mPolicy.contentRequirements, signals};
+}
+
+auto Scheduler::getRankedDisplayModes()
+        -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
+    ATRACE_CALL();
+
+    const auto configs = holdRefreshRateConfigs();
+
+    const auto displayModeSelectionParams = getDisplayModeSelectionParams();
+    return configs->getRankedRefreshRates(displayModeSelectionParams.layerRequirements,
+                                          displayModeSelectionParams.globalSignals);
 }
 
 DisplayModePtr Scheduler::getPreferredDisplayMode() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f567205..4c49562 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -24,6 +24,7 @@
 #include <mutex>
 #include <optional>
 #include <unordered_map>
+#include <utility>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -32,9 +33,11 @@
 #include <ui/GraphicTypes.h>
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
+#include <DisplayDevice.h>
 #include <scheduler/Features.h>
 #include <scheduler/Time.h>
 
+#include "Display/DisplayMap.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
 #include "LayerHistory.h"
@@ -83,11 +86,22 @@
 
 namespace scheduler {
 
+using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+// Config representing the DisplayMode and considered signals for the Display.
+struct DisplayModeConfig {
+    const GlobalSignals signals;
+    const DisplayModePtr displayModePtr;
+
+    DisplayModeConfig(GlobalSignals signals, DisplayModePtr displayModePtr)
+          : signals(signals), displayModePtr(std::move(displayModePtr)) {}
+};
+
 struct ISchedulerCallback {
     using DisplayModeEvent = scheduler::DisplayModeEvent;
 
     virtual void setVsyncEnabled(bool) = 0;
-    virtual void requestDisplayMode(DisplayModePtr, DisplayModeEvent) = 0;
+    virtual void requestDisplayModes(std::vector<DisplayModeConfig>) = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
     virtual void triggerOnFrameRateOverridesChanged() = 0;
 
@@ -95,6 +109,25 @@
     ~ISchedulerCallback() = default;
 };
 
+// Holds the total score of the FPS and
+// number of displays the FPS is found in.
+struct AggregatedFpsScore {
+    float totalScore;
+    size_t numDisplays;
+};
+
+// Represents LayerRequirements and GlobalSignals to be considered for the display mode selection.
+struct DisplayModeSelectionParams {
+    std::vector<RefreshRateConfigs::LayerRequirement> layerRequirements;
+    GlobalSignals globalSignals;
+};
+
+// Represents the RefreshRateRankings and GlobalSignals for the selected RefreshRateRankings.
+struct RefreshRateRankingsAndSignals {
+    std::vector<RefreshRateRanking> refreshRateRankings;
+    GlobalSignals globalSignals;
+};
+
 class Scheduler : android::impl::MessageQueue {
     using Impl = android::impl::MessageQueue;
 
@@ -129,8 +162,7 @@
 
     ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
                                       std::chrono::nanoseconds workDuration,
-                                      std::chrono::nanoseconds readyDuration,
-                                      android::impl::EventThread::InterceptVSyncsCallback);
+                                      std::chrono::nanoseconds readyDuration);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(
             ConnectionHandle, EventRegistrationFlags eventRegistration = {});
@@ -237,6 +269,9 @@
         return mLayerHistory.getLayerFramerate(now, id);
     }
 
+    void registerDisplay(const sp<const DisplayDevice>&);
+    void unregisterDisplay(PhysicalDisplayId);
+
 private:
     friend class TestableScheduler;
 
@@ -260,8 +295,6 @@
 
     void setVsyncPeriod(nsecs_t period);
 
-    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
-
     struct Policy;
 
     // Sets the S state of the policy to the T value under mPolicyLock, and chooses a display mode
@@ -274,6 +307,17 @@
     std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedDisplayModes()
             REQUIRES(mPolicyLock);
 
+    // Returns the best display mode per display.
+    std::vector<DisplayModeConfig> getBestDisplayModeConfigs() const REQUIRES(mPolicyLock);
+
+    // Returns the list of DisplayModeConfigs per display for the chosenFps.
+    std::vector<DisplayModeConfig> getDisplayModeConfigsForTheChosenFps(
+            Fps chosenFps, const std::vector<RefreshRateRankingsAndSignals>&) const;
+
+    // Returns the DisplayModeSelectionParams to be considered for the
+    // DisplayMode selection based on the current Policy and GlobalSignals.
+    DisplayModeSelectionParams getDisplayModeSelectionParams() const REQUIRES(mPolicyLock);
+
     bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
 
     void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
@@ -323,6 +367,10 @@
 
     mutable std::mutex mPolicyLock;
 
+    // Holds the Physical displays registered through the SurfaceFlinger, used for making
+    // the refresh rate selections.
+    display::PhysicalDisplayMap<PhysicalDisplayId, const sp<const DisplayDevice>> mDisplays;
+
     struct Policy {
         // Policy for choosing the display mode.
         LayerHistory::Summary contentRequirements;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index bd4f409..2c77142 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -138,6 +138,10 @@
     bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
 };
 
+struct FpsHash {
+    size_t operator()(Fps fps) const { return std::hash<nsecs_t>()(fps.getPeriodNsecs()); }
+};
+
 inline std::string to_string(Fps fps) {
     return base::StringPrintf("%.2f Hz", fps.getValue());
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0b628b0..5466497 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -129,17 +129,13 @@
 #include "LayerVector.h"
 #include "MutexUtils.h"
 #include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
-#include "Scheduler/DispSyncSource.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
-#include "Scheduler/VsyncController.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerProperties.h"
-#include "SurfaceInterceptor.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
 #include "Utils/Dumper.h"
@@ -303,7 +299,6 @@
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
         mPid(getpid()),
-        mInterceptor(mFactory.createSurfaceInterceptor()),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(mFactory.createFrameTracer()),
         mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
@@ -498,7 +493,6 @@
     state.isSecure = secure;
     state.displayName = displayName;
     mCurrentState.displays.add(token, state);
-    mInterceptor->saveDisplayCreation(state);
     return token;
 }
 
@@ -516,7 +510,6 @@
         ALOGE("%s: Invalid operation on physical display", __func__);
         return;
     }
-    mInterceptor->saveDisplayDeletion(state.sequenceId);
     mCurrentState.displays.removeItemsAt(index);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -747,6 +740,7 @@
 void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
+    addTransactionReadyFilters();
     Mutex::Autolock lock(mStateLock);
 
     // Get a RenderEngine for the given display / config (can't fail)
@@ -1105,7 +1099,7 @@
     }
 
     const char* const whence = __func__;
-    auto future = mScheduler->schedule([=]() -> status_t {
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
         const auto displayOpt =
                 FTL_FAKE_GUARD(mStateLock,
                                ftl::find_if(mPhysicalDisplays,
@@ -1130,13 +1124,16 @@
         }
 
         const Fps fps = *fpsOpt;
+
         // Keep the old switching type.
         const bool allowGroupSwitching =
                 display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching;
-        const scheduler::RefreshRateConfigs::Policy policy{modeId, allowGroupSwitching, {fps, fps}};
-        constexpr bool kOverridePolicy = false;
 
-        return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
+        const scheduler::RefreshRateConfigs::DisplayManagerPolicy policy{modeId,
+                                                                         allowGroupSwitching,
+                                                                         {fps, fps}};
+
+        return setDesiredDisplayModeSpecsInternal(display, policy);
     });
 
     return future.get();
@@ -1187,7 +1184,7 @@
 
 void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) {
     display->clearDesiredActiveModeState();
-    if (isDisplayActiveLocked(display)) {
+    if (display->getPhysicalId() == mActiveDisplayId) {
         mScheduler->setModeChangePending(false);
     }
 }
@@ -1217,12 +1214,12 @@
         // Store the local variable to release the lock.
         const auto desiredActiveMode = display->getDesiredActiveMode();
         if (!desiredActiveMode) {
-            // No desired active mode pending to be applied
+            // No desired active mode pending to be applied.
             continue;
         }
 
-        if (!isDisplayActiveLocked(display)) {
-            // display is no longer the active display, so abort the mode change
+        if (id != mActiveDisplayId) {
+            // Display is no longer the active display, so abort the mode change.
             clearDesiredActiveModeState(display);
             continue;
         }
@@ -1273,6 +1270,8 @@
             ALOGW("initiateModeChange failed: %d", status);
             continue;
         }
+
+        display->refreshRateConfigs().onModeChangeInitiated();
         mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
 
         if (outTimeline.refreshRequired) {
@@ -1853,10 +1852,8 @@
         return;
     }
 
-    const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
-    const bool isActiveDisplay =
-            displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken;
-    if (!isActiveDisplay) {
+    if (const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
+        displayId != mActiveDisplayId) {
         // For now, we don't do anything with non active display vsyncs.
         return;
     }
@@ -2052,8 +2049,7 @@
 
     // Save this once per commit + composite to ensure consistency
     // TODO (b/240619471): consider removing active display check once AOD is fixed
-    const auto activeDisplay =
-            FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken));
+    const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId));
     mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
             activeDisplay->getPowerMode() == hal::PowerMode::ON;
     if (mPowerHintSessionEnabled) {
@@ -2690,8 +2686,6 @@
         const auto& display = displayOpt->get();
 
         if (const ssize_t index = mCurrentState.displays.indexOfKey(display.token()); index >= 0) {
-            const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
-            mInterceptor->saveDisplayDeletion(state.sequenceId);
             mCurrentState.displays.removeItemsAt(index);
         }
 
@@ -2746,7 +2740,6 @@
     state.displayName = std::move(info.name);
 
     mCurrentState.displays.add(token, state);
-    mInterceptor->saveDisplayCreation(state);
     return "Connecting";
 }
 
@@ -2938,7 +2931,7 @@
         if (display->isPrimary()) {
             mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
         }
-
+        mScheduler->registerDisplay(display);
         dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
     }
 
@@ -2954,6 +2947,7 @@
             releaseVirtualDisplay(display->getVirtualId());
         } else {
             dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
+            mScheduler->unregisterDisplay(display->getPhysicalId());
         }
     }
 
@@ -2990,6 +2984,8 @@
             display->disconnect();
             if (display->isVirtual()) {
                 releaseVirtualDisplay(display->getVirtualId());
+            } else {
+                mScheduler->unregisterDisplay(display->getPhysicalId());
             }
         }
 
@@ -3026,7 +3022,7 @@
             (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
             display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
                                    currentState.orientedDisplaySpaceRect);
-            if (isDisplayActiveLocked(display)) {
+            if (display->getId() == mActiveDisplayId) {
                 mActiveDisplayTransformHint = display->getTransformHint();
             }
         }
@@ -3034,7 +3030,7 @@
             currentState.height != drawingState.height) {
             display->setDisplaySize(currentState.width, currentState.height);
 
-            if (isDisplayActiveLocked(display)) {
+            if (display->getId() == mActiveDisplayId) {
                 onActiveDisplaySizeChanged(display);
             }
         }
@@ -3319,25 +3315,34 @@
     mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
-void SurfaceFlinger::requestDisplayMode(DisplayModePtr mode, DisplayModeEvent event) {
+void SurfaceFlinger::requestDisplayModes(
+        std::vector<scheduler::DisplayModeConfig> displayModeConfigs) {
+    if (mBootStage != BootStage::FINISHED) {
+        ALOGV("Currently in the boot stage, skipping display mode changes");
+        return;
+    }
+
+    ATRACE_CALL();
     // If this is called from the main thread mStateLock must be locked before
     // Currently the only way to call this function from the main thread is from
     // Scheduler::chooseRefreshRateForContent
 
     ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
 
-    const auto display = getDefaultDisplayDeviceLocked();
-    if (!display || mBootStage != BootStage::FINISHED) {
-        return;
-    }
-    ATRACE_CALL();
-
-    if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) {
-        ALOGV("Skipping disallowed mode %d", mode->getId().value());
-        return;
-    }
-
-    setDesiredActiveMode({std::move(mode), event});
+    std::for_each(displayModeConfigs.begin(), displayModeConfigs.end(),
+                  [&](const auto& config) REQUIRES(mStateLock) {
+                      const auto& displayModePtr = config.displayModePtr;
+                      if (const auto display =
+                                  getDisplayDeviceLocked(displayModePtr->getPhysicalDisplayId());
+                          display->refreshRateConfigs().isModeAllowed(displayModePtr->getId())) {
+                          const auto event = config.signals.idle ? DisplayModeEvent::None
+                                                                 : DisplayModeEvent::Changed;
+                          setDesiredActiveMode({displayModePtr, event});
+                      } else {
+                          ALOGV("Skipping disallowed mode %d for display %" PRId64,
+                                displayModePtr->getId().value(), display->getPhysicalId().value);
+                      }
+                  });
 }
 
 void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3386,6 +3391,7 @@
 
         mScheduler->createVsyncSchedule(features);
         mScheduler->setRefreshRateConfigs(std::move(configs));
+        mScheduler->registerDisplay(display);
     }
     setVsyncEnabled(false);
     mScheduler->startTimers();
@@ -3395,15 +3401,11 @@
     mAppConnectionHandle =
             mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
                                          /*workDuration=*/configs.late.appWorkDuration,
-                                         /*readyDuration=*/configs.late.sfWorkDuration,
-                                         impl::EventThread::InterceptVSyncsCallback());
+                                         /*readyDuration=*/configs.late.sfWorkDuration);
     mSfConnectionHandle =
             mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
                                          /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
-                                         /*readyDuration=*/configs.late.sfWorkDuration,
-                                         [this](nsecs_t timestamp) {
-                                             mInterceptor->saveVSyncEvent(timestamp);
-                                         });
+                                         /*readyDuration=*/configs.late.sfWorkDuration);
 
     mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
                           *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
@@ -3647,122 +3649,117 @@
     }
 }
 
-int SurfaceFlinger::flushPendingTransactionQueues(
-        std::vector<TransactionState>& transactions,
-        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-        bool tryApplyUnsignaled) {
-    std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
-    int transactionsPendingBarrier = 0;
-    auto it = mPendingTransactionQueues.begin();
-    while (it != mPendingTransactionQueues.end()) {
-        auto& [applyToken, transactionQueue] = *it;
-        while (!transactionQueue.empty()) {
-            // if we are in LatchUnsignaledConfig::AutoSingleLayer
-            // then we should have only one applyToken for processing.
-            // so we can stop further transactions on this applyToken.
-            if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer &&
-                !applyTokensWithUnsignaledTransactions.empty()) {
-                ATRACE_NAME("stopTransactionProcessing");
-                break;
-            }
-
-            auto& transaction = transactionQueue.front();
-            const auto ready =
-                    transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo,
-                                                  transaction.isAutoTimestamp,
-                                                  TimePoint::fromNs(transaction.desiredPresentTime),
-                                                  transaction.originUid, transaction.states,
-                                                  bufferLayersReadyToPresent, transactions.size(),
-                                                  tryApplyUnsignaled);
-            ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
-            if (ready == TransactionReadiness::NotReady) {
-                setTransactionFlags(eTransactionFlushNeeded);
-                break;
-            }
-            if (ready == TransactionReadiness::NotReadyBarrier) {
-                transactionsPendingBarrier++;
-                setTransactionFlags(eTransactionFlushNeeded);
-                break;
-            }
-            transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
-                const bool frameNumberChanged = state.bufferData->flags.test(
-                        BufferData::BufferDataChange::frameNumberChanged);
-                if (frameNumberChanged) {
-                    bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
-                } else {
-                    // Barrier function only used for BBQ which always includes a frame number
-                    bufferLayersReadyToPresent[state.surface] =
-                        std::numeric_limits<uint64_t>::max();
-                }
-            });
-            const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
-            if (appliedUnsignaled) {
-                applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
-            }
-
-            transactions.emplace_back(std::move(transaction));
-            transactionQueue.pop();
-            mPendingTransactionCount--;
-            ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
-        }
-
-        if (transactionQueue.empty()) {
-            it = mPendingTransactionQueues.erase(it);
-        } else {
-            it = std::next(it, 1);
-        }
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
+        const TransactionHandler::TransactionFlushState& flushState) {
+    using TransactionReadiness = TransactionHandler::TransactionReadiness;
+    const auto& transaction = *flushState.transaction;
+    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64,
+                  transaction.frameTimelineInfo.vsyncId);
+    TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+    // Do not present if the desiredPresentTime has not passed unless it is more than
+    // one second in the future. We ignore timestamps more than 1 second in the future
+    // for stability reasons.
+    if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
+        desiredPresentTime < mExpectedPresentTime + 1s) {
+        ATRACE_NAME("not current");
+        return TransactionReadiness::NotReady;
     }
-    return transactionsPendingBarrier;
+
+    if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
+        ATRACE_NAME("!isVsyncValid");
+        return TransactionReadiness::NotReady;
+    }
+
+    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the
+    // expected present time of this transaction.
+    if (transaction.isAutoTimestamp &&
+        frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
+        ATRACE_NAME("frameIsEarly");
+        return TransactionReadiness::NotReady;
+    }
+    return TransactionReadiness::Ready;
+}
+
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+        const TransactionHandler::TransactionFlushState& flushState) {
+    using TransactionReadiness = TransactionHandler::TransactionReadiness;
+    auto ready = TransactionReadiness::Ready;
+    flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool {
+        sp<Layer> layer = Layer::fromHandle(s.surface).promote();
+        const auto& transaction = *flushState.transaction;
+        // check for barrier frames
+        if (s.bufferData->hasBarrier &&
+            ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
+            const bool willApplyBarrierFrame =
+                    flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+                    (flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+                     s.bufferData->barrierFrameNumber);
+            if (!willApplyBarrierFrame) {
+                ATRACE_NAME("NotReadyBarrier");
+                ready = TransactionReadiness::NotReadyBarrier;
+                return false;
+            }
+        }
+
+        // If backpressure is enabled and we already have a buffer to commit, keep
+        // the transaction in the queue.
+        const bool hasPendingBuffer =
+                flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+        if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
+            ATRACE_NAME("hasPendingBuffer");
+            ready = TransactionReadiness::NotReady;
+            return false;
+        }
+
+        // check fence status
+        const bool allowLatchUnsignaled = shouldLatchUnsignaled(layer, s, transaction.states.size(),
+                                                                flushState.firstTransaction);
+        ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
+                      allowLatchUnsignaled ? "true" : "false");
+
+        const bool acquireFenceChanged = s.bufferData &&
+                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+                s.bufferData->acquireFence;
+        const bool fenceSignaled =
+                (!acquireFenceChanged ||
+                 s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled);
+        if (!fenceSignaled) {
+            if (!allowLatchUnsignaled) {
+                ready = TransactionReadiness::NotReady;
+                auto& listener = s.bufferData->releaseBufferListener;
+                if (listener &&
+                    (flushState.queueProcessTime - transaction.postTime) >
+                            std::chrono::nanoseconds(4s).count()) {
+                    mTransactionHandler.onTransactionQueueStalled(transaction, listener);
+                }
+                return false;
+            }
+
+            ready = enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer
+                    ? TransactionReadiness::ReadyUnsignaledSingle
+                    : TransactionReadiness::ReadyUnsignaled;
+        }
+        return true;
+    });
+    ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
+    return ready;
+}
+
+void SurfaceFlinger::addTransactionReadyFilters() {
+    mTransactionHandler.addTransactionReadyFilter(
+            std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
+    mTransactionHandler.addTransactionReadyFilter(
+            std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
 }
 
 bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
     // states) around outside the scope of the lock
-    std::vector<TransactionState> transactions;
-    // Layer handles that have transactions with buffers that are ready to be applied.
-    std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
+    std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
     {
         Mutex::Autolock _l(mStateLock);
-        {
-            while (!mLocklessTransactionQueue.isEmpty()) {
-                auto maybeTransaction = mLocklessTransactionQueue.pop();
-                if (!maybeTransaction.has_value()) {
-                    break;
-                }
-                auto transaction = maybeTransaction.value();
-                mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
-            }
-
-            // Transactions with a buffer pending on a barrier may be on a different applyToken
-            // than the transaction which satisfies our barrier. In fact this is the exact use case
-            // that the primitive is designed for. This means we may first process
-            // the barrier dependent transaction, determine it ineligible to complete
-            // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
-            // The barrier dependent transaction was eligible to be presented in this frame
-            // but we would have prevented it without case. To fix this we continually
-            // loop through flushPendingTransactionQueues until we perform an iteration
-            // where the number of transactionsPendingBarrier doesn't change. This way
-            // we can continue to resolve dependency chains of barriers as far as possible.
-            int lastTransactionsPendingBarrier = 0;
-            int transactionsPendingBarrier = 0;
-            do {
-                lastTransactionsPendingBarrier = transactionsPendingBarrier;
-                transactionsPendingBarrier =
-                        flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                                                      /*tryApplyUnsignaled*/ false);
-            } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
-
-            // We collected all transactions that could apply without latching unsignaled buffers.
-            // If we are allowing latch unsignaled of some form, now it's the time to go over the
-            // transactions that were not applied and try to apply them unsignaled.
-            if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
-                flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                                              /*tryApplyUnsignaled*/ true);
-            }
-
-            return applyTransactions(transactions, vsyncId);
-        }
+        return applyTransactions(transactions, vsyncId);
     }
 }
 
@@ -3789,7 +3786,7 @@
 }
 
 bool SurfaceFlinger::transactionFlushNeeded() {
-    return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+    return mTransactionHandler.hasPendingTransactions();
 }
 
 bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId) const {
@@ -3815,7 +3812,7 @@
 }
 
 bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
-                                           size_t numStates, size_t totalTXapplied) const {
+                                           size_t numStates, bool firstTransaction) const {
     if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
         ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
         return false;
@@ -3834,9 +3831,9 @@
     }
 
     if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
-        if (totalTXapplied > 0) {
-            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)",
-                  __func__, totalTXapplied);
+        if (!firstTransaction) {
+            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first transaction)",
+                  __func__);
             return false;
         }
 
@@ -3860,116 +3857,6 @@
     return true;
 }
 
-auto SurfaceFlinger::transactionIsReadyToBeApplied(
-        TransactionState& transaction, const FrameTimelineInfo& info, bool isAutoTimestamp,
-        TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>& states,
-        const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
-                bufferLayersReadyToPresent,
-        size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
-    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
-    // Do not present if the desiredPresentTime has not passed unless it is more than one second
-    // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
-    if (!isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
-        desiredPresentTime < mExpectedPresentTime + 1s) {
-        ATRACE_NAME("not current");
-        return TransactionReadiness::NotReady;
-    }
-
-    if (!mScheduler->isVsyncValid(mExpectedPresentTime, originUid)) {
-        ATRACE_NAME("!isVsyncValid");
-        return TransactionReadiness::NotReady;
-    }
-
-    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
-    // present time of this transaction.
-    if (isAutoTimestamp && frameIsEarly(mExpectedPresentTime, VsyncId{info.vsyncId})) {
-        ATRACE_NAME("frameIsEarly");
-        return TransactionReadiness::NotReady;
-    }
-
-    bool fenceUnsignaled = false;
-    auto queueProcessTime = systemTime();
-    for (const ComposerState& state : states) {
-        const layer_state_t& s = state.state;
-
-        sp<Layer> layer = nullptr;
-        if (s.surface) {
-            layer = fromHandle(s.surface).promote();
-        } else if (s.hasBufferChanges()) {
-            ALOGW("Transaction with buffer, but no Layer?");
-            continue;
-        }
-        if (!layer) {
-            continue;
-        }
-
-        if (s.hasBufferChanges() && s.bufferData->hasBarrier &&
-            ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
-            const bool willApplyBarrierFrame =
-                (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) &&
-                (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber);
-            if (!willApplyBarrierFrame) {
-                ATRACE_NAME("NotReadyBarrier");
-                return TransactionReadiness::NotReadyBarrier;
-            }
-        }
-
-        const bool allowLatchUnsignaled = tryApplyUnsignaled &&
-                shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
-        ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
-                      allowLatchUnsignaled ? "true" : "false");
-
-        const bool acquireFenceChanged = s.bufferData &&
-                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
-                s.bufferData->acquireFence;
-        fenceUnsignaled = fenceUnsignaled ||
-                (acquireFenceChanged &&
-                 s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled);
-
-        if (fenceUnsignaled && !allowLatchUnsignaled) {
-            if (!transaction.sentFenceTimeoutWarning &&
-                queueProcessTime - transaction.postTime > std::chrono::nanoseconds(4s).count()) {
-                transaction.sentFenceTimeoutWarning = true;
-                auto listener = s.bufferData->releaseBufferListener;
-                if (listener) {
-                    listener->onTransactionQueueStalled();
-                }
-            }
-
-            ATRACE_NAME("fence unsignaled");
-            return TransactionReadiness::NotReady;
-        }
-
-        if (s.hasBufferChanges()) {
-            // If backpressure is enabled and we already have a buffer to commit, keep the
-            // transaction in the queue.
-            const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) !=
-                bufferLayersReadyToPresent.end();
-            if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
-                ATRACE_NAME("hasPendingBuffer");
-                return TransactionReadiness::NotReady;
-            }
-        }
-    }
-    return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready;
-}
-
-void SurfaceFlinger::queueTransaction(TransactionState& state) {
-    mLocklessTransactionQueue.push(state);
-    mPendingTransactionCount++;
-    ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
-
-    const auto schedule = [](uint32_t flags) {
-        if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
-        if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
-        return TransactionSchedule::Late;
-    }(state.flags);
-
-    const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
-
-    setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
-}
-
 status_t SurfaceFlinger::setTransactionState(
         const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
@@ -4020,7 +3907,16 @@
     if (mTransactionTracing) {
         mTransactionTracing->addQueuedTransaction(state);
     }
-    queueTransaction(state);
+
+    const auto schedule = [](uint32_t flags) {
+        if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+        if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+        return TransactionSchedule::Late;
+    }(state.flags);
+
+    const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
+    setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
+    mTransactionHandler.queueTransaction(std::move(state));
 
     return NO_ERROR;
 }
@@ -4084,11 +3980,6 @@
 
     bool needsTraversal = false;
     if (transactionFlags) {
-        if (mInterceptor->isEnabled()) {
-            mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
-                                          originPid, originUid, transactionId);
-        }
-
         // We are on the main thread, we are about to preform a traversal. Clear the traversal bit
         // so we don't have to wake up again next frame to preform an unnecessary traversal.
         if (transactionFlags & eTraversalNeeded) {
@@ -4721,11 +4612,12 @@
         return;
     }
 
+    const bool isActiveDisplay = displayId == mActiveDisplayId;
     const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
                                            .transform(&PhysicalDisplay::isInternal)
                                            .value_or(false);
 
-    const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken);
+    const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId);
     if (isInternalDisplay && activeDisplay != display && activeDisplay &&
         activeDisplay->isPoweredOn()) {
         ALOGW("Trying to change power mode on non active display while the active display is ON");
@@ -4733,9 +4625,6 @@
 
     display->setPowerMode(mode);
 
-    if (mInterceptor->isEnabled()) {
-        mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
-    }
     const auto refreshRate = display->refreshRateConfigs().getActiveMode().getFps();
     if (*currentMode == hal::PowerMode::OFF) {
         // Turn on the display
@@ -4751,7 +4640,7 @@
             ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
         }
         getHwComposer().setPowerMode(displayId, mode);
-        if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) {
+        if (isActiveDisplay && mode != hal::PowerMode::DOZE_SUSPEND) {
             setHWCVsyncEnabled(displayId, mHWCVsyncPendingState);
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, refreshRate);
@@ -4767,7 +4656,7 @@
         if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
             ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
         }
-        if (isDisplayActiveLocked(display) && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
+        if (isActiveDisplay && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4781,7 +4670,7 @@
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
         getHwComposer().setPowerMode(displayId, mode);
-        if (isDisplayActiveLocked(display) && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
+        if (isActiveDisplay && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
             ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
             mVisibleRegionsDirty = true;
             scheduleRepaint();
@@ -4790,7 +4679,7 @@
         }
     } else if (mode == hal::PowerMode::DOZE_SUSPEND) {
         // Leave display going to doze
-        if (isDisplayActiveLocked(display)) {
+        if (isActiveDisplay) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
@@ -4800,7 +4689,7 @@
         getHwComposer().setPowerMode(displayId, mode);
     }
 
-    if (isDisplayActiveLocked(display)) {
+    if (isActiveDisplay) {
         mTimeStats->setPowerMode(mode);
         mRefreshRateStats->setPowerMode(mode);
         mScheduler->setDisplayPowerMode(mode);
@@ -5186,7 +5075,7 @@
         }
 
         StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
-                      (isDisplayActiveLocked(display) ? "active" : "inactive"));
+                      displayId == mActiveDisplayId ? "active" : "inactive");
         Layer::miniDumpHeader(result);
 
         const DisplayDevice& ref = *display;
@@ -5625,17 +5514,8 @@
                 mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns);
                 return NO_ERROR;
             }
-            case 1020: { // Layer updates interceptor
-                n = data.readInt32();
-                if (n) {
-                    ALOGV("Interceptor enabled");
-                    mInterceptor->enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
-                }
-                else{
-                    ALOGV("Interceptor disabled");
-                    mInterceptor->disable();
-                }
-                return NO_ERROR;
+            case 1020: { // Unused
+                return NAME_NOT_FOUND;
             }
             case 1021: { // Disable HWC virtual displays
                 const bool enable = data.readInt32() != 0;
@@ -5827,7 +5707,7 @@
             case 1036: {
                 if (data.readInt32() > 0) { // turn on
                     return mScheduler
-                            ->schedule([this] {
+                            ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
                                 const auto display =
                                         FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
 
@@ -5837,24 +5717,21 @@
                                 // defaultMode. The defaultMode doesn't matter for the override
                                 // policy though, since we set allowGroupSwitching to true, so it's
                                 // not a problem.
-                                scheduler::RefreshRateConfigs::Policy overridePolicy;
+                                scheduler::RefreshRateConfigs::OverridePolicy overridePolicy;
                                 overridePolicy.defaultMode = display->refreshRateConfigs()
                                                                      .getDisplayManagerPolicy()
                                                                      .defaultMode;
                                 overridePolicy.allowGroupSwitching = true;
-                                constexpr bool kOverridePolicy = true;
-                                return setDesiredDisplayModeSpecsInternal(display, overridePolicy,
-                                                                          kOverridePolicy);
+                                return setDesiredDisplayModeSpecsInternal(display, overridePolicy);
                             })
                             .get();
                 } else { // turn off
                     return mScheduler
-                            ->schedule([this] {
+                            ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
                                 const auto display =
                                         FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
-                                constexpr bool kOverridePolicy = true;
-                                return setDesiredDisplayModeSpecsInternal(display, {},
-                                                                          kOverridePolicy);
+                                return setDesiredDisplayModeSpecsInternal(
+                                        display, scheduler::RefreshRateConfigs::NoOverridePolicy{});
                             })
                             .get();
                 }
@@ -6693,7 +6570,9 @@
 
 status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
         const sp<DisplayDevice>& display,
-        const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
+        const scheduler::RefreshRateConfigs::PolicyVariant& policy) {
+    const auto displayId = display->getPhysicalId();
+
     Mutex::Autolock lock(mStateLock);
 
     if (mDebugDisplayModeSetByBackdoor) {
@@ -6701,31 +6580,31 @@
         return NO_ERROR;
     }
 
-    const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy);
-    if (setPolicyResult < 0) {
-        return BAD_VALUE;
-    }
-    if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) {
-        return NO_ERROR;
+    auto& configs = display->refreshRateConfigs();
+    using SetPolicyResult = scheduler::RefreshRateConfigs::SetPolicyResult;
+
+    switch (configs.setPolicy(policy)) {
+        case SetPolicyResult::Invalid:
+            return BAD_VALUE;
+        case SetPolicyResult::Unchanged:
+            return NO_ERROR;
+        case SetPolicyResult::Changed:
+            break;
     }
 
-    const scheduler::RefreshRateConfigs::Policy currentPolicy =
-            display->refreshRateConfigs().getCurrentPolicy();
-
+    const scheduler::RefreshRateConfigs::Policy currentPolicy = configs.getCurrentPolicy();
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
-    const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr();
-    if (isDisplayActiveLocked(display)) {
+    if (const auto activeModePtr = configs.getActiveModePtr(); displayId == mActiveDisplayId) {
         mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
         toggleKernelIdleTimer();
     } else {
         mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
     }
 
-    auto preferredModeOpt =
-            getPreferredDisplayMode(display->getPhysicalId(), currentPolicy.defaultMode);
+    auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
     if (!preferredModeOpt) {
         ALOGE("%s: Preferred mode is unknown", __func__);
         return NAME_NOT_FOUND;
@@ -6737,7 +6616,7 @@
     ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
           to_string(preferredMode->getFps()).c_str());
 
-    if (!display->refreshRateConfigs().isModeAllowed(preferredModeId)) {
+    if (!configs.isModeAllowed(preferredModeId)) {
         ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
         return INVALID_OPERATION;
     }
@@ -6756,7 +6635,7 @@
         return BAD_VALUE;
     }
 
-    auto future = mScheduler->schedule([=]() -> status_t {
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
         const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
         if (!display) {
             ALOGE("Attempt to set desired display modes for invalid display token %p",
@@ -6766,16 +6645,15 @@
             ALOGW("Attempt to set desired display modes for virtual display");
             return INVALID_OPERATION;
         } else {
-            using Policy = scheduler::RefreshRateConfigs::Policy;
+            using Policy = scheduler::RefreshRateConfigs::DisplayManagerPolicy;
             const Policy policy{DisplayModeId(defaultMode),
                                 allowGroupSwitching,
                                 {Fps::fromValue(primaryRefreshRateMin),
                                  Fps::fromValue(primaryRefreshRateMax)},
                                 {Fps::fromValue(appRequestRefreshRateMin),
                                  Fps::fromValue(appRequestRefreshRateMax)}};
-            constexpr bool kOverridePolicy = false;
 
-            return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
+            return setDesiredDisplayModeSpecsInternal(display, policy);
         }
     });
 
@@ -6910,17 +6788,6 @@
     }
 }
 
-status_t SurfaceFlinger::addTransactionTraceListener(
-        const sp<gui::ITransactionTraceListener>& listener) {
-    if (!listener) {
-        return BAD_VALUE;
-    }
-
-    mInterceptor->addTransactionTraceListener(listener);
-
-    return NO_ERROR;
-}
-
 int SurfaceFlinger::getGpuContextPriority() {
     return getRenderEngine().getContextPriority();
 }
@@ -7000,7 +6867,6 @@
     if (mTransactionTracing) {
         mTransactionTracing->onLayerAddedToDrawingState(layer->getSequence(), vsyncId.value);
     }
-    mInterceptor->saveSurfaceCreation(layer);
 }
 
 void SurfaceFlinger::sample() {
@@ -7019,7 +6885,7 @@
 void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
     ATRACE_CALL();
 
-    if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+    if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
         display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
     }
 
@@ -7027,7 +6893,7 @@
         ALOGE("%s: activeDisplay is null", __func__);
         return;
     }
-    mActiveDisplayToken = activeDisplay->getDisplayToken();
+    mActiveDisplayId = activeDisplay->getPhysicalId();
     activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
     updateInternalDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
@@ -7852,19 +7718,6 @@
     return binderStatusFromStatusT(status);
 }
 
-binder::Status SurfaceComposerAIDL::addTransactionTraceListener(
-        const sp<gui::ITransactionTraceListener>& listener) {
-    status_t status;
-    IPCThreadState* ipc = IPCThreadState::self();
-    const int uid = ipc->getCallingUid();
-    if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
-        status = mFlinger->addTransactionTraceListener(listener);
-    } else {
-        status = PERMISSION_DENIED;
-    }
-    return binderStatusFromStatusT(status);
-}
-
 binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) {
     *outPriority = mFlinger->getGpuContextPriority();
     return binder::Status::ok();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index cbb209a..1bc45d9 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -57,7 +57,6 @@
 #include <scheduler/Time.h>
 #include <ui/FenceResult.h>
 
-#include "ClientCache.h"
 #include "Display/DisplayMap.h"
 #include "Display/PhysicalDisplay.h"
 #include "DisplayDevice.h"
@@ -66,19 +65,17 @@
 #include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
 #include "FlagManager.h"
-#include "FrameTracker.h"
 #include "LayerVector.h"
-#include "LocklessQueue.h"
 #include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/RefreshRateStats.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "ThreadContext.h"
-#include "TracedOrdinal.h"
 #include "Tracing/LayerTracing.h"
 #include "Tracing/TransactionTracing.h"
 #include "TransactionCallbackInvoker.h"
+#include "TransactionHandler.h"
 #include "TransactionState.h"
 
 #include <atomic>
@@ -337,6 +334,7 @@
     friend class RegionSamplingThread;
     friend class LayerRenderArea;
     friend class LayerTracing;
+    friend class SurfaceComposerAIDL;
 
     // For unit tests
     friend class TestableSurfaceFlinger;
@@ -590,8 +588,6 @@
 
     status_t setOverrideFrameRate(uid_t uid, float frameRate);
 
-    status_t addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener);
-
     int getGpuContextPriority();
 
     status_t getMaxAcquiredBufferCount(int* buffers) const;
@@ -633,8 +629,8 @@
 
     // Toggles hardware VSYNC by calling into HWC.
     void setVsyncEnabled(bool) override;
-    // Sets the desired display mode if allowed by policy.
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override;
+    // Sets the desired display mode per display if allowed by policy .
+    void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override;
     // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
     void kernelTimerChanged(bool expired) override;
     // Called when the frame rate override list changed to trigger an event.
@@ -680,11 +676,9 @@
                                                           DisplayModeId defaultModeId) const
             REQUIRES(mStateLock);
 
-    // Sets the desired display mode specs.
-    status_t setDesiredDisplayModeSpecsInternal(
-            const sp<DisplayDevice>& display,
-            const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
-            EXCLUDES(mStateLock);
+    status_t setDesiredDisplayModeSpecsInternal(const sp<DisplayDevice>&,
+                                                const scheduler::RefreshRateConfigs::PolicyVariant&)
+            EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
 
     void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
     void commitTransactionsLocked(uint32_t transactionFlags)
@@ -725,11 +719,13 @@
     bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
-
-    int flushPendingTransactionQueues(
-            std::vector<TransactionState>& transactions,
-            std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-            bool tryApplyUnsignaled) REQUIRES(mStateLock) REQUIRES(kMainThreadContext);
+    void addTransactionReadyFilters();
+    TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
+            const TransactionHandler::TransactionFlushState& flushState)
+            REQUIRES(kMainThreadContext);
+    TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
+            const TransactionHandler::TransactionFlushState& flushState)
+            REQUIRES(kMainThreadContext);
 
     uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
                                   int64_t desiredPresentTime, bool isAutoTimestamp,
@@ -747,23 +743,9 @@
 
     void commitOffscreenLayers();
 
-    enum class TransactionReadiness {
-        NotReady,
-        NotReadyBarrier,
-        Ready,
-        ReadyUnsignaled,
-    };
-    TransactionReadiness transactionIsReadyToBeApplied(
-            TransactionState&, const FrameTimelineInfo&, bool isAutoTimestamp,
-            TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>&,
-            const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
-                    bufferLayersReadyToPresent,
-            size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock)
-            REQUIRES(kMainThreadContext);
-
     static LatchUnsignaledConfig getLatchUnsignaledConfig();
     bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
-                               size_t totalTXapplied) const;
+                               bool firstTransaction) const;
     bool applyTransactions(std::vector<TransactionState>& transactions, VsyncId)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -835,10 +817,6 @@
     void initializeDisplays();
     void onInitializeDisplays() REQUIRES(mStateLock, kMainThreadContext);
 
-    bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
-        return display->getDisplayToken() == mActiveDisplayToken;
-    }
-
     sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
             REQUIRES(mStateLock) {
         return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(displayToken);
@@ -873,12 +851,12 @@
     }
 
     sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
-        if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+        if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
             return display;
         }
         // The active display is outdated, so fall back to the primary display.
-        mActiveDisplayToken.clear();
-        return getDisplayDeviceLocked(getPrimaryDisplayTokenLocked());
+        mActiveDisplayId = getPrimaryDisplayIdLocked();
+        return getDisplayDeviceLocked(mActiveDisplayId);
     }
 
     sp<const DisplayDevice> getDefaultDisplayDevice() const EXCLUDES(mStateLock) {
@@ -1088,7 +1066,6 @@
     status_t CheckTransactCodeCredentials(uint32_t code);
 
     // Add transaction to the Transaction Queue
-    void queueTransaction(TransactionState& state);
 
     /*
      * Generic Layer Metadata
@@ -1201,6 +1178,9 @@
 
     display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
 
+    // The inner or outer display for foldables, assuming they have mutually exclusive power states.
+    PhysicalDisplayId mActiveDisplayId GUARDED_BY(mStateLock);
+
     struct {
         DisplayIdGenerator<GpuVirtualDisplayId> gpu;
         std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
@@ -1214,7 +1194,6 @@
 
     bool mLayerCachingEnabled = false;
     bool mPropagateBackpressureClientComposition = false;
-    sp<SurfaceInterceptor> mInterceptor;
 
     LayerTracing mLayerTracing{*this};
     bool mLayerTracingEnabled = false;
@@ -1245,10 +1224,6 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
-    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
-            mPendingTransactionQueues;
-    LocklessQueue<TransactionState> mLocklessTransactionQueue;
-    std::atomic<size_t> mPendingTransactionCount = 0;
     std::atomic<size_t> mNumLayers = 0;
 
     // to linkToDeath
@@ -1390,8 +1365,6 @@
                 [](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
     }
 
-    wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock);
-
     const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
 
     FlagManager mFlagManager;
@@ -1408,7 +1381,7 @@
         bool early = false;
     } mPowerHintSessionMode;
 
-    friend class SurfaceComposerAIDL;
+    TransactionHandler mTransactionHandler;
 };
 
 class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1511,8 +1484,6 @@
             const sp<IBinder>& displayToken,
             std::optional<gui::DisplayDecorationSupport>* outSupport) override;
     binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
-    binder::Status addTransactionTraceListener(
-            const sp<gui::ITransactionTraceListener>& listener) override;
     binder::Status getGpuContextPriority(int32_t* outPriority) override;
     binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
     binder::Status addWindowInfosListener(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 3e30dcb..2f1f263 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -29,7 +29,6 @@
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerDefaultFactory.h"
 #include "SurfaceFlingerProperties.h"
-#include "SurfaceInterceptor.h"
 
 #include "DisplayHardware/ComposerHal.h"
 #include "FrameTimeline/FrameTimeline.h"
@@ -54,10 +53,6 @@
     }
 }
 
-sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
-    return sp<android::impl::SurfaceInterceptor>::make();
-}
-
 sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
         bool timestampPropertyValue) {
     return sp<StartPropertySetThread>::make(timestampPropertyValue);
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 6fca402..447a02f 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,6 @@
     std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) override;
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override;
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 6d18ade..9c4d5c8 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -40,7 +40,6 @@
 class Layer;
 class StartPropertySetThread;
 class SurfaceFlinger;
-class SurfaceInterceptor;
 class TimeStats;
 
 struct DisplayDeviceCreationArgs;
@@ -71,7 +70,6 @@
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
     virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) = 0;
-    virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
             bool timestampPropertyValue) = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
deleted file mode 100644
index c90ae4c..0000000
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ /dev/null
@@ -1,711 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#undef LOG_TAG
-#define LOG_TAG "SurfaceInterceptor"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "Layer.h"
-#include "SurfaceFlinger.h"
-#include "SurfaceInterceptor.h"
-
-#include <fstream>
-
-#include <android-base/file.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-// TODO(marissaw): add new layer state values to SurfaceInterceptor
-
-SurfaceInterceptor::~SurfaceInterceptor() = default;
-
-namespace impl {
-
-void SurfaceInterceptor::addTransactionTraceListener(
-        const sp<gui::ITransactionTraceListener>& listener) {
-    sp<IBinder> asBinder = IInterface::asBinder(listener);
-
-    std::scoped_lock lock(mListenersMutex);
-
-    asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
-
-    listener->onToggled(mEnabled); // notifies of current state
-
-    mTraceToggledListeners.emplace(asBinder, listener);
-}
-
-void SurfaceInterceptor::binderDied(const wp<IBinder>& who) {
-    std::scoped_lock lock(mListenersMutex);
-    mTraceToggledListeners.erase(who);
-}
-
-void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
-        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
-{
-    if (mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mTraceToggledListeners) {
-            listener->onToggled(true);
-        }
-    }
-    mEnabled = true;
-    std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
-    saveExistingDisplaysLocked(displays);
-    saveExistingSurfacesLocked(layers);
-}
-
-void SurfaceInterceptor::disable() {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mTraceToggledListeners) {
-            listener->onToggled(false);
-        }
-    }
-    mEnabled = false;
-    std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
-    status_t err(writeProtoFileLocked());
-    ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
-    ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
-    mTrace.Clear();
-}
-
-bool SurfaceInterceptor::isEnabled() {
-    return mEnabled;
-}
-
-void SurfaceInterceptor::saveExistingDisplaysLocked(
-        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
-{
-    // Caveat: The initial snapshot does not capture the power mode of the existing displays
-    ATRACE_CALL();
-    for (size_t i = 0 ; i < displays.size() ; i++) {
-        addDisplayCreationLocked(createTraceIncrementLocked(), displays[i]);
-        addInitialDisplayStateLocked(createTraceIncrementLocked(), displays[i]);
-    }
-}
-
-void SurfaceInterceptor::saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers) {
-    ATRACE_CALL();
-    for (const auto& l : layers) {
-        l->traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* layer) {
-            addSurfaceCreationLocked(createTraceIncrementLocked(), sp<Layer>::fromExisting(layer));
-            addInitialSurfaceStateLocked(createTraceIncrementLocked(),
-                                         sp<Layer>::fromExisting(layer));
-        });
-    }
-}
-
-void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    Transaction* transaction(increment->mutable_transaction());
-    const uint32_t layerFlags = layer->getTransactionFlags();
-    transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
-
-    const int32_t layerId(getLayerId(layer));
-    addPositionLocked(transaction, layerId, layer->mDrawingState.transform.tx(),
-                      layer->mDrawingState.transform.ty());
-    addDepthLocked(transaction, layerId, layer->mDrawingState.z);
-    addAlphaLocked(transaction, layerId, layer->mDrawingState.color.a);
-    addLayerStackLocked(transaction, layerId, layer->mDrawingState.layerStack);
-    addCropLocked(transaction, layerId, layer->mDrawingState.crop);
-    addCornerRadiusLocked(transaction, layerId, layer->mDrawingState.cornerRadius);
-    addBackgroundBlurRadiusLocked(transaction, layerId, layer->mDrawingState.backgroundBlurRadius);
-    addBlurRegionsLocked(transaction, layerId, layer->mDrawingState.blurRegions);
-    addFlagsLocked(transaction, layerId, layer->mDrawingState.flags,
-                   layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
-                           layer_state_t::eLayerSecure);
-    addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mDrawingParent));
-    addRelativeParentLocked(transaction, layerId,
-                            getLayerIdFromWeakRef(layer->mDrawingState.zOrderRelativeOf),
-                            layer->mDrawingState.z);
-    addShadowRadiusLocked(transaction, layerId, layer->mDrawingState.shadowRadius);
-    addTrustedOverlayLocked(transaction, layerId, layer->mDrawingState.isTrustedOverlay);
-}
-
-void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
-        const DisplayDeviceState& display)
-{
-    Transaction* transaction(increment->mutable_transaction());
-    transaction->set_synchronous(false);
-    transaction->set_animation(false);
-
-    addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface);
-    addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
-    addDisplayFlagsLocked(transaction, display.sequenceId, display.flags);
-    addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
-    addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
-                               display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
-}
-
-status_t SurfaceInterceptor::writeProtoFileLocked() {
-    ATRACE_CALL();
-    std::string output;
-
-    if (!mTrace.IsInitialized()) {
-        return NOT_ENOUGH_DATA;
-    }
-    if (!mTrace.SerializeToString(&output)) {
-        return PERMISSION_DENIED;
-    }
-    if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
-        return PERMISSION_DENIED;
-    }
-
-    return NO_ERROR;
-}
-
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const {
-    sp<IBinder> handle = weakHandle.promote();
-    return Layer::fromHandle(handle).promote();
-}
-
-int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
-    return layer->sequence;
-}
-
-int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) const {
-    if (layer == nullptr) {
-        return -1;
-    }
-    auto strongLayer = layer.promote();
-    return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
-}
-
-int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const {
-    if (handle == nullptr) {
-        return -1;
-    }
-    const sp<const Layer> layer = Layer::fromHandle(handle).promote();
-    return layer == nullptr ? -1 : getLayerId(layer);
-}
-
-Increment* SurfaceInterceptor::createTraceIncrementLocked() {
-    Increment* increment(mTrace.add_increment());
-    increment->set_time_stamp(elapsedRealtimeNano());
-    return increment;
-}
-
-SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transaction,
-        int32_t layerId)
-{
-    SurfaceChange* change(transaction->add_surface_change());
-    change->set_id(layerId);
-    return change;
-}
-
-DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
-        int32_t sequenceId)
-{
-    DisplayChange* dispChange(transaction->add_display_change());
-    dispChange->set_id(sequenceId);
-    return dispChange;
-}
-
-void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) {
-    protoRect->set_left(rect.left);
-    protoRect->set_top(rect.top);
-    protoRect->set_right(rect.right);
-    protoRect->set_bottom(rect.bottom);
-}
-
-void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid,
-                                                    int32_t uid) {
-    Origin* origin(transaction->mutable_origin());
-    origin->set_pid(pid);
-    origin->set_uid(uid);
-}
-
-void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
-        float x, float y)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    PositionChange* posChange(change->mutable_position());
-    posChange->set_x(x);
-    posChange->set_y(y);
-}
-
-void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId,
-        uint32_t z)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    LayerChange* depthChange(change->mutable_layer());
-    depthChange->set_layer(z);
-}
-
-void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w,
-        uint32_t h)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    SizeChange* sizeChange(change->mutable_size());
-    sizeChange->set_w(w);
-    sizeChange->set_h(h);
-}
-
-void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId,
-        float alpha)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    AlphaChange* alphaChange(change->mutable_alpha());
-    alphaChange->set_alpha(alpha);
-}
-
-void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId,
-        const layer_state_t::matrix22_t& matrix)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    MatrixChange* matrixChange(change->mutable_matrix());
-    matrixChange->set_dsdx(matrix.dsdx);
-    matrixChange->set_dtdx(matrix.dtdx);
-    matrixChange->set_dsdy(matrix.dsdy);
-    matrixChange->set_dtdy(matrix.dtdy);
-}
-
-void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction,
-        int32_t layerId, const Region& transRegion)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint());
-
-    for (const auto& rect : transRegion) {
-        Rectangle* protoRect(transparentChange->add_region());
-        setProtoRectLocked(protoRect, rect);
-    }
-}
-
-void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags,
-                                        uint8_t mask) {
-    // There can be multiple flags changed
-    if (mask & layer_state_t::eLayerHidden) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        HiddenFlagChange* flagChange(change->mutable_hidden_flag());
-        flagChange->set_hidden_flag(flags & layer_state_t::eLayerHidden);
-    }
-    if (mask & layer_state_t::eLayerOpaque) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
-        flagChange->set_opaque_flag(flags & layer_state_t::eLayerOpaque);
-    }
-    if (mask & layer_state_t::eLayerSecure) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        SecureFlagChange* flagChange(change->mutable_secure_flag());
-        flagChange->set_secure_flag(flags & layer_state_t::eLayerSecure);
-    }
-}
-
-void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
-                                             ui::LayerStack layerStack) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    LayerStackChange* layerStackChange(change->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack.id);
-}
-
-void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
-        const Rect& rect)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    CropChange* cropChange(change->mutable_crop());
-    Rectangle* protoRect(cropChange->mutable_rectangle());
-    setProtoRectLocked(protoRect, rect);
-}
-
-void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId,
-                                       float cornerRadius)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius());
-    cornerRadiusChange->set_corner_radius(cornerRadius);
-}
-
-void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
-                                                       int32_t backgroundBlurRadius) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius());
-    blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
-}
-
-void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
-                                              const std::vector<BlurRegion>& blurRegions) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions());
-    for (const auto blurRegion : blurRegions) {
-        const auto blurRegionChange = blurRegionsChange->add_blur_regions();
-        blurRegionChange->set_blur_radius(blurRegion.blurRadius);
-        blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL);
-        blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR);
-        blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL);
-        blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR);
-        blurRegionChange->set_alpha(blurRegion.alpha);
-        blurRegionChange->set_left(blurRegion.left);
-        blurRegionChange->set_top(blurRegion.top);
-        blurRegionChange->set_right(blurRegion.right);
-        blurRegionChange->set_bottom(blurRegion.bottom);
-    }
-}
-
-void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
-                                           int32_t parentId) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    ReparentChange* overrideChange(change->mutable_reparent());
-    overrideChange->set_parent_id(parentId);
-}
-
-void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
-                                                 int32_t parentId, int z) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    RelativeParentChange* overrideChange(change->mutable_relative_parent());
-    overrideChange->set_relative_parent_id(parentId);
-    overrideChange->set_z(z);
-}
-
-void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId,
-                                               float shadowRadius) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    ShadowRadiusChange* overrideChange(change->mutable_shadow_radius());
-    overrideChange->set_radius(shadowRadius);
-}
-
-void SurfaceInterceptor::addTrustedOverlayLocked(Transaction* transaction, int32_t layerId,
-                                                 bool isTrustedOverlay) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    TrustedOverlayChange* overrideChange(change->mutable_trusted_overlay());
-    overrideChange->set_is_trusted_overlay(isTrustedOverlay);
-}
-
-void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
-        const layer_state_t& state)
-{
-    const sp<const Layer> layer(getLayer(state.surface));
-    if (layer == nullptr) {
-        ALOGE("An existing layer could not be retrieved with the surface "
-                "from the layer_state_t surface in the update transaction");
-        return;
-    }
-
-    const int32_t layerId(getLayerId(layer));
-
-    if (state.what & layer_state_t::ePositionChanged) {
-        addPositionLocked(transaction, layerId, state.x, state.y);
-    }
-    if (state.what & layer_state_t::eLayerChanged) {
-        addDepthLocked(transaction, layerId, state.z);
-    }
-    if (state.what & layer_state_t::eAlphaChanged) {
-        addAlphaLocked(transaction, layerId, state.alpha);
-    }
-    if (state.what & layer_state_t::eMatrixChanged) {
-        addMatrixLocked(transaction, layerId, state.matrix);
-    }
-    if (state.what & layer_state_t::eTransparentRegionChanged) {
-        addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
-    }
-    if (state.what & layer_state_t::eFlagsChanged) {
-        addFlagsLocked(transaction, layerId, state.flags, state.mask);
-    }
-    if (state.what & layer_state_t::eLayerStackChanged) {
-        addLayerStackLocked(transaction, layerId, state.layerStack);
-    }
-    if (state.what & layer_state_t::eCropChanged) {
-        addCropLocked(transaction, layerId, state.crop);
-    }
-    if (state.what & layer_state_t::eCornerRadiusChanged) {
-        addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
-    }
-    if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
-        addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
-    }
-    if (state.what & layer_state_t::eBlurRegionsChanged) {
-        addBlurRegionsLocked(transaction, layerId, state.blurRegions);
-    }
-    if (state.what & layer_state_t::eReparent) {
-        auto parentHandle = (state.parentSurfaceControlForChild)
-                ? state.parentSurfaceControlForChild->getHandle()
-                : nullptr;
-        addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
-    }
-    if (state.what & layer_state_t::eRelativeLayerChanged) {
-        addRelativeParentLocked(transaction, layerId,
-                                getLayerIdFromHandle(
-                                        state.relativeLayerSurfaceControl->getHandle()),
-                                state.z);
-    }
-    if (state.what & layer_state_t::eShadowRadiusChanged) {
-        addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
-    }
-    if (state.what & layer_state_t::eTrustedOverlayChanged) {
-        addTrustedOverlayLocked(transaction, layerId, state.isTrustedOverlay);
-    }
-    if (state.what & layer_state_t::eStretchChanged) {
-        ALOGW("SurfaceInterceptor not implemented for eStretchChanged");
-    }
-}
-
-void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
-        const DisplayState& state, int32_t sequenceId)
-{
-    if (state.what & DisplayState::eSurfaceChanged) {
-        addDisplaySurfaceLocked(transaction, sequenceId, state.surface);
-    }
-    if (state.what & DisplayState::eLayerStackChanged) {
-        addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack);
-    }
-    if (state.what & DisplayState::eFlagsChanged) {
-        addDisplayFlagsLocked(transaction, sequenceId, state.flags);
-    }
-    if (state.what & DisplayState::eDisplaySizeChanged) {
-        addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
-    }
-    if (state.what & DisplayState::eDisplayProjectionChanged) {
-        addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
-                                   state.layerStackSpaceRect, state.orientedDisplaySpaceRect);
-    }
-}
-
-void SurfaceInterceptor::addTransactionLocked(
-        Increment* increment, const Vector<ComposerState>& stateUpdates,
-        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-        const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPid,
-        int originUid, uint64_t transactionId) {
-    Transaction* transaction(increment->mutable_transaction());
-    transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
-    setTransactionOriginLocked(transaction, originPid, originUid);
-    transaction->set_id(transactionId);
-    for (const auto& compState: stateUpdates) {
-        addSurfaceChangesLocked(transaction, compState.state);
-    }
-    for (const auto& disp: changedDisplays) {
-        ssize_t dpyIdx = displays.indexOfKey(disp.token);
-        if (dpyIdx >= 0) {
-            const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
-            addDisplayChangesLocked(transaction, disp, dispState.sequenceId);
-        }
-    }
-}
-
-void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    SurfaceCreation* creation(increment->mutable_surface_creation());
-    creation->set_id(getLayerId(layer));
-    creation->set_name(layer->getName());
-}
-
-void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    SurfaceDeletion* deletion(increment->mutable_surface_deletion());
-    deletion->set_id(getLayerId(layer));
-}
-
-void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, int32_t layerId,
-        uint32_t width, uint32_t height, uint64_t frameNumber)
-{
-    BufferUpdate* update(increment->mutable_buffer_update());
-    update->set_id(layerId);
-    update->set_w(width);
-    update->set_h(height);
-    update->set_frame_number(frameNumber);
-}
-
-void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp) {
-    VSyncEvent* event(increment->mutable_vsync_event());
-    event->set_when(timestamp);
-}
-
-void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
-        const sp<const IGraphicBufferProducer>& surface)
-{
-    if (surface == nullptr) {
-        return;
-    }
-    uint64_t bufferQueueId = 0;
-    status_t err(surface->getUniqueId(&bufferQueueId));
-    if (err == NO_ERROR) {
-        DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-        DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
-        surfaceChange->set_buffer_queue_id(bufferQueueId);
-        surfaceChange->set_buffer_queue_name(surface->getConsumerName().string());
-    }
-    else {
-        ALOGE("invalid graphic buffer producer received while tracing a display change (%s)",
-                strerror(-err));
-    }
-}
-
-void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
-                                                    ui::LayerStack layerStack) {
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack.id);
-}
-
-void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId,
-                                               uint32_t flags) {
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    DisplayFlagsChange* flagsChange(dispChange->mutable_flags());
-    flagsChange->set_flags(flags);
-}
-
-void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId,
-        uint32_t w, uint32_t h)
-{
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    SizeChange* sizeChange(dispChange->mutable_size());
-    sizeChange->set_w(w);
-    sizeChange->set_h(h);
-}
-
-void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
-        int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame)
-{
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    ProjectionChange* projectionChange(dispChange->mutable_projection());
-    projectionChange->set_orientation(orientation);
-    Rectangle* viewportRect(projectionChange->mutable_viewport());
-    setProtoRectLocked(viewportRect, viewport);
-    Rectangle* frameRect(projectionChange->mutable_frame());
-    setProtoRectLocked(frameRect, frame);
-}
-
-void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment,
-        const DisplayDeviceState& info)
-{
-    DisplayCreation* creation(increment->mutable_display_creation());
-    creation->set_id(info.sequenceId);
-    creation->set_name(info.displayName);
-    creation->set_is_secure(info.isSecure);
-    if (info.physical) {
-        creation->set_display_id(info.physical->id.value);
-    }
-}
-
-void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t sequenceId) {
-    DisplayDeletion* deletion(increment->mutable_display_deletion());
-    deletion->set_id(sequenceId);
-}
-
-void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId,
-        int32_t mode)
-{
-    PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
-    powerModeUpdate->set_id(sequenceId);
-    powerModeUpdate->set_mode(mode);
-}
-
-void SurfaceInterceptor::saveTransaction(
-        const Vector<ComposerState>& stateUpdates,
-        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-        const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, int originUid,
-        uint64_t transactionId) {
-    if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
-                         flags, originPid, originUid, transactionId);
-}
-
-void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
-    if (!mEnabled || layer == nullptr) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
-}
-
-void SurfaceInterceptor::saveSurfaceDeletion(const sp<const Layer>& layer) {
-    if (!mEnabled || layer == nullptr) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
-}
-
-/**
- * Here we pass the layer by ID instead of by sp<> since this is called without
- * holding the state-lock from a Binder thread. If we required the caller
- * to pass 'this' by sp<> the temporary sp<> constructed could end up
- * being the last reference and we might accidentally destroy the Layer
- * from this binder thread.
- */
-void SurfaceInterceptor::saveBufferUpdate(int32_t layerId, uint32_t width,
-        uint32_t height, uint64_t frameNumber)
-{
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addBufferUpdateLocked(createTraceIncrementLocked(), layerId, width, height, frameNumber);
-}
-
-void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
-    if (!mEnabled) {
-        return;
-    }
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addVSyncUpdateLocked(createTraceIncrementLocked(), timestamp);
-}
-
-void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addDisplayCreationLocked(createTraceIncrementLocked(), info);
-}
-
-void SurfaceInterceptor::saveDisplayDeletion(int32_t sequenceId) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addDisplayDeletionLocked(createTraceIncrementLocked(), sequenceId);
-}
-
-void SurfaceInterceptor::savePowerModeUpdate(int32_t sequenceId, int32_t mode) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addPowerModeUpdateLocked(createTraceIncrementLocked(), sequenceId, mode);
-}
-
-} // namespace impl
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
deleted file mode 100644
index 970c3e5..0000000
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEINTERCEPTOR_H
-#define ANDROID_SURFACEINTERCEPTOR_H
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <mutex>
-
-#include <binder/IBinder.h>
-
-#include <gui/LayerState.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/SortedVector.h>
-#include <utils/StrongPointer.h>
-#include <utils/Vector.h>
-
-#include "DisplayDevice.h"
-
-namespace android {
-
-class BufferItem;
-class Layer;
-class SurfaceFlinger;
-struct ComposerState;
-struct DisplayDeviceState;
-struct DisplayState;
-struct layer_state_t;
-using Transaction = surfaceflinger::Transaction;
-using Trace = surfaceflinger::Trace;
-using Rectangle = surfaceflinger::Rectangle;
-using SurfaceChange = surfaceflinger::SurfaceChange;
-using Increment = surfaceflinger::Increment;
-using DisplayChange = surfaceflinger::DisplayChange;
-
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
-
-class SurfaceInterceptor : public IBinder::DeathRecipient {
-public:
-    virtual ~SurfaceInterceptor();
-
-    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
-    virtual void enable(const SortedVector<sp<Layer>>& layers,
-                        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) = 0;
-    virtual void disable() = 0;
-    virtual bool isEnabled() = 0;
-
-    virtual void addTransactionTraceListener(
-            const sp<gui::ITransactionTraceListener>& listener) = 0;
-    virtual void binderDied(const wp<IBinder>& who) = 0;
-
-    // Intercept display and surface transactions
-    virtual void saveTransaction(
-            const Vector<ComposerState>& stateUpdates,
-            const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-            const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
-            int originUid, uint64_t transactionId) = 0;
-
-    // Intercept surface data
-    virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
-    virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0;
-    virtual void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
-                                  uint64_t frameNumber) = 0;
-
-    // Intercept display data
-    virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0;
-    virtual void saveDisplayDeletion(int32_t sequenceId) = 0;
-    virtual void savePowerModeUpdate(int32_t sequenceId, int32_t mode) = 0;
-    virtual void saveVSyncEvent(nsecs_t timestamp) = 0;
-};
-
-namespace impl {
-
-/*
- * SurfaceInterceptor intercepts and stores incoming streams of window
- * properties on SurfaceFlinger.
- */
-class SurfaceInterceptor final : public android::SurfaceInterceptor {
-public:
-    SurfaceInterceptor() = default;
-    ~SurfaceInterceptor() override = default;
-
-    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
-    void enable(const SortedVector<sp<Layer>>& layers,
-                const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) override;
-    void disable() override;
-    bool isEnabled() override;
-
-    void addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener) override;
-    void binderDied(const wp<IBinder>& who) override;
-
-    // Intercept display and surface transactions
-    void saveTransaction(const Vector<ComposerState>& stateUpdates,
-                         const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-                         const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
-                         int originUid, uint64_t transactionId) override;
-
-    // Intercept surface data
-    void saveSurfaceCreation(const sp<const Layer>& layer) override;
-    void saveSurfaceDeletion(const sp<const Layer>& layer) override;
-    void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
-                          uint64_t frameNumber) override;
-
-    // Intercept display data
-    void saveDisplayCreation(const DisplayDeviceState& info) override;
-    void saveDisplayDeletion(int32_t sequenceId) override;
-    void savePowerModeUpdate(int32_t sequenceId, int32_t mode) override;
-    void saveVSyncEvent(nsecs_t timestamp) override;
-
-private:
-    // The creation increments of Surfaces and Displays do not contain enough information to capture
-    // the initial state of each object, so a transaction with all of the missing properties is
-    // performed at the initial snapshot for each display and surface.
-    void saveExistingDisplaysLocked(
-            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
-    void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers);
-    void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer);
-    void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
-
-    status_t writeProtoFileLocked();
-    const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const;
-    int32_t getLayerId(const sp<const Layer>& layer) const;
-    int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
-    int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const;
-
-    Increment* createTraceIncrementLocked();
-    void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
-    void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
-    void addBufferUpdateLocked(Increment* increment, int32_t layerId, uint32_t width,
-            uint32_t height, uint64_t frameNumber);
-    void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
-    void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
-    void addDisplayDeletionLocked(Increment* increment, int32_t sequenceId);
-    void addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode);
-
-    // Add surface transactions to the trace
-    SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
-    void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
-    void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
-    void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
-    void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
-    void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
-    void addMatrixLocked(Transaction* transaction, int32_t layerId,
-            const layer_state_t::matrix22_t& matrix);
-    void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
-            const Region& transRegion);
-    void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask);
-    void addLayerStackLocked(Transaction* transaction, int32_t layerId, ui::LayerStack);
-    void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
-    void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
-    void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
-                                       int32_t backgroundBlurRadius);
-    void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
-                              const std::vector<BlurRegion>& effectRegions);
-    void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
-    void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
-                              const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-                              const Vector<DisplayState>& changedDisplays,
-                              uint32_t transactionFlags, int originPid, int originUid,
-                              uint64_t transactionId);
-    void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
-    void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
-                                 int z);
-    void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
-    void addTrustedOverlayLocked(Transaction* transaction, int32_t layerId, bool isTrustedOverlay);
-
-    // Add display transactions to the trace
-    DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
-    void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
-            const sp<const IGraphicBufferProducer>& surface);
-    void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, ui::LayerStack);
-    void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags);
-    void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
-            uint32_t h);
-    void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId,
-            int32_t orientation, const Rect& viewport, const Rect& frame);
-    void addDisplayChangesLocked(Transaction* transaction,
-            const DisplayState& state, int32_t sequenceId);
-
-    // Add transaction origin to trace
-    void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid);
-
-    bool mEnabled {false};
-    std::string mOutputFileName {DEFAULT_FILENAME};
-    std::mutex mTraceMutex {};
-    Trace mTrace {};
-    std::mutex mListenersMutex;
-    std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners
-            GUARDED_BY(mListenersMutex);
-};
-
-} // namespace impl
-
-} // namespace android
-
-#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 872a901..2232bb9 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include <gui/fake/BufferData.h>
 #include <layerproto/TransactionProto.h>
 #include <utils/RefBase.h>
 
@@ -43,35 +44,6 @@
     TracingLayerCreationArgs args;
 };
 
-// Class which exposes buffer properties from BufferData without holding on to the actual buffer
-// handle.
-class BufferDataStub : public BufferData {
-public:
-    BufferDataStub(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
-                   uint64_t outUsage)
-          : mBufferId(bufferId),
-            mWidth(width),
-            mHeight(height),
-            mPixelFormat(pixelFormat),
-            mOutUsage(outUsage) {}
-    bool hasBuffer() const override { return mBufferId != 0; }
-    bool hasSameBuffer(const BufferData& other) const override {
-        return getId() == other.getId() && frameNumber == other.frameNumber;
-    }
-    uint32_t getWidth() const override { return mWidth; }
-    uint32_t getHeight() const override { return mHeight; }
-    uint64_t getId() const override { return mBufferId; }
-    PixelFormat getPixelFormat() const override { return mPixelFormat; }
-    uint64_t getUsage() const override { return mOutUsage; }
-
-private:
-    uint64_t mBufferId;
-    uint32_t mWidth;
-    uint32_t mHeight;
-    int32_t mPixelFormat;
-    uint64_t mOutUsage;
-};
-
 class TransactionProtoParser {
 public:
     // Utility class to map handles to ids and buffers to buffer properties without pulling
@@ -87,7 +59,7 @@
         virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
                                                            uint32_t height, int32_t pixelFormat,
                                                            uint64_t usage) const {
-            return std::make_shared<BufferDataStub>(bufferId, width, height, pixelFormat, usage);
+            return std::make_shared<fake::BufferData>(bufferId, width, height, pixelFormat, usage);
         }
         virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */,
                                                          uint64_t* /* outBufferId */,
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index b143160..d0364f2 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -47,10 +47,6 @@
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return sp<android::impl::SurfaceInterceptor>::make();
-    }
-
     sp<StartPropertySetThread> createStartPropertySetThread(
             bool /* timestampPropertyValue */) override {
         return sp<StartPropertySetThread>();
diff --git a/services/surfaceflinger/TransactionHandler.cpp b/services/surfaceflinger/TransactionHandler.cpp
new file mode 100644
index 0000000..6c6a487
--- /dev/null
+++ b/services/surfaceflinger/TransactionHandler.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionHandler"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cutils/trace.h>
+#include <utils/Log.h>
+
+#include "TransactionHandler.h"
+
+namespace android {
+
+void TransactionHandler::queueTransaction(TransactionState&& state) {
+    mLocklessTransactionQueue.push(std::move(state));
+    mPendingTransactionCount.fetch_add(1);
+    ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+}
+
+std::vector<TransactionState> TransactionHandler::flushTransactions() {
+    while (!mLocklessTransactionQueue.isEmpty()) {
+        auto maybeTransaction = mLocklessTransactionQueue.pop();
+        if (!maybeTransaction.has_value()) {
+            break;
+        }
+        auto transaction = maybeTransaction.value();
+        mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
+    }
+
+    // Collect transaction that are ready to be applied.
+    std::vector<TransactionState> transactions;
+    TransactionFlushState flushState;
+    flushState.queueProcessTime = systemTime();
+    // Transactions with a buffer pending on a barrier may be on a different applyToken
+    // than the transaction which satisfies our barrier. In fact this is the exact use case
+    // that the primitive is designed for. This means we may first process
+    // the barrier dependent transaction, determine it ineligible to complete
+    // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
+    // The barrier dependent transaction was eligible to be presented in this frame
+    // but we would have prevented it without case. To fix this we continually
+    // loop through flushPendingTransactionQueues until we perform an iteration
+    // where the number of transactionsPendingBarrier doesn't change. This way
+    // we can continue to resolve dependency chains of barriers as far as possible.
+    int lastTransactionsPendingBarrier = 0;
+    int transactionsPendingBarrier = 0;
+    do {
+        lastTransactionsPendingBarrier = transactionsPendingBarrier;
+        // Collect transactions that are ready to be applied.
+        transactionsPendingBarrier = flushPendingTransactionQueues(transactions, flushState);
+    } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
+
+    mPendingTransactionCount.fetch_sub(transactions.size());
+    ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+    return transactions;
+}
+
+TransactionHandler::TransactionReadiness TransactionHandler::applyFilters(
+        TransactionFlushState& flushState) {
+    auto ready = TransactionReadiness::Ready;
+    for (auto& filter : mTransactionReadyFilters) {
+        auto perFilterReady = filter(flushState);
+        switch (perFilterReady) {
+            case TransactionReadiness::NotReady:
+            case TransactionReadiness::NotReadyBarrier:
+                return perFilterReady;
+
+            case TransactionReadiness::ReadyUnsignaled:
+            case TransactionReadiness::ReadyUnsignaledSingle:
+                // If one of the filters allows latching an unsignaled buffer, latch this ready
+                // state.
+                ready = perFilterReady;
+                break;
+            case TransactionReadiness::Ready:
+                continue;
+        }
+    }
+    return ready;
+}
+
+int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions,
+                                                      TransactionFlushState& flushState) {
+    int transactionsPendingBarrier = 0;
+    auto it = mPendingTransactionQueues.begin();
+    while (it != mPendingTransactionQueues.end()) {
+        auto& queue = it->second;
+        IBinder* queueToken = it->first.get();
+
+        // if we have already flushed a transaction with an unsignaled buffer then stop queue
+        // processing
+        if (std::find(flushState.queuesWithUnsignaledBuffers.begin(),
+                      flushState.queuesWithUnsignaledBuffers.end(),
+                      queueToken) != flushState.queuesWithUnsignaledBuffers.end()) {
+            continue;
+        }
+
+        while (!queue.empty()) {
+            auto& transaction = queue.front();
+            flushState.transaction = &transaction;
+            auto ready = applyFilters(flushState);
+            if (ready == TransactionReadiness::NotReadyBarrier) {
+                transactionsPendingBarrier++;
+                break;
+            } else if (ready == TransactionReadiness::NotReady) {
+                break;
+            }
+
+            // Transaction is ready move it from the pending queue.
+            flushState.firstTransaction = false;
+            removeFromStalledTransactions(transaction.id);
+            transactions.emplace_back(std::move(transaction));
+            queue.pop();
+
+            // If the buffer is unsignaled, then we don't want to signal other transactions using
+            // the buffer as a barrier.
+            auto& readyToApplyTransaction = transactions.back();
+            if (ready == TransactionReadiness::Ready) {
+                readyToApplyTransaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+                    const bool frameNumberChanged = state.bufferData->flags.test(
+                            BufferData::BufferDataChange::frameNumberChanged);
+                    if (frameNumberChanged) {
+                        flushState.bufferLayersReadyToPresent
+                                .emplace_or_replace(state.surface.get(),
+                                                    state.bufferData->frameNumber);
+                    } else {
+                        // Barrier function only used for BBQ which always includes a frame number.
+                        // This value only used for barrier logic.
+                        flushState.bufferLayersReadyToPresent
+                                .emplace_or_replace(state.surface.get(),
+                                                    std::numeric_limits<uint64_t>::max());
+                    }
+                });
+            } else if (ready == TransactionReadiness::ReadyUnsignaledSingle) {
+                // Track queues with a flushed unsingaled buffer.
+                flushState.queuesWithUnsignaledBuffers.emplace_back(queueToken);
+                break;
+            }
+        }
+
+        if (queue.empty()) {
+            it = mPendingTransactionQueues.erase(it);
+        } else {
+            it = std::next(it, 1);
+        }
+    }
+    return transactionsPendingBarrier;
+}
+
+void TransactionHandler::addTransactionReadyFilter(TransactionFilter&& filter) {
+    mTransactionReadyFilters.emplace_back(std::move(filter));
+}
+
+bool TransactionHandler::hasPendingTransactions() {
+    return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+}
+
+void TransactionHandler::onTransactionQueueStalled(const TransactionState& transaction,
+                                                   sp<ITransactionCompletedListener>& listener) {
+    if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transaction.id) !=
+        mStalledTransactions.end()) {
+        return;
+    }
+
+    mStalledTransactions.push_back(transaction.id);
+    listener->onTransactionQueueStalled();
+}
+
+void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
+    auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
+    if (it != mStalledTransactions.end()) {
+        mStalledTransactions.erase(it);
+    }
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/TransactionHandler.h b/services/surfaceflinger/TransactionHandler.h
new file mode 100644
index 0000000..237b48d
--- /dev/null
+++ b/services/surfaceflinger/TransactionHandler.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <semaphore.h>
+#include <cstdint>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <vector>
+
+#include <LocklessQueue.h>
+#include <TransactionState.h>
+#include <android-base/thread_annotations.h>
+#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
+
+namespace android {
+class TransactionHandler {
+public:
+    struct TransactionFlushState {
+        const TransactionState* transaction;
+        bool firstTransaction = true;
+        nsecs_t queueProcessTime = 0;
+        // Layer handles that have transactions with buffers that are ready to be applied.
+        ftl::SmallMap<IBinder* /* binder address */, uint64_t /* framenumber */, 15>
+                bufferLayersReadyToPresent = {};
+        ftl::SmallVector<IBinder* /* queueToken */, 15> queuesWithUnsignaledBuffers;
+    };
+    enum class TransactionReadiness {
+        NotReady,
+        NotReadyBarrier,
+        Ready,
+        ReadyUnsignaled,
+        ReadyUnsignaledSingle,
+    };
+    using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>;
+
+    bool hasPendingTransactions();
+    std::vector<TransactionState> flushTransactions();
+    void addTransactionReadyFilter(TransactionFilter&&);
+    void queueTransaction(TransactionState&&);
+    void onTransactionQueueStalled(const TransactionState&, sp<ITransactionCompletedListener>&);
+    void removeFromStalledTransactions(uint64_t transactionId);
+
+private:
+    // For unit tests
+    friend class TestableSurfaceFlinger;
+
+    int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
+    TransactionReadiness applyFilters(TransactionFlushState&);
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+            mPendingTransactionQueues;
+    LocklessQueue<TransactionState> mLocklessTransactionQueue;
+    std::atomic<size_t> mPendingTransactionCount = 0;
+    ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
+    std::vector<uint64_t> mStalledTransactions;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 95eb503..3cbfe81 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -64,6 +64,15 @@
         }
     }
 
+    template <typename Visitor>
+    void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) const {
+        for (const auto& [state] : states) {
+            if (state.hasBufferChanges() && state.hasValidBuffer() && state.surface) {
+                if (!visitor(state)) return;
+            }
+        }
+    }
+
     // TODO(b/185535769): Remove FrameHint. Instead, reset the idle timer (of the relevant physical
     // display) on the main thread if commit leads to composite. Then, RefreshRateOverlay should be
     // able to setFrameRate once, rather than for each transaction.
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index fae9165..f8fc6f5 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -116,7 +116,7 @@
 class DisplayHardwareFuzzer {
 public:
     DisplayHardwareFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
-        mPhysicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value_or(
+        mPhysicalDisplayId = TestableSurfaceFlinger::getFirstDisplayId().value_or(
                 PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint8_t>()));
     };
     void process();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index c96520d..03630c9 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -46,7 +46,6 @@
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerDefaultFactory.h"
-#include "SurfaceInterceptor.h"
 #include "ThreadContext.h"
 #include "TimeStats/TimeStats.h"
 
@@ -60,7 +59,6 @@
 #include "tests/unittests/mock/MockFrameTimeline.h"
 #include "tests/unittests/mock/MockFrameTracer.h"
 #include "tests/unittests/mock/MockNativeWindowSurface.h"
-#include "tests/unittests/mock/MockSurfaceInterceptor.h"
 #include "tests/unittests/mock/MockTimeStats.h"
 #include "tests/unittests/mock/MockVSyncTracker.h"
 #include "tests/unittests/mock/MockVsyncController.h"
@@ -316,10 +314,6 @@
         return nullptr;
     }
 
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return sp<android::impl::SurfaceInterceptor>::make();
-    }
-
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
         return sp<StartPropertySetThread>::make(timestampPropertyValue);
     }
@@ -522,6 +516,13 @@
         mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
     }
 
+    // TODO(b/248317436): extend to cover all displays for multi-display devices
+    static std::optional<PhysicalDisplayId> getFirstDisplayId() {
+        std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        if (ids.empty()) return {};
+        return ids.front();
+    }
+
     sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
         mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
         const sp<Client> client = sp<Client>::make(mFlinger);
@@ -533,9 +534,8 @@
         ui::PixelFormat pixelFormat{};
         mFlinger->getHwComposer().allocateVirtualDisplay(halVirtualDisplayId, uiSize, &pixelFormat);
 
-        PhysicalDisplayId physicalDisplayId =
-                SurfaceComposerClient::getInternalDisplayId().value_or(
-                        PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>()));
+        PhysicalDisplayId physicalDisplayId = getFirstDisplayId().value_or(
+                PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>()));
         mFlinger->getHwComposer().allocatePhysicalDisplay(kHwDisplayId, physicalDisplayId);
 
         sp<IBinder> display =
@@ -728,8 +728,10 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto &getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
-    auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+    auto &getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+    auto &getPendingTransactionQueue() {
+        return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+    }
 
     auto setTransactionState(
             const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states,
@@ -757,12 +759,10 @@
     /* Read-write access to private data to set up preconditions and assert
      * post-conditions.
      */
-
     auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
     auto& mutableDisplays() { return mFlinger->mDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
-    auto& mutableInterceptor() { return mFlinger->mInterceptor; }
 
     auto fromHandle(const sp<IBinder> &handle) { return mFlinger->fromHandle(handle); }
 
@@ -770,7 +770,6 @@
         mutableDisplays().clear();
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
-        mutableInterceptor().clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
         mFlinger->mCompositionEngine->setRenderEngine(
@@ -779,7 +778,7 @@
 
 private:
     void setVsyncEnabled(bool) override {}
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+    void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 66bac44..2614288 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -92,7 +92,7 @@
     const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
     std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
             android::impl::EventThread>(std::move(std::make_unique<FuzzImplVSyncSource>()), nullptr,
-                                        nullptr, nullptr, getVsyncPeriod);
+                                        nullptr, getVsyncPeriod);
 
     thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
     sp<EventThreadConnection> connection =
@@ -362,11 +362,24 @@
                                                              mFdp.ConsumeFloatingPoint<float>()),
                                                      globalSignals);
 
-    refreshRateConfigs.setDisplayManagerPolicy(
-            {modeId,
-             {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
-              Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
-    FTL_FAKE_GUARD(kMainThreadContext, refreshRateConfigs.setActiveModeId(modeId));
+    {
+        ftl::FakeGuard guard(kMainThreadContext);
+
+        refreshRateConfigs.setPolicy(
+                RefreshRateConfigs::
+                        DisplayManagerPolicy{modeId,
+                                             {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+                                              Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
+        refreshRateConfigs.setPolicy(
+                RefreshRateConfigs::OverridePolicy{modeId,
+                                                   {Fps::fromValue(
+                                                            mFdp.ConsumeFloatingPoint<float>()),
+                                                    Fps::fromValue(
+                                                            mFdp.ConsumeFloatingPoint<float>())}});
+        refreshRateConfigs.setPolicy(RefreshRateConfigs::NoOverridePolicy{});
+
+        refreshRateConfigs.setActiveModeId(modeId);
+    }
 
     RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue(
                                                            mFdp.ConsumeFloatingPoint<float>()),
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 13ce65d..71b1c0e 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -56,13 +56,11 @@
         "SetFrameRateOverride_test.cpp",
         "SetGeometry_test.cpp",
         "Stress_test.cpp",
-        "SurfaceInterceptor_test.cpp",
         "VirtualDisplay_test.cpp",
         "WindowInfosListener_test.cpp",
     ],
     data: ["SurfaceFlinger_test.filter"],
     static_libs: [
-        "libtrace_proto",
         "liblayers_proto",
         "android.hardware.graphics.composer@2.1",
     ],
diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
index 432e227..f2874ae 100644
--- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp
+++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
@@ -30,7 +30,10 @@
 
 TEST(BootDisplayModeTest, setBootDisplayMode) {
     sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-    auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     bool bootModeSupport = false;
     binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
     ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
@@ -42,7 +45,9 @@
 
 TEST(BootDisplayModeTest, clearBootDisplayMode) {
     sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-    auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     bool bootModeSupport = false;
     binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
     ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 775de4a..4f04934 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -74,8 +74,17 @@
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
     }
 
+    static sp<IBinder> getFirstDisplayToken() {
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        if (ids.empty()) {
+            return nullptr;
+        }
+
+        return SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+    }
+
     void setupBackgroundSurface() {
-        mDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        mDisplay = getFirstDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr);
 
         ui::DisplayMode mode;
@@ -158,9 +167,7 @@
 }
 
 TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
-    std::function<bool()> condition = [] {
-        return SurfaceComposerClient::getInternalDisplayToken() != nullptr;
-    };
+    std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; };
     // Anyone can access display information.
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
 }
@@ -169,7 +176,7 @@
     // The following methods are tested with a UID that is not root, graphics,
     // or system, to show that anyone can access them.
     UIDFaker f(AID_BIN);
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_TRUE(display != nullptr);
 
     ui::DisplayMode mode;
@@ -181,7 +188,7 @@
 }
 
 TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         ui::DynamicDisplayInfo info;
         return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
@@ -190,7 +197,7 @@
 }
 
 TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         ui::DisplayPrimaries primaries;
         return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
@@ -199,7 +206,7 @@
 }
 
 TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ui::DisplayModeId defaultMode;
     bool allowGroupSwitching;
     float primaryFpsMin;
@@ -222,7 +229,7 @@
 }
 
 TEST_F(CredentialsTest, SetActiveColorModeTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE);
     };
@@ -274,7 +281,7 @@
 }
 
 TEST_F(CredentialsTest, CaptureTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         sp<GraphicBuffer> outBuffer;
         DisplayCaptureArgs captureArgs;
@@ -333,7 +340,7 @@
 }
 
 TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_FALSE(display == nullptr);
     bool result = false;
     status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
@@ -357,7 +364,7 @@
 }
 
 TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_FALSE(display == nullptr);
     std::function<status_t()> condition = [=]() {
         bool result = false;
@@ -367,7 +374,7 @@
 }
 
 TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_FALSE(display == nullptr);
     ui::DynamicDisplayInfo info;
     SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 2dc96b8..02c934e 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -48,7 +48,9 @@
 
 protected:
     void SetUp() override {
-        mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         status_t res =
                 SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken,
                                                                   &initialDefaultMode,
@@ -149,4 +151,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 9fa0452..52aa502 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -28,7 +28,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -177,7 +179,9 @@
 }
 
 TEST_F(EffectLayerTest, EffectLayerWithColorNoCrop) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
     const ui::Size& resolution = mode.resolution;
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index c63d251..40a5d57 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -224,7 +224,9 @@
         mClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        mPrimaryDisplay = mClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mPrimaryDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ui::DisplayMode mode;
         mClient->getActiveDisplayMode(mPrimaryDisplay, &mode);
         mDisplayWidth = mode.resolution.getWidth();
diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp
index 85108ad..00e134b 100644
--- a/services/surfaceflinger/tests/LayerBorder_test.cpp
+++ b/services/surfaceflinger/tests/LayerBorder_test.cpp
@@ -34,7 +34,9 @@
         toHalf3 = ColorTransformHelper::toHalf3;
         toHalf4 = ColorTransformHelper::toHalf4;
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
         mColorOrange = toHalf4({255, 140, 0, 255});
         mParentLayer = createColorLayer("Parent layer", Color::RED);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 774c1d7..badd5be 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -289,7 +289,9 @@
 
 private:
     void SetUpDisplay() {
-        mDisplay = mClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
 
         ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index e1a7ecc..867eddb 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -33,7 +33,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index faaef5d..e69db7c 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -29,8 +29,10 @@
     virtual void SetUp() {
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -231,7 +233,10 @@
 
 // Test that the mirror layer is initially offscreen.
 TEST_F(MirrorLayerTest, InitialMirrorState) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     SurfaceComposerClient::getActiveDisplayMode(display, &mode);
     const ui::Size& size = mode.resolution;
@@ -275,7 +280,9 @@
 
 // Test that a mirror layer can be screenshot when offscreen
 TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     SurfaceComposerClient::getActiveDisplayMode(display, &mode);
     const ui::Size& size = mode.resolution;
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 1ed6c65..15ff696 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -35,7 +35,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
         SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
 
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 50a4092..9cebf11 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -33,7 +33,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         // Back layer
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 3a5e532..976ee35 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -30,7 +30,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
deleted file mode 100644
index d79e592..0000000
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <android-base/stringprintf.h>
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <gtest/gtest.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-#include <ui/DisplayMode.h>
-
-#include <fstream>
-#include <random>
-#include <thread>
-
-namespace android {
-
-using Transaction = SurfaceComposerClient::Transaction;
-using SurfaceChange = surfaceflinger::SurfaceChange;
-using Trace = surfaceflinger::Trace;
-using Increment = surfaceflinger::Increment;
-
-constexpr uint32_t BUFFER_UPDATES = 18;
-constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
-constexpr uint32_t SIZE_UPDATE = 134;
-constexpr uint32_t STACK_UPDATE = 1;
-constexpr int32_t RELATIVE_Z = 42;
-constexpr float ALPHA_UPDATE = 0.29f;
-constexpr float CORNER_RADIUS_UPDATE = 0.2f;
-constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24;
-constexpr float POSITION_UPDATE = 121;
-const Rect CROP_UPDATE(16, 16, 32, 32);
-const float SHADOW_RADIUS_UPDATE = 35.0f;
-std::vector<BlurRegion> BLUR_REGIONS_UPDATE;
-
-const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
-constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
-constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface";
-constexpr auto LAYER_NAME = "Layer Create and Delete Test";
-
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = sc->getSurface();
-    ASSERT_TRUE(s != nullptr);
-    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
-    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
-            pixel[0] = r;
-            pixel[1] = g;
-            pixel[2] = b;
-            pixel[3] = 255;
-        }
-    }
-    ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-}
-
-static status_t readProtoFile(Trace* trace) {
-    status_t err = NO_ERROR;
-
-    int fd = open(DEFAULT_FILENAME, O_RDONLY);
-    {
-        google::protobuf::io::FileInputStream f(fd);
-        if (fd && !trace->ParseFromZeroCopyStream(&f)) {
-            err = PERMISSION_DENIED;
-        }
-    }
-    close(fd);
-
-    return err;
-}
-
-static void enableInterceptor() {
-    system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
-}
-
-static void disableInterceptor() {
-    system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
-}
-
-std::string getUniqueName(const std::string& name, const Increment& increment) {
-    return base::StringPrintf("%s#%d", name.c_str(), increment.surface_creation().id());
-}
-
-int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) {
-    int32_t layerId = 0;
-    for (const auto& increment : capturedTrace.increment()) {
-        if (increment.increment_case() == increment.kSurfaceCreation) {
-            if (increment.surface_creation().name() == getUniqueName(surfaceName, increment)) {
-                layerId = increment.surface_creation().id();
-            }
-        }
-    }
-    return layerId;
-}
-
-int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) {
-    int32_t displayId = 0;
-    for (const auto& increment : capturedTrace.increment()) {
-        if (increment.increment_case() == increment.kDisplayCreation) {
-            if (increment.display_creation().name() == displayName) {
-                displayId = increment.display_creation().id();
-                break;
-            }
-        }
-    }
-    return displayId;
-}
-
-class SurfaceInterceptorTest : public ::testing::Test {
-protected:
-    void SetUp() override {
-        // Allow SurfaceInterceptor write to /data
-        system("setenforce 0");
-
-        mComposerClient = sp<SurfaceComposerClient>::make();
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-        GTEST_SKIP();
-    }
-
-    void TearDown() override {
-        mComposerClient->dispose();
-        mBGSurfaceControl.clear();
-        mFGSurfaceControl.clear();
-        mComposerClient.clear();
-        system("setenforce 1");
-    }
-
-    sp<SurfaceComposerClient> mComposerClient;
-    sp<SurfaceControl> mBGSurfaceControl;
-    sp<SurfaceControl> mFGSurfaceControl;
-    int32_t mBGLayerId;
-    int32_t mFGLayerId;
-
-public:
-    using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
-    using TestAction = void (SurfaceInterceptorTest::*)();
-    using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&);
-    using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&);
-
-    void setupBackgroundSurface();
-    void preProcessTrace(const Trace& trace);
-
-    // captureTest will enable SurfaceInterceptor, setup background surface,
-    // disable SurfaceInterceptor, collect the trace and process the trace for
-    // id of background surface before further verification.
-    void captureTest(TestTransactionAction action, TestBooleanVerification verification);
-    void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase);
-    void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase);
-    void captureTest(TestAction action, TestBooleanVerification verification);
-    void captureTest(TestAction action, TestVerification verification);
-    void runInTransaction(TestTransactionAction action);
-
-    // Verification of changes to a surface
-    bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
-    bool sizeUpdateFound(const SurfaceChange& change, bool foundSize);
-    bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
-    bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
-    bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
-    bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
-    bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
-                                         bool foundBackgroundBlurRadius);
-    bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions);
-    bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
-    bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
-    bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
-    bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack);
-    bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
-    bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
-    bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
-    bool reparentUpdateFound(const SurfaceChange& change, bool found);
-    bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
-    bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
-    bool trustedOverlayUpdateFound(const SurfaceChange& change, bool found);
-    bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
-
-    // Find all of the updates in the single trace
-    void assertAllUpdatesFound(const Trace& trace);
-
-    // Verification of creation and deletion of a surface
-    bool surfaceCreationFound(const Increment& increment, bool foundSurface);
-    bool surfaceDeletionFound(const Increment& increment, const int32_t targetId,
-            bool foundSurface);
-    bool displayCreationFound(const Increment& increment, bool foundDisplay);
-    bool displayDeletionFound(const Increment& increment, const int32_t targetId,
-            bool foundDisplay);
-    bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase);
-
-    // Verification of buffer updates
-    bool bufferUpdatesFound(const Trace& trace);
-
-    // Perform each of the possible changes to a surface
-    void positionUpdate(Transaction&);
-    void sizeUpdate(Transaction&);
-    void alphaUpdate(Transaction&);
-    void layerUpdate(Transaction&);
-    void cropUpdate(Transaction&);
-    void cornerRadiusUpdate(Transaction&);
-    void backgroundBlurRadiusUpdate(Transaction&);
-    void blurRegionsUpdate(Transaction&);
-    void matrixUpdate(Transaction&);
-    void transparentRegionHintUpdate(Transaction&);
-    void layerStackUpdate(Transaction&);
-    void hiddenFlagUpdate(Transaction&);
-    void opaqueFlagUpdate(Transaction&);
-    void secureFlagUpdate(Transaction&);
-    void reparentUpdate(Transaction&);
-    void relativeParentUpdate(Transaction&);
-    void shadowRadiusUpdate(Transaction&);
-    void trustedOverlayUpdate(Transaction&);
-    void surfaceCreation(Transaction&);
-    void displayCreation(Transaction&);
-    void displayDeletion(Transaction&);
-
-    void nBufferUpdates();
-    void runAllUpdates();
-
-private:
-    void captureInTransaction(TestTransactionAction action, Trace*);
-    void capture(TestAction action, Trace*);
-};
-
-void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) {
-    enableInterceptor();
-    setupBackgroundSurface();
-    runInTransaction(action);
-    disableInterceptor();
-    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
-    preProcessTrace(*outTrace);
-}
-
-void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) {
-    enableInterceptor();
-    setupBackgroundSurface();
-    (this->*action)();
-    disableInterceptor();
-    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
-    preProcessTrace(*outTrace);
-}
-
-void SurfaceInterceptorTest::setupBackgroundSurface() {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
-    ASSERT_FALSE(display == nullptr);
-
-    ui::DisplayMode mode;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-    const ui::Size& resolution = mode.resolution;
-
-    // Background surface
-    mBGSurfaceControl =
-            mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(),
-                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mBGSurfaceControl != nullptr);
-    ASSERT_TRUE(mBGSurfaceControl->isValid());
-
-    // Foreground surface
-    mFGSurfaceControl =
-            mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(),
-                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mFGSurfaceControl != nullptr);
-    ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-    Transaction t;
-    t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-    ASSERT_EQ(NO_ERROR,
-              t.setLayer(mBGSurfaceControl, INT_MAX - 3)
-                      .show(mBGSurfaceControl)
-                      .setLayer(mFGSurfaceControl, INT_MAX - 3)
-                      .show(mFGSurfaceControl)
-                      .apply());
-}
-
-void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
-    mBGLayerId = getSurfaceId(trace, TEST_BG_SURFACE_NAME);
-    mFGLayerId = getSurfaceId(trace, TEST_FG_SURFACE_NAME);
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        TestBooleanVerification verification) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE((this->*verification)(capturedTrace));
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        Increment::IncrementCase incrementCase) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase));
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        SurfaceChange::SurfaceChangeCase changeCase) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase));
-}
-
-void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) {
-    Trace capturedTrace;
-    capture(action, &capturedTrace);
-    ASSERT_TRUE((this->*verification)(capturedTrace));
-}
-
-void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) {
-    Trace capturedTrace;
-    capture(action, &capturedTrace);
-    (this->*verification)(capturedTrace);
-}
-
-void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) {
-    Transaction t;
-    (this->*action)(t);
-    t.apply(true);
-}
-
-void SurfaceInterceptorTest::positionUpdate(Transaction& t) {
-    t.setPosition(mBGSurfaceControl, POSITION_UPDATE, POSITION_UPDATE);
-}
-
-void SurfaceInterceptorTest::sizeUpdate(Transaction&) {}
-
-void SurfaceInterceptorTest::alphaUpdate(Transaction& t) {
-    t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
-}
-
-void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
-    t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) {
-    t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) {
-    BLUR_REGIONS_UPDATE.empty();
-    BLUR_REGIONS_UPDATE.push_back(BlurRegion());
-    t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE);
-}
-
-void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
-    t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
-}
-
-void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
-    t.setCrop(mBGSurfaceControl, CROP_UPDATE);
-}
-
-void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
-    t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
-}
-
-void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) {
-    Region region(CROP_UPDATE);
-    t.setTransparentRegionHint(mBGSurfaceControl, region);
-}
-
-void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) {
-    t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE));
-}
-
-void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
-}
-
-void SurfaceInterceptorTest::opaqueFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
-}
-
-void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
-}
-
-void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
-    t.reparent(mBGSurfaceControl, mFGSurfaceControl);
-}
-
-void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
-    t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
-}
-
-void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
-    t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::trustedOverlayUpdate(Transaction& t) {
-    t.setTrustedOverlay(mBGSurfaceControl, true);
-}
-
-void SurfaceInterceptorTest::displayCreation(Transaction&) {
-    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
-    SurfaceComposerClient::destroyDisplay(testDisplay);
-}
-
-void SurfaceInterceptorTest::displayDeletion(Transaction&) {
-    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
-    SurfaceComposerClient::destroyDisplay(testDisplay);
-}
-
-void SurfaceInterceptorTest::runAllUpdates() {
-    runInTransaction(&SurfaceInterceptorTest::positionUpdate);
-    runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
-    runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
-    runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate);
-    runInTransaction(&SurfaceInterceptorTest::layerUpdate);
-    runInTransaction(&SurfaceInterceptorTest::cropUpdate);
-    runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
-    runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
-    runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
-    runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
-    runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
-    runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::trustedOverlayUpdate);
-}
-
-void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
-    mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
-            PIXEL_FORMAT_RGBA_8888, 0);
-}
-
-void SurfaceInterceptorTest::nBufferUpdates() {
-    std::random_device rd;
-    std::mt19937_64 gen(rd());
-    // This makes testing fun
-    std::uniform_int_distribution<uint8_t> dis;
-    for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
-        fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
-    }
-}
-
-bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) {
-    // There should only be one position transaction with x and y = POSITION_UPDATE
-    bool hasX(change.position().x() == POSITION_UPDATE);
-    bool hasY(change.position().y() == POSITION_UPDATE);
-    if (hasX && hasY && !foundPosition) {
-        foundPosition = true;
-    } else if (hasX && hasY && foundPosition) {
-        // Failed because the position update was found a second time
-        [] () { FAIL(); }();
-    }
-    return foundPosition;
-}
-
-bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange&, bool) {
-    return true;
-}
-
-bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) {
-    bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
-    if (hasAlpha && !foundAlpha) {
-        foundAlpha = true;
-    } else if (hasAlpha && foundAlpha) {
-        [] () { FAIL(); }();
-    }
-    return foundAlpha;
-}
-
-bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
-                                                     bool foundCornerRadius) {
-    bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
-    if (hasCornerRadius && !foundCornerRadius) {
-        foundCornerRadius = true;
-    } else if (hasCornerRadius && foundCornerRadius) {
-        [] () { FAIL(); }();
-    }
-    return foundCornerRadius;
-}
-
-bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
-                                                             bool foundBackgroundBlur) {
-    bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() ==
-                           BACKGROUND_BLUR_RADIUS_UPDATE);
-    if (hasBackgroundBlur && !foundBackgroundBlur) {
-        foundBackgroundBlur = true;
-    } else if (hasBackgroundBlur && foundBackgroundBlur) {
-        []() { FAIL(); }();
-    }
-    return foundBackgroundBlur;
-}
-
-bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change,
-                                                    bool foundBlurRegions) {
-    bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size());
-    if (hasBlurRegions && !foundBlurRegions) {
-        foundBlurRegions = true;
-    } else if (hasBlurRegions && foundBlurRegions) {
-        []() { FAIL(); }();
-    }
-    return foundBlurRegions;
-}
-
-bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
-    bool hasLayer(change.layer().layer() == LAYER_UPDATE);
-    if (hasLayer && !foundLayer) {
-        foundLayer = true;
-    } else if (hasLayer && foundLayer) {
-        [] () { FAIL(); }();
-    }
-    return foundLayer;
-}
-
-bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) {
-    bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
-    bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
-    bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
-    bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
-    if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
-        foundCrop = true;
-    } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
-        [] () { FAIL(); }();
-    }
-    return foundCrop;
-}
-
-bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
-    bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
-    bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
-    bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2);
-    bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2);
-    if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
-        foundMatrix = true;
-    } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
-        [] () { FAIL(); }();
-    }
-    return foundMatrix;
-}
-
-bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
-        bool foundTransparentRegion) {
-    auto traceRegion = change.transparent_region_hint().region(0);
-    bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
-    bool hasTop(traceRegion.top() == CROP_UPDATE.top);
-    bool hasRight(traceRegion.right() == CROP_UPDATE.right);
-    bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
-    if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
-        foundTransparentRegion = true;
-    } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
-        [] () { FAIL(); }();
-    }
-    return foundTransparentRegion;
-}
-
-bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
-        bool foundLayerStack) {
-    bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
-    if (hasLayerStackUpdate && !foundLayerStack) {
-        foundLayerStack = true;
-    } else if (hasLayerStackUpdate && foundLayerStack) {
-        [] () { FAIL(); }();
-    }
-    return foundLayerStack;
-}
-
-bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
-        bool foundHiddenFlag) {
-    bool hasHiddenFlag(change.hidden_flag().hidden_flag());
-    if (hasHiddenFlag && !foundHiddenFlag) {
-        foundHiddenFlag = true;
-    } else if (hasHiddenFlag && foundHiddenFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundHiddenFlag;
-}
-
-bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
-        bool foundOpaqueFlag) {
-    bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
-    if (hasOpaqueFlag && !foundOpaqueFlag) {
-        foundOpaqueFlag = true;
-    } else if (hasOpaqueFlag && foundOpaqueFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundOpaqueFlag;
-}
-
-bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
-        bool foundSecureFlag) {
-    bool hasSecureFlag(change.secure_flag().secure_flag());
-    if (hasSecureFlag && !foundSecureFlag) {
-        foundSecureFlag = true;
-    } else if (hasSecureFlag && foundSecureFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundSecureFlag;
-}
-
-bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) {
-    bool hasId(change.reparent().parent_id() == mFGLayerId);
-    if (hasId && !found) {
-        found = true;
-    } else if (hasId && found) {
-        []() { FAIL(); }();
-    }
-    return found;
-}
-
-bool SurfaceInterceptorTest::relativeParentUpdateFound(const SurfaceChange& change, bool found) {
-    bool hasId(change.relative_parent().relative_parent_id() == mFGLayerId);
-    if (hasId && !found) {
-        found = true;
-    } else if (hasId && found) {
-        []() { FAIL(); }();
-    }
-    return found;
-}
-
-bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
-                                                     bool foundShadowRadius) {
-    bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
-    if (hasShadowRadius && !foundShadowRadius) {
-        foundShadowRadius = true;
-    } else if (hasShadowRadius && foundShadowRadius) {
-        []() { FAIL(); }();
-    }
-    return foundShadowRadius;
-}
-
-bool SurfaceInterceptorTest::trustedOverlayUpdateFound(const SurfaceChange& change,
-                                                       bool foundTrustedOverlay) {
-    bool hasTrustedOverlay(change.trusted_overlay().is_trusted_overlay());
-    if (hasTrustedOverlay && !foundTrustedOverlay) {
-        foundTrustedOverlay = true;
-    } else if (hasTrustedOverlay && foundTrustedOverlay) {
-        []() { FAIL(); }();
-    }
-    return foundTrustedOverlay;
-}
-
-bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
-        SurfaceChange::SurfaceChangeCase changeCase) {
-    bool foundUpdate = false;
-    for (const auto& increment : trace.increment()) {
-        if (increment.increment_case() == increment.kTransaction) {
-            for (const auto& change : increment.transaction().surface_change()) {
-                if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
-                    switch (changeCase) {
-                        case SurfaceChange::SurfaceChangeCase::kPosition:
-                            // foundUpdate is sent for the tests to fail on duplicated increments
-                            foundUpdate = positionUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kSize:
-                            foundUpdate = sizeUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kAlpha:
-                            foundUpdate = alphaUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kLayer:
-                            foundUpdate = layerUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kCrop:
-                            foundUpdate = cropUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kCornerRadius:
-                            foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
-                            foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kBlurRegions:
-                            foundUpdate = blurRegionsUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kMatrix:
-                            foundUpdate = matrixUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
-                            foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kLayerStack:
-                            foundUpdate = layerStackUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
-                            foundUpdate = hiddenFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
-                            foundUpdate = opaqueFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kSecureFlag:
-                            foundUpdate = secureFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kReparent:
-                            foundUpdate = reparentUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kRelativeParent:
-                            foundUpdate = relativeParentUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kShadowRadius:
-                            foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kTrustedOverlay:
-                            foundUpdate = trustedOverlayUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
-                            break;
-                    }
-                }
-            }
-        }
-    }
-    return foundUpdate;
-}
-
-void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) {
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent));
-}
-
-bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
-    bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment));
-    if (isMatch && !foundSurface) {
-        foundSurface = true;
-    } else if (isMatch && foundSurface) {
-        [] () { FAIL(); }();
-    }
-    return foundSurface;
-}
-
-bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment,
-        const int32_t targetId, bool foundSurface) {
-    bool isMatch(increment.surface_deletion().id() == targetId);
-    if (isMatch && !foundSurface) {
-        foundSurface = true;
-    } else if (isMatch && foundSurface) {
-        [] () { FAIL(); }();
-    }
-    return foundSurface;
-}
-
-bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
-    bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
-                 !increment.display_creation().is_secure());
-    if (isMatch && !foundDisplay) {
-        foundDisplay = true;
-    } else if (isMatch && foundDisplay) {
-        [] () { FAIL(); }();
-    }
-    return foundDisplay;
-}
-
-bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment,
-        const int32_t targetId, bool foundDisplay) {
-    bool isMatch(increment.display_deletion().id() == targetId);
-    if (isMatch && !foundDisplay) {
-        foundDisplay = true;
-    } else if (isMatch && foundDisplay) {
-        [] () { FAIL(); }();
-    }
-    return foundDisplay;
-}
-
-bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace,
-        Increment::IncrementCase incrementCase) {
-    bool foundIncrement = false;
-    for (const auto& increment : trace.increment()) {
-        if (increment.increment_case() == incrementCase) {
-            int32_t targetId = 0;
-            switch (incrementCase) {
-                case Increment::IncrementCase::kSurfaceCreation:
-                    foundIncrement = surfaceCreationFound(increment, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kSurfaceDeletion:
-                    // Find the id of created surface.
-                    targetId = getSurfaceId(trace, LAYER_NAME);
-                    foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kDisplayCreation:
-                    foundIncrement = displayCreationFound(increment, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kDisplayDeletion:
-                    // Find the id of created display.
-                    targetId = getDisplayId(trace, DISPLAY_NAME.string());
-                    foundIncrement = displayDeletionFound(increment, targetId, foundIncrement);
-                    break;
-                default:
-                    /* code */
-                    break;
-            }
-        }
-    }
-    return foundIncrement;
-}
-
-bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) {
-    uint32_t updates = 0;
-    for (const auto& inc : trace.increment()) {
-        if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
-            updates++;
-        }
-    }
-    return updates == BUFFER_UPDATES;
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::positionUpdate,
-            SurfaceChange::SurfaceChangeCase::kPosition);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
-            SurfaceChange::SurfaceChangeCase::kCornerRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate,
-                SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::blurRegionsUpdate,
-                SurfaceChange::SurfaceChangeCase::kBlurRegions);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
-            SurfaceChange::SurfaceChangeCase::kTransparentRegionHint);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::layerStackUpdate,
-            SurfaceChange::SurfaceChangeCase::kLayerStack);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kHiddenFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kOpaqueFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::secureFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kSecureFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::reparentUpdate,
-                SurfaceChange::SurfaceChangeCase::kReparent);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::relativeParentUpdate,
-                SurfaceChange::SurfaceChangeCase::kRelativeParent);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
-                SurfaceChange::SurfaceChangeCase::kShadowRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptTrustedOverlayUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::trustedOverlayUpdate,
-                SurfaceChange::SurfaceChangeCase::kTrustedOverlay);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
-    captureTest(&SurfaceInterceptorTest::runAllUpdates,
-                &SurfaceInterceptorTest::assertAllUpdatesFound);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
-    captureTest(&SurfaceInterceptorTest::surfaceCreation,
-            Increment::IncrementCase::kSurfaceCreation);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
-    captureTest(&SurfaceInterceptorTest::displayCreation,
-            Increment::IncrementCase::kDisplayCreation);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
-    enableInterceptor();
-    runInTransaction(&SurfaceInterceptorTest::displayDeletion);
-    disableInterceptor();
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
-}
-
-// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
-// first create a snapshot of the existing displays and surfaces and then start capturing
-// the buffer updates
-TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
-    setupBackgroundSurface();
-    std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
-    enableInterceptor();
-    disableInterceptor();
-    bufferUpdates.join();
-
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    const auto& firstIncrement = capturedTrace.mutable_increment(0);
-    ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
-}
-}
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index ad03ed3..797a64c 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -35,7 +35,10 @@
                 return mDelegate->screenshot();
             case RenderPath::VIRTUAL_DISPLAY:
 
-                const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+                const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+                const auto displayToken = ids.empty()
+                        ? nullptr
+                        : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
 
                 ui::DisplayState displayState;
                 SurfaceComposerClient::getDisplayState(displayToken, &displayState);
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 06afdb1..0d40e70 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -53,7 +53,6 @@
         "libgmock",
         "libperfetto_client_experimental",
         "librenderengine",
-        "libtrace_proto",
         "libaidlcommonsupport",
     ],
     header_libs: [
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 4469df0..d2b5813 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -34,7 +34,6 @@
         "mock/MockFrameTimeline.cpp",
         "mock/MockFrameTracer.cpp",
         "mock/MockNativeWindowSurface.cpp",
-        "mock/MockSurfaceInterceptor.cpp",
         "mock/MockTimeStats.cpp",
         "mock/MockVsyncController.cpp",
         "mock/MockVSyncTracker.cpp",
@@ -168,7 +167,6 @@
         "libtimestats_atoms_proto",
         "libtimestats_proto",
         "libtonemap",
-        "libtrace_proto",
         "perfetto_trace_protos",
     ],
     shared_libs: [
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index abe32c9..8b91c67 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -50,7 +50,6 @@
 
     injectMockScheduler();
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
-    mFlinger.mutableInterceptor() = mSurfaceInterceptor;
 
     injectMockComposer(0);
 }
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 20e776f..9cceb5e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -49,7 +49,6 @@
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSurfaceInterceptor.h"
 #include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
 
@@ -120,7 +119,6 @@
     // to keep a reference to them for use in setting up call expectations.
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     Hwc2::mock::Composer* mComposer = nullptr;
-    sp<mock::SurfaceInterceptor> mSurfaceInterceptor = sp<mock::SurfaceInterceptor>::make();
 
     mock::VsyncController* mVsyncController = new mock::VsyncController;
     mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 978afc5..a5beaba 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -93,7 +93,6 @@
     void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
                                             std::chrono::nanoseconds expectedReadyDuration);
     VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
-    void expectInterceptCallReceived(nsecs_t expectedTimestamp);
     void expectVsyncEventReceivedByConnection(const char* name,
                                               ConnectionEventRecorder& connectionEventRecorder,
                                               nsecs_t expectedTimestamp, unsigned expectedCount);
@@ -114,7 +113,6 @@
     AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)>
             mVSyncSetDurationCallRecorder;
     AsyncCallRecorder<void (*)()> mResyncCallRecorder;
-    AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
     AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
     ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
@@ -181,7 +179,6 @@
 
     mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
     mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
-                                                  mInterceptVSyncCallRecorder.getInvocable(),
                                                   throttleVsync, getVsyncPeriod);
 
     // EventThread should register itself as VSyncSource callback.
@@ -219,12 +216,6 @@
     return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
 }
 
-void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) {
-    auto args = mInterceptVSyncCallRecorder.waitForCall();
-    ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
-}
-
 void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
     auto args = mThrottleVsyncCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
@@ -348,7 +339,6 @@
     EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
-    EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
 }
 
@@ -374,17 +364,15 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Use the received callback to signal a first vsync event.
-    // The interceptor should receive the event, as well as the connection.
+    // The throttler should receive the event, as well as the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // Use the received callback to signal a second vsync event.
-    // The interceptor should receive the event, but the connection should
+    // The throttler should receive the event, but the connection should
     // not as it was only interested in the first.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -400,10 +388,9 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Use the received callback to signal a vsync event.
-    // The interceptor should receive the event, as well as the connection.
+    // The throttler should receive the event, as well as the connection.
     VSyncSource::VSyncData vsyncData = {456, 789};
     mCallback->onVSyncEvent(123, vsyncData);
-    expectInterceptCallReceived(123);
     expectVsyncEventFrameTimelinesCorrect(123, vsyncData);
 }
 
@@ -477,10 +464,9 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Send a vsync event. EventThread should then make a call to the
-    // interceptor, and the second connection. The first connection should not
+    // the second connection. The first connection should not
     // get the event.
     mCallback->onVSyncEvent(123, {456, 0});
-    expectInterceptCallReceived(123);
     EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
     expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
                                          1u);
@@ -493,21 +479,18 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Send a vsync event. EventThread should then make a call to the
-    // interceptor, and the connection.
+    // throttler, and the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     expectThrottleVsyncReceived(123, mConnectionUid);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
     mCallback->onVSyncEvent(789, {777, 111});
-    expectInterceptCallReceived(789);
     expectThrottleVsyncReceived(777, mConnectionUid);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
@@ -518,27 +501,23 @@
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
-    // The first event will be seen by the interceptor, and not the connection.
+    // The first event will not be seen by the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The second event will be seen by the interceptor and the connection.
+    // The second event will be seen by the connection.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The third event will be seen by the interceptor, and not the connection.
+    // The third event will not be seen by the connection.
     mCallback->onVSyncEvent(789, {777, 744});
-    expectInterceptCallReceived(789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The fourth event will be seen by the interceptor and the connection.
+    // The fourth event will be seen by the connection.
     mCallback->onVSyncEvent(101112, {7847, 86});
-    expectInterceptCallReceived(101112);
     expectVsyncEventReceivedByConnection(101112, 4u);
 }
 
@@ -551,9 +530,8 @@
     // Destroy the only (strong) reference to the connection.
     mConnection = nullptr;
 
-    // The first event will be seen by the interceptor, and not the connection.
+    // The first event will not be seen by the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should disable vsync callbacks
@@ -568,16 +546,12 @@
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an error.
+    // The first event will be seen by the connection, which then returns an error.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
-    // A subsequent event will be seen by the interceptor and not by the
-    // connection.
+    // A subsequent event will not be seen by the connection.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should disable vsync callbacks with the second event
@@ -599,10 +573,8 @@
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an error.
+    // The first event will be seen by the connection, which then returns an error.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
     expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
                                          1u);
@@ -617,16 +589,13 @@
     // EventThread should enable vsync callbacks.
     expectVSyncSetEnabledCallReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an non-fatal error.
+    // The first event will be seen by the connection, which then returns a non-fatal error.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
-    // A subsequent event will be seen by the interceptor, and by the connection,
-    // which still then returns an non-fatal error.
+    // A subsequent event will be seen by the connection, which still then returns a non-fatal
+    // error.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
 
     // EventThread will not disable vsync callbacks as the errors are non-fatal.
@@ -748,17 +717,15 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // Use the received callback to signal a first vsync event.
-    // The interceptor should receive the event, but not the connection.
+    // The throttler should receive the event, but not the connection.
     mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
     expectThrottleVsyncReceived(456, mThrottledConnectionUid);
     mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
 
     // Use the received callback to signal a second vsync event.
-    // The interceptor should receive the event, but the connection should
+    // The throttler should receive the event, but the connection should
     // not as it was only interested in the first.
     mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
     expectThrottleVsyncReceived(123, mThrottledConnectionUid);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
new file mode 100644
index 0000000..81b420c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::hardware::graphics::composer::hal::HWDisplayId;
+using android::Hwc2::mock::PowerAdvisor;
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Mock;
+using testing::ResultOf;
+using testing::Return;
+using testing::SetArgPointee;
+
+class FakeDisplayInjector {
+public:
+    sp<DisplayDevice> injectDefaultInternalDisplay(
+            const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra,
+            TestableSurfaceFlinger& flinger, uint8_t port = 255u) const {
+        constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
+        constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
+        constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
+
+        const PhysicalDisplayId physicalDisplayId = PhysicalDisplayId::fromPort(port);
+
+        // The DisplayDevice is required to have a framebuffer (behind the
+        // ANativeWindow interface) which uses the actual hardware display
+        // size.
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+        auto compositionDisplay = compositionengine::impl::
+                createDisplay(flinger.getCompositionEngine(),
+                              compositionengine::DisplayCreationArgsBuilder()
+                                      .setId(physicalDisplayId)
+                                      .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                                      .setPowerAdvisor(mPowerAdvisor)
+                                      .build());
+
+        constexpr bool kIsPrimary = true;
+        auto injector = FakeDisplayDeviceInjector(flinger, compositionDisplay,
+                                                  ui::DisplayConnectionType::Internal,
+                                                  DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
+
+        injector.setNativeWindow(mNativeWindow);
+        if (injectExtra) {
+            injectExtra(injector);
+        }
+
+        auto displayDevice = injector.inject();
+
+        Mock::VerifyAndClear(mNativeWindow.get());
+
+        return displayDevice;
+    }
+
+    sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+    PowerAdvisor* mPowerAdvisor = new PowerAdvisor();
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index a706c4b..620825f 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -34,6 +34,7 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
+using SetPolicyResult = RefreshRateConfigs::SetPolicyResult;
 using LayerVoteType = RefreshRateConfigs::LayerVoteType;
 using LayerRequirement = RefreshRateConfigs::LayerRequirement;
 
@@ -76,7 +77,9 @@
     std::vector<RefreshRateRanking> getRefreshRatesByPolicy(
             std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
         std::lock_guard lock(mLock);
-        return RefreshRateConfigs::getRefreshRatesByPolicyLocked(anchorGroupOpt, refreshRateOrder);
+        return RefreshRateConfigs::
+                getRefreshRatesByPolicyLocked(anchorGroupOpt, refreshRateOrder,
+                                              /*preferredDisplayModeOpt*/ std::nullopt);
     }
 
     const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
@@ -93,6 +96,15 @@
                                       GlobalSignals signals = {}) const {
         return getRankedRefreshRatesAndSignals(layers, signals).first.front().displayModePtr;
     }
+
+    SetPolicyResult setPolicy(const PolicyVariant& policy) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return RefreshRateConfigs::setPolicy(policy);
+    }
+
+    SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) {
+        return setPolicy(policy);
+    }
 };
 
 class RefreshRateConfigsTest : public testing::Test {
@@ -178,9 +190,33 @@
 }
 
 TEST_F(RefreshRateConfigsTest, invalidPolicy) {
-    RefreshRateConfigs configs(kModes_60, kModeId60);
-    EXPECT_LT(configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0);
-    EXPECT_LT(configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}), 0);
+    TestableRefreshRateConfigs configs(kModes_60, kModeId60);
+
+    EXPECT_EQ(SetPolicyResult::Invalid,
+              configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
+    EXPECT_EQ(SetPolicyResult::Invalid,
+              configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
+}
+
+TEST_F(RefreshRateConfigsTest, unchangedPolicy) {
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+
+    // Override to the same policy.
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              configs.setPolicy(RefreshRateConfigs::OverridePolicy{kModeId90, {60_Hz, 90_Hz}}));
+
+    // Clear override to restore DisplayManagerPolicy.
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              configs.setPolicy(RefreshRateConfigs::NoOverridePolicy{}));
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
 }
 
 TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) {
@@ -211,7 +247,8 @@
     EXPECT_EQ(kMode60, minRate60);
     EXPECT_EQ(kMode60, performanceRate60);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
     configs.setActiveModeId(kModeId90);
 
     const auto minRate90 = configs.getMinRefreshRateByPolicy();
@@ -234,7 +271,8 @@
     EXPECT_EQ(kMode60, minRate60);
     EXPECT_EQ(kMode60, performanceRate60);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
     configs.setActiveModeId(kModeId90);
 
     const auto minRate90 = configs.getMinRefreshRateByPolicy();
@@ -254,7 +292,8 @@
     EXPECT_EQ(kMode60, minRate);
     EXPECT_EQ(kMode90, performanceRate);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
 
     const auto minRate60 = configs.getMinRefreshRateByPolicy();
     const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
@@ -276,7 +315,8 @@
         EXPECT_EQ(mode.getId(), kModeId90);
     }
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
     {
         const auto& mode = configs.getActiveMode();
         EXPECT_EQ(mode.getId(), kModeId90);
@@ -291,19 +331,33 @@
         // range.
         EXPECT_EQ(kMode90, configs.getBestRefreshRate());
 
-        EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), NO_ERROR);
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
         EXPECT_EQ(kMode60, configs.getBestRefreshRate());
     }
     {
         // We select max even when this will cause a non-seamless switch.
         TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
         constexpr bool kAllowGroupSwitching = true;
-        EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}),
-                  NO_ERROR);
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  configs.setDisplayManagerPolicy(
+                          {kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}));
         EXPECT_EQ(kMode90_G1, configs.getBestRefreshRate());
     }
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) {
+    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId72);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].vote = LayerVoteType::ExplicitExact;
+    layers[0].desiredRefreshRate = 120_Hz;
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
+    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
+}
+
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
@@ -340,7 +394,8 @@
     EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
     lr.name = "";
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
@@ -364,7 +419,8 @@
     lr.desiredRefreshRate = 24_Hz;
     EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
@@ -388,7 +444,8 @@
     lr.desiredRefreshRate = 24_Hz;
     EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
 
@@ -1039,7 +1096,8 @@
                                                                    RefreshRateRanking{kMode60},
                                                                    RefreshRateRanking{kMode90}};
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
 
     const std::vector<RefreshRateRanking>& refreshRates =
             configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
@@ -1062,7 +1120,8 @@
                                                                    RefreshRateRanking{kMode60},
                                                                    RefreshRateRanking{kMode30}};
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
 
     const std::vector<RefreshRateRanking>& refreshRates =
             configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
@@ -1273,36 +1332,14 @@
             EXPECT_EQ(lr.desiredRefreshRate, configs.getBestRefreshRate(layers)->getFps());
         }
     }
-
-    // Test that 23.976 will choose 24 if 23.976 is not supported
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.vote = LayerVoteType::ExplicitExact;
-        lr.desiredRefreshRate = 23.976_Hz;
-        lr.name = "ExplicitExact 23.976 Hz";
-        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId());
-    }
-
-    // Test that 24 will choose 23.976 if 24 is not supported
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.desiredRefreshRate = 24_Hz;
-        lr.name = "ExplicitExact 24 Hz";
-        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
-    }
 }
 
 TEST_F(RefreshRateConfigsTest,
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
-    RefreshRateConfigs configs(kModes_60_90, kModeId90);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1323,7 +1360,8 @@
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1472,7 +1510,8 @@
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
     TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
     const auto [mode, signals] = configs.getRankedRefreshRatesAndSignals({}, {});
     EXPECT_EQ(mode.front().displayModePtr, kMode90);
@@ -1546,10 +1585,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1564,10 +1603,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     // Verify that we won't change the group if seamless switch is required.
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1583,10 +1622,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1604,10 +1643,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1628,10 +1667,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1657,10 +1696,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1690,10 +1729,10 @@
 TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
 
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     configs.setActiveModeId(kModeId90);
 
@@ -1721,10 +1760,10 @@
     TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
 
     // Allow group switching.
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& layer = layers[0];
@@ -1744,10 +1783,10 @@
     TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60);
 
     // Allow group switching.
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
                                              .vote = LayerVoteType::ExplicitDefault,
@@ -1776,10 +1815,10 @@
     TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90);
 
     // Allow group switching.
-    RefreshRateConfigs::Policy policy;
+    RefreshRateConfigs::DisplayManagerPolicy policy;
     policy.defaultMode = configs.getCurrentPolicy().defaultMode;
     policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
+    EXPECT_EQ(SetPolicyResult::Changed, configs.setPolicy(policy));
 
     std::vector<LayerRequirement> layers = {
             {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
@@ -1807,7 +1846,8 @@
         return configs.getBestRefreshRate(layers, {.touch = args.touch})->getId();
     };
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}));
 
     EXPECT_EQ(kModeId60, configs.getBestRefreshRate()->getId());
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
@@ -1831,7 +1871,8 @@
     EXPECT_EQ(kModeId60,
               getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}));
 
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
@@ -1860,7 +1901,8 @@
         return refreshRate.front().displayModePtr->getId();
     };
 
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
 
     // Idle should be lower priority than touch boost.
     {
@@ -2156,43 +2198,50 @@
 TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
     using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
 
-    RefreshRateConfigs configs(kModes_60_90, kModeId90);
+    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
 
-    // SetPolicy(60, 90), current 90Hz => TurnOn.
+    // setPolicy(60, 90), current 90Hz => TurnOn.
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
-    // SetPolicy(60, 90), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}), 0);
+    // setPolicy(60, 90), current 60Hz => TurnOn.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
-    // SetPolicy(60, 60), current 60Hz => TurnOff
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    // setPolicy(60, 60), current 60Hz => TurnOff
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 
-    // SetPolicy(90, 90), current 90Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
+    // setPolicy(90, 90), current 90Hz => TurnOff.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 }
 
 TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
     using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
 
-    RefreshRateConfigs configs(kModes_60_120, kModeId120);
+    TestableRefreshRateConfigs configs(kModes_60_120, kModeId120);
 
-    // SetPolicy(0, 60), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}), 0);
+    // setPolicy(0, 60), current 60Hz => TurnOn.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
-    // SetPolicy(60, 60), current 60Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
+    // setPolicy(60, 60), current 60Hz => TurnOff.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 
-    // SetPolicy(60, 120), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}), 0);
+    // setPolicy(60, 120), current 60Hz => TurnOn.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
 
-    // SetPolicy(120, 120), current 120Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}), 0);
+    // setPolicy(120, 120), current 120Hz => TurnOff.
+    EXPECT_EQ(SetPolicyResult::Changed,
+              configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}));
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 53e49eb..8d2130f 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -20,6 +20,7 @@
 
 #include <mutex>
 
+#include "FakeDisplayInjector.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/RefreshRateConfigs.h"
 #include "TestableScheduler.h"
@@ -40,6 +41,7 @@
 
 using MockEventThread = android::mock::EventThread;
 using MockLayer = android::mock::MockLayer;
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
 
 constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
 
@@ -59,11 +61,13 @@
 
     SchedulerTest();
 
-    static inline const DisplayModePtr kMode60 = createDisplayMode(DisplayModeId(0), 60_Hz);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(DisplayModeId(1), 120_Hz);
+    static inline const DisplayModePtr kMode60_1 = createDisplayMode(DisplayModeId(0), 60_Hz);
+    static inline const DisplayModePtr kMode120_1 = createDisplayMode(DisplayModeId(1), 120_Hz);
+    static inline const DisplayModePtr kMode60_2 = createDisplayMode(DisplayModeId(2), 60_Hz);
+    static inline const DisplayModePtr kMode120_2 = createDisplayMode(DisplayModeId(3), 120_Hz);
 
     std::shared_ptr<RefreshRateConfigs> mConfigs =
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60), kMode60->getId());
+            std::make_shared<RefreshRateConfigs>(makeModes(kMode60_1), kMode60_1->getId());
 
     mock::SchedulerCallback mSchedulerCallback;
     TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
@@ -71,6 +75,7 @@
     ConnectionHandle mConnectionHandle;
     MockEventThread* mEventThread;
     sp<MockEventThreadConnection> mEventThreadConnection;
+    FakeDisplayInjector mFakeDisplayInjector;
 
     TestableSurfaceFlinger mFlinger;
 };
@@ -166,7 +171,7 @@
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
     mScheduler->chooseRefreshRateForContent();
 }
 
@@ -176,7 +181,8 @@
     ASSERT_EQ(1u, mScheduler->layerHistorySize());
 
     mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+            std::make_shared<RefreshRateConfigs>(makeModes(kMode60_1, kMode120_1),
+                                                 kMode60_1->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -215,12 +221,18 @@
 }
 
 MATCHER(Is120Hz, "") {
-    return isApproxEqual(arg->getFps(), 120_Hz);
+    return isApproxEqual(arg.front().displayModePtr->getFps(), 120_Hz);
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
-    mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+    auto display = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+            },
+            mFlinger);
+
+    mScheduler->registerDisplay(display);
+    mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
 
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
@@ -233,12 +245,111 @@
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(Is120Hz(), _)).Times(1);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1);
     mScheduler->chooseRefreshRateForContent();
 
     // No-op if layer requirements have not changed.
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
     mScheduler->chooseRefreshRateForContent();
 }
 
+TEST_F(SchedulerTest, getBestDisplayMode_singleDisplay) {
+    auto display = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+            },
+            mFlinger);
+    mScheduler->registerDisplay(display);
+
+    std::vector<RefreshRateConfigs::LayerRequirement> layers =
+            std::vector<RefreshRateConfigs::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+    mScheduler->setContentRequirements(layers);
+    GlobalSignals globalSignals = {.idle = true};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+    std::vector<DisplayModeConfig> displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(1ul, displayModeConfigs.size());
+    EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode60_1);
+    EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+    globalSignals = {.idle = false};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(1ul, displayModeConfigs.size());
+    EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode120_1);
+    EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+    globalSignals = {.touch = true};
+    mScheduler->replaceTouchTimer(10);
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(1ul, displayModeConfigs.size());
+    EXPECT_EQ(displayModeConfigs.front().displayModePtr, kMode120_1);
+    EXPECT_EQ(displayModeConfigs.front().signals, globalSignals);
+
+    mScheduler->unregisterDisplay(display->getPhysicalId());
+    EXPECT_TRUE(mScheduler->mutableDisplays().empty());
+}
+
+TEST_F(SchedulerTest, getBestDisplayModes_multipleDisplays) {
+    auto display1 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_1, kMode120_1), kMode60_1->getId());
+            },
+            mFlinger);
+    auto display2 = mFakeDisplayInjector.injectDefaultInternalDisplay(
+            [&](FakeDisplayDeviceInjector& injector) {
+                injector.setDisplayModes(makeModes(kMode60_2, kMode120_2), kMode60_2->getId());
+            },
+            mFlinger, /* port */ 253u);
+    mScheduler->registerDisplay(display1);
+    mScheduler->registerDisplay(display2);
+
+    const std::vector<sp<DisplayDevice>>& expectedDisplays = {display1, display2};
+    std::vector<RefreshRateConfigs::LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    GlobalSignals globalSignals = {.idle = true};
+    std::vector<DisplayModeConfig> expectedConfigs = {DisplayModeConfig{globalSignals, kMode60_1},
+                                                      DisplayModeConfig{globalSignals, kMode60_2}};
+
+    mScheduler->setContentRequirements(layers);
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    std::vector<DisplayModeConfig> displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(displayModeConfigs.size(), expectedConfigs.size());
+    for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+        EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+                << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+                << " Actual fps "
+                << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+        EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+    }
+
+    expectedConfigs = std::vector<DisplayModeConfig>{DisplayModeConfig{globalSignals, kMode120_1},
+                                                     DisplayModeConfig{globalSignals, kMode120_2}};
+
+    globalSignals = {.idle = false};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
+    for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+        EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+                << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+                << " Actual fps "
+                << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+        EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+    }
+
+    globalSignals = {.touch = true};
+    mScheduler->replaceTouchTimer(10);
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+    displayModeConfigs = mScheduler->getBestDisplayModeConfigs();
+    ASSERT_EQ(expectedConfigs.size(), displayModeConfigs.size());
+    for (size_t i = 0; i < expectedConfigs.size(); ++i) {
+        EXPECT_EQ(expectedConfigs.at(i).displayModePtr, displayModeConfigs.at(i).displayModePtr)
+                << "Expected fps " << expectedConfigs.at(i).displayModePtr->getFps().getIntValue()
+                << " Actual fps "
+                << displayModeConfigs.at(i).displayModePtr->getFps().getIntValue();
+        EXPECT_EQ(globalSignals, displayModeConfigs.at(i).signals);
+    }
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index f7d34ac..6a9c970 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -30,9 +30,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
     // --------------------------------------------------------------------
     // Invocation
 
@@ -61,9 +58,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
     // --------------------------------------------------------------------
     // Invocation
     int64_t oldId = IPCThreadState::self()->clearCallingIdentity();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index 40ef949..93a3811 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -37,9 +37,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
     // Destroying the display commits a display transaction.
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 73f654b..94d517a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -90,15 +90,12 @@
     Case::HdrSupport::setupComposerCallExpectations(this);
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
 
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
     expectHotplugReceived<Case, true>(mEventThread);
     expectHotplugReceived<Case, true>(mSFEventThread);
 }
 
 template <typename Case>
 void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() {
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
     expectHotplugReceived<Case, false>(mEventThread);
     expectHotplugReceived<Case, false>(mSFEventThread);
 }
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index ec7e8a7..4e9f293 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -21,6 +21,7 @@
 #include <thread>
 
 #include "DisplayTransactionTestHelpers.h"
+#include "FakeDisplayInjector.h"
 
 #include <android/hardware/power/Boost.h>
 
@@ -32,6 +33,8 @@
 TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
     using namespace std::chrono_literals;
 
+    injectDefaultInternalDisplay([](FakeDisplayDeviceInjector&) {});
+
     mFlinger.scheduler()->replaceTouchTimer(100);
     std::this_thread::sleep_for(10ms);                  // wait for callback to be triggered
     EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index 98249bf..f553a23 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -38,11 +38,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // We expect the surface interceptor to possibly be used, but we treat it as
-    // disabled since it is called as a side effect rather than directly by this
-    // function.
-    EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
-
     // We expect a call to get the active display config.
     Case::Display::setupHwcGetActiveConfigCallExpectations(this);
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index e256d2c..bc66961 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -97,7 +97,7 @@
                     .setNativeWindow(mNativeWindow)
                     .setPowerMode(hal::PowerMode::ON)
                     .inject();
-    mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken();
+    mFlinger.mutableActiveDisplayId() = mDisplay->getPhysicalId();
 }
 
 void SurfaceFlingerPowerHintTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 9e54083..25857ec 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -262,7 +262,7 @@
         if (injector.physicalDisplay()
                     .transform(&display::PhysicalDisplay::isInternal)
                     .value_or(false)) {
-            test->mFlinger.mutableActiveDisplayToken() = display->getDisplayToken();
+            test->mFlinger.mutableActiveDisplayId() = display->getPhysicalId();
         }
 
         return display;
@@ -276,13 +276,6 @@
         EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
     }
 
-    static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
-                                                        PowerMode mode) {
-        EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
-        EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
-                .Times(1);
-    }
-
     static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
         // Any calls to get the active config will return a default value.
         EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
@@ -349,7 +342,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
     Case::Transition::template setupCallExpectations<Case>(this);
 
     // --------------------------------------------------------------------
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 93e3059..68df987 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -68,6 +68,8 @@
 
     auto& mutableLayerHistory() { return mLayerHistory; }
 
+    auto& mutableDisplays() { return mDisplays; }
+
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
         return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
     }
@@ -94,6 +96,22 @@
         return mPolicy.touch == Scheduler::TouchState::Active;
     }
 
+    void setTouchStateAndIdleTimerPolicy(GlobalSignals globalSignals) {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.touch = globalSignals.touch ? TouchState::Active : TouchState::Inactive;
+        mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
+    }
+
+    void setContentRequirements(std::vector<RefreshRateConfigs::LayerRequirement> layers) {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.contentRequirements = std::move(layers);
+    }
+
+    std::vector<DisplayModeConfig> getBestDisplayModeConfigs() {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        return Scheduler::getBestDisplayModeConfigs();
+    }
+
     void dispatchCachedReportedMode() {
         std::lock_guard<std::mutex> lock(mPolicyLock);
         return Scheduler::dispatchCachedReportedMode();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 2e90c7e..29ff5ee 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -40,7 +40,6 @@
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerDefaultFactory.h"
-#include "SurfaceInterceptor.h"
 #include "TestableScheduler.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
@@ -81,10 +80,6 @@
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return sp<android::impl::SurfaceInterceptor>::make();
-    }
-
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
         return sp<StartPropertySetThread>::make(timestampPropertyValue);
     }
@@ -417,8 +412,13 @@
         return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
     }
 
-    auto& getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
-    auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+    auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+    auto& getPendingTransactionQueue() {
+        return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+    }
+    size_t getPendingTransactionCount() {
+        return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
+    }
 
     auto setTransactionState(
             const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
@@ -511,7 +511,6 @@
     auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
     auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
-    auto& mutableInterceptor() { return mFlinger->mInterceptor; }
     auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
     auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
     auto& mutableTexturePool() { return mFlinger->mTexturePool; }
@@ -522,7 +521,7 @@
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
-    auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
+    auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
 
     auto fromHandle(const sp<IBinder>& handle) {
         return mFlinger->fromHandle(handle);
@@ -536,7 +535,6 @@
         mutableDisplays().clear();
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
-        mutableInterceptor().clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
         mFlinger->mCompositionEngine->setRenderEngine(
@@ -835,6 +833,9 @@
 
             sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
             mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
+            if (mFlinger.scheduler()) {
+                mFlinger.scheduler()->registerDisplay(display);
+            }
 
             DisplayDeviceState state;
             state.isSecure = mCreationArgs.isSecure;
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index b4030b3..db438b7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
 
@@ -22,12 +21,17 @@
 #include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <gui/LayerState.h>
 #include <gui/SurfaceComposerClient.h>
+#include <gui/fake/BufferData.h>
 #include <log/log.h>
 #include <ui/MockFence.h>
 #include <utils/String8.h>
+#include <vector>
+#include <binder/Binder.h>
 
 #include "TestableSurfaceFlinger.h"
+#include "TransactionHandler.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockVsyncController.h"
 
@@ -78,6 +82,7 @@
         mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
                                 std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
                                 std::move(eventThread), std::move(sfEventThread));
+        mFlinger.flinger()->addTransactionReadyFilters();
     }
 
     TestableSurfaceFlinger mFlinger;
@@ -314,7 +319,10 @@
 
     ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) {
         ComposerState state;
-        state.state.bufferData = std::make_shared<BufferData>();
+        state.state.bufferData =
+                std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1,
+                                                   /* height */ 2, /* pixelFormat */ 0,
+                                                   /* outUsage */ 0);
         state.state.bufferData->acquireFence = std::move(fence);
         state.state.layerId = layerId;
         state.state.surface =
@@ -361,7 +369,7 @@
         }
         mFlinger.flushTransactionQueues();
         EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
-        EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionQueue().size());
+        EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionCount());
     }
 };
 
@@ -413,7 +421,9 @@
                                   {
                                           createComposerState(kLayerId,
                                                               fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eCropChanged),
+                                                              layer_state_t::eCropChanged |
+                                                                      layer_state_t::
+                                                                              eBufferChanged),
                                   });
     setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
@@ -536,41 +546,6 @@
                          kExpectedTransactionsPending);
 }
 
-TEST_F(LatchUnsignaledAutoSingleLayerTest, UnsignaledNotAppliedWhenThereAreSignaled_SignaledFirst) {
-    const sp<IBinder> kApplyToken1 =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
-    const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsPending = 1u;
-
-    const auto signaledTransaction =
-            createTransactionInfo(kApplyToken1,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto signaledTransaction2 =
-            createTransactionInfo(kApplyToken2,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken3,
-                                  {
-                                          createComposerState(kLayerId2,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-
-    setTransactionStates({signaledTransaction, signaledTransaction2, unsignaledTransaction},
-                         kExpectedTransactionsPending);
-}
-
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
@@ -798,7 +773,7 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsPending = 1u;
+    const auto kExpectedTransactionsPending = 2u;
 
     const auto unsignaledTransaction =
             createTransactionInfo(kApplyToken,
@@ -1004,4 +979,16 @@
     setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
+TEST(TransactionHandlerTest, QueueTransaction) {
+    TransactionHandler handler;
+    TransactionState transaction;
+    transaction.applyToken = sp<BBinder>::make();
+    transaction.id = 42;
+    handler.queueTransaction(std::move(transaction));
+    std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
+
+    EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
+    EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 5267586..8af2dfa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -24,14 +24,14 @@
 
 struct SchedulerCallback final : ISchedulerCallback {
     MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
-    MOCK_METHOD(void, requestDisplayMode, (DisplayModePtr, DisplayModeEvent), (override));
+    MOCK_METHOD(void, requestDisplayModes, (std::vector<scheduler::DisplayModeConfig>), (override));
     MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
     MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
     void setVsyncEnabled(bool) override {}
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+    void requestDisplayModes(std::vector<scheduler::DisplayModeConfig>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
deleted file mode 100644
index 0a0e7b5..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include "mock/MockSurfaceInterceptor.h"
-
-namespace android::mock {
-
-// Explicit default instantiation is recommended.
-SurfaceInterceptor::SurfaceInterceptor() = default;
-SurfaceInterceptor::~SurfaceInterceptor() = default;
-
-} // namespace android::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
deleted file mode 100644
index b085027..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "SurfaceInterceptor.h"
-
-namespace android::mock {
-
-class SurfaceInterceptor : public android::SurfaceInterceptor {
-public:
-    SurfaceInterceptor();
-    ~SurfaceInterceptor() override;
-
-    MOCK_METHOD2(enable,
-                 void(const SortedVector<sp<Layer>>&,
-                      const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
-    MOCK_METHOD0(disable, void());
-    MOCK_METHOD0(isEnabled, bool());
-    MOCK_METHOD1(addTransactionTraceListener, void(const sp<gui::ITransactionTraceListener>&));
-    MOCK_METHOD1(binderDied, void(const wp<IBinder>&));
-    MOCK_METHOD7(saveTransaction,
-                 void(const Vector<ComposerState>&,
-                      const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
-                      const Vector<DisplayState>&, uint32_t, int, int, uint64_t));
-    MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
-    MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
-    MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
-    MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&));
-    MOCK_METHOD1(saveDisplayDeletion, void(int32_t));
-    MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t));
-    MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
-};
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 224868c..f297da5 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -51,7 +51,11 @@
     }
 
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
-        captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        // TODO(b/248317436): extend to cover all displays for multi-display devices
+        const auto display =
+                ids.empty() ? nullptr : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+        captureScreen(sc, display);
     }
 
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {