Merge "[SF] set render rate as peakFps when new display device is added" into main
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
index c3f2fed..14540ec 100644
--- a/data/etc/input/motion_predictor_config.xml
+++ b/data/etc/input/motion_predictor_config.xml
@@ -35,7 +35,10 @@
 
     The jerk thresholds are based on normalized dt = 1 calculations.
   -->
-  <low-jerk>1.0</low-jerk>
-  <high-jerk>1.1</high-jerk>
+  <low-jerk>1.5</low-jerk>
+  <high-jerk>2.0</high-jerk>
+
+  <!-- The forget factor in the first-order IIR filter for jerk smoothing -->
+  <jerk-forget-factor>0.25</jerk-forget-factor>
 </motion-predictor>
 
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index f715039..2f1ef86 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -56,12 +56,20 @@
     // acceleration) and has the units of d^3p/dt^3.
     std::optional<float> jerkMagnitude() const;
 
+    // forgetFactor is the coefficient of the first-order IIR filter for jerk. A factor of 1 results
+    // in no smoothing.
+    void setForgetFactor(float forgetFactor);
+    float getForgetFactor() const;
+
 private:
     const bool mNormalizedDt;
+    // Coefficient of first-order IIR filter to smooth jerk calculation.
+    float mForgetFactor = 1;
 
     RingBuffer<int64_t> mTimestamps{4};
     std::array<float, 4> mXDerivatives{}; // [x, x', x'', x''']
     std::array<float, 4> mYDerivatives{}; // [y, y', y'', y''']
+    float mJerkMagnitude;
 };
 
 /**
@@ -116,6 +124,11 @@
 
     bool isPredictionAvailable(int32_t deviceId, int32_t source);
 
+    /**
+     * Currently used to expose config constants in testing.
+     */
+    const TfLiteMotionPredictorModel::Config& getModelConfig();
+
 private:
     const nsecs_t mPredictionTimestampOffsetNanos;
     const std::function<bool()> mCheckMotionPredictionEnabled;
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 728a8e1..08a4330 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -110,6 +110,9 @@
         // High jerk means more predictions will be pruned, vice versa for low.
         float lowJerk = 0;
         float highJerk = 0;
+
+        // Coefficient for the first-order IIR filter for jerk calculation.
+        float jerkForgetFactor = 1;
     };
 
     // Creates a model from an encoded Flatbuffer model.
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 62b8433..7c19614 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -111,7 +111,9 @@
                     } else {
                         binder = getRandomBinder(&provider);
                     }
-                    CHECK(OK == p->writeStrongBinder(binder));
+
+                    // may fail if mixing kernel binder and RPC binder
+                    (void) p->writeStrongBinder(binder);
                 },
         });
 
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index f4cf11e..a9bd11e 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 #include <set>
+#include <utility>
 
 #include <android-base/file.h>
 #include <android-base/parseint.h>
@@ -115,7 +116,7 @@
 
 /* list of extra hal interfaces to dump containing process during native dumps */
 // This is filled when dumpstate is called.
-static std::set<const std::string> extra_hal_interfaces_to_dump;
+static std::set<std::string> extra_hal_interfaces_to_dump;
 
 static void read_extra_hals_to_dump_from_property() {
     // extra hals to dump are already filled
@@ -129,7 +130,7 @@
         if (trimmed_token.length() == 0) {
             continue;
         }
-        extra_hal_interfaces_to_dump.insert(trimmed_token);
+        extra_hal_interfaces_to_dump.insert(std::move(trimmed_token));
     }
 }
 
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 5b61d39..9204b95 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -75,6 +75,9 @@
 JerkTracker::JerkTracker(bool normalizedDt) : mNormalizedDt(normalizedDt) {}
 
 void JerkTracker::pushSample(int64_t timestamp, float xPos, float yPos) {
+    // If we previously had full samples, we have a previous jerk calculation
+    // to do weighted smoothing.
+    const bool applySmoothing = mTimestamps.size() == mTimestamps.capacity();
     mTimestamps.pushBack(timestamp);
     const int numSamples = mTimestamps.size();
 
@@ -115,6 +118,16 @@
         }
     }
 
+    if (numSamples == static_cast<int>(mTimestamps.capacity())) {
+        float newJerkMagnitude = std::hypot(newXDerivatives[3], newYDerivatives[3]);
+        ALOGD_IF(isDebug(), "raw jerk: %f", newJerkMagnitude);
+        if (applySmoothing) {
+            mJerkMagnitude = mJerkMagnitude + (mForgetFactor * (newJerkMagnitude - mJerkMagnitude));
+        } else {
+            mJerkMagnitude = newJerkMagnitude;
+        }
+    }
+
     std::swap(newXDerivatives, mXDerivatives);
     std::swap(newYDerivatives, mYDerivatives);
 }
@@ -125,11 +138,19 @@
 
 std::optional<float> JerkTracker::jerkMagnitude() const {
     if (mTimestamps.size() == mTimestamps.capacity()) {
-        return std::hypot(mXDerivatives[3], mYDerivatives[3]);
+        return mJerkMagnitude;
     }
     return std::nullopt;
 }
 
+void JerkTracker::setForgetFactor(float forgetFactor) {
+    mForgetFactor = forgetFactor;
+}
+
+float JerkTracker::getForgetFactor() const {
+    return mForgetFactor;
+}
+
 // --- MotionPredictor ---
 
 MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
@@ -159,6 +180,7 @@
     if (!mModel) {
         mModel = TfLiteMotionPredictorModel::create();
         LOG_ALWAYS_FATAL_IF(!mModel);
+        mJerkTracker.setForgetFactor(mModel->config().jerkForgetFactor);
     }
 
     if (!mBuffers) {
@@ -357,4 +379,12 @@
     return true;
 }
 
+const TfLiteMotionPredictorModel::Config& MotionPredictor::getModelConfig() {
+    if (!mModel) {
+        mModel = TfLiteMotionPredictorModel::create();
+        LOG_ALWAYS_FATAL_IF(!mModel);
+    }
+    return mModel->config();
+}
+
 } // namespace android
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index b843a4b..b401c98 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -283,6 +283,7 @@
             .distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"),
             .lowJerk = parseXMLFloat(*configRoot, "low-jerk"),
             .highJerk = parseXMLFloat(*configRoot, "high-jerk"),
+            .jerkForgetFactor = parseXMLFloat(*configRoot, "jerk-forget-factor"),
     };
 
     return std::unique_ptr<TfLiteMotionPredictorModel>(
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 5ab8ca9..3830751 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -150,3 +150,10 @@
   description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic"
   bug: "263559234"
 }
+
+flag {
+  name: "show_pointers_for_partial_screenshare"
+  namespace: "input"
+  description: "Show touch and pointer indicators when mirroring a single task"
+  bug: "310179437"
+}
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index d077760..5bd5794 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -88,6 +88,7 @@
 
 TEST(JerkTrackerTest, JerkCalculationNormalizedDtTrue) {
     JerkTracker jerkTracker(true);
+    jerkTracker.setForgetFactor(.5);
     jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
     jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
     jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
@@ -118,11 +119,14 @@
      * y'':  3 -> -15
      * y''': -18
      */
-    EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(-50, -18));
+    const float newJerk = (1 - jerkTracker.getForgetFactor()) * std::hypot(10, -1) +
+            jerkTracker.getForgetFactor() * std::hypot(-50, -18);
+    EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk);
 }
 
 TEST(JerkTrackerTest, JerkCalculationNormalizedDtFalse) {
     JerkTracker jerkTracker(false);
+    jerkTracker.setForgetFactor(.5);
     jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
     jerkTracker.pushSample(/*timestamp=*/10, 25, 53);
     jerkTracker.pushSample(/*timestamp=*/20, 30, 60);
@@ -153,7 +157,9 @@
      * y'':  .03 -> -.125 (delta above, divide by 10)
      * y''': -.0155 (delta above, divide by 10)
      */
-    EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(-.0375, -.0155));
+    const float newJerk = (1 - jerkTracker.getForgetFactor()) * std::hypot(.01, -.001) +
+            jerkTracker.getForgetFactor() * std::hypot(-.0375, -.0155);
+    EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk);
 }
 
 TEST(JerkTrackerTest, JerkCalculationAfterReset) {
@@ -291,15 +297,19 @@
     MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
                               []() { return true /*enable prediction*/; });
 
-    // Jerk is medium (1.05 normalized, which is halfway between LOW_JANK and HIGH_JANK)
-    predictor.record(getMotionEvent(DOWN, 0, 5.2, 20ms));
-    predictor.record(getMotionEvent(MOVE, 0, 11.5, 30ms));
-    predictor.record(getMotionEvent(MOVE, 0, 22, 40ms));
-    predictor.record(getMotionEvent(MOVE, 0, 37.75, 50ms));
-    predictor.record(getMotionEvent(MOVE, 0, 59.8, 60ms));
+    const float mediumJerk =
+            (predictor.getModelConfig().lowJerk + predictor.getModelConfig().highJerk) / 2;
+    const float a = 3; // initial acceleration
+    const float b = 4; // initial velocity
+    const float c = 5; // initial position
+    predictor.record(getMotionEvent(DOWN, 0, c, 20ms));
+    predictor.record(getMotionEvent(MOVE, 0, c + b, 30ms));
+    predictor.record(getMotionEvent(MOVE, 0, c + 2 * b + a, 40ms));
+    predictor.record(getMotionEvent(MOVE, 0, c + 3 * b + 3 * a + mediumJerk, 50ms));
+    predictor.record(getMotionEvent(MOVE, 0, c + 4 * b + 6 * a + 4 * mediumJerk, 60ms));
     std::unique_ptr<MotionEvent> predicted = predictor.predict(82 * NSEC_PER_MSEC);
     EXPECT_NE(nullptr, predicted);
-    // Halfway between LOW_JANK and HIGH_JANK means that half of the predictions
+    // Halfway between LOW_JERK and HIGH_JERK means that half of the predictions
     // will be pruned. If model prediction window is close enough to predict()
     // call time window, then half of the model predictions (5/2 -> 2) will be
     // ouputted.
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 6fcb3a4..d05ff34 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -326,7 +326,7 @@
      * COMPOSER_OVERLAY, the system will try to prioritize the buffer receiving
      * an overlay plane & avoid caching it in intermediate composition buffers.
      */
-    AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1UL << 32,
+    AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1ULL << 32,
 
     AHARDWAREBUFFER_USAGE_VENDOR_0  = 1ULL << 28,
     AHARDWAREBUFFER_USAGE_VENDOR_1  = 1ULL << 29,
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
index 6f2a96a..8d0fbba 100644
--- a/libs/renderengine/ExternalTexture.cpp
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
+#include <common/trace.h>
 #include <log/log.h>
 #include <renderengine/RenderEngine.h>
 #include <renderengine/impl/ExternalTexture.h>
 #include <ui/GraphicBuffer.h>
