Merge "Prevent waiting synchronous case if transaction applied first" into sc-dev
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index f1b2258..3e5674e 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -33,6 +33,7 @@
 #include <unordered_map>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/result.h>
 #include <android-base/unique_fd.h>
 
 #include <binder/IBinder.h>
@@ -374,20 +375,24 @@
      */
     status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting);
 
+    struct Finished {
+        uint32_t seq;
+        bool handled;
+        nsecs_t consumeTime;
+    };
+
     /* Receives the finished signal from the consumer in reply to the original dispatch signal.
-     * If a signal was received, returns the message sequence number,
-     * whether the consumer handled the message, and the time the event was first read by the
-     * consumer.
+     * If a signal was received, returns a Finished object.
      *
      * The returned sequence number is never 0 unless the operation failed.
      *
-     * Returns OK on success.
-     * Returns WOULD_BLOCK if there is no signal present.
-     * Returns DEAD_OBJECT if the channel's peer has been closed.
-     * Other errors probably indicate that the channel is broken.
+     * Returned error codes:
+     *         OK on success.
+     *         WOULD_BLOCK if there is no signal present.
+     *         DEAD_OBJECT if the channel's peer has been closed.
+     *         Other errors probably indicate that the channel is broken.
      */
-    status_t receiveFinishedSignal(
-            const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback);
+    android::base::Result<Finished> receiveFinishedSignal();
 
 private:
     std::shared_ptr<InputChannel> mChannel;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 6ef0173..c2a3cf1 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -629,24 +629,26 @@
     return mChannel->sendMessage(&msg);
 }
 
-status_t InputPublisher::receiveFinishedSignal(
-        const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback) {
+android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedSignal() {
     if (DEBUG_TRANSPORT_ACTIONS) {
-        ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+        ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
     }
 
     InputMessage msg;
     status_t result = mChannel->receiveMessage(&msg);
     if (result) {
-        return result;
+        return android::base::Error(result);
     }
     if (msg.header.type != InputMessage::Type::FINISHED) {
         ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
               mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
-        return UNKNOWN_ERROR;
+        return android::base::Error(UNKNOWN_ERROR);
     }
-    callback(msg.header.seq, msg.body.finished.handled, msg.body.finished.consumeTime);
-    return OK;
+    return Finished{
+            .seq = msg.header.seq,
+            .handled = msg.body.finished.handled,
+            .consumeTime = msg.body.finished.consumeTime,
+    };
 }
 
 // --- InputConsumer ---
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index b5ed8d7..fc31715 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -27,6 +27,8 @@
 #include <utils/StopWatch.h>
 #include <utils/Timers.h>
 
+using android::base::Result;
+
 namespace android {
 
 class InputPublisherAndConsumerTest : public testing::Test {
@@ -122,23 +124,13 @@
     ASSERT_EQ(OK, status)
             << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = false;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status)
-            << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
+            << "receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(result->handled)
+            << "receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -272,23 +264,13 @@
     ASSERT_EQ(OK, status)
             << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = true;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status)
-            << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_FALSE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
+            << "receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_FALSE(result->handled)
+            << "receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -322,22 +304,14 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = false;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+
+    ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
+            << "receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(result->handled)
+            << "receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -369,22 +343,13 @@
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = false;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
-            << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(handled)
-            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
+            << "receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(result->handled)
+            << "receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
@@ -410,32 +375,23 @@
     ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
             << "consumer should have returned a drag event";
 
-    DragEvent* dragEvent = static_cast<DragEvent*>(event);
+    const DragEvent& dragEvent = static_cast<const DragEvent&>(*event);
     EXPECT_EQ(seq, consumeSeq);
-    EXPECT_EQ(eventId, dragEvent->getId());
-    EXPECT_EQ(isExiting, dragEvent->isExiting());
-    EXPECT_EQ(x, dragEvent->getX());
-    EXPECT_EQ(y, dragEvent->getY());
+    EXPECT_EQ(eventId, dragEvent.getId());
+    EXPECT_EQ(isExiting, dragEvent.isExiting());
+    EXPECT_EQ(x, dragEvent.getX());
+    EXPECT_EQ(y, dragEvent.getY());
 
     status = mConsumer->sendFinishedSignal(seq, true);
     ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
 
-    uint32_t finishedSeq = 0;
-    bool handled = false;
-    nsecs_t consumeTime;
-    status = mPublisher->receiveFinishedSignal(
-            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
-                                                   nsecs_t inConsumeTime) -> void {
-                finishedSeq = inSeq;
-                handled = inHandled;
-                consumeTime = inConsumeTime;
-            });
-    ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
-    ASSERT_EQ(seq, finishedSeq)
+    android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+    ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, result->seq)
             << "publisher receiveFinishedSignal should have returned the original sequence number";
