Merge "Add additional predictor unit tests" into sc-dev
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index c62d302..0712c0a 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -411,7 +411,6 @@
 }
 
 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
-    SectionExists("HIGH connectivity", /* bytes= */ 3000);
     SectionExists("connectivity", /* bytes= */ 5000);
 }
 
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb3b434..026b19a 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -84,6 +84,7 @@
     name: "librenderengine_skia_sources",
     srcs: [
         "skia/AutoBackendTexture.cpp",
+        "skia/Cache.cpp",
         "skia/ColorSpaces.cpp",
         "skia/SkiaRenderEngine.cpp",
         "skia/SkiaGLRenderEngine.cpp",
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 2b09c15..a2963a7 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -515,7 +515,7 @@
     return mDrawingBuffer.get();
 }
 
-void GLESRenderEngine::primeCache() const {
+void GLESRenderEngine::primeCache() {
     ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
                                            mUseColorManagement, mPrecacheToneMapperShaderOnly);
 }
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 7496b74..06a1722 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -57,7 +57,7 @@
                      EGLSurface protectedStub);
     ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
 
-    void primeCache() const override;
+    void primeCache() override;
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
     void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ddae34a..7c51f1b 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -97,7 +97,7 @@
     // This interface, while still in use until a suitable replacement is built,
     // should be considered deprecated, minus some methods which still may be
     // used to support legacy behavior.
-    virtual void primeCache() const = 0;
+    virtual void primeCache() = 0;
 
     // dump the extension strings. always call the base class.
     virtual void dump(std::string& result) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 895ba3f..5f75b81 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,7 +35,7 @@
     RenderEngine();
     ~RenderEngine() override;
 
-    MOCK_CONST_METHOD0(primeCache, void());
+    MOCK_METHOD0(primeCache, void());
     MOCK_METHOD1(dump, void(std::string&));
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
new file mode 100644
index 0000000..4fdae74
--- /dev/null
+++ b/libs/renderengine/skia/Cache.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Cache.h"
+#include "AutoBackendTexture.h"
+#include "SkiaRenderEngine.h"
+#include "android-base/unique_fd.h"
+#include "renderengine/DisplaySettings.h"
+#include "renderengine/LayerSettings.h"
+#include "ui/GraphicBuffer.h"
+#include "ui/GraphicTypes.h"
+#include "ui/PixelFormat.h"
+#include "ui/Rect.h"
+#include "utils/Timers.h"
+
+namespace android::renderengine::skia {
+
+static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                            sp<GraphicBuffer> dstBuffer) {
+    // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
+    // on actual use.
+    FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = rect,
+                            .roundedCornersCrop = rect,
+                    },
+            .shadow =
+                    ShadowSettings{
+                            .ambientColor = vec4(0, 0, 0, 0.00935997f),
+                            .spotColor = vec4(0, 0, 0, 0.0455841f),
+                            .lightPos = vec3(370.508f, -1527.03f, 1650.f),
+                            .lightRadius = 2200.0f,
+                            .length = 0.955342f,
+                    },
+    };
+
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    // The identity matrix will generate the fast shaders, and the second matrix
+    // (based on one seen while going from dialer to the home screen) will
+    // generate the slower (more general case) version. If we also need a
+    // slow version without color correction, we should use this matrix with
+    // display.outputDataspace set to SRGB.
+    for (const mat4 transform : { mat4(), mat4(0.728872f,   0.f,          0.f, 0.f,
+                                               0.f,         0.727627f,    0.f, 0.f,
+                                               0.f,         0.f,          1.f, 0.f,
+                                               167.355743f, 1852.257812f, 0.f, 1.f) }) {
+        layer.geometry.positionTransform = transform;
+        renderengine->drawLayers(display, layers, dstBuffer, false /* useFrameBufferCache*/,
+                                 base::unique_fd(), nullptr);
+    }
+}
+
+static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+                            sp<GraphicBuffer> dstBuffer, sp<GraphicBuffer> srcBuffer) {
+    const Rect& displayRect = display.physicalDisplay;
+    FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = rect,
+                            // This matrix is based on actual data seen when opening the dialer.
+                            // What seems to be important in matching the actual use cases are:
+                            // - it is not identity
+                            // - the layer will be drawn (not clipped out etc)
+                            .positionTransform = mat4(.19f,     .0f, .0f,   .0f,
+                                                      .0f,     .19f, .0f,   .0f,
+                                                      .0f,      .0f, 1.f,   .0f,
+                                                      169.f, 1527.f, .0f,   1.f),
+                            .roundedCornersCrop = rect,
+                    },
+            .source = PixelSource{.buffer =
+                                          Buffer{
+                                                  .buffer = srcBuffer,
+                                                  .maxMasteringLuminance = 1000.f,
+                                                  .maxContentLuminance = 1000.f,
+                                          }},
+    };
+
+    // Test both drawRect and drawRRect
+    auto layers = std::vector<const LayerSettings*>{&layer};
+    for (float roundedCornersRadius : {0.0f, 500.f}) {
+        layer.geometry.roundedCornersRadius = roundedCornersRadius;
+        // No need to check UNKNOWN, which is treated as SRGB.
+        for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) {
+            layer.sourceDataspace = dataspace;
+            for (bool isOpaque : {true, false}) {
+                layer.source.buffer.isOpaque = isOpaque;
+                for (auto alpha : {half(.23999f), half(1.0f)}) {
+                    layer.alpha = alpha;
+                    renderengine->drawLayers(display, layers, dstBuffer,
+                                             false /* useFrameBufferCache*/, base::unique_fd(),
+                                             nullptr);
+                }
+            }
+        }
+    }
+}
+
+void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+    const nsecs_t timeBefore = systemTime();
+    // The dimensions should not matter, so long as we draw inside them.
+    const Rect displayRect(0, 0, 1080, 2340);
+    DisplaySettings display{
+            .physicalDisplay = displayRect,
+            .clip = displayRect,
+            .maxLuminance = 500,
+            .outputDataspace = ui::Dataspace::DISPLAY_P3,
+    };
+
+    const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+    sp<GraphicBuffer> dstBuffer =
+            new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+                              usage, "primeShaderCache_dst");
+    // This buffer will be the source for the call to drawImageLayers. Draw
+    // something to it as a placeholder for what an app draws. We should draw
+    // something, but the details are not important. We only need one version of
+    // the shadow's SkSL, so draw it here, giving us both a placeholder image
+    // and a chance to compile the shadow's SkSL.
+    sp<GraphicBuffer> srcBuffer =
+            new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+                              usage, "drawImageLayer_src");
+    drawShadowLayer(renderengine, display, srcBuffer);
+
+    drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
+    const nsecs_t timeAfter = systemTime();
+    const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+    ALOGD("shader cache generated in %f ms\n", compileTimeMs);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/Cache.h b/libs/renderengine/skia/Cache.h
new file mode 100644
index 0000000..437571e
--- /dev/null
+++ b/libs/renderengine/skia/Cache.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::renderengine::skia {
+
+class SkiaRenderEngine;
+
+class Cache {
+public:
+    static void primeShaderCache(SkiaRenderEngine*);
+
+private:
+    Cache() = default;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 91b163e..66efb09 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -40,6 +40,7 @@
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/Trace.h>
+#include "Cache.h"
 
 #include <cmath>
 #include <cstdint>
@@ -224,6 +225,10 @@
     return engine;
 }
 
+void SkiaGLRenderEngine::primeCache() {
+    Cache::primeShaderCache(this);
+}
+
 EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
     status_t err;
     EGLConfig config;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 1c3a633..15d834d 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -50,6 +50,7 @@
                        EGLSurface protectedPlaceholder);
     ~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
 
+    void primeCache() override;
     void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
     void unbindExternalTextureBuffer(uint64_t bufferId) override;
     status_t drawLayers(const DisplaySettings& display,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 79a1040..f403725 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -39,7 +39,7 @@
     SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
     ~SkiaRenderEngine() override {}
 
-    virtual void primeCache() const override{};
+    virtual void primeCache() override{};
     virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
     virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
     virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
@@ -64,4 +64,4 @@
 } // namespace renderengine
 } // namespace android
 
-#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 7c7d165..6a91c7c 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -86,7 +86,7 @@
     }
 }
 
-void RenderEngineThreaded::primeCache() const {
+void RenderEngineThreaded::primeCache() {
     std::promise<void> resultPromise;
     std::future<void> resultFuture = resultPromise.get_future();
     {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index d362e17..df0551d 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -42,7 +42,7 @@
 
     RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
     ~RenderEngineThreaded() override;
-    void primeCache() const override;
+    void primeCache() override;
 
     void dump(std::string& result) override;
 
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a19b04f..5270b8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -130,6 +130,23 @@
                         pointerCaptureEnabled ? "true" : "false");
 }
 
+// --- DragEntry ---
+
+// Drag notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting,
+                     float x, float y)
+      : EventEntry(id, Type::DRAG, eventTime, POLICY_FLAG_PASS_TO_USER),
+        connectionToken(connectionToken),
+        isExiting(isExiting),
+        x(x),
+        y(y) {}
+
+DragEntry::~DragEntry() {}
+
+std::string DragEntry::getDescription() const {
+    return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
+}
+
 // --- KeyEntry ---
 
 KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ed17e68..e5fb26c 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -38,6 +38,7 @@
         MOTION,
         SENSOR,
         POINTER_CAPTURE_CHANGED,
