SF: Move/Refactor updateCursorAsync to CompositionEngine

As part of this change, the existing LayerFE::latchCompositionState is
extended to allow for more state types to be fetched.

Test: atest libsurfaceflinger_unittest libcompositionengine_test
Bug: 121291683
Change-Id: I71e0f547440a64c1025f36741beb68e72c18e475
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 31d6365..298aff5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -58,6 +58,9 @@
     virtual bool needsAnotherUpdate() const = 0;
     virtual nsecs_t getLastFrameRefreshTimestamp() const = 0;
 
+    // Updates the cursor position for the indicated outputs.
+    virtual void updateCursorAsync(CompositionRefreshArgs&) = 0;
+
     // TODO(b/121291683): These will become private/internal
     virtual void preComposition(CompositionRefreshArgs&) = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 2a901ae..db4f969 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -43,6 +43,10 @@
     // geometry state can be skipped.
     virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0;
 
+    // Latches the minimal bit of state for the cursor for a fast asynchronous
+    // update.
+    virtual void latchCursorCompositionState(LayerFECompositionState&) const = 0;
+
     struct ClientCompositionTargetSettings {
         // The clip region, or visible region that is being rendered to
         const Region& clip;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index d5763d5..6a0caf0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -122,6 +122,13 @@
 
     // True if the layer has protected content
     bool hasProtectedContent{false};
+
+    /*
+     * Cursor state
+     */
+
+    // The output-independent frame for the cursor
+    Rect cursorFrame;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 5f62b32c..cedd728 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -78,12 +78,18 @@
     // skipped.
     virtual void writeStateToHWC(bool includeGeometry) = 0;
 
+    // Updates the cursor position with the HWC
+    virtual void writeCursorPositionToHWC() const = 0;
+
     // Returns the HWC2::Layer associated with this layer, if it exists
     virtual HWC2::Layer* getHwcLayer() const = 0;
 
     // Returns true if the current layer state requires client composition
     virtual bool requiresClientComposition() const = 0;
 
+    // Returns true if the current layer should be treated as a cursor layer
+    virtual bool isHardwareCursor() const = 0;
+
     // Applies a HWC device requested composition type change
     virtual void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 96e609d..982a376 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -39,6 +39,8 @@
     bool needsAnotherUpdate() const override;
     nsecs_t getLastFrameRefreshTimestamp() const override;
 
+    void updateCursorAsync(CompositionRefreshArgs&) override;
+
     void preComposition(CompositionRefreshArgs&) override;
 
     // Testing
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 4c3f935..fa4d8cd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -49,9 +49,11 @@
 
     void updateCompositionState(bool) override;
     void writeStateToHWC(bool) override;
+    void writeCursorPositionToHWC() const override;
 
     HWC2::Layer* getHwcLayer() const override;
     bool requiresClientComposition() const override;
+    bool isHardwareCursor() const override;
     void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) override;
     void prepareForDeviceLayerRequests() override;
     void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 82ecec5..6450b22 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -44,6 +44,7 @@
     MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
     MOCK_CONST_METHOD0(getLastFrameRefreshTimestamp, nsecs_t());
 
+    MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&));
     MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 48c2dbf..e280295 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -33,6 +33,7 @@
     MOCK_METHOD1(onPreComposition, bool(nsecs_t));
 
     MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool));