-    ASSERT_TRUE(handled)
+    ASSERT_TRUE(result->handled)
             << "publisher receiveFinishedSignal should have set handled to consumer's reply";
-    ASSERT_GE(consumeTime, publishTime)
+    ASSERT_GE(result->consumeTime, publishTime)
             << "finished signal's consume time should be greater than publish time";
 }
 
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/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3e80bd7..fe46d17 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -80,6 +80,7 @@
 #define INDENT4 "        "
 
 using android::base::HwTimeoutMultiplier;
+using android::base::Result;
 using android::base::StringPrintf;
 using android::os::BlockUntrustedTouchesMode;
 using android::os::IInputConstants;
@@ -3283,17 +3284,17 @@
 
             nsecs_t currentTime = now();
             bool gotOne = false;
-            status_t status;
+            status_t status = OK;
             for (;;) {
-                std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
-                        std::bind(&InputDispatcher::finishDispatchCycleLocked, d, currentTime,
-                                  connection, std::placeholders::_1, std::placeholders::_2,
-                                  std::placeholders::_3);
-
-                status = connection->inputPublisher.receiveFinishedSignal(callback);
-                if (status) {
+                Result<InputPublisher::Finished> result =
+                        connection->inputPublisher.receiveFinishedSignal();
+                if (!result.ok()) {
+                    status = result.error().code();
                     break;
                 }
+                const InputPublisher::Finished& finished = *result;
+                d->finishDispatchCycleLocked(currentTime, connection, finished.seq,
+                                             finished.handled, finished.consumeTime);
                 gotOne = true;
             }
             if (gotOne) {
@@ -4998,8 +4999,7 @@
     }
 }
 
-base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(
-        const std::string& name) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
 #if DEBUG_CHANNEL_CREATION
     ALOGD("channel '%s' ~ createInputChannel", name.c_str());
 #endif
@@ -5028,8 +5028,10 @@
     return clientChannel;
 }
 
-base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
-        int32_t displayId, bool isGestureMonitor, const std::string& name, int32_t pid) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId,
+                                                                          bool isGestureMonitor,
+                                                                          const std::string& name,
+                                                                          int32_t pid) {
     std::shared_ptr<InputChannel> serverChannel;
     std::unique_ptr<InputChannel> clientChannel;
     status_t result = openInputChannelPair(name, serverChannel, clientChannel);
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 18d0226..743587c 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -67,6 +67,7 @@
     cflags: ["-Wall", "-Werror"],
 
     shared_libs: [
+        "libbase",
         "libbinder",
         "libinputflingerhost",
         "libutils",
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 e25f1c7..96a0c3c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -257,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;
@@ -310,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;
@@ -781,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;
@@ -848,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);
 }
 