+        DRAG,
     };
 
     int32_t id;
@@ -111,6 +112,18 @@
     virtual ~PointerCaptureChangedEntry();
 };
 
+struct DragEntry : EventEntry {
+    sp<IBinder> connectionToken;
+    bool isExiting;
+    float x, y;
+
+    DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
+              float y);
+    std::string getDescription() const override;
+
+    ~DragEntry() override;
+};
+
 struct KeyEntry : EventEntry {
     int32_t deviceId;
     uint32_t source;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3183a98..fe46d17 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -753,6 +753,14 @@
             break;
         }
 
+        case EventEntry::Type::DRAG: {
+            std::shared_ptr<DragEntry> typedEntry =
+                    std::static_pointer_cast<DragEntry>(mPendingEvent);
+            dispatchDragLocked(currentTime, typedEntry);
+            done = true;
+            break;
+        }
+
         case EventEntry::Type::KEY: {
             std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
             if (isAppSwitchDue) {
@@ -921,7 +929,8 @@
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG: {
             // nothing to do
             break;
         }
@@ -943,7 +952,8 @@
 sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                  int32_t y, TouchState* touchState,
                                                                  bool addOutsideTargets,
-                                                                 bool addPortalWindows) {
+                                                                 bool addPortalWindows,
+                                                                 bool ignoreDragWindow) {
     if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
         LOG_ALWAYS_FATAL(
                 "Must provide a valid touch state if adding portal windows or outside targets");
@@ -951,6 +961,9 @@
     // Traverse windows from front to back to find touched window.
     const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+        if (ignoreDragWindow && haveSameToken(windowHandle, touchState->dragWindow)) {
+            continue;
+        }
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
         if (windowInfo->displayId == displayId) {
             auto flags = windowInfo->flags;
@@ -1060,7 +1073,8 @@
         case EventEntry::Type::SENSOR: {
             break;
         }
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG: {
             break;
         }
         case EventEntry::Type::FOCUS:
@@ -1554,6 +1568,35 @@
     return true;
 }
 
+void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle,
+                                             bool isExiting, const MotionEntry& motionEntry) {
+    // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should
+    // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid.
+    LOG_ALWAYS_FATAL_IF(motionEntry.pointerCount != 1);
+    PointerCoords pointerCoords;
+    pointerCoords.copyFrom(motionEntry.pointerCoords[0]);
+    pointerCoords.transform(windowHandle->getInfo()->transform);
+
+    std::unique_ptr<DragEntry> dragEntry =
+            std::make_unique<DragEntry>(mIdGenerator.nextId(), motionEntry.eventTime,
+                                        windowHandle->getToken(), isExiting, pointerCoords.getX(),
+                                        pointerCoords.getY());
+
+    enqueueInboundEventLocked(std::move(dragEntry));
+}
+
+void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) {
+    std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+    if (channel == nullptr) {
+        return; // Window has gone away
+    }
+    InputTarget target;
+    target.inputChannel = channel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+    entry->dispatchInProgress = true;
+    dispatchEventLocked(currentTime, entry, {target});
+}
+
 void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
@@ -1660,7 +1703,8 @@
         case EventEntry::Type::FOCUS:
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
-        case EventEntry::Type::SENSOR: {
+        case EventEntry::Type::SENSOR:
+        case EventEntry::Type::DRAG: {
             ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
             return ADISPLAY_ID_NONE;
         }
@@ -2017,6 +2061,8 @@
             goto Failed;
         }
 
+        addDragEventLocked(entry, tempTouchState);
+
         // Check whether touches should slip outside of the current foreground window.
         if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
             tempTouchState.isSlippery()) {
@@ -2272,6 +2318,38 @@
     return injectionResult;
 }
 
+void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) {
+    if (entry.pointerCount != 1 || !state.dragWindow) {
+        return;
+    }
+
+    int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
+    int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+    int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+    if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+        const sp<InputWindowHandle> hoverWindowHandle =
+                findTouchedWindowAtLocked(entry.displayId, x, y, &state,
+                                          false /*addOutsideTargets*/, false /*addPortalWindows*/,
+                                          true /*ignoreDragWindow*/);
+        // enqueue drag exit if needed.
+        if (hoverWindowHandle != state.dragHoverWindowHandle &&
+            !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) {
+            if (state.dragHoverWindowHandle != nullptr) {
+                enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry);
+            }
+            state.dragHoverWindowHandle = hoverWindowHandle;
+        }
+        // enqueue drag location if needed.
+        if (hoverWindowHandle != nullptr) {
+            enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
+        }
+    } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+               maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+        state.dragWindow = nullptr;
+        state.dragHoverWindowHandle = nullptr;
+    }
+}
+
 void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
                                             int32_t targetFlags, BitSet32 pointerIds,
                                             std::vector<InputTarget>& inputTargets) {
@@ -2543,7 +2621,8 @@
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
     if (eventEntry.type == EventEntry::Type::FOCUS ||
-        eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) {
+        eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
+        eventEntry.type == EventEntry::Type::DRAG) {
         // Focus or pointer capture changed events are passed to apps, but do not represent user
         // activity.
         return;
@@ -2585,7 +2664,8 @@
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET:
         case EventEntry::Type::SENSOR:
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG: {
             LOG_ALWAYS_FATAL("%s events are not user activity",
                              NamedEnum::string(eventEntry.type).c_str());
             break;
@@ -2799,7 +2879,8 @@
             break;
         }
         case EventEntry::Type::FOCUS:
-        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+        case EventEntry::Type::DRAG: {
             break;
         }
         case EventEntry::Type::SENSOR: {
@@ -3022,6 +3103,15 @@
                 break;
             }
 
+            case EventEntry::Type::DRAG: {
+                const DragEntry& dragEntry = static_cast<const DragEntry&>(eventEntry);
+                status = connection->inputPublisher.publishDragEvent(dispatchEntry->seq,
+                                                                     dragEntry.id, dragEntry.x,
+                                                                     dragEntry.y,
+                                                                     dragEntry.isExiting);
+                break;
+            }
+
             case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::SENSOR: {
@@ -3319,7 +3409,8 @@
                 break;
             }
             case EventEntry::Type::FOCUS:
-            case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+            case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+            case EventEntry::Type::DRAG: {
                 LOG_ALWAYS_FATAL("Canceling %s events is not supported",
                                  NamedEnum::string(cancelationEventEntry->type).c_str());
                 break;
@@ -3384,7 +3475,8 @@
             case EventEntry::Type::CONFIGURATION_CHANGED:
             case EventEntry::Type::DEVICE_RESET:
             case EventEntry::Type::POINTER_CAPTURE_CHANGED:
-            case EventEntry::Type::SENSOR: {
+            case EventEntry::Type::SENSOR:
+            case EventEntry::Type::DRAG: {
                 LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
                                  NamedEnum::string(downEventEntry->type).c_str());
                 break;
@@ -4348,6 +4440,15 @@
                 ++i;
             }
         }
+
+        // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we
+        // could just clear the state here.
+        if (state.dragWindow &&
+            std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) ==
+                    windowHandles.end()) {
+            state.dragWindow = nullptr;
+            state.dragHoverWindowHandle = nullptr;
+        }
     }
 
     // Release information for windows that are no longer present.
@@ -4538,7 +4639,8 @@
     mBlockUntrustedTouchesMode = mode;
 }
 
-bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+                                         bool isDragDrop) {
     if (fromToken == toToken) {
         if (DEBUG_FOCUS) {
             ALOGD("Trivial transfer to same window.");
@@ -4582,6 +4684,11 @@
                              InputTarget::FLAG_DISPATCH_AS_IS);
                     state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
 
+                    // Store the dragging window.
+                    if (isDragDrop) {
+                        state.dragWindow = toWindowHandle;
+                    }
+
                     found = true;
                     goto Found;
                 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 83094c2..b2f3625 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -120,8 +120,8 @@
     virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
     virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
 
-    virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
-                                    const sp<IBinder>& toToken) override;
+    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+                                    bool isDragDrop = false) override;
 
     virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
             const std::string& name) override;
@@ -185,6 +185,9 @@
     // Enqueues a focus event.
     void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
                                  const std::string& reason) REQUIRES(mLock);
+    // Enqueues a drag event.
+    void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting,
+                                const MotionEntry& motionEntry) REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
     void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -204,7 +207,8 @@
     sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
                                                     TouchState* touchState,
                                                     bool addOutsideTargets = false,
-                                                    bool addPortalWindows = false) REQUIRES(mLock);
+                                                    bool addPortalWindows = false,
+                                                    bool ignoreDragWindow = false) REQUIRES(mLock);
 
     // All registered connections mapped by channel file descriptor.
     std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
@@ -387,6 +391,7 @@
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
     void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
                               DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
     void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
     void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
 
@@ -489,10 +494,12 @@
                                    std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
     void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
                                           float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
-
     void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
                                   const InjectionState* injectionState);
+    // Enqueue a drag event if needed, and update the touch state.
+    // Uses findTouchedWindowTargetsLocked to make the decision
+    void addDragEventLocked(const MotionEntry& entry, TouchState& state) REQUIRES(mLock);
 
     struct TouchOcclusionInfo {
         bool hasBlockingOcclusion;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 81b3cf0..4165f49 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -49,6 +49,8 @@
     windows = other.windows;
     portalWindows = other.portalWindows;
     gestureMonitors = other.gestureMonitors;
+    dragHoverWindowHandle = other.dragHoverWindowHandle;
+    dragWindow = other.dragWindow;
 }
 
 void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 623c6a8..d7a561c 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,6 +41,11 @@
 
     std::vector<TouchedMonitor> gestureMonitors;
 