-#include <utils/Trace.h>
 
 namespace android::renderengine::impl {
 
@@ -35,7 +35,7 @@
 }
 
 void ExternalTexture::remapBuffer() {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     {
         auto buf = mBuffer;
         mRenderEngine.unmapExternalTextureBuffer(std::move(buf));
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index e1a6f6a..f84db0b 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -57,6 +57,7 @@
         "libui",
         "libutils",
         "server_configurable_flags",
+        "libtracing_perfetto",
     ],
 
     data: ["resources/*"],
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 8aeef9f..b7b7a4d 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -25,8 +25,8 @@
 
 #include "compat/SkiaBackendTexture.h"
 
+#include <common/trace.h>
 #include <log/log_main.h>
-#include <utils/Trace.h>
 
 namespace android {
 namespace renderengine {
@@ -63,7 +63,7 @@
 }
 
 sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
 
     sk_sp<SkImage> image = mBackendTexture->makeImage(alphaType, dataspace, releaseImageProc, this);
     // The following ref will be counteracted by releaseProc, when SkImage is discarded.
@@ -75,7 +75,7 @@
 }
 
 sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     LOG_ALWAYS_FATAL_IF(!mBackendTexture->isOutputBuffer(),
                         "You can't generate an SkSurface for a read-only texture");
     if (!mSurface.get() || mDataspace != dataspace) {
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index d246870..59b0656 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -729,8 +729,7 @@
         const auto externalTexture =
                 std::make_shared<impl::ExternalTexture>(externalBuffer, *renderengine,
                                                         impl::ExternalTexture::Usage::READABLE);
-        std::vector<const std::shared_ptr<ExternalTexture>> textures =
-            {srcTexture, externalTexture};
+        std::vector<std::shared_ptr<ExternalTexture>> textures = {srcTexture, externalTexture};
 
         // Another external texture with a different pixel format triggers useIsOpaqueWorkaround.
         // It doesn't have to be f16, but it can't be the usual 8888.
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
index 68798bf..a3a43e2 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
@@ -21,9 +21,9 @@
 
 #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
 
+#include <common/trace.h>
 #include <log/log_main.h>
 #include <sync/sync.h>
-#include <utils/Trace.h>
 
 namespace android::renderengine::skia {
 
@@ -78,7 +78,7 @@
                                                      sk_sp<SkSurface> dstSurface) {
     sk_sp<GrDirectContext> grContext = context->grDirectContext();
     {
-        ATRACE_NAME("flush surface");
+        SFTRACE_NAME("flush surface");
         // TODO: Investigate feasibility of combining this "surface flush" into the "context flush"
         // below.
         context->grDirectContext()->flush(dstSurface.get());
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 48270e1..af24600 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -28,12 +28,11 @@
 #include <GrContextOptions.h>
 #include <GrTypes.h>
 #include <android-base/stringprintf.h>
+#include <common/trace.h>
 #include <gl/GrGLInterface.h>
 #include <include/gpu/ganesh/gl/GrGLDirectContext.h>
-#include <gui/TraceUtils.h>
 #include <sync/sync.h>
 #include <ui/DebugUtils.h>
-#include <utils/Trace.h>
 
 #include <cmath>
 #include <cstdint>
@@ -332,7 +331,7 @@
 
 void SkiaGLRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceFd) {
     if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) {
-        ATRACE_NAME("SkiaGLRenderEngine::waitFence");
+        SFTRACE_NAME("SkiaGLRenderEngine::waitFence");
         sync_wait(fenceFd.get(), -1);
     }
 }
@@ -341,19 +340,19 @@
                                                    sk_sp<SkSurface> dstSurface) {
     sk_sp<GrDirectContext> grContext = context->grDirectContext();
     {
-        ATRACE_NAME("flush surface");
+        SFTRACE_NAME("flush surface");
         grContext->flush(dstSurface.get());
     }
     base::unique_fd drawFence = flushGL();
 
     bool requireSync = drawFence.get() < 0;
     if (requireSync) {
-        ATRACE_BEGIN("Submit(sync=true)");
+        SFTRACE_BEGIN("Submit(sync=true)");
     } else {
-        ATRACE_BEGIN("Submit(sync=false)");
+        SFTRACE_BEGIN("Submit(sync=false)");
     }
     bool success = grContext->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo);
-    ATRACE_END();
+    SFTRACE_END();
     if (!success) {
         ALOGE("Failed to flush RenderEngine commands");
         // Chances are, something illegal happened (Skia's internal GPU object
@@ -400,7 +399,7 @@
 }
 
 base::unique_fd SkiaGLRenderEngine::flushGL() {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     if (!GLExtensions::getInstance().hasNativeFenceSync()) {
         return base::unique_fd();
     }
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index a609f2d..9709cd1 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -54,8 +54,8 @@
 #include <SkTileMode.h>
 #include <android-base/stringprintf.h>
 #include <common/FlagManager.h>
+#include <common/trace.h>
 #include <gui/FenceMonitor.h>
-#include <gui/TraceUtils.h>
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include <pthread.h>
 #include <src/core/SkTraceEventCommon.h>
@@ -64,7 +64,6 @@
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/HdrRenderTypeUtils.h>
-#include <utils/Trace.h>
 
 #include <cmath>
 #include <cstdint>
@@ -261,7 +260,7 @@
                                                const SkString& description) {
     mShadersCachedSinceLastCall++;
     mTotalShadersCompiled++;
-    ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled);
+    SFTRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled);
 }
 
 int SkiaRenderEngine::reportShadersCompiled() {
@@ -416,7 +415,7 @@
     if (isProtectedBuffer || isProtected() || !isGpuSampleable) {
         return;
     }
-    ATRACE_CALL();
+    SFTRACE_CALL();
 
     // If we were to support caching protected buffers then we will need to switch the
     // currently bound context if we are not already using the protected context (and subsequently
@@ -441,7 +440,7 @@
 }
 
 void SkiaRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
         iter != mGraphicBufferExternalRefs.end()) {
@@ -498,7 +497,7 @@
 }
 
 void SkiaRenderEngine::cleanupPostRender() {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     mTextureCleanupMgr.cleanup();
 }
@@ -696,7 +695,7 @@
         const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
         const DisplaySettings& display, const std::vector<LayerSettings>& layers,
         const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
-    ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str());
+    SFTRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str());
 
     std::lock_guard<std::mutex> lock(mRenderingMutex);
 