+    MOCK_CONST_METHOD1(latchCursorCompositionState, void(LayerFECompositionState&));
     MOCK_METHOD1(prepareClientComposition,
                  std::optional<renderengine::LayerSettings>(
                          compositionengine::LayerFE::ClientCompositionTargetSettings&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index d8d637d..6b2224a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -40,9 +40,11 @@
 
     MOCK_METHOD1(updateCompositionState, void(bool));
     MOCK_METHOD1(writeStateToHWC, void(bool));
+    MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
 
     MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
     MOCK_CONST_METHOD0(requiresClientComposition, bool());
+    MOCK_CONST_METHOD0(isHardwareCursor, bool());
     MOCK_METHOD1(applyDeviceCompositionTypeChange, void(Hwc2::IComposerClient::Composition));
     MOCK_METHOD0(prepareForDeviceLayerRequests, void());
     MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 9558266..8bc3a34 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -16,6 +16,7 @@
 
 #include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/CompositionEngine.h>
 #include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/Layer.h>
@@ -70,6 +71,22 @@
     return mRefreshStartTime;
 }
 
+void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) {
+    std::unordered_map<compositionengine::LayerFE*, compositionengine::LayerFECompositionState*>
+            uniqueVisibleLayers;
+
+    for (const auto& output : args.outputs) {
+        for (auto& layer : output->getOutputLayersOrderedByZ()) {
+            if (layer->isHardwareCursor()) {
+                // Latch the cursor composition state from each front-end layer.
+                layer->getLayerFE().latchCursorCompositionState(
+                        layer->getLayer().editState().frontEnd);
+                layer->writeCursorPositionToHWC();
+            }
+        }
+    }
+}
+
 void CompositionEngine::preComposition(CompositionRefreshArgs& args) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index e721cf5..73bb03b 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -550,6 +550,27 @@
     }
 }
 
+void OutputLayer::writeCursorPositionToHWC() const {
+    // Skip doing this if there is no HWC interface
+    auto hwcLayer = getHwcLayer();
+    if (!hwcLayer) {
+        return;
+    }
+
+    const auto& layerFEState = mLayer->getState().frontEnd;
+    const auto& outputState = mOutput.getState();
+
+    Rect frame = layerFEState.cursorFrame;
+    frame.intersect(outputState.viewport, &frame);
+    Rect position = outputState.transform.transform(frame);
+
+    if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set cursor position to (%d, %d): %s (%d)", mLayerFE->getDebugName(),
+              position.left, position.top, to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+}
+
 HWC2::Layer* OutputLayer::getHwcLayer() const {
     return mState.hwc ? mState.hwc->hwcLayer.get() : nullptr;
 }
@@ -559,6 +580,11 @@
             mState.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;
 }
 
+bool OutputLayer::isHardwareCursor() const {
+    return mState.hwc &&
+            mState.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CURSOR;
+}
+
 void OutputLayer::detectDisallowedCompositionTypeChange(
         Hwc2::IComposerClient::Composition from, Hwc2::IComposerClient::Composition to) const {
     bool result = false;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index c83cae6..75e960c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -782,6 +782,61 @@
 }
 
 /*
+ * OutputLayer::writeCursorPositionToHWC()
+ */
+
+struct OutputLayerWriteCursorPositionToHWCTest : public OutputLayerTest {
+    static constexpr int kDefaultTransform = TR_IDENT;
+    static constexpr HWC2::Error kDefaultError = HWC2::Error::Unsupported;
+
+    static const Rect kDefaultDisplayViewport;
+    static const Rect kDefaultCursorFrame;
+
+    OutputLayerWriteCursorPositionToHWCTest() {
+        auto& outputLayerState = mOutputLayer.editState();
+        outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
+
+        mLayerState.frontEnd.cursorFrame = kDefaultCursorFrame;
+
+        mOutputState.viewport = kDefaultDisplayViewport;
+        mOutputState.transform = ui::Transform{kDefaultTransform};
+    }
+
+    std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
+};
+
+const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultDisplayViewport{0, 0, 1920, 1080};
+const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultCursorFrame{1, 2, 3, 4};
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCHandlesNoHwcState) {
+    mOutputLayer.editState().hwc.reset();
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCWritesStateToHWC) {
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(1, 2)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCIntersectedWithViewport) {
+    mLayerState.frontEnd.cursorFrame = Rect{3000, 3000, 3016, 3016};
+
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(1920, 1080)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCRotatedByTransform) {
+    mOutputState.transform = ui::Transform{TR_ROT_90};
+
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(-4, 1)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+/*
  * OutputLayer::getHwcLayer()
  */
 
@@ -829,6 +884,30 @@
 }
 
 /*
+ * OutputLayer::isHardwareCursor()
+ */
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfNoHWC2State) {
+    mOutputLayer.editState().hwc.reset();
+
+    EXPECT_FALSE(mOutputLayer.isHardwareCursor());
+}
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsTrueIfSetToCursorComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CURSOR;
+
+    EXPECT_TRUE(mOutputLayer.isHardwareCursor());
+}
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfSetToDeviceComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    EXPECT_FALSE(mOutputLayer.isHardwareCursor());
+}
+
+/*
  * OutputLayer::applyDeviceCompositionTypeChange()
  */
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5121835..557d0bb 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -464,6 +464,21 @@
     }
 }
 