+    // The last drag hover window which could receive the drag event.
+    sp<InputWindowHandle> dragHoverWindowHandle;
+    // The window being dragged.
+    sp<InputWindowHandle> dragWindow;
+
     TouchState();
     ~TouchState();
     void reset();
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3491893..b601dfc 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -149,8 +149,8 @@
      *
      * Returns true on success.  False if the window did not actually have touch focus.
      */
-    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
-
+    virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+                                    bool isDragDrop) = 0;
     /**
      * Sets focus on the specified window.
      */
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5cdcfaf..32f9b69 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -766,6 +766,9 @@
             case AINPUT_EVENT_TYPE_CAPTURE: {
                 FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
             }
+            case AINPUT_EVENT_TYPE_DRAG: {
+                FAIL() << "Use 'consumeDragEvent' for DRAG events";
+            }
             default: {
                 FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
             }
@@ -803,6 +806,23 @@
         EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
     }
 
+    void consumeDragEvent(bool isExiting, float x, float y) {
+        const InputEvent* event = consume();
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
+                << "Got " << inputEventTypeToString(event->getType())
+                << " event instead of DRAG event";
+
+        EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+                << mName.c_str() << ": event displayId should always be NONE.";
+
+        const auto& dragEvent = static_cast<const DragEvent&>(*event);
+        EXPECT_EQ(isExiting, dragEvent.isExiting());
+        EXPECT_EQ(x, dragEvent.getX());
+        EXPECT_EQ(y, dragEvent.getY());
+    }
+
     void assertNoEvents() {
         InputEvent* event = consume();
         if (event == nullptr) {
@@ -905,7 +925,7 @@
         mInfo.frameTop = frame.top;
         mInfo.frameRight = frame.right;
         mInfo.frameBottom = frame.bottom;
-        mInfo.transform.set(frame.left, frame.top);
+        mInfo.transform.set(-frame.left, -frame.top);
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(frame);
     }
@@ -1003,6 +1023,10 @@
                                      expectedFlags);
     }
 
+    void consumeDragEvent(bool isExiting, float x, float y) {
+        mInputReceiver->consumeDragEvent(isExiting, x, y);
+    }
+
     std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
         if (mInputReceiver == nullptr) {
             ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -4682,4 +4706,87 @@
     mTouchWindow->consumeAnyMotionDown();
 }
 
+class InputDispatcherDragTests : public InputDispatcherTest {
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApp;
+    sp<FakeWindowHandle> mWindow;
+    sp<FakeWindowHandle> mSecondWindow;
+    sp<FakeWindowHandle> mDragWindow;
+
+    void SetUp() override {
+        InputDispatcherTest::SetUp();
+        mApp = std::make_shared<FakeApplicationHandle>();
+        mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+        mWindow->setFrame(Rect(0, 0, 100, 100));
+        mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+        mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+        mSecondWindow->setFrame(Rect(100, 0, 200, 100));
+        mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+    }
+
+    // Start performing drag, we will create a drag window and transfer touch to it.
+    void performDrag() {
+        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                   {50, 50}))
+                << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+        // Window should receive motion event.
+        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+        // The drag window covers the entire display
+        mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+        mDispatcher->setInputWindows(
+                {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+        // Transfer touch focus to the drag window
+        mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+                                        true /* isDragDrop */);
+        mWindow->consumeMotionCancel();
+        mDragWindow->consumeMotionDown();
+    }
+};
+
+TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
+    performDrag();
+
+    // Move on window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->assertNoEvents();
+
+    // Move to another window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(true, 150, 50);
+    mSecondWindow->consumeDragEvent(false, 50, 50);
+
+    // Move back to original window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->consumeDragEvent(true, -50, 50);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index d215298..5fed79f 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -182,7 +182,7 @@
     bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
 
     // Returns true if the next buffer should be presented at the expected present time
-    bool shouldPresentNow(nsecs_t expectedPresentTime) const final;
+    bool shouldPresentNow(nsecs_t expectedPresentTime) const;
 
     // Returns true if the next buffer should be presented at the expected present time,
     // overridden by BufferStateLayer and BufferQueueLayer for implementation
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index e017cdc..96a0c3c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -177,6 +177,8 @@
         // leak.
         ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
               mName.c_str());
+        std::string miniDump = mPendingJankClassifications.front()->miniDump();
+        ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
         mPendingJankClassifications.pop_front();
     }
     mPendingJankClassifications.emplace_back(surfaceFrame);
@@ -255,8 +257,8 @@
 }
 
 bool BufferStateLayer::setTransform(uint32_t transform) {
-    if (mCurrentState.transform == transform) return false;
-    mCurrentState.transform = transform;
+    if (mCurrentState.bufferTransform == transform) return false;
+    mCurrentState.bufferTransform = transform;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -308,17 +310,17 @@
         h = frame.bottom;
     }
 
-    if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y &&
-        mCurrentState.active.w == w && mCurrentState.active.h == h) {
+    if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y &&
+        mCurrentState.width == w && mCurrentState.height == h) {
         return false;
     }
 
     if (!frame.isValid()) {
         x = y = w = h = 0;
     }
-    mCurrentState.active.transform.set(x, y);
-    mCurrentState.active.w = w;
-    mCurrentState.active.h = h;
+    mCurrentState.transform.set(x, y);
+    mCurrentState.width = w;
+    mCurrentState.height = h;
 
     mCurrentState.sequence++;
     mCurrentState.modified = true;
@@ -779,7 +781,7 @@
     mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
     mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
     mBufferInfo.mFence = s.acquireFence;
-    mBufferInfo.mTransform = s.transform;
+    mBufferInfo.mTransform = s.bufferTransform;
     mBufferInfo.mDataspace = translateDataspace(s.dataspace);
     mBufferInfo.mCrop = computeCrop(s);
     mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
@@ -846,10 +848,10 @@
     const State& s(getDrawingState());
     if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
         return RoundedCornerState();
-    return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
-                                        static_cast<float>(s.active.transform.ty()),
-                                        static_cast<float>(s.active.transform.tx() + s.active.w),
-                                        static_cast<float>(s.active.transform.ty() + s.active.h)),
+    return RoundedCornerState(FloatRect(static_cast<float>(s.transform.tx()),
+                                        static_cast<float>(s.transform.ty()),
+                                        static_cast<float>(s.transform.tx() + s.width),
+                                        static_cast<float>(s.transform.ty() + s.height)),
                               radius);
 }
 
@@ -863,7 +865,7 @@
     uint32_t bufferHeight = s.buffer->height;
 
     // Undo any transformations on the buffer and return the result.
-    if (s.transform & ui::Transform::ROT_90) {
+    if (s.bufferTransform & ui::Transform::ROT_90) {
         std::swap(bufferWidth, bufferHeight);
     }
 
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 003edd5..93fb2cd 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -55,11 +55,9 @@
     void pushPendingState() override;*/
     bool applyPendingStates(Layer::State* stateToCommit) override;
 
-    uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
-    uint32_t getActiveHeight(const Layer::State& s) const override { return s.active.h; }
-    ui::Transform getActiveTransform(const Layer::State& s) const override {
-        return s.active.transform;
-    }
+    uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; }
+    uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
+    ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
     Region getActiveTransparentRegion(const Layer::State& s) const override {
         return s.transparentRegionHint;
     }
@@ -118,6 +116,8 @@
     std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
     std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
 
+    bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; }
+
 protected:
     void gatherBufferInfo() override;
     uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 00424b2..fa87fb8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -17,15 +17,12 @@
 #pragma once
 
 #include <compositionengine/impl/planner/LayerState.h>