@@ -788,7 +787,7 @@
         logSettings(display);
     }
     for (const auto& layer : layers) {
-        ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
+        SFTRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
 
         if (kPrintLayerSettings) {
             logSettings(layer);
@@ -882,7 +881,7 @@
             // TODO(b/182216890): Filter out empty layers earlier
             if (blurRect.width() > 0 && blurRect.height() > 0) {
                 if (layer.backgroundBlurRadius > 0) {
-                    ATRACE_NAME("BackgroundBlur");
+                    SFTRACE_NAME("BackgroundBlur");
                     auto blurredImage = mBlurFilter->generate(context, layer.backgroundBlurRadius,
                                                               blurInput, blurRect);
 
@@ -895,7 +894,7 @@
                 canvas->concat(getSkM44(layer.blurRegionTransform).asM33());
                 for (auto region : layer.blurRegions) {
                     if (cachedBlurs[region.blurRadius] == nullptr) {
-                        ATRACE_NAME("BlurRegion");
+                        SFTRACE_NAME("BlurRegion");
                         cachedBlurs[region.blurRadius] =
                                 mBlurFilter->generate(context, region.blurRadius, blurInput,
                                                       blurRect);
@@ -978,7 +977,7 @@
 
         SkPaint paint;
         if (layer.source.buffer.buffer) {
-            ATRACE_NAME("DrawImage");
+            SFTRACE_NAME("DrawImage");
             validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
             const auto& item = layer.source.buffer;
             auto imageTextureRef = getOrCreateBackendTexture(item.buffer->getBuffer(), false);
@@ -1111,7 +1110,7 @@
                 paint.setColorFilter(SkColorFilters::Matrix(colorMatrix));
             }
         } else {
-            ATRACE_NAME("DrawColor");
+            SFTRACE_NAME("DrawColor");
             const auto color = layer.source.solidColor;
             sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
                                                                 .fG = color.g,
@@ -1168,7 +1167,7 @@
             canvas->drawRect(bounds.rect(), paint);
         }
         if (kGaneshFlushAfterEveryLayer) {
-            ATRACE_NAME("flush surface");
+            SFTRACE_NAME("flush surface");
             // No-op in Graphite. If "flushing" Skia's drawing commands after each layer is desired
             // in Graphite, then a graphite::Recording would need to be snapped and tracked for each
             // layer, which is likely possible but adds non-trivial complexity (in both bookkeeping
@@ -1183,7 +1182,7 @@
     LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
     auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
 
-    if (ATRACE_ENABLED()) {
+    if (SFTRACE_ENABLED()) {
         static gui::FenceMonitor sMonitor("RE Completion");
         sMonitor.queueFence(drawFence);
     }
@@ -1201,7 +1200,7 @@
 void SkiaRenderEngine::drawShadow(SkCanvas* canvas,
                                   const SkRRect& casterRRect,
                                   const ShadowSettings& settings) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     const float casterZ = settings.length / 2.0f;
     const auto flags =
             settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index a1f917d..d89e818 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -32,9 +32,8 @@
 #include <vk/GrVkTypes.h>
 
 #include <android-base/stringprintf.h>
-#include <gui/TraceUtils.h>
+#include <common/trace.h>
 #include <sync/sync.h>
-#include <utils/Trace.h>
 
 #include <memory>
 #include <string>
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
index d246466..3fbc6ca 100644
--- a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
@@ -32,15 +32,15 @@
 #include "skia/compat/SkiaBackendTexture.h"
 
 #include <android/hardware_buffer.h>
+#include <common/trace.h>
 #include <log/log_main.h>
-#include <utils/Trace.h>
 
 namespace android::renderengine::skia {
 
 GaneshBackendTexture::GaneshBackendTexture(sk_sp<GrDirectContext> grContext,
                                            AHardwareBuffer* buffer, bool isOutputBuffer)
       : SkiaBackendTexture(buffer, isOutputBuffer), mGrContext(grContext) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     AHardwareBuffer_Desc desc;
     AHardwareBuffer_describe(buffer, &desc);
     const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
diff --git a/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
index 3dd9ed2..a6e93ba 100644
--- a/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
+++ b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
@@ -28,16 +28,16 @@
 #include "skia/ColorSpaces.h"
 
 #include <android/hardware_buffer.h>
+#include <common/trace.h>
 #include <inttypes.h>
 #include <log/log_main.h>
-#include <utils/Trace.h>
 
 namespace android::renderengine::skia {
 
 GraphiteBackendTexture::GraphiteBackendTexture(std::shared_ptr<skgpu::graphite::Recorder> recorder,
                                                AHardwareBuffer* buffer, bool isOutputBuffer)
       : SkiaBackendTexture(buffer, isOutputBuffer), mRecorder(std::move(recorder)) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     AHardwareBuffer_Desc desc;
     AHardwareBuffer_describe(buffer, &desc);
     const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
diff --git a/libs/renderengine/skia/debug/CommonPool.cpp b/libs/renderengine/skia/debug/CommonPool.cpp
index bf15300..9d7c69b 100644
--- a/libs/renderengine/skia/debug/CommonPool.cpp
+++ b/libs/renderengine/skia/debug/CommonPool.cpp
@@ -20,8 +20,8 @@
 #define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <common/trace.h>
 #include <sys/resource.h>
-#include <utils/Trace.h>
 
 #include <system/thread_defs.h>
 #include <array>
@@ -31,7 +31,7 @@
 namespace skia {
 
 CommonPool::CommonPool() {
-    ATRACE_CALL();
+    SFTRACE_CALL();
 
     CommonPool* pool = this;
     // Create 2 workers
diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp
index e778884..e6a0e22 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.cpp
+++ b/libs/renderengine/skia/debug/SkiaCapture.cpp
@@ -22,9 +22,9 @@
 
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <common/trace.h>
 #include <log/log.h>
 #include <renderengine/RenderEngine.h>
-#include <utils/Trace.h>
 
 #include "CommonPool.h"
 #include "SkCanvas.h"
@@ -48,7 +48,7 @@
 }
 
 SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS {
-    ATRACE_CALL();
+    SFTRACE_CALL();
 
     // If we are not running yet, set up.
     if (CC_LIKELY(!mCaptureRunning)) {
@@ -86,7 +86,7 @@
 }
 
 void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     // Don't end anything if we are not running.
     if (CC_LIKELY(!mCaptureRunning)) {
         return;
@@ -102,7 +102,7 @@
 }
 
 SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     // Don't start anything if we are not running.
     if (CC_LIKELY(!mCaptureRunning)) {
         return surface->getCanvas();
@@ -122,7 +122,7 @@
 }
 
 uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     // Don't end anything if we are not running.
     if (CC_LIKELY(!mCaptureRunning)) {
         return 0;
@@ -151,7 +151,7 @@
 }
 
 void SkiaCapture::writeToFile() {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     // Pass mMultiPic and mOpenMultiPicStream to a background thread, which will
     // handle the heavyweight serialization work and destroy them.
     // mOpenMultiPicStream is released to a bare pointer because keeping it in
@@ -169,7 +169,7 @@
 }
 
 bool SkiaCapture::setupMultiFrameCapture() {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     ALOGD("Set up multi-frame capture, ms = %llu", mTimerInterval.count());
     base::SetProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME, "");
 
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 1e0c4cf..cd1bd71 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -25,8 +25,8 @@
 #include <SkString.h>
 #include <SkSurface.h>
 #include <SkTileMode.h>
+#include <common/trace.h>
 #include <log/log.h>
-#include <utils/Trace.h>
 
 namespace android {
 namespace renderengine {
@@ -79,7 +79,7 @@
                                 const uint32_t blurRadius, const float blurAlpha,
                                 const SkRect& blurRect, sk_sp<SkImage> blurredImage,
                                 sk_sp<SkImage> input) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
 
     SkPaint paint;
     paint.setAlphaf(blurAlpha);
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
index c9499cb..8c52c57 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -19,18 +19,18 @@
 #include "GaussianBlurFilter.h"
 #include <SkBlendMode.h>
 #include <SkCanvas.h>
+#include <SkImageFilters.h>
 #include <SkPaint.h>
 #include <SkRRect.h>
 #include <SkRuntimeEffect.h>
-#include <SkImageFilters.h>
 #include <SkSize.h>
 #include <SkString.h>
 #include <SkSurface.h>
 #include <SkTileMode.h>
+#include <common/trace.h>
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
-#include "include/gpu/GpuTypes.h" // from Skia
 #include <log/log.h>
-#include <utils/Trace.h>
+#include "include/gpu/GpuTypes.h" // from Skia
 
 namespace android {
 namespace renderengine {
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
index 7a070d7..defaf6e 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -29,10 +29,10 @@
 #include <SkString.h>
 #include <SkSurface.h>
 #include <SkTileMode.h>
+#include <common/trace.h>
 #include <include/gpu/GpuTypes.h>
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include <log/log.h>
-#include <utils/Trace.h>
 
 namespace android {
 namespace renderengine {
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index f7dcd3a..3bc3564 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -19,9 +19,9 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <SkString.h>
+#include <common/trace.h>
 #include <log/log.h>
 #include <shaders/shaders.h>
-#include <utils/Trace.h>
 
 #include <math/mat4.h>
 
@@ -30,7 +30,7 @@
 namespace skia {
 
 sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     SkString shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
 
     auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
@@ -45,7 +45,7 @@
         sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance,
         float currentDisplayLuminanceNits, float maxLuminance, AHardwareBuffer* buffer,
         aidl::android::hardware::graphics::composer3::RenderIntent renderIntent) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
 
     effectBuilder.child("child") = shader;
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 0783714..7fbbf49 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -66,5 +66,6 @@
         "libutils",
         "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
+        "libtracing_perfetto",
     ],
 }
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index d27c151..f5a90fd 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -23,9 +23,9 @@
 #include <future>
 
 #include <android-base/stringprintf.h>
+#include <common/trace.h>
 #include <private/gui/SyncFeatures.h>
 #include <processgroup/processgroup.h>
-#include <utils/Trace.h>
 
 using namespace std::chrono_literals;
 
@@ -39,7 +39,7 @@
 
 RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory)
       : RenderEngine(Threaded::YES) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
 
     std::lock_guard lockThread(mThreadMutex);
     mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory);
@@ -76,7 +76,7 @@
 
 // NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
 void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS {
-    ATRACE_CALL();
+    SFTRACE_CALL();
 
     if (!SetTaskProfiles(0, {"SFRenderEnginePolicy"})) {
         ALOGW("Failed to set render-engine task profile!");
@@ -133,13 +133,13 @@
 std::future<void> RenderEngineThreaded::primeCache(PrimeCacheConfig config) {
     const auto resultPromise = std::make_shared<std::promise<void>>();
     std::future<void> resultFuture = resultPromise->get_future();
-    ATRACE_CALL();
+    SFTRACE_CALL();
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([resultPromise, config](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::primeCache");
+            SFTRACE_NAME("REThreaded::primeCache");
             if (setSchedFifo(false) != NO_ERROR) {
                 ALOGW("Couldn't set SCHED_OTHER for primeCache");
             }
@@ -163,7 +163,7 @@
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::dump");
+            SFTRACE_NAME("REThreaded::dump");
             std::string localResult = result;
             instance.dump(localResult);
             resultPromise.set_value(std::move(localResult));
@@ -176,13 +176,13 @@
 
 void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
                                                     bool isRenderable) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::mapExternalTextureBuffer");
+            SFTRACE_NAME("REThreaded::mapExternalTextureBuffer");
             instance.mapExternalTextureBuffer(buffer, isRenderable);
         });
     }
@@ -190,14 +190,14 @@
 }
 
 void RenderEngineThreaded::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     // This function is designed so it can run asynchronously, so we do not need to wait
     // for the futures.
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push(
                 [=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable {
-                    ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+                    SFTRACE_NAME("REThreaded::unmapExternalTextureBuffer");
                     instance.unmapExternalTextureBuffer(std::move(buffer));
                 });
     }
@@ -229,7 +229,7 @@
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::cleanupPostRender");
+            SFTRACE_NAME("REThreaded::cleanupPostRender");
             instance.cleanupPostRender();
         });
         mNeedsPostRenderCleanup = false;
@@ -252,7 +252,7 @@
 ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
         const DisplaySettings& display, const std::vector<LayerSettings>& layers,
         const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
-    ATRACE_CALL();
+    SFTRACE_CALL();
     const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
     std::future<FenceResult> resultFuture = resultPromise->get_future();
     int fd = bufferFence.release();
@@ -261,7 +261,7 @@
         mNeedsPostRenderCleanup = true;
         mFunctionCalls.push(
                 [resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) {
-                    ATRACE_NAME("REThreaded::drawLayers");
+                    SFTRACE_NAME("REThreaded::drawLayers");
                     instance.updateProtectedContext(layers, buffer);
                     instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
                                                 base::unique_fd(fd));
@@ -277,7 +277,7 @@
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::getContextPriority");
+            SFTRACE_NAME("REThreaded::getContextPriority");
             int priority = instance.getContextPriority();
             resultPromise.set_value(priority);
         });
@@ -297,7 +297,7 @@
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([size](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::onActiveDisplaySizeChanged");
+            SFTRACE_NAME("REThreaded::onActiveDisplaySizeChanged");
             instance.onActiveDisplaySizeChanged(size);
         });
     }
@@ -324,7 +324,7 @@
     {
         std::lock_guard lock(mThreadMutex);
         mFunctionCalls.push([tracingEnabled](renderengine::RenderEngine& instance) {
-            ATRACE_NAME("REThreaded::setEnableTracing");
+            SFTRACE_NAME("REThreaded::setEnableTracing");
             instance.setEnableTracing(tracingEnabled);
         });
     }
diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h
index 0b2b0af..59c43d6 100644
--- a/libs/tracing_perfetto/include/tracing_perfetto.h
+++ b/libs/tracing_perfetto/include/tracing_perfetto.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef TRACING_PERFETTO_H
-#define TRACING_PERFETTO_H
+#pragma once
 
 #include <stdint.h>
 
@@ -29,6 +28,8 @@
 
 void traceAsyncBegin(uint64_t category, const char* name, int32_t cookie);
 
+void traceFormatBegin(uint64_t category, const char* fmt, ...);
+
 void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie);
 
 void traceAsyncBeginForTrack(uint64_t category, const char* name,
@@ -39,12 +40,14 @@
 
 void traceInstant(uint64_t category, const char* name);
 
+void traceFormatInstant(uint64_t category, const char* fmt, ...);
+
 void traceInstantForTrack(uint64_t category, const char* trackName,
                             const char* name);
 
 void traceCounter(uint64_t category, const char* name, int64_t value);
 
+void traceCounter32(uint64_t category, const char* name, int32_t value);
+
 bool isTagEnabled(uint64_t category);
 }  // namespace tracing_perfetto
-
-#endif  // TRACING_PERFETTO_H
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
index fc5336d..c35e078 100644
--- a/libs/tracing_perfetto/tracing_perfetto.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -17,6 +17,7 @@
 #include "tracing_perfetto.h"
 
 #include <cutils/trace.h>
+#include <cstdarg>
 
 #include "perfetto/public/te_category_macros.h"
 #include "trace_categories.h"
@@ -39,6 +40,31 @@
   }
 }
 
+void traceFormatBegin(uint64_t category, const char* fmt, ...) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category);
+  const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory);
+  if (CC_LIKELY(!(preferAtrace || preferPerfetto))) {
+    return;
+  }
+
+  const int BUFFER_SIZE = 256;
+  va_list ap;
+  char buf[BUFFER_SIZE];
+
+  va_start(ap, fmt);
+  vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+  va_end(ap);
+
+
+  if (preferAtrace) {
+    atrace_begin(category, buf);
+  } else if (preferPerfetto) {
+    internal::perfettoTraceBegin(*perfettoTeCategory, buf);
+  }
+}
+
 void traceEnd(uint64_t category) {
   struct PerfettoTeCategory* perfettoTeCategory =
       internal::toPerfettoCategory(category);
@@ -107,6 +133,30 @@
   }
 }
 
+void traceFormatInstant(uint64_t category, const char* fmt, ...) {
+  struct PerfettoTeCategory* perfettoTeCategory =
+      internal::toPerfettoCategory(category);
+  const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category);
+  const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory);
+  if (CC_LIKELY(!(preferAtrace || preferPerfetto))) {
+    return;
+  }
+
+  const int BUFFER_SIZE = 256;
+  va_list ap;
+  char buf[BUFFER_SIZE];
+
+  va_start(ap, fmt);
+  vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+  va_end(ap);
+
+  if (preferAtrace) {
+    atrace_instant(category, buf);
+  } else if (preferPerfetto) {
+    internal::perfettoTraceInstant(*perfettoTeCategory, buf);
+  }
+}
+
 void traceInstantForTrack(uint64_t category, const char* trackName,
                             const char* name) {
   struct PerfettoTeCategory* perfettoTeCategory =
@@ -130,10 +180,21 @@
   }
 }
 