@@ -865,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/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 1c47691..d297d74 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -105,6 +105,7 @@
     srcs: [
         "tests/planner/CachedSetTest.cpp",
         "tests/planner/FlattenerTest.cpp",
+        "tests/planner/LayerStateTest.cpp",
         "tests/CompositionEngineTest.cpp",
         "tests/DisplayColorProfileTest.cpp",
         "tests/DisplayTest.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index d19ac62..7f8cb4e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -40,6 +40,7 @@
 
 // clang-format off
 enum class LayerStateField : uint32_t {
+    None            = 0u,
     Id              = 1u << 0,
     Name            = 1u << 1,
     DisplayFrame    = 1u << 2,
@@ -173,8 +174,12 @@
     // Returns which fields were updated
     Flags<LayerStateField> update(compositionengine::OutputLayer*);
 
+    // Computes a hash for this LayerState.
+    // The hash is only computed from NonUniqueFields.
     size_t getHash(Flags<LayerStateField> skipFields) const;
 
+    // Returns the bit-set of differing fields between this LayerState and another LayerState.
+    // This bit-set is based on NonUniqueFields only
     Flags<LayerStateField> getDifferingFields(const LayerState& other,
                                               Flags<LayerStateField> skipFields) const;
 
@@ -280,19 +285,7 @@
 
     // Output-independent per-frame state
 
-    OutputLayerState<mat4, LayerStateField::ColorTransform>
-            mColorTransform{[](auto layer) {
-                                const auto state = layer->getLayerFE().getCompositionState();
-                                return state->colorTransformIsIdentity ? mat4{}
-                                                                       : state->colorTransform;
-                            },
-                            [](const mat4& mat) {
-                                using namespace std::string_literals;
-                                std::vector<std::string> split =
-                                        base::Split(std::string(mat.asString().string()), "\n"s);
-                                split.pop_back(); // Strip the last (empty) line
-                                return split;
-                            }};
+    OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
 
     // TODO(b/180638831): Surface damage
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 7cf4819..f3746de 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -16,9 +16,28 @@
 
 #include <compositionengine/impl/planner/LayerState.h>
 
+namespace {
+extern "C" const char* __attribute__((unused)) __asan_default_options() {
+    return "detect_container_overflow=0";
+}
+} // namespace
+
 namespace android::compositionengine::impl::planner {
 
-LayerState::LayerState(compositionengine::OutputLayer* layer) : mOutputLayer(layer) {
+LayerState::LayerState(compositionengine::OutputLayer* layer)
+      : mOutputLayer(layer),
+        mColorTransform({[](auto layer) {
+                             const auto state = layer->getLayerFE().getCompositionState();
+                             return state->colorTransformIsIdentity ? mat4{}
+                                                                    : state->colorTransform;
+                         },
+                         [](const mat4& mat) {
+                             using namespace std::string_literals;
+                             std::vector<std::string> split =
+                                     base::Split(std::string(mat.asString().string()), "\n"s);
+                             split.pop_back(); // Strip the last (empty) line
+                             return split;
+                         }}) {
     update(layer);
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
new file mode 100644
index 0000000..8f235ab
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -0,0 +1,1020 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerStateTest"
+
+#include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/OutputLayer.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+namespace android::compositionengine::impl::planner {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+
+const std::string sDebugName = std::string("Test LayerFE");
+const std::string sDebugNameTwo = std::string("Test LayerFE2");
+const constexpr int32_t sSequenceId = 12345;
+const constexpr int32_t sSequenceIdTwo = 123456;
+const Rect sRectOne = Rect(10, 20, 30, 40);
+const Rect sRectTwo = Rect(40, 30, 20, 10);
+const FloatRect sFloatRectOne = FloatRect(100.f, 200.f, 300.f, 400.f);
+const FloatRect sFloatRectTwo = FloatRect(400.f, 300.f, 200.f, 100.f);
+const constexpr int32_t sZOne = 100;
+const constexpr int32_t sZTwo = 101;
+const constexpr float sAlphaOne = 0.25f;
+const constexpr float sAlphaTwo = 0.5f;
+const Region sRegionOne = Region(sRectOne);
+const Region sRegionTwo = Region(sRectTwo);
+const mat4 sMat4One = mat4::scale(vec4(2.f, 3.f, 1.f, 1.f));
+native_handle_t* const sFakeSidebandStreamOne = reinterpret_cast<native_handle_t*>(10);
+native_handle_t* const sFakeSidebandStreamTwo = reinterpret_cast<native_handle_t*>(11);
+const half4 sHalf4One = half4(0.2f, 0.3f, 0.4f, 0.5f);
+const half4 sHalf4Two = half4(0.5f, 0.4f, 0.43, 0.2f);
+
+struct LayerStateTest : public testing::Test {
+    LayerStateTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~LayerStateTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    void setupMocksForLayer(mock::OutputLayer& layer, mock::LayerFE& layerFE,
+                            const OutputLayerCompositionState& outputLayerState,
+                            const LayerFECompositionState& layerFEState,
+                            int32_t sequenceId = sSequenceId,
+                            const std::string& debugName = sDebugName) {
+        EXPECT_CALL(layer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+        EXPECT_CALL(layer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+        EXPECT_CALL(layerFE, getSequence()).WillRepeatedly(Return(sequenceId));
+        EXPECT_CALL(layerFE, getDebugName()).WillRepeatedly(Return(debugName.c_str()));
+        EXPECT_CALL(layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+    }
+
+    mock::LayerFE mLayerFE;
+    mock::OutputLayer mOutputLayer;
+    std::unique_ptr<LayerState> mLayerState;
+};
+
+TEST_F(LayerStateTest, getOutputLayer) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(&mOutputLayer, mLayerState->getOutputLayer());
+}
+
+TEST_F(LayerStateTest, getId) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(sSequenceId, mLayerState->getId());
+}
+
+TEST_F(LayerStateTest, updateId) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionState, sSequenceIdTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(sSequenceIdTwo, mLayerState->getId());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Id), updates);
+}
+
+TEST_F(LayerStateTest, compareId) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionState, sSequenceIdTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getId(), otherLayerState->getId());
+
+    // Id is a unique field, so it's not computed in the hash for a layer state.
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Id),
+              otherLayerState->getHash(LayerStateField::Id));
+
+    // Similarly, Id cannot be included in differing fields.
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Id));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Id));
+
+    EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+    EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getName) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(sDebugName, mLayerState->getName());
+}
+
+TEST_F(LayerStateTest, updateName) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionState, sSequenceId, sDebugNameTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(sDebugNameTwo, mLayerState->getName());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Name), updates);
+}
+
+TEST_F(LayerStateTest, compareName) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionState, sSequenceId, sDebugNameTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getName(), otherLayerState->getName());
+
+    // Name is a unique field, so it's not computed in the hash for a layer state.
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Name),
+              otherLayerState->getHash(LayerStateField::Name));
+
+    // Similarly, Name cannot be included in differing fields.
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Name));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Name));
+
+    EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+    EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getDisplayFrame) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(sRectOne, mLayerState->getDisplayFrame());
+}
+
+TEST_F(LayerStateTest, updateDisplayFrame) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(sRectTwo, mLayerState->getDisplayFrame());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame), updates);
+}
+
+TEST_F(LayerStateTest, compareDisplayFrame) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getDisplayFrame(), otherLayerState->getDisplayFrame());
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::DisplayFrame),
+              otherLayerState->getHash(LayerStateField::DisplayFrame));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::DisplayFrame));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::DisplayFrame),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::DisplayFrame));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getCompositionType) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.compositionType =
+            hardware::graphics::composer::hal::Composition::DEVICE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(hardware::graphics::composer::hal::Composition::DEVICE,
+              mLayerState->getCompositionType());
+}
+
+TEST_F(LayerStateTest, getCompositionType_forcedClient) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.forceClientComposition = true;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.compositionType =
+            hardware::graphics::composer::hal::Composition::DEVICE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(hardware::graphics::composer::hal::Composition::CLIENT,
+              mLayerState->getCompositionType());
+}
+
+TEST_F(LayerStateTest, updateCompositionType) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.compositionType =
+            hardware::graphics::composer::hal::Composition::DEVICE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.compositionType =
+            hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(hardware::graphics::composer::hal::Composition::SOLID_COLOR,
+              mLayerState->getCompositionType());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType), updates);
+}
+
+TEST_F(LayerStateTest, compareCompositionType) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.compositionType =
+            hardware::graphics::composer::hal::Composition::DEVICE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.compositionType =
+            hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getCompositionType(), otherLayerState->getCompositionType());
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::CompositionType),
+              otherLayerState->getHash(LayerStateField::CompositionType));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::CompositionType));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::CompositionType));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, getBuffer) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    EXPECT_EQ(layerFECompositionState.buffer, mLayerState->getBuffer());
+}
+
+TEST_F(LayerStateTest, updateBuffer) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(layerFECompositionStateTwo.buffer, mLayerState->getBuffer());
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates);
+}
+
+TEST_F(LayerStateTest, compareBuffer) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getBuffer(), otherLayerState->getBuffer());
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Buffer),
+              otherLayerState->getHash(LayerStateField::Buffer));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Buffer));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Buffer));
+
+    // Buffers are explicitly excluded from comparison
+    EXPECT_FALSE(mLayerState->compare(*otherLayerState));
+    EXPECT_FALSE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSourceCrop) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.sourceCrop = sFloatRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop), updates);
+}
+
+TEST_F(LayerStateTest, compareSourceCrop) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.sourceCrop = sFloatRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::SourceCrop),
+              otherLayerState->getHash(LayerStateField::SourceCrop));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SourceCrop));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SourceCrop));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateZOrder) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.z = sZOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.z = sZTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder), updates);
+}
+
+TEST_F(LayerStateTest, compareZOrder) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.z = sZOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.z = sZTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::ZOrder),
+              otherLayerState->getHash(LayerStateField::ZOrder));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ZOrder));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ZOrder),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ZOrder));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBufferTransform) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform), updates);
+}
+
+TEST_F(LayerStateTest, compareBufferTransform) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::BufferTransform),
+              otherLayerState->getHash(LayerStateField::BufferTransform));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BufferTransform));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BufferTransform));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateBlendMode) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.blendMode = hal::BlendMode::COVERAGE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode), updates);
+}
+
+TEST_F(LayerStateTest, compareBlendMode) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.blendMode = hal::BlendMode::COVERAGE;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::BlendMode),
+              otherLayerState->getHash(LayerStateField::BlendMode));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::BlendMode));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::BlendMode));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateAlpha) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.alpha = sAlphaOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.alpha = sAlphaTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha), updates);
+}
+
+TEST_F(LayerStateTest, compareAlpha) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.alpha = sAlphaOne;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.alpha = sAlphaTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Alpha),
+              otherLayerState->getHash(LayerStateField::Alpha));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Alpha));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Alpha));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateVisibleRegion) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.visibleRegion = sRegionOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.visibleRegion = sRegionTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion), updates);
+}
+
+TEST_F(LayerStateTest, compareVisibleRegion) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.visibleRegion = sRegionOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.visibleRegion = sRegionTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::VisibleRegion),
+              otherLayerState->getHash(LayerStateField::VisibleRegion));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::VisibleRegion));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::VisibleRegion));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateDataspace) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.dataspace = ui::Dataspace::SRGB;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace), updates);
+}
+
+TEST_F(LayerStateTest, compareDataspace) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.dataspace = ui::Dataspace::SRGB;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::Dataspace),
+              otherLayerState->getHash(LayerStateField::Dataspace));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::Dataspace));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::Dataspace));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateColorTransform) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.colorTransformIsIdentity = true;
+    layerFECompositionState.colorTransform = mat4();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.colorTransformIsIdentity = false;
+    layerFECompositionStateTwo.colorTransform = sMat4One;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform), updates);
+}
+
+TEST_F(LayerStateTest, compareColorTransform) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.colorTransformIsIdentity = true;
+    layerFECompositionState.colorTransform = mat4();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.colorTransformIsIdentity = false;
+    layerFECompositionStateTwo.colorTransform = sMat4One;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::ColorTransform),
+              otherLayerState->getHash(LayerStateField::ColorTransform));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::ColorTransform));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::ColorTransform));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSidebandStream) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false);
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false);
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream), updates);
+}
+
+TEST_F(LayerStateTest, compareSidebandStream) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false);
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false);
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::SidebandStream),
+              otherLayerState->getHash(LayerStateField::SidebandStream));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SidebandStream));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SidebandStream));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, updateSolidColor) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.color = sHalf4One;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.color = sHalf4Two;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor), updates);
+}
+
+TEST_F(LayerStateTest, compareSolidColor) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.color = sHalf4One;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.color = sHalf4Two;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(mLayerState->getHash(LayerStateField::None),
+              otherLayerState->getHash(LayerStateField::None));
+    EXPECT_EQ(mLayerState->getHash(LayerStateField::SolidColor),
+              otherLayerState->getHash(LayerStateField::SolidColor));
+
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              mLayerState->getDifferingFields(*otherLayerState, LayerStateField::SolidColor));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::None));
+    EXPECT_EQ(Flags<LayerStateField>(LayerStateField::None),
+              otherLayerState->getDifferingFields(*mLayerState, LayerStateField::SolidColor));
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
+TEST_F(LayerStateTest, dumpDoesNotCrash) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    std::string dump;
+    mLayerState->dump(dump);
+    EXPECT_TRUE(dump.size() > 0);
+}
+
+TEST_F(LayerStateTest, framesSinceBufferUpdate) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    EXPECT_EQ(0, mLayerState->getFramesSinceBufferUpdate());
+    mLayerState->incrementFramesSinceBufferUpdate();
+    EXPECT_EQ(1, mLayerState->getFramesSinceBufferUpdate());
+    mLayerState->resetFramesSinceBufferUpdate();
+    EXPECT_EQ(0, mLayerState->getFramesSinceBufferUpdate());
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_doesNotCommute) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_NE(getNonBufferHash({mLayerState.get(), otherLayerState.get()}),
+              getNonBufferHash({otherLayerState.get(), mLayerState.get()}));
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_isIdempotent) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    outputLayerCompositionState.displayFrame = sRectOne;
+    LayerFECompositionState layerFECompositionState;
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    OutputLayerCompositionState outputLayerCompositionStateTwo;
+    outputLayerCompositionStateTwo.displayFrame = sRectTwo;
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo,
+                       layerFECompositionState);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_EQ(getNonBufferHash({mLayerState.get(), otherLayerState.get()}),
+              getNonBufferHash({mLayerState.get(), otherLayerState.get()}));
+}
+
+TEST_F(LayerStateTest, getNonBufferHash_filtersOutBuffers) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.buffer = new GraphicBuffer();
+    setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    mock::LayerFE newLayerFE;
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    EXPECT_EQ(getNonBufferHash({mLayerState.get()}), getNonBufferHash({otherLayerState.get()}));
+}
+
+} // namespace
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 38e9b65..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);
@@ -2443,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);
@@ -2472,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 227d523..0994954 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3372,7 +3372,7 @@
 
 bool SurfaceFlinger::transactionFlushNeeded() {
     Mutex::Autolock _l(mQueueLock);
-    return !mPendingTransactionQueues.empty();
+    return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
 }
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(
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/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;