+#include <renderengine/RenderEngine.h>
 
 #include <chrono>
 
 namespace android {
 
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
 namespace compositionengine::impl::planner {
 
 std::string durationString(std::chrono::milliseconds duration);
@@ -63,7 +60,7 @@
     const Layer& getFirstLayer() const { return mLayers[0]; }
     const Rect& getBounds() const { return mBounds; }
     size_t getAge() const { return mAge; }
-    const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+    const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
     const sp<Fence>& getDrawFence() const { return mDrawFence; }
 
     NonBufferHash getNonBufferHash() const;
@@ -82,7 +79,7 @@
 
     void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
     void append(const CachedSet& other) {
-        mBuffer = nullptr;
+        mTexture.setBuffer(nullptr, nullptr);
         mDrawFence = nullptr;
 
         mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
@@ -105,7 +102,32 @@
     std::vector<Layer> mLayers;
     Rect mBounds = Rect::EMPTY_RECT;
     size_t mAge = 0;
-    sp<GraphicBuffer> mBuffer;
+
+    class Texture {
+    public:
+        ~Texture() { setBuffer(nullptr, nullptr); }
+
+        void setBuffer(const sp<GraphicBuffer>& buffer, renderengine::RenderEngine* re) {
+            if (mRE && mBuffer) {
+                mRE->unbindExternalTextureBuffer(mBuffer->getId());
+            }
+
+            mBuffer = buffer;
+            mRE = re;
+
+            if (mRE && mBuffer) {
+                mRE->cacheExternalTextureBuffer(mBuffer);
+            }
+        }
+
+        const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+
+    private:
+        sp<GraphicBuffer> mBuffer = nullptr;
+        renderengine::RenderEngine* mRE = nullptr;
+    };
+
+    Texture mTexture;
     sp<Fence> mDrawFence;
 
     static const bool sDebugHighlighLayers;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ab3fe9e..ba03655 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -126,7 +126,7 @@
 }
 
 bool CachedSet::hasReadyBuffer() const {
-    return mBuffer != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+    return mTexture.getBuffer() != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
 }
 
 std::vector<CachedSet> CachedSet::decompose() const {
@@ -209,11 +209,12 @@
                                                  HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
     LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
     base::unique_fd drawFence;
+
     status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
                                               base::unique_fd(), &drawFence);
 
     if (result == NO_ERROR) {
-        mBuffer = buffer;
+        mTexture.setBuffer(buffer, &renderEngine);
         mDrawFence = new Fence(drawFence.release());
     }
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 6d1ce4c..c33828f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -310,8 +310,14 @@
     EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
     EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
     cachedSet.render(mRenderEngine);
     expectReadyBuffer(cachedSet);
+
+    // Now check that appending a new cached set properly cleans up RenderEngine resources.
+    EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    cachedSet.append(CachedSet(layer3));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 42bbfcc..c4bd5b3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -55,8 +55,10 @@
     // TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
     // mPredictor should be mocked and checked for expectations.
     Predictor mPredictor;
-    std::unique_ptr<Flattener> mFlattener;
+
+    // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
     renderengine::mock::RenderEngine mRenderEngine;
+    std::unique_ptr<Flattener> mFlattener;
 
     const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
     std::chrono::steady_clock::time_point mTime = kStartTime;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 3442706..b1dff8d 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -296,7 +296,7 @@
                            frametimeline::TimelineItem&& predictions,
                            std::shared_ptr<TimeStats> timeStats,
                            JankClassificationThresholds thresholds,
-                           TraceCookieCounter* traceCookieCounter)
+                           TraceCookieCounter* traceCookieCounter, bool isBuffer)
       : mToken(frameTimelineInfo.vsyncId),
         mInputEventId(frameTimelineInfo.inputEventId),
         mOwnerPid(ownerPid),
@@ -310,7 +310,8 @@
         mActuals({0, 0, 0}),
         mTimeStats(timeStats),
         mJankClassificationThresholds(thresholds),
-        mTraceCookieCounter(*traceCookieCounter) {}
+        mTraceCookieCounter(*traceCookieCounter),
+        mIsBuffer(isBuffer) {}
 
 void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
     std::scoped_lock lock(mMutex);
@@ -395,6 +396,20 @@
     return mDropTime;
 }
 
+void SurfaceFrame::promoteToBuffer() {
+    std::scoped_lock lock(mMutex);
+    LOG_ALWAYS_FATAL_IF(mIsBuffer == true,
+                        "Trying to promote an already promoted BufferSurfaceFrame from layer %s "
+                        "with token %" PRId64 "",
+                        mDebugName.c_str(), mToken);
+    mIsBuffer = true;
+}
+
+bool SurfaceFrame::getIsBuffer() const {
+    std::scoped_lock lock(mMutex);
+    return mIsBuffer;
+}
+
 void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
     std::scoped_lock lock(mMutex);
     StringAppendF(&result, "%s", indent.c_str());
@@ -407,6 +422,8 @@
     StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
     StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+    StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
     StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
@@ -444,6 +461,21 @@
     dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
 }
 
+std::string SurfaceFrame::miniDump() const {
+    std::scoped_lock lock(mMutex);
+    std::string result;
+    StringAppendF(&result, "Layer - %s\n", mDebugName.c_str());
+    StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+    StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+    StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
+    StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+    StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+    StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+    StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+    StringAppendF(&result, "Present time: %" PRId64 "", mActuals.presentTime);
+    return result;
+}
+
 void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
                                       nsecs_t& deadlineDelta) {
     if (mPredictionState == PredictionState::Expired ||
@@ -744,13 +776,14 @@
 
 std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
         const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
-        std::string layerName, std::string debugName) {
+        std::string layerName, std::string debugName, bool isBuffer) {
     ATRACE_CALL();
     if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
         return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
                                               std::move(layerName), std::move(debugName),
                                               PredictionState::None, TimelineItem(), mTimeStats,
-                                              mJankClassificationThresholds, &mTraceCookieCounter);
+                                              mJankClassificationThresholds, &mTraceCookieCounter,
+                                              isBuffer);
     }
     std::optional<TimelineItem> predictions =
             mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
@@ -759,12 +792,13 @@
                                               std::move(layerName), std::move(debugName),
                                               PredictionState::Valid, std::move(*predictions),
                                               mTimeStats, mJankClassificationThresholds,
-                                              &mTraceCookieCounter);
+                                              &mTraceCookieCounter, isBuffer);
     }
     return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
                                           std::move(layerName), std::move(debugName),
                                           PredictionState::Expired, TimelineItem(), mTimeStats,
-                                          mJankClassificationThresholds, &mTraceCookieCounter);
+                                          mJankClassificationThresholds, &mTraceCookieCounter,
+                                          isBuffer);
 }
 
 FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index b66e02a..3cf35f0 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -159,7 +159,7 @@
                  int32_t layerId, std::string layerName, std::string debugName,
                  PredictionState predictionState, TimelineItem&& predictions,
                  std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
-                 TraceCookieCounter* traceCookieCounter);
+                 TraceCookieCounter* traceCookieCounter, bool isBuffer);
     ~SurfaceFrame() = default;
 
     // Returns std::nullopt if the frame hasn't been classified yet.
@@ -181,6 +181,10 @@
     void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
     void setRenderRate(Fps renderRate);
 
+    // When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update
+    // isBuffer.
+    void promoteToBuffer();
+
     // Functions called by FrameTimeline
     // BaseTime is the smallest timestamp in this SurfaceFrame.
     // Used for dumping all timestamps relative to the oldest, making it easy to read.
@@ -192,6 +196,8 @@
                    nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta);
     // All the timestamps are dumped relative to the baseTime
     void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
+    // Dumps only the layer, token, is buffer, jank metadata, prediction and present states.
+    std::string miniDump() const;
     // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
     // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
     // DisplayFrame at the trace processor side.
@@ -206,6 +212,7 @@
     FrameReadyMetadata getFrameReadyMetadata() const;
     FramePresentMetadata getFramePresentMetadata() const;
     nsecs_t getDropTime() const;
+    bool getIsBuffer() const;
 
     // For prediction expired frames, this delta is subtracted from the actual end time to get a
     // start time decent enough to see in traces.
@@ -253,6 +260,9 @@
     // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a
     // reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame.
     TraceCookieCounter& mTraceCookieCounter;
+    // Tells if the SurfaceFrame is representing a buffer or a transaction without a
+    // buffer(animations)
+    bool mIsBuffer;
 };
 
 /*
@@ -272,7 +282,7 @@
     // Debug name is the human-readable debugging string for dumpsys.
     virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
             const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
-            int32_t layerId, std::string layerName, std::string debugName) = 0;
+            int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) = 0;
 
     // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
     // composited into one display frame.
@@ -431,7 +441,7 @@
     frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
     std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
             const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
-            int32_t layerId, std::string layerName, std::string debugName) override;
+            int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) override;
     void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
     void setSfPresent(nsecs_t sfPresentTime,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 061ad0e..44f1a70 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -109,11 +109,11 @@
     mCurrentState.layerStack = 0;
     mCurrentState.sequence = 0;
     mCurrentState.requested_legacy = mCurrentState.active_legacy;
-    mCurrentState.active.w = UINT32_MAX;
-    mCurrentState.active.h = UINT32_MAX;
-    mCurrentState.active.transform.set(0, 0);
+    mCurrentState.width = UINT32_MAX;
+    mCurrentState.height = UINT32_MAX;
+    mCurrentState.transform.set(0, 0);
     mCurrentState.frameNumber = 0;
-    mCurrentState.transform = 0;
+    mCurrentState.bufferTransform = 0;
     mCurrentState.transformToDisplayInverse = false;
     mCurrentState.crop.makeInvalid();
     mCurrentState.acquireFence = new Fence(-1);
@@ -1058,6 +1058,15 @@
 }
 
 void Layer::commitTransaction(State& stateToCommit) {
+    if (auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+        mDrawingState.buffer != stateToCommit.buffer && bufferSurfaceFrame != nullptr &&
+        bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+        // If the previous buffer was committed but not latched (refreshPending - happens during
+        // back to back invalidates), it gets silently dropped here. Mark the corresponding
+        // SurfaceFrame as dropped to prevent it from getting stuck in the pending classification
+        // list.
+        addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
+    }
     mDrawingState = stateToCommit;
 
     // Set the present state for all bufferlessSurfaceFramesTX to Presented. The
@@ -1537,6 +1546,7 @@
         // Promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX
         mCurrentState.bufferSurfaceFrameTX = it->second;
         mCurrentState.bufferlessSurfaceFramesTX.erase(it);
+        mCurrentState.bufferSurfaceFrameTX->promoteToBuffer();
         mCurrentState.bufferSurfaceFrameTX->setActualQueueTime(postTime);
     } else {
         mCurrentState.bufferSurfaceFrameTX =
@@ -1596,7 +1606,8 @@
     auto surfaceFrame =
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName,
-                                                                 mTransactionName);
+                                                                 mTransactionName,
+                                                                 /*isBuffer*/ false);
     // For Transactions, the post time is considered to be both queue and acquire fence time.
     surfaceFrame->setActualQueueTime(postTime);
     surfaceFrame->setAcquireFenceTime(postTime);