+void traceCounter32(uint64_t category, const char* name, int32_t value) {
+  struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category);
+  if (internal::shouldPreferAtrace(perfettoTeCategory, category)) {
+    atrace_int(category, name, value);
+  } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) {
+    internal::perfettoTraceCounter(*perfettoTeCategory, name,
+                                          static_cast<int64_t>(value));
+  }
+}
+
 bool isTagEnabled(uint64_t category) {
   struct PerfettoTeCategory* perfettoTeCategory =
       internal::toPerfettoCategory(category);
   return internal::isPerfettoCategoryEnabled(perfettoTeCategory)
       || atrace_is_tag_enabled(category);
 }
+
 }  // namespace tracing_perfetto
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6c4870f..5ed4e69 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -901,6 +901,24 @@
     const nsecs_t mProcessingTimestamp;
 };
 
+/**
+ * This is needed to help use "InputEventInjectionResult" with base::Result.
+ */
+template <typename T>
+struct EnumErrorWrapper {
+    T mVal;
+    EnumErrorWrapper(T&& e) : mVal(std::forward<T>(e)) {}
+    operator const T&() const { return mVal; }
+    T value() const { return mVal; }
+    std::string print() const { return ftl::enum_string(mVal); }
+};
+
+Error<EnumErrorWrapper<InputEventInjectionResult>> injectionError(InputEventInjectionResult&& e) {
+    LOG_ALWAYS_FATAL_IF(e == InputEventInjectionResult::SUCCEEDED);
+    return Error<EnumErrorWrapper<InputEventInjectionResult>>(
+            std::forward<InputEventInjectionResult>(e));
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -1929,20 +1947,21 @@
     }
 
     // Identify targets.
-    InputEventInjectionResult injectionResult;
-    sp<WindowInfoHandle> focusedWindow =
-            findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime,
-                                          /*byref*/ injectionResult);
-    if (injectionResult == InputEventInjectionResult::PENDING) {
-        return false;
-    }
+    Result<sp<WindowInfoHandle>, InputEventInjectionResult> result =
+            findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime);
 
-    setInjectionResult(*entry, injectionResult);
-    if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
+    if (!result.ok()) {
+        if (result.error().code() == InputEventInjectionResult::PENDING) {
+            return false;
+        }
+        setInjectionResult(*entry, result.error().code());
         return true;
     }
+    sp<WindowInfoHandle>& focusedWindow = *result;
     LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
 
+    setInjectionResult(*entry, InputEventInjectionResult::SUCCEEDED);
+
     std::vector<InputTarget> inputTargets;
     addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS,
                           InputTarget::Flags::FOREGROUND, getDownTime(*entry), inputTargets);
@@ -2047,19 +2066,28 @@
             pilferPointersLocked(mDragState->dragWindow->getToken());
         }
 
-        inputTargets =
-                findTouchedWindowTargetsLocked(currentTime, *entry, /*byref*/ injectionResult);
-        LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED &&
-                            !inputTargets.empty());
+        Result<std::vector<InputTarget>, InputEventInjectionResult> result =
+                findTouchedWindowTargetsLocked(currentTime, *entry);
+
+        if (result.ok()) {
+            inputTargets = std::move(*result);
+            injectionResult = InputEventInjectionResult::SUCCEEDED;
+        } else {
+            injectionResult = result.error().code();
+        }
     } else {
         // Non touch event.  (eg. trackball)
-        sp<WindowInfoHandle> focusedWindow =
-                findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, injectionResult);
-        if (injectionResult == InputEventInjectionResult::SUCCEEDED) {
+        Result<sp<WindowInfoHandle>, InputEventInjectionResult> result =
+                findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime);
+        if (result.ok()) {
+            sp<WindowInfoHandle>& focusedWindow = *result;
             LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
             addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS,
                                   InputTarget::Flags::FOREGROUND, getDownTime(*entry),
                                   inputTargets);
+            injectionResult = InputEventInjectionResult::SUCCEEDED;
+        } else {
+            injectionResult = result.error().code();
         }
     }
     if (injectionResult == InputEventInjectionResult::PENDING) {
@@ -2266,11 +2294,9 @@
     return false;
 }
 
-sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(
-        nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime,
-        InputEventInjectionResult& outInjectionResult) {
-    outInjectionResult = InputEventInjectionResult::FAILED; // Default result
-
+Result<sp<WindowInfoHandle>, InputEventInjectionResult>
+InputDispatcher::findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry,
+                                               nsecs_t& nextWakeupTime) {
     ui::LogicalDisplayId displayId = getTargetDisplayId(entry);
     sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
     std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
@@ -2282,12 +2308,12 @@
         ALOGI("Dropping %s event because there is no focused window or focused application in "
               "display %s.",
               ftl::enum_string(entry.type).c_str(), displayId.toString().c_str());
-        return nullptr;
+        return injectionError(InputEventInjectionResult::FAILED);
     }
 
     // Drop key events if requested by input feature
     if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
-        return nullptr;
+        return injectionError(InputEventInjectionResult::FAILED);
     }
 
     // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
@@ -2307,17 +2333,15 @@
                   "window when it finishes starting up. Will wait for %" PRId64 "ms",
                   mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
             nextWakeupTime = std::min(nextWakeupTime, *mNoFocusedWindowTimeoutTime);
-            outInjectionResult = InputEventInjectionResult::PENDING;
-            return nullptr;
+            return injectionError(InputEventInjectionResult::PENDING);
         } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
             // Already raised ANR. Drop the event
             ALOGE("Dropping %s event because there is no focused window",
                   ftl::enum_string(entry.type).c_str());
-            return nullptr;
+            return injectionError(InputEventInjectionResult::FAILED);
         } else {
             // Still waiting for the focused window
-            outInjectionResult = InputEventInjectionResult::PENDING;
-            return nullptr;
+            return injectionError(InputEventInjectionResult::PENDING);
         }
     }
 
@@ -2327,15 +2351,13 @@
     // Verify targeted injection.
     if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {
         ALOGW("Dropping injected event: %s", (*err).c_str());
-        outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
-        return nullptr;
+        return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
     }
 
     if (focusedWindowHandle->getInfo()->inputConfig.test(
                 WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
         ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
-        outInjectionResult = InputEventInjectionResult::PENDING;
-        return nullptr;
+        return injectionError(InputEventInjectionResult::PENDING);
     }
 
     // If the event is a key event, then we must wait for all previous events to
@@ -2352,12 +2374,10 @@
     if (entry.type == EventEntry::Type::KEY) {
         if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
             nextWakeupTime = std::min(nextWakeupTime, *mKeyIsWaitingForEventsTimeout);
-            outInjectionResult = InputEventInjectionResult::PENDING;
-            return nullptr;
+            return injectionError(InputEventInjectionResult::PENDING);
         }
     }
-
-    outInjectionResult = InputEventInjectionResult::SUCCEEDED;
+    // Success!
     return focusedWindowHandle;
 }
 
@@ -2381,9 +2401,8 @@
     return responsiveMonitors;
 }
 
-std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
-        nsecs_t currentTime, const MotionEntry& entry,
-        InputEventInjectionResult& outInjectionResult) {
+base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
+InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) {
     ATRACE_CALL();
 
     std::vector<InputTarget> targets;
@@ -2393,9 +2412,6 @@
     const int32_t action = entry.action;
     const int32_t maskedAction = MotionEvent::getActionMasked(action);
 
-    // Update the touch state as needed based on the properties of the touch event.
-    outInjectionResult = InputEventInjectionResult::PENDING;
-
     // Copy current touch state into tempTouchState.
     // This state will be used to update mTouchStatesByDisplay at the end of this function.
     // If no state for the specified display exists, then our initial state will be empty.
@@ -2435,8 +2451,7 @@
             // Started hovering, but the device is already down: reject the hover event
             LOG(ERROR) << "Got hover event " << entry.getDescription()
                        << " but the device is already down " << oldState->dump();
-            outInjectionResult = InputEventInjectionResult::FAILED;
-            return {};
+            return injectionError(InputEventInjectionResult::FAILED);
         }
         // For hover actions, we will treat 'tempTouchState' as a new state, so let's erase
         // all of the existing hovering pointers and recompute.
@@ -2468,9 +2483,7 @@
         // Verify targeted injection.
         if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
             ALOGW("Dropping injected touch event: %s", (*err).c_str());
-            outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
-            newTouchedWindowHandle = nullptr;
-            return {};
+            return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
         }
 
         // Figure out whether splitting will be allowed for this window.