+void Layer::latchCursorCompositionState(
+        compositionengine::LayerFECompositionState& compositionState) const {
+    // This gives us only the "orientation" component of the transform
+    const State& drawingState{getDrawingState()};
+
+    // Apply the layer's transform, followed by the display's global transform
+    // Here we're guaranteed that the layer's transform preserves rects
+    Rect win = getCroppedBufferSize(drawingState);
+    // Subtract the transparent region and snap to the bounds
+    Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
+    Rect frame(getTransform().transform(bounds));
+
+    compositionState.cursorFrame = frame;
+}
+
 bool Layer::onPreComposition(nsecs_t) {
     return false;
 }
@@ -481,38 +496,6 @@
     return mName.string();
 }
 
-void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-
-    if (!outputLayer->getState().hwc ||
-        (*outputLayer->getState().hwc).hwcCompositionType !=
-                Hwc2::IComposerClient::Composition::CURSOR) {
-        return;
-    }
-
-    // This gives us only the "orientation" component of the transform
-    const State& s(getDrawingState());
-
-    // Apply the layer's transform, followed by the display's global transform
-    // Here we're guaranteed that the layer's transform preserves rects
-    Rect win = getCroppedBufferSize(s);
-    // Subtract the transparent region and snap to the bounds
-    Rect bounds = reduce(win, getActiveTransparentRegion(s));
-    Rect frame(getTransform().transform(bounds));
-    frame.intersect(display->getViewport(), &frame);
-    auto& displayTransform = display->getTransform();
-    auto position = displayTransform.transform(frame);
-
-    auto error =
-            (*outputLayer->getState().hwc).hwcLayer->setCursorPosition(position.left, position.top);
-    ALOGE_IF(error != HWC2::Error::None,
-             "[%s] Failed to set cursor position "
-             "to (%d, %d): %s (%d)",
-             mName.string(), position.left, position.top, to_string(error).c_str(),
-             static_cast<int32_t>(error));
-}
-
 // ---------------------------------------------------------------------------
 // drawing...
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index aa3970e..23763d2 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -476,6 +476,7 @@
     bool onPreComposition(nsecs_t) override;
     void latchCompositionState(compositionengine::LayerFECompositionState&,
                                bool includeGeometry) const override;
+    void latchCursorCompositionState(compositionengine::LayerFECompositionState&) const override;
     std::optional<renderengine::LayerSettings> prepareClientComposition(
             compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
@@ -493,7 +494,6 @@
     Hwc2::IComposerClient::Composition getCompositionType(
             const sp<const DisplayDevice>& display) const;
     bool getClearClientTarget(const sp<const DisplayDevice>& display) const;
-    void updateCursorPosition(const sp<const DisplayDevice>& display);
 
     virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
     virtual void setTransformHint(uint32_t /*orientation*/) const { }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 83f3b2c..8760bb0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2715,15 +2715,14 @@
 
 void SurfaceFlinger::updateCursorAsync()
 {
-    for (const auto& [token, display] : mDisplays) {
-        if (!display->getId()) {
-            continue;
-        }
-
-        for (auto& layer : display->getVisibleLayersSortedByZ()) {
-            layer->updateCursorPosition(display);
+    compositionengine::CompositionRefreshArgs refreshArgs;
+    for (const auto& [_, display] : mDisplays) {
+        if (display->getId()) {
+            refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
+
+    mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
 void SurfaceFlinger::commitTransaction()