@@ -1612,7 +1623,8 @@
         const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
     auto surfaceFrame =
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
-                                                                 getSequence(), mName, debugName);
+                                                                 getSequence(), mName, debugName,
+                                                                 /*isBuffer*/ true);
     // For buffers, acquire fence time will set during latch.
     surfaceFrame->setActualQueueTime(queueTime);
     const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -2431,7 +2443,7 @@
     const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
     const State& state = useDrawing ? mDrawingState : mCurrentState;
 
-    ui::Transform requestedTransform = state.active_legacy.transform;
+    ui::Transform requestedTransform = state.transform;
 
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
         layerInfo->set_id(sequence);
@@ -2460,11 +2472,10 @@
                                                    return layerInfo->mutable_requested_position();
                                                });
 
-        LayerProtoHelper::writeSizeToProto(state.active_legacy.w, state.active_legacy.h,
+        LayerProtoHelper::writeSizeToProto(state.width, state.height,
                                            [&]() { return layerInfo->mutable_size(); });
 
-        LayerProtoHelper::writeToProto(state.crop_legacy,
-                                       [&]() { return layerInfo->mutable_crop(); });
+        LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
 
         layerInfo->set_is_opaque(isOpaque(state));
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 34a9f39..4a114e2 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -250,9 +250,11 @@
 
         // The fields below this point are only used by BufferStateLayer
         uint64_t frameNumber;
-        Geometry active;
+        uint32_t width;
+        uint32_t height;
+        ui::Transform transform;
 
-        uint32_t transform;
+        uint32_t bufferTransform;
         bool transformToDisplayInverse;
 
         Rect crop;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index ba43e70..6553efe 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -354,7 +354,7 @@
             return mTokenManager->generateTokenForPredictions(
                     {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
         }
-        return static_cast<int64_t>(0);
+        return FrameTimelineInfo::INVALID_VSYNC_ID;
     }();
 
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
@@ -494,10 +494,16 @@
                 const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
                 const auto deadlineTimestamp = now + timeout.count();
                 const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
-                // TODO(b/162890590): use TokenManager to populate vsyncId
+                const int64_t vsyncId = [&] {
+                    if (mTokenManager != nullptr) {
+                        return mTokenManager->generateTokenForPredictions(
+                                {now, deadlineTimestamp, expectedVSyncTime});
+                    }
+                    return FrameTimelineInfo::INVALID_VSYNC_ID;
+                }();
                 mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
                                                    ++mVSyncState->count, expectedVSyncTime,
-                                                   deadlineTimestamp, /*vsyncId=*/0));
+                                                   deadlineTimestamp, vsyncId));
             }
         }
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 593855e..7d75657 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1879,7 +1879,12 @@
             // underestimated.
             mFrameStartTime = frameStart;
         }
-        signalRefresh();
+
+        // Run the refresh immediately after invalidate as there is no point going thru the message
+        // queue again, and to ensure that we actually refresh the screen instead of handling
+        // other messages that were queued us already in the MessageQueue.
+        mRefreshPending = true;
+        onMessageRefresh();
     }
 }
 
@@ -3041,9 +3046,8 @@
 
 void SurfaceFlinger::commitTransaction() {
     commitTransactionLocked();
-    mTransactionPending = false;
+    signalSynchronousTransactions();
     mAnimTransactionPending = false;
-    mTransactionCV.broadcast();
 }
 
 void SurfaceFlinger::commitTransactionLocked() {
@@ -3312,15 +3316,16 @@
                 auto& [applyToken, transactionQueue] = *it;
 
                 while (!transactionQueue.empty()) {
-                    const auto& transaction = transactionQueue.front();
+                    auto& transaction = transactionQueue.front();
                     if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                        transaction.isAutoTimestamp,
                                                        transaction.desiredPresentTime,
-                                                       transaction.states, pendingBuffers)) {
+                                                       transaction.originUid, transaction.states,
+                                                       pendingBuffers)) {
                         setTransactionFlags(eTransactionFlushNeeded);
                         break;
                     }
-                    transactions.push_back(transaction);
+                    transactions.emplace_back(std::move(transaction));
                     transactionQueue.pop();
                 }
 
@@ -3337,17 +3342,18 @@
             // Case 2: push to pending when there exist a pending queue.
             // Case 3: others are ready to apply.
             while (!mTransactionQueue.empty()) {
-                const auto& transaction = mTransactionQueue.front();
+                auto& transaction = mTransactionQueue.front();
                 bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
                         mPendingTransactionQueues.end();
                 if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
                                                    transaction.isAutoTimestamp,
                                                    transaction.desiredPresentTime,
-                                                   transaction.states, pendingBuffers) ||
+                                                   transaction.originUid, transaction.states,
+                                                   pendingBuffers) ||
                     pendingTransactions) {
-                    mPendingTransactionQueues[transaction.applyToken].push(transaction);
+                    mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
                 } else {
-                    transactions.push_back(transaction);
+                    transactions.emplace_back(std::move(transaction));
                 }
                 mTransactionQueue.pop();
                 ATRACE_INT("TransactionQueue", mTransactionQueue.size());
@@ -3363,25 +3369,36 @@
                                   transaction.postTime, transaction.permissions,
                                   transaction.hasListenerCallbacks, transaction.listenerCallbacks,
                                   transaction.originPid, transaction.originUid, transaction.id);
+            if (transaction.transactionCommittedSignal) {
+                mTransactionCommittedSignals.emplace_back(
+                        std::move(transaction.transactionCommittedSignal));
+            }
         }
     }
 }
 
 bool SurfaceFlinger::transactionFlushNeeded() {
     Mutex::Autolock _l(mQueueLock);
-    return !mPendingTransactionQueues.empty();
+    return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
 }
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
-        const Vector<ComposerState>& states,
+        uid_t originUid, const Vector<ComposerState>& states,
         std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+    ATRACE_CALL();
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     bool ready = true;
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
     // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
     if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
         desiredPresentTime < expectedPresentTime + s2ns(1)) {
+        ATRACE_NAME("not current");
+        ready = false;
+    }
+
+    if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
+        ATRACE_NAME("!isVsyncValid");
         ready = false;
     }
 
@@ -3404,15 +3421,12 @@
             continue;
         }
 
+        ATRACE_NAME(layer->getName().c_str());
+
         const bool frameTimelineInfoChanged = (s.what & layer_state_t::eFrameTimelineInfoChanged);
         const auto vsyncId = frameTimelineInfoChanged ? s.frameTimelineInfo.vsyncId : info.vsyncId;
         if (isAutoTimestamp && layer->frameIsEarly(expectedPresentTime, vsyncId)) {
             ATRACE_NAME("frameIsEarly()");
-            return false;
-        }
-
-        if (!mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
-            ATRACE_NAME("!isVsyncValidForUid");
             ready = false;
         }
 
@@ -3421,16 +3435,16 @@
             // transaction in the queue.
             const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
             if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
+                ATRACE_NAME("hasPendingBuffer");
                 ready = false;
             }
             pendingBuffers.insert(s.surface);
         }
-        pendingBuffers.insert(s.surface);
     }
     return ready;
 }
 
-void SurfaceFlinger::queueTransaction(TransactionState state) {
+void SurfaceFlinger::queueTransaction(TransactionState& state) {
     Mutex::Autolock _l(mQueueLock);
 
     // If its TransactionQueue already has a pending TransactionState or if it is pending
@@ -3450,20 +3464,15 @@
         }
     }
 
+    // Generate a CountDownLatch pending state if this is a synchronous transaction.
+    if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
+        state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
+                (state.inputWindowCommands.syncInputWindows ? 2 : 1));
+    }
+
     mTransactionQueue.emplace(state);
     ATRACE_INT("TransactionQueue", mTransactionQueue.size());
 