@@ -2502,8 +2515,7 @@
         if (newTouchedWindows.empty()) {
             LOG(INFO) << "Dropping event because there is no touchable window at (" << x << ", "
                       << y << ") on display " << displayId << ": " << entry;
-            outInjectionResult = InputEventInjectionResult::FAILED;
-            return {};
+            return injectionError(InputEventInjectionResult::FAILED);
         }
 
         for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
@@ -2603,8 +2615,7 @@
                           << " is not down or we previously dropped the pointer down event in "
                           << "display " << displayId << ": " << entry.getDescription();
             }
-            outInjectionResult = InputEventInjectionResult::FAILED;
-            return {};
+            return injectionError(InputEventInjectionResult::FAILED);
         }
 
         // If the pointer is not currently hovering, then ignore the event.
@@ -2615,8 +2626,7 @@
                 LOG(INFO) << "Dropping event because the hovering pointer is not in any windows in "
                              "display "
                           << displayId << ": " << entry.getDescription();
-                outInjectionResult = InputEventInjectionResult::FAILED;
-                return {};
+                return injectionError(InputEventInjectionResult::FAILED);
             }
             tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
         }
@@ -2637,8 +2647,7 @@
             // Verify targeted injection.
             if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
                 ALOGW("Dropping injected event: %s", (*err).c_str());
-                outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
-                return {};
+                return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
             }
 
             // Do not slide events to the window which can not receive motion event
@@ -2707,6 +2716,9 @@
                 if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
                     continue;
                 }
+                if (!touchedWindow.hasTouchingPointers(entry.deviceId)) {
+                    continue;
+                }
                 touchedWindow.addTouchingPointers(entry.deviceId, touchingPointers);
             }
         }
@@ -2738,8 +2750,7 @@
             ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
                   "%s:%s",
                   entry.injectionState->targetUid->toString().c_str(), errs.c_str());
-            outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
-            return {};
+            return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
         }
     }
 
@@ -2796,8 +2807,7 @@
 
     if (targets.empty()) {
         LOG(INFO) << "Dropping event because no targets were found: " << entry.getDescription();
-        outInjectionResult = InputEventInjectionResult::FAILED;
-        return {};
+        return injectionError(InputEventInjectionResult::FAILED);
     }
 
     // If we only have windows getting ACTION_OUTSIDE, then drop the event, because there is no
@@ -2807,12 +2817,9 @@
         })) {
         LOG(INFO) << "Dropping event because all windows would just receive ACTION_OUTSIDE: "
                   << entry.getDescription();
-        outInjectionResult = InputEventInjectionResult::FAILED;
-        return {};
+        return injectionError(InputEventInjectionResult::FAILED);
     }
 
-    outInjectionResult = InputEventInjectionResult::SUCCEEDED;
-
     // Now that we have generated all of the input targets for this event, reset the dispatch
     // mode for all touched window to AS_IS.
     for (TouchedWindow& touchedWindow : tempTouchState.windows) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 2125226..3c2f3e9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -537,12 +537,11 @@
     void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
 
     ui::LogicalDisplayId getTargetDisplayId(const EventEntry& entry);
-    sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
-            nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime,
-            android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
-    std::vector<InputTarget> findTouchedWindowTargetsLocked(
-            nsecs_t currentTime, const MotionEntry& entry,
-            android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
+    base::Result<sp<android::gui::WindowInfoHandle>, android::os::InputEventInjectionResult>
+    findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry,
+                                  nsecs_t& nextWakeupTime) REQUIRES(mLock);
+    base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
+    findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) REQUIRES(mLock);
     std::vector<Monitor> selectResponsiveMonitorsLocked(
             const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
 
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 65e0429..b2fd391 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -78,6 +78,7 @@
         "PropertyProvider_test.cpp",
         "RotaryEncoderInputMapper_test.cpp",
         "SlopController_test.cpp",
+        "SwitchInputMapper_test.cpp",
         "SyncQueue_test.cpp",
         "TimerProvider_test.cpp",
         "TestInputListener.cpp",
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 4e662d4..9ad3de0 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5659,6 +5659,72 @@
     rightWindow->assertNoEvents();
 }
 
+/**
+ * Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a
+ * down event to the right window. Device B sends a down event to the left window, and then a
+ * POINTER_DOWN event to the right window. However, since the left window prevents splitting, the
+ * POINTER_DOWN event should only go to the left window, and not to the right window.
+ * This test attempts to reproduce a crash.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> leftWindow =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Left window (prevent splitting)",
+                                       ui::LogicalDisplayId::DEFAULT);
+    leftWindow->setFrame(Rect(0, 0, 100, 100));
+    leftWindow->setPreventSplitting(true);
+
+    sp<FakeWindowHandle> rightWindow =
+            sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
+                                       ui::LogicalDisplayId::DEFAULT);
+    rightWindow->setFrame(Rect(100, 0, 200, 100));
+
+    mDispatcher->onWindowInfosChanged(
+            {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+    const DeviceId deviceA = 9;
+    const DeviceId deviceB = 3;
+    // Touch the right window with device A
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+                                      .deviceId(deviceA)
+                                      .build());
+    rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+    // Touch the left window with device B
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .deviceId(deviceB)
+                                      .build());
+    leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+    // Send a second pointer from device B to the right window. It shouldn't go to the right window
+    // because the left window prevents splitting.
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(deviceB)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
+                                      .build());
+    leftWindow->consumeMotionPointerDown(1, WithDeviceId(deviceB));
+
+    // Finish the gesture for both devices
+    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .deviceId(deviceB)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
+                                      .build());
+    leftWindow->consumeMotionPointerUp(1, WithDeviceId(deviceB));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+                                      .deviceId(deviceB)
+                                      .build());
+    leftWindow->consumeMotionEvent(
+            AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceB), WithPointerId(0, 0)));
+    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+                                      .deviceId(deviceA)
+                                      .build());
+    rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA)));
+}
+
 TEST_F(InputDispatcherTest, TouchpadThreeFingerSwipeOnlySentToTrustedOverlays) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 5722444..e773f58 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -89,6 +89,13 @@
     }
 }
 
+void InputMapperUnitTest::setSwitchState(int32_t state, std::set<int32_t> switchCodes) {
+    for (const auto& switchCode : switchCodes) {
+        EXPECT_CALL(mMockEventHub, getSwitchState(EVENTHUB_ID, switchCode))
+                .WillRepeatedly(testing::Return(static_cast<int>(state)));
+    }
+}
+
 std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, int32_t value) {
     nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
     return process(when, type, code, value);
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index 5bd8cda..88057dc 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -58,6 +58,8 @@
 
     void setKeyCodeState(KeyState state, std::set<int> keyCodes);
 
+    void setSwitchState(int32_t state, std::set<int32_t> switchCodes);
+
     std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value);
     std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value);
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 1ba79a9..0b780be 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -31,7 +31,6 @@
 #include <PeripheralController.h>
 #include <SensorInputMapper.h>
 #include <SingleTouchInputMapper.h>
-#include <SwitchInputMapper.h>
 #include <TestEventMatchers.h>
 #include <TestInputListener.h>
 #include <TouchInputMapper.h>
@@ -3063,48 +3062,6 @@
     mapper.assertProcessWasCalled();
 }
 
-// --- SwitchInputMapperTest ---
-
-class SwitchInputMapperTest : public InputMapperTest {
-protected:
-};
-
-TEST_F(SwitchInputMapperTest, GetSources) {
-    SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
-
-    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources());
-}
-
-TEST_F(SwitchInputMapperTest, GetSwitchState) {
-    SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
-
-    mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1);
-    ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
-
-    mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 0);
-    ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
-}
-
-TEST_F(SwitchInputMapperTest, Process) {
-    SwitchInputMapper& mapper = constructAndAddMapper<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);
-
-    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),
-            args.switchMask);
-    ASSERT_EQ(uint32_t(0), args.policyFlags);
-}
-
 // --- VibratorInputMapperTest ---
 class VibratorInputMapperTest : public InputMapperTest {
 protected:
diff --git a/services/inputflinger/tests/SwitchInputMapper_test.cpp b/services/inputflinger/tests/SwitchInputMapper_test.cpp
new file mode 100644
index 0000000..4020e78
--- /dev/null
+++ b/services/inputflinger/tests/SwitchInputMapper_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SwitchInputMapper.h"
+
+#include <list>
+#include <variant>
+
+#include <NotifyArgs.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <linux/input-event-codes.h>
+
+#include "InputMapperTest.h"
+#include "TestConstants.h"
+
+namespace android {
+
+class SwitchInputMapperTest : public InputMapperUnitTest {
+protected:
+    void SetUp() override {
+        InputMapperUnitTest::SetUp();
+        createDevice();
+        mMapper = createInputMapper<SwitchInputMapper>(*mDeviceContext,
+                                                       mFakePolicy->getReaderConfiguration());
+    }
+};
+
+TEST_F(SwitchInputMapperTest, GetSources) {
+    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mMapper->getSources());
+}
+
+TEST_F(SwitchInputMapperTest, GetSwitchState) {
+    setSwitchState(1, {SW_LID});
+    ASSERT_EQ(1, mMapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+
+    setSwitchState(0, {SW_LID});
+    ASSERT_EQ(0, mMapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+}
+
+TEST_F(SwitchInputMapperTest, Process) {
+    std::list<NotifyArgs> out;
+    out = process(ARBITRARY_TIME, EV_SW, SW_LID, 1);
+    ASSERT_TRUE(out.empty());
+    out = process(ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+    ASSERT_TRUE(out.empty());
+    out = process(ARBITRARY_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+    ASSERT_TRUE(out.empty());
+    out = process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+    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),
+              args.switchMask);
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
index 6be6735..9c0e072 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -92,15 +92,15 @@
     }
 
 private:
-    std::vector<const LayerState> copyLayers(const std::vector<const LayerState*>& layers) {
-        std::vector<const LayerState> copiedLayers;
+    std::vector<LayerState> copyLayers(const std::vector<const LayerState*>& layers) {
+        std::vector<LayerState> copiedLayers;
         copiedLayers.reserve(layers.size());
         std::transform(layers.cbegin(), layers.cend(), std::back_inserter(copiedLayers),
                        [](const LayerState* layerState) { return *layerState; });
         return copiedLayers;
     }
 
-    std::vector<const LayerState> mLayers;
+    std::vector<LayerState> mLayers;
 
     // TODO(b/180976743): Tune kMaxDifferingFields
     constexpr static int kMaxDifferingFields = 6;
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 30b8eee..0d2987c 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -331,6 +331,7 @@
         TransactionTraceWriter::getInstance().invoke("DuplicateLayer", /* overwrite= */ false);
         return;
     }
