Change small area detection to support new front end design

This will still use legacy layer to calculate the small dirty, and
update the info to the LayerSnapshot.

Also fix some issues in setIsSmallDirty() for
1. Set mSmallDirty flag as false if the surface damage region is
   invalid.
2. Apply the scaling to the damage region before dirty area
   calculations.

Bug: 295062543
Bug: 303258910
Bug: 303609027
Test: atest LayerHistoryTest
Test: atest SmallAreaDetectionTest
Change-Id: Ib0e3737e63b5653422b5b5054b893578dc63f768
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 7f3171f..48f8923 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -421,6 +421,17 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setDamageRegion(uint32_t id, const Region& damageRegion) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void setDataspace(uint32_t id, ui::Dataspace dataspace) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
@@ -432,6 +443,19 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
+        layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
+
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eMatrixChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.matrix = matrix;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void setShadowRadius(uint32_t id, float shadowRadius) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index a462082..631adf1 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -866,6 +866,104 @@
     }
 }
 
+class SmallAreaDetectionTest : public LayerHistoryIntegrationTest {
+protected:
+    static constexpr int32_t DISPLAY_WIDTH = 100;
+    static constexpr int32_t DISPLAY_HEIGHT = 100;
+
+    static constexpr int32_t kAppId1 = 10100;
+    static constexpr int32_t kAppId2 = 10101;
+
+    static constexpr float kThreshold1 = 0.05f;
+    static constexpr float kThreshold2 = 0.07f;
+
+    SmallAreaDetectionTest() : LayerHistoryIntegrationTest() {
+        std::vector<std::pair<int32_t, float>> mappings;
+        mappings.reserve(2);
+        mappings.push_back(std::make_pair(kAppId1, kThreshold1));
+        mappings.push_back(std::make_pair(kAppId2, kThreshold2));
+
+        mFlinger.enableNewFrontEnd();
+
+        mScheduler->onActiveDisplayAreaChanged(DISPLAY_WIDTH * DISPLAY_HEIGHT);
+        mScheduler->updateSmallAreaDetection(mappings);
+    }
+
+    auto createLegacyAndFrontedEndLayer(uint32_t sequence) {
+        std::string layerName = "test layer:" + std::to_string(sequence);
+
+        LayerCreationArgs args = LayerCreationArgs{mFlinger.flinger(),
+                                                   nullptr,
+                                                   layerName,
+                                                   0,
+                                                   {},
+                                                   std::make_optional<uint32_t>(sequence)};
+        args.ownerUid = kAppId1;
+        args.metadata.setInt32(gui::METADATA_WINDOW_TYPE, 2); // APPLICATION
+        const auto layer = sp<Layer>::make(args);
+        mFlinger.injectLegacyLayer(layer);
+        createRootLayer(sequence);
+        return layer;
+    }
+};
+
+TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+    setBuffer(sequence);
+    setDamageRegion(sequence, Region(Rect(10, 10)));
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    ASSERT_EQ(true, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
+TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+    setBuffer(sequence);
+    setDamageRegion(sequence, Region(Rect(50, 50)));
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    ASSERT_EQ(false, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
+TEST_F(SmallAreaDetectionTest, smallDirtyLayerWithMatrix) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // Original damage region is a small dirty.
+    uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+    setBuffer(sequence);
+    setDamageRegion(sequence, Region(Rect(20, 20)));
+    updateLayerSnapshotsAndLayerHistory(time);
+    ASSERT_EQ(true, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+
+    setMatrix(sequence, 2.0f, 0, 0, 2.0f);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    // Verify if the small dirty is scaled.
+    ASSERT_EQ(false, mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->isSmallDirty);
+}
+
 INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryIntegrationTestParameterized,
                         ::testing::Values(1s, 2s, 3s, 4s, 5s));
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 014d07c..3d1c900 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -44,7 +44,10 @@
     TestableScheduler(std::unique_ptr<VsyncController> controller,
                       std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
                       sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
-          : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
+          : Scheduler(*this, callback,
+                      (FeatureFlags)Feature::kContentDetection |
+                              Feature::kSmallDirtyContentDetection,
+                      std::move(modulatorPtr)) {
         const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
         registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
                         std::move(tracker));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3b39420..8f1982d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -670,6 +670,11 @@
         return mFlinger->initTransactionTraceWriter();
     }
 
+    void enableNewFrontEnd() {
+        mFlinger->mLayerLifecycleManagerEnabled = true;
+        mFlinger->mLegacyFrontEndEnabled = false;
+    }
+
     ~TestableSurfaceFlinger() {
         // All these pointer and container clears help ensure that GMock does
         // not report a leaked object, since the SurfaceFlinger instance may