-    // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
-    if (state.flags & eEarlyWakeup) {
-        ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
-    }
-
-    if (!(state.permissions & Permission::ACCESS_SURFACE_FLINGER) &&
-        (state.flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
-        ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
-        state.flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
-    }
-
     const auto schedule = [](uint32_t flags) {
         if (flags & eEarlyWakeup) return TransactionSchedule::Early;
         if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
@@ -3474,28 +3483,23 @@
     setTransactionFlags(eTransactionFlushNeeded, schedule);
 }
 
-void SurfaceFlinger::waitForSynchronousTransaction(bool synchronous, bool syncInput) {
-    Mutex::Autolock _l(mStateLock);
-    if (synchronous) {
-        mTransactionPending = true;
+void SurfaceFlinger::waitForSynchronousTransaction(
+        const CountDownLatch& transactionCommittedSignal) {
+    // applyTransactionState is called on the main SF thread.  While a given process may wish
+    // to wait on synchronous transactions, the main SF thread should apply the transaction and
+    // set the value to notify this after committed.
+    if (!transactionCommittedSignal.wait_until(std::chrono::seconds(5))) {
+        ALOGE("setTransactionState timed out!");
     }
-    if (syncInput) {
-        mPendingSyncInputWindows = true;
-    }
+}
 
-    // applyTransactionState can be called by either the main SF thread or by
-    // another process through setTransactionState.  While a given process may wish
-    // to wait on synchronous transactions, the main SF thread should never
-    // be blocked.  Therefore, we only wait if isMainThread is false.
-    while (mTransactionPending || mPendingSyncInputWindows) {
-        status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
-        if (CC_UNLIKELY(err != NO_ERROR)) {
-            // just in case something goes wrong in SF, return to the
-            // called after a few seconds.
-            ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
-            mTransactionPending = false;
-            mPendingSyncInputWindows = false;
-            break;
+void SurfaceFlinger::signalSynchronousTransactions() {
+    for (auto it = mTransactionCommittedSignals.begin();
+         it != mTransactionCommittedSignals.end();) {
+        if ((*it)->countDown() == 0) {
+            it = mTransactionCommittedSignals.erase(it);
+        } else {
+            it++;
         }
     }
 }
@@ -3524,21 +3528,35 @@
         permissions |= Permission::ROTATE_SURFACE_FLINGER;
     }
 
+    // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
+    if (flags & eEarlyWakeup) {
+        ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+    }
+
+    if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
+        (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+        ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+        flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+    }
+
     const int64_t postTime = systemTime();
 
     IPCThreadState* ipc = IPCThreadState::self();
     const int originPid = ipc->getCallingPid();
     const int originUid = ipc->getCallingUid();
+    TransactionState state{frameTimelineInfo,  states,
+                           displays,           flags,
+                           applyToken,         inputWindowCommands,
+                           desiredPresentTime, isAutoTimestamp,
+                           uncacheBuffer,      postTime,
+                           permissions,        hasListenerCallbacks,
+                           listenerCallbacks,  originPid,
+                           originUid,          transactionId};
+    queueTransaction(state);
 
-    queueTransaction({frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands,
-                      desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, permissions,
-                      hasListenerCallbacks, listenerCallbacks, originPid, originUid,
-                      transactionId});
-
-    const bool synchronous = flags & eSynchronous;
-    const bool syncInput = inputWindowCommands.syncInputWindows;
-    if (synchronous || syncInput) {
-        waitForSynchronousTransaction(synchronous, syncInput);
+    // Check the pending state to make sure the transaction is synchronous.
+    if (state.transactionCommittedSignal) {
+        waitForSynchronousTransaction(*state.transactionCommittedSignal);
     }
 
     return NO_ERROR;
@@ -6078,10 +6096,7 @@
 
 void SurfaceFlinger::setInputWindowsFinished() {
     Mutex::Autolock _l(mStateLock);
-
-    mPendingSyncInputWindows = false;
-
-    mTransactionCV.broadcast();
+    signalSynchronousTransactions();
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 40d63b2..3787b9d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -474,6 +474,41 @@
         hal::Connection connection = hal::Connection::INVALID;
     };
 
+    class CountDownLatch {
+    public:
+        explicit CountDownLatch(int32_t count) : mCount(count) {}
+
+        int32_t countDown() {
+            std::unique_lock<std::mutex> lock(mMutex);
+            if (mCount == 0) {
+                return 0;
+            }
+            if (--mCount == 0) {
+                mCountDownComplete.notify_all();
+            }
+            return mCount;
+        }
+
+        // Return true if triggered.
+        bool wait_until(const std::chrono::seconds& timeout) const {
+            std::unique_lock<std::mutex> lock(mMutex);
+            const auto untilTime = std::chrono::system_clock::now() + timeout;
+            while (mCount != 0) {
+                // Conditional variables can be woken up sporadically, so we check count
+                // to verify the wakeup was triggered by |countDown|.
+                if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+    private:
+        int32_t mCount;
+        mutable std::condition_variable mCountDownComplete;
+        mutable std::mutex mMutex;
+    };
+
     struct TransactionState {
         TransactionState(const FrameTimelineInfo& frameTimelineInfo,
                          const Vector<ComposerState>& composerStates,
@@ -517,6 +552,7 @@
         int originPid;
         int originUid;
         uint64_t id;
+        std::shared_ptr<CountDownLatch> transactionCommittedSignal;
     };
 
     template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
@@ -801,7 +837,7 @@
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
-            const Vector<ComposerState>& states,
+            uid_t originUid, const Vector<ComposerState>& states,
             std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -1080,8 +1116,9 @@
     status_t CheckTransactCodeCredentials(uint32_t code);
 
     // Add transaction to the Transaction Queue
-    void queueTransaction(TransactionState state) EXCLUDES(mQueueLock);
-    void waitForSynchronousTransaction(bool synchronous, bool syncInput) EXCLUDES(mStateLock);
+    void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
+    void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
+    void signalSynchronousTransactions();
 
     /*
      * Generic Layer Metadata
@@ -1111,8 +1148,7 @@
     mutable Mutex mStateLock;
     State mCurrentState{LayerVector::StateSet::Current};
     std::atomic<int32_t> mTransactionFlags = 0;
-    Condition mTransactionCV;
-    bool mTransactionPending = false;
+    std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
     bool mAnimTransactionPending = false;
     SortedVector<sp<Layer>> mLayersPendingRemoval;
     bool mForceTraversal = false;
@@ -1324,7 +1360,6 @@
 
     sp<SetInputWindowsListener> mSetInputWindowsListener;
 
-    bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
 
     // This should only be accessed on the main thread.
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 256be27..c3946cb 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -836,8 +836,8 @@
     static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
         layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
-        layerDrawingState.active.w = 100;
-        layerDrawingState.active.h = 100;
+        layerDrawingState.width = 100;
+        layerDrawingState.height = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
         layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 81efe0b..d1385c0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -193,10 +193,12 @@
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
     EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
 }
@@ -204,7 +206,8 @@
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
 }
 
@@ -213,7 +216,8 @@
     flushTokens(systemTime() + maxTokenRetentionTime);
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
 }
@@ -222,7 +226,8 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
     EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -233,7 +238,8 @@
     constexpr int32_t inputEventId = 1;
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
 }
@@ -243,7 +249,8 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -271,11 +278,11 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdTwo, sLayerNameTwo,
-                                                       sLayerNameTwo);
+                                                       sLayerNameTwo, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -317,7 +324,8 @@
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
                                                            sPidOne, sUidOne, sLayerIdOne,
-                                                           sLayerNameOne, sLayerNameOne);
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ true);
         mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
         mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -339,7 +347,7 @@
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
     surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -353,18 +361,20 @@
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
-                                                                   "acquireFenceAfterQueue",
-                                                                   "acquireFenceAfterQueue");
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+                                                       "acquireFenceAfterQueue",
+                                                       "acquireFenceAfterQueue", /*isBuffer*/ true);
     surfaceFrame->setActualQueueTime(123);
     surfaceFrame->setAcquireFenceTime(456);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
-                                                                   "acquireFenceAfterQueue",
-                                                                   "acquireFenceAfterQueue");
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+                                                       "acquireFenceAfterQueue",
+                                                       "acquireFenceAfterQueue", /*isBuffer*/ true);
     surfaceFrame->setActualQueueTime(456);
     surfaceFrame->setAcquireFenceTime(123);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -378,7 +388,8 @@
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                           sLayerNameOne, sLayerNameOne);
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ true);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -394,7 +405,8 @@
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                           sLayerNameOne, sLayerNameOne);
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ true);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -410,7 +422,8 @@
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                           sLayerNameOne, sLayerNameOne);
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ true);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -430,7 +443,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setAcquireFenceTime(20);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -458,7 +471,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setAcquireFenceTime(20);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -484,7 +497,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setAcquireFenceTime(20);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -508,7 +521,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     surfaceFrame1->setAcquireFenceTime(20);
@@ -532,7 +545,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(45);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -558,7 +571,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -584,7 +597,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -610,7 +623,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
 
@@ -637,7 +650,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(45);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -665,7 +678,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(45);
     // Trigger a prediction expiry
     flushTokens(systemTime() + maxTokenRetentionTime);
@@ -701,7 +714,8 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -727,7 +741,8 @@
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -772,7 +787,8 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -1085,11 +1101,11 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setActualQueueTime(10);
     surfaceFrame1->setDropTime(15);
 
@@ -1245,7 +1261,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
                                                        sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setActualQueueTime(appEndTime);
     surfaceFrame1->setAcquireFenceTime(appEndTime);
 
@@ -1315,7 +1331,7 @@
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1493,7 +1509,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(16);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1513,7 +1529,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(36);
     mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1573,7 +1589,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(16);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1593,7 +1609,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(36);
     mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1652,7 +1668,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1696,7 +1712,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(26);
     mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1716,7 +1732,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1770,7 +1786,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1790,7 +1806,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(84);
     mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1848,7 +1864,7 @@
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1868,7 +1884,7 @@
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
                                                        sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne);
+                                                       sLayerNameOne, /*isBuffer*/ true);
     surfaceFrame2->setAcquireFenceTime(80);
     mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
     // Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
@@ -1923,7 +1939,8 @@
 
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1938,7 +1955,8 @@
     const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1947,7 +1965,8 @@
 
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1962,7 +1981,8 @@
     const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1971,7 +1991,8 @@
 
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+                                                       /*isBuffer*/ true);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1986,7 +2007,8 @@
     const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1995,7 +2017,8 @@
 
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+                                                       /*isBuffer*/ true);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2013,7 +2036,8 @@
     const auto sixHundredMs = std::chrono::nanoseconds(600ms).count();
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -2022,7 +2046,8 @@
 
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2031,7 +2056,8 @@
 
     auto surfaceFrame3 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+                                                       /*isBuffer*/ true);
     auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame3);