+    mVisitedLayers.insert(snapshot->uniqueSequence);
     LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags);
 
     for (const auto& [child, variant] : root.mChildren) {
diff --git a/services/surfaceflinger/LocklessQueue.h b/services/surfaceflinger/LocklessQueue.h
index 6b63360..4d0b261 100644
--- a/services/surfaceflinger/LocklessQueue.h
+++ b/services/surfaceflinger/LocklessQueue.h
@@ -15,11 +15,11 @@
  */
 
 #pragma once
+
 #include <atomic>
 #include <optional>
 
-template <typename T>
-// Single consumer multi producer stack. We can understand the two operations independently to see
+// Single consumer multi producer queue. We can understand the two operations independently to see
 // why they are without race condition.
 //
 // push is responsible for maintaining a linked list stored in mPush, and called from multiple
@@ -36,33 +36,27 @@
 // then store the list and pop one element.
 //
 // If we already had something in the pop list we just pop directly.
+template <typename T>
 class LocklessQueue {
 public:
-    class Entry {
-    public:
-        T mValue;
-        std::atomic<Entry*> mNext;
-        Entry(T value) : mValue(value) {}
-    };
-    std::atomic<Entry*> mPush = nullptr;
-    std::atomic<Entry*> mPop = nullptr;
     bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); }
 
     void push(T value) {
-        Entry* entry = new Entry(value);
+        Entry* entry = new Entry(std::move(value));
         Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
         do {
             entry->mNext = previousHead;
         } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
     }
+
     std::optional<T> pop() {
         Entry* popped = mPop.load(/*std::memory_order_acquire*/);
         if (popped) {
             // Single consumer so this is fine
             mPop.store(popped->mNext /* , std::memory_order_release */);
-            auto value = popped->mValue;
+            auto value = std::move(popped->mValue);
             delete popped;
-            return std::move(value);
+            return value;
         } else {
             Entry* grabbedList = mPush.exchange(nullptr /* , std::memory_order_acquire */);
             if (!grabbedList) return std::nullopt;
@@ -74,9 +68,19 @@
                 grabbedList = next;
             }
             mPop.store(popped /* , std::memory_order_release */);
-            auto value = grabbedList->mValue;
+            auto value = std::move(grabbedList->mValue);
             delete grabbedList;
-            return std::move(value);
+            return value;
         }
     }
+
+private:
+    class Entry {
+    public:
+        T mValue;
+        std::atomic<Entry*> mNext;
+        Entry(T value) : mValue(value) {}
+    };
+    std::atomic<Entry*> mPush = nullptr;
+    std::atomic<Entry*> mPop = nullptr;
 };
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index fbd788b..0dffa4b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -117,10 +117,10 @@
     }
 }
 
-void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+void Scheduler::setPacesetterDisplay(PhysicalDisplayId pacesetterId) {
     demotePacesetterDisplay();
 
-    promotePacesetterDisplay(pacesetterIdOpt);
+    promotePacesetterDisplay(pacesetterId);
 }
 
 void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
@@ -917,22 +917,22 @@
     return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
 }
 
-void Scheduler::promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId) {
     std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
 
     {
         std::scoped_lock lock(mDisplayLock);
-        pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterIdOpt);
+        pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterId);
     }
 
     applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
 }
 
 std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked(
-        std::optional<PhysicalDisplayId> pacesetterIdOpt) {
-    // TODO(b/241286431): Choose the pacesetter display.
-    mPacesetterDisplayId = pacesetterIdOpt.value_or(mDisplays.begin()->first);
-    ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
+        PhysicalDisplayId pacesetterId) {
+    // TODO: b/241286431 - Choose the pacesetter among mDisplays.
+    mPacesetterDisplayId = pacesetterId;
+    ALOGI("Display %s is the pacesetter", to_string(pacesetterId).c_str());
 
     std::shared_ptr<VsyncSchedule> newVsyncSchedulePtr;
     if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 1a4aa79..4dba6fc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -92,8 +92,8 @@
 
     void startTimers();
 
-    // TODO(b/241285191): Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
-    void setPacesetterDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+    // TODO: b/241285191 - Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
+    void setPacesetterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext)
             EXCLUDES(mDisplayLock);
 
     using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
@@ -377,10 +377,8 @@
     void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock);
     void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
 
-    // Chooses a pacesetter among the registered displays, unless `pacesetterIdOpt` is specified.
-    // The new `mPacesetterDisplayId` is never `std::nullopt`.
-    void promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
-            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+    void promotePacesetterDisplay(PhysicalDisplayId pacesetterId) REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
 
     // Changes to the displays (e.g. registering and unregistering) must be made
     // while mDisplayLock is locked, and the new pacesetter then must be promoted while
@@ -388,8 +386,7 @@
     // MessageQueue and EventThread need to use the new pacesetter's
     // VsyncSchedule, and this must happen while mDisplayLock is *not* locked,
     // or else we may deadlock with EventThread.
-    std::shared_ptr<VsyncSchedule> promotePacesetterDisplayLocked(
-            std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
+    std::shared_ptr<VsyncSchedule> promotePacesetterDisplayLocked(PhysicalDisplayId pacesetterId)
             REQUIRES(kMainThreadContext, mDisplayLock);
     void applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule>) EXCLUDES(mDisplayLock);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d718f9a..a68deca 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -729,6 +729,7 @@
     mBootFinished = true;
     FlagManager::getMutableInstance().markBootCompleted();
 
+    ::tracing_perfetto::registerWithPerfetto();
     mInitBootPropsFuture.wait();
     mRenderEnginePrimeCacheFuture.wait();
 
@@ -4264,11 +4265,6 @@
     getHwComposer().setVsyncEnabled(displayId, enable ? hal::Vsync::ENABLE : hal::Vsync::DISABLE);
 }
 