@@ -2040,7 +2066,8 @@
 
     auto surfaceFrame4 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame4);
@@ -2049,7 +2076,8 @@
 
     auto surfaceFrame5 =
             mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true);
     auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     // Dropped frames will be excluded from fps computation
     surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 363bd80..623a5e0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -116,6 +116,7 @@
         const auto surfaceFrame = layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
         commitTransaction(layer.get());
         EXPECT_EQ(1, surfaceFrame->getToken());
+        EXPECT_EQ(false, surfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
     }
 
@@ -139,6 +140,7 @@
         layer->updateTexImage(computeVisisbleRegions, 15, 0);
 
         EXPECT_EQ(1, surfaceFrame->getToken());
+        EXPECT_EQ(true, surfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
     }
 
@@ -172,12 +174,14 @@
         layer->updateTexImage(computeVisisbleRegions, 15, 0);
 
         EXPECT_EQ(1, droppedSurfaceFrame->getToken());
+        EXPECT_EQ(true, droppedSurfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame->getPresentState());
         EXPECT_EQ(0u, droppedSurfaceFrame->getActuals().endTime);
         auto dropTime = droppedSurfaceFrame->getDropTime();
         EXPECT_TRUE(dropTime > start && dropTime < end);
 
         EXPECT_EQ(1, presentedSurfaceFrame->getToken());
+        EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
     }
 
@@ -204,6 +208,7 @@
 
         commitTransaction(layer.get());
         EXPECT_EQ(1, surfaceFrame->getToken());
+        EXPECT_EQ(true, surfaceFrame->getIsBuffer());
         // Buffers are presented only at latch time.
         EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
 
@@ -260,12 +265,15 @@
         commitTransaction(layer.get());
 
         EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
 
         EXPECT_EQ(4, bufferlessSurfaceFrame2->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
 
         EXPECT_EQ(3, bufferSurfaceFrameTX->getToken());
+        EXPECT_EQ(true, bufferSurfaceFrameTX->getIsBuffer());
         // Buffers are presented only at latch time.
         EXPECT_EQ(PresentState::Unknown, bufferSurfaceFrameTX->getPresentState());
 
@@ -297,10 +305,12 @@
         commitTransaction(layer.get());
 
         EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
         EXPECT_EQ(10, bufferlessSurfaceFrame1->getActuals().endTime);
 
         EXPECT_EQ(2, bufferlessSurfaceFrame2->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
         EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
     }
@@ -327,9 +337,11 @@
         commitTransaction(layer.get());
 
         EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
         EXPECT_EQ(PresentState::Unknown, bufferlessSurfaceFrame1->getPresentState());
 
         EXPECT_EQ(1, bufferlessSurfaceFrame2->getToken());
+        EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
         EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
     }
@@ -410,20 +422,85 @@
         layer->updateTexImage(computeVisisbleRegions, 15, 0);
 
         EXPECT_EQ(1, droppedSurfaceFrame1->getToken());
+        EXPECT_EQ(true, droppedSurfaceFrame1->getIsBuffer());
         EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame1->getPresentState());
         EXPECT_EQ(0u, droppedSurfaceFrame1->getActuals().endTime);
         auto dropTime1 = droppedSurfaceFrame1->getDropTime();
         EXPECT_TRUE(dropTime1 > dropStartTime1 && dropTime1 < dropEndTime1);
 
         EXPECT_EQ(FrameTimelineInfo::INVALID_VSYNC_ID, droppedSurfaceFrame2->getToken());
+        EXPECT_EQ(true, droppedSurfaceFrame2->getIsBuffer());
         EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame2->getPresentState());
         EXPECT_EQ(0u, droppedSurfaceFrame2->getActuals().endTime);
         auto dropTime2 = droppedSurfaceFrame2->getDropTime();
         EXPECT_TRUE(dropTime2 > dropStartTime2 && dropTime2 < dropEndTime2);
 
         EXPECT_EQ(2, presentedSurfaceFrame->getToken());
+        EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
         EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
     }
+
+    void MultipleCommitsBeforeLatch() {
+        sp<BufferStateLayer> layer = createBufferStateLayer();
+        uint32_t surfaceFramesPendingClassification = 0;
+        std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
+        for (int i = 0; i < 10; i += 2) {
+            sp<Fence> fence1(new Fence());
+            sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+            layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+                             {/*vsyncId*/ 1, /*inputEventId*/ 0});
+            layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
+                                                                  /*inputEventId*/ 0},
+                                                                 10);
+            ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+            EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+            auto& bufferlessSurfaceFrame =
+                    layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2);
+            bufferlessSurfaceFrames.push_back(bufferlessSurfaceFrame);
+
+            commitTransaction(layer.get());
+            surfaceFramesPendingClassification += 2;
+            EXPECT_EQ(surfaceFramesPendingClassification,
+                      layer->mPendingJankClassifications.size());
+        }
+
+        auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
+        bool computeVisisbleRegions;
+        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
+        // Since we don't have access to DisplayFrame here, trigger an onPresent directly.
+        for (auto& surfaceFrame : bufferlessSurfaceFrames) {
+            surfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+                                    /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
+        }
+        presentedBufferSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+                                               /*displayDeadlineDelta*/ 0,
+                                               /*displayPresentDelta*/ 0);
+
+        // There should be 10 bufferlessSurfaceFrames and 1 bufferSurfaceFrame
+        ASSERT_EQ(10u, surfaceFramesPendingClassification);
+        ASSERT_EQ(surfaceFramesPendingClassification, layer->mPendingJankClassifications.size());
+
+        // For the frames upto 8, the bufferSurfaceFrame should have been dropped while the
+        // bufferlessSurfaceFrame presented
+        for (uint32_t i = 0; i < 8; i += 2) {
+            auto& bufferSurfaceFrame = layer->mPendingJankClassifications[i];
+            auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[i + 1];
+            EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Dropped);
+            EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+        }
+        {
+            auto& bufferSurfaceFrame = layer->mPendingJankClassifications[8u];
+            auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[9u];
+            EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Presented);
+            EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+        }
+
+        layer->releasePendingBuffer(25);
+
+        // There shouldn't be any pending classifications. Everything should have been cleared.
+        EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
+    }
 };
 
 TEST_F(TransactionSurfaceFrameTest, PresentedBufferlessSurfaceFrame) {
@@ -469,4 +546,8 @@
     BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer();
 }
 
+TEST_F(TransactionSurfaceFrameTest, MultipleCommitsBeforeLatch) {
+    MultipleCommitsBeforeLatch();
+}
+
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 7efd730..ddaa5a1 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -85,6 +85,7 @@
 
     void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
         ASSERT_NE(nullptr, mOutBuffer);
+        ASSERT_NE(nullptr, mPixels);
         ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
         TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
     }
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 537e49b..2de2e0e 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -209,6 +209,18 @@
     return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
 }
 
+HalResult<float> HalController::getResonantFrequency() {
+    hal_fn<float> getResonantFrequencyFn = [](std::shared_ptr<HalWrapper> hal) {
+        return hal->getResonantFrequency();
+    };
+    return apply(getResonantFrequencyFn, "getResonantFrequency");
+}
+
+HalResult<float> HalController::getQFactor() {
+    hal_fn<float> getQFactorFn = [](std::shared_ptr<HalWrapper> hal) { return hal->getQFactor(); };
+    return apply(getQFactorFn, "getQFactor");
+}
+
 HalResult<milliseconds> HalController::performEffect(
         Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 6faab38..3d20fa1 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -205,6 +205,17 @@
                                  mSupportedPrimitives);
 }
 
+HalResult<float> AidlHalWrapper::getResonantFrequency() {
+    std::lock_guard<std::mutex> lock(mResonantFrequencyMutex);
+    return loadCached<float>(std::bind(&AidlHalWrapper::getResonantFrequencyInternal, this),
+                             mResonantFrequency);
+}
+
+HalResult<float> AidlHalWrapper::getQFactor() {
+    std::lock_guard<std::mutex> lock(mQFactorMutex);
+    return loadCached<float>(std::bind(&AidlHalWrapper::getQFactorInternal, this), mQFactor);
+}
+
 HalResult<milliseconds> AidlHalWrapper::performEffect(
         Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
     HalResult<Capabilities> capabilities = getCapabilities();
@@ -283,6 +294,18 @@
     return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
 }
 
+HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
+    float f0 = 0;
+    auto result = getHal()->getResonantFrequency(&f0);
+    return HalResult<float>::fromStatus(result, f0);
+}
+
+HalResult<float> AidlHalWrapper::getQFactorInternal() {
+    float qFactor = 0;
+    auto result = getHal()->getQFactor(&qFactor);
+    return HalResult<float>::fromStatus(result, qFactor);
+}
+
 sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
     std::lock_guard<std::mutex> lock(mHandleMutex);
     return mHandle;
@@ -366,6 +389,18 @@
 }
 
 template <typename I>