-// This callback originates from Scheduler::applyPolicy, whose thread context may be the main thread
-// (via Scheduler::chooseRefreshRateForContent) or a OneShotTimer thread. The latter case imposes a
-// deadlock prevention rule: If the main thread is processing hotplug, then mStateLock is locked as
-// the main thread stops the OneShotTimer and joins with its thread. Hence, the OneShotTimer thread
-// must not lock mStateLock in this callback, which would deadlock with the join.
 void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest> modeRequests) {
     if (mBootStage != BootStage::FINISHED) {
         ALOGV("Currently in the boot stage, skipping display mode changes");
@@ -4277,14 +4273,21 @@
 
     SFTRACE_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);
+
     for (auto& request : modeRequests) {
         const auto& modePtr = request.mode.modePtr;
+
         const auto displayId = modePtr->getPhysicalDisplayId();
+        const auto display = getDisplayDeviceLocked(displayId);
 
-        const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
-        if (!selectorPtr) continue;
+        if (!display) continue;
 
-        if (selectorPtr->isModeAllowed(request.mode)) {
+        if (display->refreshRateSelector().isModeAllowed(request.mode)) {
             setDesiredMode(std::move(request));
         } else {
             ALOGV("%s: Mode %d is disallowed for display %s", __func__,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 2787c1f..3369e00 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -734,7 +734,7 @@
     // Show hdr sdr ratio overlay
     bool mHdrSdrRatioOverlay = false;
 
-    void setDesiredMode(display::DisplayModeRequest&&);
+    void setDesiredMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
 
     status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
                                        Fps maxFps);
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index ea86911..a6a0152 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -20,6 +20,7 @@
         "libtimestats_atoms_proto",
         "libui",
         "libutils",
+        "libtracing_perfetto",
     ],
 
     static_libs: [
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index 8afca41..63c1b37 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -28,6 +28,7 @@
         "libsurfaceflinger_mocks_defaults",
         "librenderengine_deps",
         "surfaceflinger_defaults",
+        "libsurfaceflinger_common_deps",
     ],
     srcs: [
         ":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index bcf1886..f9c99bf 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -18,6 +18,7 @@
         "libSurfaceFlingerProp",
         "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
+        "libtracing_perfetto",
     ],
     static_libs: [
         "librenderengine_includes",
@@ -27,6 +28,7 @@
     ],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
+    export_shared_lib_headers: ["libtracing_perfetto"],
 }
 
 cc_library_static {
@@ -60,6 +62,7 @@
     shared_libs: [
         "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
+        "libtracing_perfetto",
     ],
     static_libs: [
         "libsurfaceflinger_common",
@@ -75,6 +78,7 @@
     shared_libs: [
         "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
+        "libtracing_perfetto",
     ],
     static_libs: [
         "libsurfaceflinger_common_test",
diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h
index 344359e..0d7ac9b 100644
--- a/services/surfaceflinger/common/include/common/trace.h
+++ b/services/surfaceflinger/common/include/common/trace.h
@@ -17,43 +17,57 @@
 
 #pragma once
 
-#include <cutils/trace.h>
-#include <stdint.h>
-
 #ifndef ATRACE_TAG
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #endif
 
-#define SFTRACE_ENABLED() ATRACE_ENABLED()
-#define SFTRACE_BEGIN(name) ATRACE_BEGIN(name)
-#define SFTRACE_END() ATRACE_END()
-#define SFTRACE_ASYNC_BEGIN(name, cookie) ATRACE_ASYNC_BEGIN(name, cookie)
-#define SFTRACE_ASYNC_END(name, cookie) ATRACE_ASYNC_END(name, cookie)
+#include <cutils/trace.h>
+#include <tracing_perfetto.h>
+
+// prevent using atrace directly, calls should go through tracing_perfetto lib
+#undef ATRACE_ENABLED
+#undef ATRACE_BEGIN
+#undef ATRACE_END
+#undef ATRACE_ASYNC_BEGIN
+#undef ATRACE_ASYNC_END
+#undef ATRACE_ASYNC_FOR_TRACK_BEGIN
+#undef ATRACE_ASYNC_FOR_TRACK_END
+#undef ATRACE_INSTANT
+#undef ATRACE_INSTANT_FOR_TRACK
+#undef ATRACE_INT
+#undef ATRACE_INT64
+#undef ATRACE_CALL
+#undef ATRACE_NAME
+#undef ATRACE_FORMAT
+#undef ATRACE_FORMAT_INSTANT
+
+#define SFTRACE_ENABLED() ::tracing_perfetto::isTagEnabled(ATRACE_TAG)
+#define SFTRACE_BEGIN(name) ::tracing_perfetto::traceBegin(ATRACE_TAG, name)
+#define SFTRACE_END() ::tracing_perfetto::traceEnd(ATRACE_TAG)
+#define SFTRACE_ASYNC_BEGIN(name, cookie) \
+    ::tracing_perfetto::traceAsyncBegin(ATRACE_TAG, name, cookie)
+#define SFTRACE_ASYNC_END(name, cookie) ::tracing_perfetto::traceAsyncEnd(ATRACE_TAG, name, cookie)
 #define SFTRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
-    ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie)
+    ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, track_name, name, cookie)
 #define SFTRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \
-    ATRACE_ASYNC_FOR_TRACK_END(track_name, cookie)
-#define SFTRACE_INSTANT(name) ATRACE_INSTANT(name)
-#define SFTRACE_INSTANT_FOR_TRACK(trackName, name) ATRACE_INSTANT_FOR_TRACK(trackName, name)
-#define SFTRACE_INT(name, value) ATRACE_INT(name, value)
-#define SFTRACE_INT64(name, value) ATRACE_INT64(name, value)
+    ::tracing_perfetto::traceAsyncEndForTrack(ATRACE_TAG, track_name, cookie)
+#define SFTRACE_INSTANT(name) ::tracing_perfetto::traceInstant(ATRACE_TAG, name)
+#define SFTRACE_FORMAT_INSTANT(fmt, ...) \
+    ::tracing_perfetto::traceFormatInstant(ATRACE_TAG, fmt, ##__VA_ARGS__)
+#define SFTRACE_INSTANT_FOR_TRACK(trackName, name) \
+    ::tracing_perfetto::traceInstantForTrack(ATRACE_TAG, trackName, name)
+#define SFTRACE_INT(name, value) ::tracing_perfetto::traceCounter32(ATRACE_TAG, name, value)
+#define SFTRACE_INT64(name, value) ::tracing_perfetto::traceCounter(ATRACE_TAG, name, value)
 
 // SFTRACE_NAME traces from its location until the end of its enclosing scope.
 #define _PASTE(x, y) x##y
 #define PASTE(x, y) _PASTE(x, y)
-#define SFTRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name)
-
-// SFTRACE_CALL is an ATRACE_NAME that uses the current function name.
+#define SFTRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(name)
+// SFTRACE_CALL is an SFTRACE_NAME that uses the current function name.
 #define SFTRACE_CALL() SFTRACE_NAME(__FUNCTION__)
 
-#define SFTRACE_FORMAT(fmt, ...)                                                \
-    TraceUtils::TraceEnder traceEnder =                                         \
-            (CC_UNLIKELY(ATRACE_ENABLED()) &&                                   \
-                     (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), true), \
-             TraceUtils::TraceEnder())
-
-#define SFTRACE_FORMAT_INSTANT(fmt, ...) \
-    (CC_UNLIKELY(ATRACE_ENABLED()) && (TraceUtils::instantFormat(fmt, ##__VA_ARGS__), true))
+#define SFTRACE_FORMAT(fmt, ...) \
+    ::android::ScopedTrace PASTE(___tracer, __LINE__)(fmt, ##__VA_ARGS__)
 
 #define ALOGE_AND_TRACE(fmt, ...)                   \
     do {                                            \
@@ -63,46 +77,14 @@
 
 namespace android {
 
-class TraceUtils {
-public:
-    class TraceEnder {
-    public:
-        ~TraceEnder() { ATRACE_END(); }
-    };
-
-    static void atraceFormatBegin(const char* fmt, ...) {
-        const int BUFFER_SIZE = 256;
-        va_list ap;
-        char buf[BUFFER_SIZE];
-
-        va_start(ap, fmt);
-        vsnprintf(buf, BUFFER_SIZE, fmt, ap);
-        va_end(ap);
-
-        SFTRACE_BEGIN(buf);
-    }
-
-    static void instantFormat(const char* fmt, ...) {
-        const int BUFFER_SIZE = 256;
-        va_list ap;
-        char buf[BUFFER_SIZE];
-
-        va_start(ap, fmt);
-        vsnprintf(buf, BUFFER_SIZE, fmt, ap);
-        va_end(ap);
-
-        SFTRACE_INSTANT(buf);
-    }
-};
-
 class ScopedTrace {
 public:
-    inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); }
-
-    inline ~ScopedTrace() { atrace_end(mTag); }
-
-private:
-    uint64_t mTag;
+    template <typename... Args>
+    inline ScopedTrace(const char* fmt, Args&&... args) {
+        ::tracing_perfetto::traceFormatBegin(ATRACE_TAG, fmt, std::forward<Args>(args)...);
+    }
+    inline ScopedTrace(const char* name) { SFTRACE_BEGIN(name); }
+    inline ~ScopedTrace() { SFTRACE_END(); }
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/common/Android.bp b/services/surfaceflinger/tests/common/Android.bp
new file mode 100644
index 0000000..2dfa8af
--- /dev/null
+++ b/services/surfaceflinger/tests/common/Android.bp
@@ -0,0 +1,13 @@
+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_headers {
+    name: "surfaceflinger_tests_common_headers",
+    export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
new file mode 100644
index 0000000..2e5913b
--- /dev/null
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/fake/BufferData.h>
+#include <renderengine/mock/FakeExternalTexture.h>
+#include <ui/ShadowSettings.h>
+
+#include <Client.h> // temporarily needed for LayerCreationArgs
+#include <FrontEnd/LayerCreationArgs.h>
+#include <FrontEnd/LayerHierarchy.h>
+#include <FrontEnd/LayerLifecycleManager.h>
+#include <FrontEnd/LayerSnapshotBuilder.h>
+
+namespace android::surfaceflinger::frontend {
+
+class LayerLifecycleManagerHelper {
+public:
+    LayerLifecycleManagerHelper(LayerLifecycleManager& layerLifecycleManager)
+          : mLifecycleManager(layerLifecycleManager) {}
+    ~LayerLifecycleManagerHelper() = default;
+
+    static LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
+                                        uint32_t layerIdToMirror) {
+        LayerCreationArgs args(std::make_optional(id));
+        args.name = "testlayer";
+        args.addToRoot = canBeRoot;
+        args.parentId = parentId;
+        args.layerIdToMirror = layerIdToMirror;
+        return args;
+    }
+
+    static LayerCreationArgs createDisplayMirrorArgs(uint32_t id,
+                                                     ui::LayerStack layerStackToMirror) {
+        LayerCreationArgs args(std::make_optional(id));
+        args.name = "testlayer";
+        args.addToRoot = true;
+        args.layerStackToMirror = layerStackToMirror;
+        return args;
+    }
+
+    static std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
+        return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true,
+                                                                /*parent=*/UNASSIGNED_LAYER_ID,
+                                                                /*mirror=*/UNASSIGNED_LAYER_ID));
+    }
+
+    static std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
+        return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
+                                                                parentId,
+                                                                /*mirror=*/UNASSIGNED_LAYER_ID));
+    }
+
+    static std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.z = z;
+        return transactions;
+    }
+
+    void createRootLayer(uint32_t id) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+                           /*mirror=*/UNASSIGNED_LAYER_ID)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createDisplayMirrorArgs(/*id=*/id, layerStack)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    void createLayer(uint32_t id, uint32_t parentId) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+                           /*mirror=*/UNASSIGNED_LAYER_ID)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().parentId = newParentId;
+        transactions.back().states.front().state.what = layer_state_t::eReparent;
+        transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID;
+        transactions.back().states.front().layerId = id;
+        return transactions;
+    }
+
+    void reparentLayer(uint32_t id, uint32_t newParentId) {
+        mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
+    }
+
+    std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().relativeParentId = relativeParentId;
+        transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+        transactions.back().states.front().layerId = id;
+        return transactions;
+    }
+
+    void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+        mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId));
+    }
+
+    void removeRelativeZ(uint32_t id) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setPosition(uint32_t id, float x, float y) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().state.what = layer_state_t::ePositionChanged;
+        transactions.back().states.front().state.x = x;
+        transactions.back().states.front().state.y = y;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+                           /*mirror=*/layerIdToMirror)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    void updateBackgroundColor(uint32_t id, half alpha) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+        transactions.back().states.front().state.bgColor.a = alpha;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); }
+
+    void setZ(uint32_t id, int32_t z) {
+        mLifecycleManager.applyTransactions(setZTransaction(id, z));
+    }
+
+    void setCrop(uint32_t id, const Rect& crop) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eCropChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.crop = crop;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eFlagsChanged;
+        transactions.back().states.front().state.flags = flags;
+        transactions.back().states.front().state.mask = mask;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setAlpha(uint32_t id, float alpha) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eAlphaChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.color.a = static_cast<half>(alpha);
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void hideLayer(uint32_t id) {
+        setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
+    }
+
+    void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); }
+
+    void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().state.what = layer_state_t::eColorChanged;
+        transactions.back().states.front().state.color.rgb = rgb;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setLayerStack(uint32_t id, int32_t layerStack) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setTouchableRegion(uint32_t id, Region region) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.windowInfoHandle =
+                sp<gui::WindowInfoHandle>::make();
+        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+        inputInfo->touchableRegion = region;
+        inputInfo->token = sp<BBinder>::make();
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.windowInfoHandle =
+                sp<gui::WindowInfoHandle>::make();
+        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+        if (!inputInfo->token) {
+            inputInfo->token = sp<BBinder>::make();
+        }
+        configureInput(*inputInfo);
+
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
+                                bool replaceTouchableRegionWithCrop) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.windowInfoHandle =
+                sp<gui::WindowInfoHandle>::make();
+        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+        inputInfo->touchableRegion = region;
+        inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
+        transactions.back().states.front().touchCropId = touchCropId;
+
+        inputInfo->token = sp<BBinder>::make();
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.backgroundBlurRadius = backgroundBlurRadius;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.frameRateSelectionPriority = priority;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setFrameRate(uint32_t id, float frameRate, int8_t compatibility,
+                      int8_t changeFrameRateStrategy) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.frameRate = frameRate;
+        transactions.back().states.front().state.frameRateCompatibility = compatibility;
+        transactions.back().states.front().state.changeFrameRateStrategy = changeFrameRateStrategy;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eFrameRateCategoryChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.frameRateCategory = frameRateCategory;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what =
+                layer_state_t::eFrameRateSelectionStrategyChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.frameRateSelectionStrategy = strategy;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what =
+                layer_state_t::eDefaultFrameRateCompatibilityChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.defaultFrameRateCompatibility =
+                defaultFrameRateCompatibility;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setRoundedCorners(uint32_t id, float radius) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.cornerRadius = radius;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eBufferChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().externalTexture = texture;
+        transactions.back().states.front().state.bufferData =
+                std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(),
+                                                   texture->getHeight(), texture->getPixelFormat(),
+                                                   texture->getUsage());
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setBuffer(uint32_t id) {
+        static uint64_t sBufferId = 1;
+        setBuffer(id,
+                  std::make_shared<renderengine::mock::
+                                           FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                                sBufferId++,
+                                                                HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                GRALLOC_USAGE_PROTECTED /*usage*/));
+    }
+
+    void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eBufferCropChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.bufferCrop = bufferCrop;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setDamageRegion(uint32_t id, const Region& damageRegion) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setDataspace(uint32_t id, ui::Dataspace dataspace) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.dataspace = dataspace;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
+        layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
+
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eMatrixChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.matrix = matrix;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setShadowRadius(uint32_t id, float shadowRadius) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.shadowRadius = shadowRadius;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eTrustedOverlayChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.trustedOverlay = trustedOverlay;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eDropInputModeChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.dropInputMode = dropInputMode;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+private:
+    LayerLifecycleManager& mLifecycleManager;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index bc35639..9ebef8c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -58,6 +58,7 @@
     ],
     test_suites: ["device-tests"],
     static_libs: ["libc++fs"],
+    header_libs: ["surfaceflinger_tests_common_headers"],
     srcs: [
         ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
@@ -209,6 +210,7 @@
         "libsync",
         "libui",
         "libutils",
+        "libtracing_perfetto",
     ],
     header_libs: [
         "android.hardware.graphics.composer3-command-buffer",
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 8b3303c..37cda3e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -25,13 +25,15 @@
 #include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/LayerHierarchy.h"
 #include "FrontEnd/LayerLifecycleManager.h"
+#include "LayerLifecycleManagerHelper.h"
+
 #include "FrontEnd/LayerSnapshotBuilder.h"
 
 namespace android::surfaceflinger::frontend {
 
-class LayerHierarchyTestBase : public testing::Test {
+class LayerHierarchyTestBase : public testing::Test, public LayerLifecycleManagerHelper {
 protected:
-    LayerHierarchyTestBase() {
+    LayerHierarchyTestBase() : LayerLifecycleManagerHelper(mLifecycleManager) {
         // tree with 3 levels of children
         // ROOT
         // ├── 1
@@ -55,24 +57,6 @@
         createLayer(1221, 122);
     }
 
-    LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
-                                 uint32_t layerIdToMirror) {
-        LayerCreationArgs args(std::make_optional(id));
-        args.name = "testlayer";
-        args.addToRoot = canBeRoot;
-        args.parentId = parentId;
-        args.layerIdToMirror = layerIdToMirror;
-        return args;
-    }
-
-    LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStackToMirror) {
-        LayerCreationArgs args(std::make_optional(id));
-        args.name = "testlayer";
-        args.addToRoot = true;
-        args.layerStackToMirror = layerStackToMirror;
-        return args;
-    }
-
     std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
         std::vector<uint32_t> layerIds;
         hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
@@ -94,98 +78,6 @@
         return layerIds;
     }
 
-    virtual void createRootLayer(uint32_t id) {
-        std::vector<std::unique_ptr<RequestedLayerState>> layers;
-        layers.emplace_back(std::make_unique<RequestedLayerState>(
-                createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
-                           /*mirror=*/UNASSIGNED_LAYER_ID)));
-        mLifecycleManager.addLayers(std::move(layers));
-    }
-
-    void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
-        std::vector<std::unique_ptr<RequestedLayerState>> layers;
-        layers.emplace_back(std::make_unique<RequestedLayerState>(
-                createDisplayMirrorArgs(/*id=*/id, layerStack)));
-        mLifecycleManager.addLayers(std::move(layers));
-    }
-
-    virtual void createLayer(uint32_t id, uint32_t parentId) {
-        std::vector<std::unique_ptr<RequestedLayerState>> layers;
-        layers.emplace_back(std::make_unique<RequestedLayerState>(
-                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
-                           /*mirror=*/UNASSIGNED_LAYER_ID)));
-        mLifecycleManager.addLayers(std::move(layers));
-    }
-
-    std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-        transactions.back().states.front().parentId = newParentId;
-        transactions.back().states.front().state.what = layer_state_t::eReparent;
-        transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID;
-        transactions.back().states.front().layerId = id;
-        return transactions;
-    }
-
-    void reparentLayer(uint32_t id, uint32_t newParentId) {
-        mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
-    }
-
-    std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-        transactions.back().states.front().relativeParentId = relativeParentId;
-        transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
-        transactions.back().states.front().layerId = id;
-        return transactions;
-    }
-
-    void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
-        mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId));
-    }
-
-    void removeRelativeZ(uint32_t id) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-        transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
-        transactions.back().states.front().layerId = id;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setPosition(uint32_t id, float x, float y) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-        transactions.back().states.front().state.what = layer_state_t::ePositionChanged;
-        transactions.back().states.front().state.x = x;
-        transactions.back().states.front().state.y = y;
-        transactions.back().states.front().layerId = id;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    virtual void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) {
-        std::vector<std::unique_ptr<RequestedLayerState>> layers;
-        layers.emplace_back(std::make_unique<RequestedLayerState>(
-                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
-                           /*mirror=*/layerIdToMirror)));
-        mLifecycleManager.addLayers(std::move(layers));
-    }
-
-    void updateBackgroundColor(uint32_t id, half alpha) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-        transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
-        transactions.back().states.front().state.bgColor.a = alpha;
-        transactions.back().states.front().layerId = id;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); }
-
     void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
         hierarchyBuilder.update(mLifecycleManager);
         mLifecycleManager.commitChanges();
@@ -201,321 +93,6 @@
                 mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
     }
 
-    std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.z = z;
-        return transactions;
-    }
-
-    void setZ(uint32_t id, int32_t z) {
-        mLifecycleManager.applyTransactions(setZTransaction(id, z));
-    }
-
-    void setCrop(uint32_t id, const Rect& crop) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eCropChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.crop = crop;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eFlagsChanged;
-        transactions.back().states.front().state.flags = flags;
-        transactions.back().states.front().state.mask = mask;
-        transactions.back().states.front().layerId = id;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setAlpha(uint32_t id, float alpha) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eAlphaChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.color.a = static_cast<half>(alpha);
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void hideLayer(uint32_t id) {
-        setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
-    }
-
-    void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); }
-
-    void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-        transactions.back().states.front().state.what = layer_state_t::eColorChanged;
-        transactions.back().states.front().state.color.rgb = rgb;
-        transactions.back().states.front().layerId = id;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setLayerStack(uint32_t id, int32_t layerStack) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setTouchableRegion(uint32_t id, Region region) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.windowInfoHandle =
-                sp<gui::WindowInfoHandle>::make();
-        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
-        inputInfo->touchableRegion = region;
-        inputInfo->token = sp<BBinder>::make();
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.windowInfoHandle =
-                sp<gui::WindowInfoHandle>::make();
-        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
-        if (!inputInfo->token) {
-            inputInfo->token = sp<BBinder>::make();
-        }
-        configureInput(*inputInfo);
-
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
-                                bool replaceTouchableRegionWithCrop) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.windowInfoHandle =
-                sp<gui::WindowInfoHandle>::make();
-        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
-        inputInfo->touchableRegion = region;
-        inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
-        transactions.back().states.front().touchCropId = touchCropId;
-
-        inputInfo->token = sp<BBinder>::make();
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.backgroundBlurRadius = backgroundBlurRadius;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eFrameRateSelectionPriority;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.frameRateSelectionPriority = priority;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setFrameRate(uint32_t id, float frameRate, int8_t compatibility,
-                      int8_t changeFrameRateStrategy) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.frameRate = frameRate;
-        transactions.back().states.front().state.frameRateCompatibility = compatibility;
-        transactions.back().states.front().state.changeFrameRateStrategy = changeFrameRateStrategy;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eFrameRateCategoryChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.frameRateCategory = frameRateCategory;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what =
-                layer_state_t::eFrameRateSelectionStrategyChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.frameRateSelectionStrategy = strategy;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what =
-                layer_state_t::eDefaultFrameRateCompatibilityChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.defaultFrameRateCompatibility =
-                defaultFrameRateCompatibility;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setRoundedCorners(uint32_t id, float radius) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.cornerRadius = radius;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eBufferChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().externalTexture = texture;
-        transactions.back().states.front().state.bufferData =
-                std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(),
-                                                   texture->getHeight(), texture->getPixelFormat(),
-                                                   texture->getUsage());
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setBuffer(uint32_t id) {
-        static uint64_t sBufferId = 1;
-        setBuffer(id,
-                  std::make_shared<renderengine::mock::
-                                           FakeExternalTexture>(1U /*width*/, 1U /*height*/,
-                                                                sBufferId++,
-                                                                HAL_PIXEL_FORMAT_RGBA_8888,
-                                                                GRALLOC_USAGE_PROTECTED /*usage*/));
-    }
-
-    void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eBufferCropChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.bufferCrop = bufferCrop;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setDamageRegion(uint32_t id, const Region& damageRegion) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setDataspace(uint32_t id, ui::Dataspace dataspace) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.dataspace = dataspace;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
-        layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
-
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eMatrixChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.matrix = matrix;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setShadowRadius(uint32_t id, float shadowRadius) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.shadowRadius = shadowRadius;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eTrustedOverlayChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.trustedOverlay = trustedOverlay;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
-    void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) {
-        std::vector<TransactionState> transactions;
-        transactions.emplace_back();
-        transactions.back().states.push_back({});
-
-        transactions.back().states.front().state.what = layer_state_t::eDropInputModeChanged;
-        transactions.back().states.front().layerId = id;
-        transactions.back().states.front().state.dropInputMode = dropInputMode;
-        mLifecycleManager.applyTransactions(transactions);
-    }
-
     LayerLifecycleManager mLifecycleManager;
 };
 
@@ -523,21 +100,6 @@
 protected:
     LayerSnapshotTestBase() : LayerHierarchyTestBase() {}
 
-    void createRootLayer(uint32_t id) override {
-        LayerHierarchyTestBase::createRootLayer(id);
-        setColor(id);
-    }
-
-    void createLayer(uint32_t id, uint32_t parentId) override {
-        LayerHierarchyTestBase::createLayer(id, parentId);
-        setColor(parentId);
-    }
-
-    void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
-        LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
-        setColor(id);
-    }
-
     void update(LayerSnapshotBuilder& snapshotBuilder) {
         mHierarchyBuilder.update(mLifecycleManager);
         LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index bc15dec..b4efe0f 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -61,18 +61,6 @@
 
 class LayerLifecycleManagerTest : public LayerHierarchyTestBase {
 protected:
-    std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
-        return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true,
-                                                                /*parent=*/UNASSIGNED_LAYER_ID,
-                                                                /*mirror=*/UNASSIGNED_LAYER_ID));
-    }
-
-    std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
-        return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
-                                                                parentId,
-                                                                /*mirror=*/UNASSIGNED_LAYER_ID));
-    }
-
     RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager,
                                                 uint32_t layerId) {
         return lifecycleManager.getLayerFromId(layerId);