+HalResult<float> HidlHalWrapper<I>::getResonantFrequency() {
+    ALOGV("Skipped getResonantFrequency because Vibrator HAL AIDL is not available");
+    return HalResult<float>::unsupported();
+}
+
+template <typename I>
+HalResult<float> HidlHalWrapper<I>::getQFactor() {
+    ALOGV("Skipped getQFactor because Vibrator HAL AIDL is not available");
+    return HalResult<float>::unsupported();
+}
+
+template <typename I>
 HalResult<std::chrono::milliseconds> HidlHalWrapper<I>::performComposedEffect(
         const std::vector<CompositeEffect>&, const std::function<void()>&) {
     ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 16d571d..14ec7b2 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -73,6 +73,9 @@
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
             final override;
 
+    HalResult<float> getResonantFrequency() final override;
+    HalResult<float> getQFactor() final override;
+
     HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) final override;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index e22ad34..039a2d9 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -185,6 +185,9 @@
     virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
     getSupportedPrimitives() = 0;
 
+    virtual HalResult<float> getResonantFrequency() = 0;
+    virtual HalResult<float> getQFactor() = 0;
+
     virtual HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) = 0;
@@ -232,6 +235,9 @@
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
             override final;
 
+    HalResult<float> getResonantFrequency() override final;
+    HalResult<float> getQFactor() override final;
+
     HalResult<std::chrono::milliseconds> performEffect(
             hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
             const std::function<void()>& completionCallback) override final;
@@ -246,6 +252,8 @@
     std::mutex mCapabilitiesMutex;
     std::mutex mSupportedEffectsMutex;
     std::mutex mSupportedPrimitivesMutex;
+    std::mutex mResonantFrequencyMutex;
+    std::mutex mQFactorMutex;
     sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
     std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
     std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
@@ -254,6 +262,8 @@
             GUARDED_BY(mSupportedPrimitivesMutex);
     std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations
             GUARDED_BY(mSupportedPrimitivesMutex);
+    std::optional<float> mResonantFrequency GUARDED_BY(mResonantFrequencyMutex);
+    std::optional<float> mQFactor GUARDED_BY(mQFactorMutex);
 
     // Loads and caches from IVibrator.
     HalResult<std::chrono::milliseconds> getPrimitiveDuration(
@@ -263,6 +273,10 @@
     HalResult<Capabilities> getCapabilitiesInternal();
     HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
+
+    HalResult<float> getResonantFrequencyInternal();
+    HalResult<float> getQFactorInternal();
+
     sp<hardware::vibrator::IVibrator> getHal();
 };
 
@@ -293,6 +307,9 @@
     HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
             override final;
 
+    HalResult<float> getResonantFrequency() override final;
+    HalResult<float> getQFactor() override final;
+
     HalResult<std::chrono::milliseconds> performComposedEffect(
             const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
             const std::function<void()>& completionCallback) override final;
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index c4b39ed..0c39247 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -67,6 +67,10 @@
     MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
     MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
                 (override));
+
+    MOCK_METHOD(vibrator::HalResult<float>, getResonantFrequency, (), (override));
+    MOCK_METHOD(vibrator::HalResult<float>, getQFactor, (), (override));
+
     MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
                 (Effect effect, EffectStrength strength,
                  const std::function<void()>& completionCallback),
@@ -106,6 +110,8 @@
                             vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
                             vibrator::HalResult<std::vector<Effect>> effectsResult,
                             vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
+                            vibrator::HalResult<float> resonantFrequencyResult,
+                            vibrator::HalResult<float> qFactorResult,
                             vibrator::HalResult<milliseconds> durationResult) {
         EXPECT_CALL(*mMockHal.get(), ping())
                 .Times(Exactly(cardinality))
@@ -138,6 +144,12 @@
         EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
                 .Times(Exactly(cardinality))
                 .WillRepeatedly(Return(primitivesResult));
+        EXPECT_CALL(*mMockHal.get(), getResonantFrequency())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(resonantFrequencyResult));
+        EXPECT_CALL(*mMockHal.get(), getQFactor())
+                .Times(Exactly(cardinality))
+                .WillRepeatedly(Return(qFactorResult));
         EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
                 .Times(Exactly(cardinality))
                 .WillRepeatedly(Return(durationResult));
@@ -147,7 +159,7 @@
 
         if (cardinality > 1) {
             // One reconnection call after each failure.
-            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality));
+            EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(14 * cardinality));
         }
     }
 };
@@ -164,23 +176,21 @@
 }
 
 TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
-    std::vector<Effect> effects;
-    effects.push_back(Effect::CLICK);
-    effects.push_back(Effect::TICK);
-    std::vector<CompositePrimitive> primitives;
-    primitives.push_back(CompositePrimitive::CLICK);
-    primitives.push_back(CompositePrimitive::THUD);
-    std::vector<CompositeEffect> compositeEffects;
-    compositeEffects.push_back(
-            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
-    compositeEffects.push_back(
-            vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+    std::vector<Effect> effects = {Effect::CLICK, Effect::TICK};
+    std::vector<CompositePrimitive> primitives = {CompositePrimitive::CLICK,
+                                                  CompositePrimitive::THUD};
+    constexpr float F0 = 123.f;
+    constexpr float Q_FACTOR = 12.f;
+    const std::vector<CompositeEffect> compositeEffects =
+            {vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f),
+             vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)};
 
     setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
                        vibrator::HalResult<vibrator::Capabilities>::ok(
                                vibrator::Capabilities::ON_CALLBACK),
                        vibrator::HalResult<std::vector<Effect>>::ok(effects),
                        vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
+                       vibrator::HalResult<float>::ok(F0), vibrator::HalResult<float>::ok(Q_FACTOR),
                        vibrator::HalResult<milliseconds>::ok(100ms));
 
     ASSERT_TRUE(mController->ping().isOk());
@@ -203,6 +213,14 @@
     ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
     ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
 
+    auto getResonantFrequencyResult = mController->getResonantFrequency();
+    ASSERT_TRUE(getResonantFrequencyResult.isOk());
+    ASSERT_EQ(F0, getResonantFrequencyResult.value());
+
+    auto getQFactorResult = mController->getQFactor();
+    ASSERT_TRUE(getQFactorResult.isOk());
+    ASSERT_EQ(Q_FACTOR, getQFactorResult.value());
+
     auto performEffectResult =
             mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
     ASSERT_TRUE(performEffectResult.isOk());
@@ -222,6 +240,8 @@
                        vibrator::HalResult<vibrator::Capabilities>::unsupported(),
                        vibrator::HalResult<std::vector<Effect>>::unsupported(),
                        vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
+                       vibrator::HalResult<float>::unsupported(),
+                       vibrator::HalResult<float>::unsupported(),
                        vibrator::HalResult<milliseconds>::unsupported());
 
     ASSERT_EQ(0, mConnectCounter);
@@ -237,6 +257,8 @@
     ASSERT_TRUE(mController->getCapabilities().isUnsupported());
     ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
     ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+    ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
+    ASSERT_TRUE(mController->getQFactor().isUnsupported());
     ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
                         .isUnsupported());
     ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
@@ -251,6 +273,8 @@
                        vibrator::HalResult<vibrator::Capabilities>::failed("message"),
                        vibrator::HalResult<std::vector<Effect>>::failed("message"),
                        vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
+                       vibrator::HalResult<float>::failed("message"),
+                       vibrator::HalResult<float>::failed("message"),
                        vibrator::HalResult<milliseconds>::failed("message"));
 
     ASSERT_EQ(0, mConnectCounter);
@@ -265,6 +289,8 @@
     ASSERT_TRUE(mController->getCapabilities().isFailed());
     ASSERT_TRUE(mController->getSupportedEffects().isFailed());
     ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
+    ASSERT_TRUE(mController->getResonantFrequency().isFailed());
+    ASSERT_TRUE(mController->getQFactor().isFailed());
     ASSERT_TRUE(
             mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
     ASSERT_TRUE(
@@ -327,13 +353,15 @@
     ASSERT_TRUE(mController->getCapabilities().isUnsupported());
     ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
     ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+    ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
+    ASSERT_TRUE(mController->getQFactor().isUnsupported());
     ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
                         .isUnsupported());
     ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
                         .isUnsupported());
 
     // One connection attempt per api call.
-    ASSERT_EQ(13, mConnectCounter);
+    ASSERT_EQ(15, mConnectCounter);
 }
 
 TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 8b5caa5..5d77595 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -413,6 +413,82 @@
     ASSERT_EQ(supportedPrimitives, result.value());
 }
 
+TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyDoesNotCacheFailedResult) {
+    constexpr float F0 = 123.f;
+    EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+            .Times(Exactly(3))
+            .WillOnce(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getResonantFrequency().isUnsupported());
+    ASSERT_TRUE(mWrapper->getResonantFrequency().isFailed());
+
+    auto result = mWrapper->getResonantFrequency();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(F0, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyCachesResult) {
+    constexpr float F0 = 123.f;
+    EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getResonantFrequency();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(F0, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getResonantFrequency();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(F0, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorDoesNotCacheFailedResult) {
+    constexpr float Q_FACTOR = 123.f;
+    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+            .Times(Exactly(3))
+            .WillOnce(
+                    Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+
+    ASSERT_TRUE(mWrapper->getQFactor().isUnsupported());
+    ASSERT_TRUE(mWrapper->getQFactor().isFailed());
+
+    auto result = mWrapper->getQFactor();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(Q_FACTOR, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorCachesResult) {
+    constexpr float Q_FACTOR = 123.f;
+    EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mWrapper->getQFactor();
+            ASSERT_TRUE(result.isOk());
+            ASSERT_EQ(Q_FACTOR, result.value());
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    auto result = mWrapper->getQFactor();
+    ASSERT_TRUE(result.isOk());
+    ASSERT_EQ(Q_FACTOR, result.value());
+}
+
 TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
     {
         InSequence seq;