VRR: Allowlist for small area detection

Add a allowlist mechanism for suppress frame rate when small area.

In this patch, we will keep a array list to record pkg name which
want to apply the small area detection for suppressing frame rate
and its threshold value of small area ratio.

In framewokr, we will check the all pkg uid by pkg name from
package manager and call a native function which include uid and
threshold.

In SF native, it get the threshold of uid and we will save them
as a map, and it will used to check should it apply small area
detection and what threshold it should use.

Bug: 281720315
Test: atest SmallAreaDetectionMappingsTest
Test: atest LayerHistoryTest
Change-Id: Iaf6c0090f9db499fc5ed097b2d3c6d9d871d4812
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 92589c5..4719e24 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2768,6 +2768,20 @@
     return statusTFromBinderStatus(status);
 }
 
+status_t SurfaceComposerClient::updateSmallAreaDetection(std::vector<int32_t>& uids,
+                                                         std::vector<float>& thresholds) {
+    binder::Status status =
+            ComposerServiceAIDL::getComposerService()->updateSmallAreaDetection(uids, thresholds);
+    return statusTFromBinderStatus(status);
+}
+
+status_t SurfaceComposerClient::setSmallAreaDetectionThreshold(uid_t uid, float threshold) {
+    binder::Status status =
+            ComposerServiceAIDL::getComposerService()->setSmallAreaDetectionThreshold(uid,
+                                                                                      threshold);
+    return statusTFromBinderStatus(status);
+}
+
 void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
     ComposerServiceAIDL::getComposerService()->setAutoLowLatencyMode(display, on);
 }
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index c2f47fc..1c604a1 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -479,6 +479,15 @@
      */
     void setOverrideFrameRate(int uid, float frameRate);
 
+    oneway void updateSmallAreaDetection(in int[] uids, in float[] thresholds);
+
+    /**
+     * Set the small area detection threshold for a specified uid by SmallAreaDetectionController.
+     * Passing the threshold and uid to SurfaceFlinger to update the uid-threshold mapping
+     * in the scheduler.
+     */
+    oneway void setSmallAreaDetectionThreshold(int uid, float threshold);
+
     /**
      * Enables or disables the frame rate overlay in the top left corner.
      * Requires root or android.permission.HARDWARE_TEST
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 2643fa7..177d5f8 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -154,6 +154,9 @@
     MOCK_METHOD(binder::Status, setDebugFlash, (int), (override));
     MOCK_METHOD(binder::Status, scheduleComposite, (), (override));
     MOCK_METHOD(binder::Status, scheduleCommit, (), (override));
+    MOCK_METHOD(binder::Status, updateSmallAreaDetection,
+                (const std::vector<int32_t>&, const std::vector<float>&), (override));
+    MOCK_METHOD(binder::Status, setSmallAreaDetectionThreshold, (int32_t, float), (override));
     MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
     MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
     MOCK_METHOD(binder::Status, addWindowInfosListener,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index fd9f186..6fef5d2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -203,6 +203,16 @@
     // by GameManager.
     static status_t setOverrideFrameRate(uid_t uid, float frameRate);
 
+    // Update the small area detection whole uid-threshold mappings by same size uid and threshold
+    // vector.
+    // Ref:setSmallAreaDetectionThreshold.
+    static status_t updateSmallAreaDetection(std::vector<int32_t>& uids,
+                                             std::vector<float>& thresholds);
+
+    // Sets the small area detection threshold to particular apps (uid). Passing value 0 means
+    // to disable small area detection to the app.
+    static status_t setSmallAreaDetectionThreshold(uid_t uid, float threshold);
+
     // Switches on/off Auto Low Latency Mode on the connected display. This should only be
     // called if the connected display supports Auto Low Latency Mode as reported by
     // #getAutoLowLatencyModeSupport
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index ffb8622..daed764 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -999,6 +999,15 @@
 
     binder::Status scheduleCommit() override { return binder::Status::ok(); }
 
+    binder::Status updateSmallAreaDetection(const std::vector<int32_t>& /*uids*/,
+                                            const std::vector<float>& /*thresholds*/) {
+        return binder::Status::ok();
+    }
+
+    binder::Status setSmallAreaDetectionThreshold(int32_t /*uid*/, float /*threshold*/) {
+        return binder::Status::ok();
+    }
+
     binder::Status getGpuContextPriority(int32_t* /*outPriority*/) override {
         return binder::Status::ok();
     }
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 0101c17..eda52bf 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -187,6 +187,7 @@
         "Scheduler/MessageQueue.cpp",
         "Scheduler/RefreshRateSelector.cpp",
         "Scheduler/Scheduler.cpp",
+        "Scheduler/SmallAreaDetectionAllowMappings.cpp",
         "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncPredictor.cpp",
         "Scheduler/VSyncReactor.cpp",
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8f785a0..077ed67 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -4388,7 +4388,8 @@
 
     // If the damage region is a small dirty, this could give the hint for the layer history that
     // it could suppress the heuristic rate when calculating.
-    mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(bounds.getWidth() * bounds.getHeight());
+    mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerUid,
+                                                         bounds.getWidth() * bounds.getHeight());
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 13cf764..c92e670 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -306,9 +306,9 @@
     return {LayerStatus::NotFound, nullptr};
 }
 
-bool LayerHistory::isSmallDirtyArea(uint32_t dirtyArea) const {
+bool LayerHistory::isSmallDirtyArea(uint32_t dirtyArea, float threshold) const {
     const float ratio = (float)dirtyArea / mDisplayArea;
-    const bool isSmallDirty = ratio <= kSmallDirtyArea;
+    const bool isSmallDirty = ratio <= threshold;
     ATRACE_FORMAT_INSTANT("small dirty=%s, ratio=%.3f", isSmallDirty ? "true" : "false", ratio);
     return isSmallDirty;
 }
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 562bb4c..5750ea7 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -85,14 +85,12 @@
     // return the frames per second of the layer with the given sequence id.
     float getLayerFramerate(nsecs_t now, int32_t id) const;
 
-    bool isSmallDirtyArea(uint32_t dirtyArea) const;
+    bool isSmallDirtyArea(uint32_t dirtyArea, float threshold) const;
 
 private:
     friend class LayerHistoryTest;
     friend class TestableScheduler;
 
-    static constexpr float kSmallDirtyArea = 0.07f;
-
     using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
     // keyed by id as returned from Layer::getSequence()
     using LayerInfos = std::unordered_map<int32_t, LayerPair>;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5a19ec5..27c96f7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -1179,4 +1179,20 @@
     mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
 }
 
+void Scheduler::updateSmallAreaDetection(
+        std::vector<std::pair<uid_t, float>>& uidThresholdMappings) {
+    mSmallAreaDetectionAllowMappings.update(uidThresholdMappings);
+}
+
+void Scheduler::setSmallAreaDetectionThreshold(uid_t uid, float threshold) {
+    mSmallAreaDetectionAllowMappings.setThesholdForUid(uid, threshold);
+}
+
+bool Scheduler::isSmallDirtyArea(uid_t uid, uint32_t dirtyArea) {
+    std::optional<float> oThreshold = mSmallAreaDetectionAllowMappings.getThresholdForUid(uid);
+    if (oThreshold) return mLayerHistory.isSmallDirtyArea(dirtyArea, oThreshold.value());
+
+    return false;
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ec20610..d65df2a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -49,6 +49,7 @@
 #include "MessageQueue.h"
 #include "OneShotTimer.h"
 #include "RefreshRateSelector.h"
+#include "SmallAreaDetectionAllowMappings.h"
 #include "Utils/Dumper.h"
 #include "VsyncModulator.h"
 
@@ -291,6 +292,13 @@
 
     void setGameModeRefreshRateForUid(FrameRateOverride);
 
+    void updateSmallAreaDetection(std::vector<std::pair<uid_t, float>>& uidThresholdMappings);
+
+    void setSmallAreaDetectionThreshold(uid_t uid, float threshold);
+
+    // Returns true if the dirty area is less than threshold.
+    bool isSmallDirtyArea(uid_t uid, uint32_t dirtyArea);
+
     // Retrieves the overridden refresh rate for a given uid.
     std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
 
@@ -314,11 +322,6 @@
         return mFeatures.test(Feature::kSmallDirtyContentDetection);
     }
 
-    // Returns true if the dirty area is less than threshold.
-    bool isSmallDirtyArea(uint32_t dirtyArea) const {
-        return mLayerHistory.isSmallDirtyArea(dirtyArea);
-    }
-
 private:
     friend class TestableScheduler;
 
@@ -557,6 +560,7 @@
     static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 
     FrameRateOverrideMappings mFrameRateOverrideMappings;
+    SmallAreaDetectionAllowMappings mSmallAreaDetectionAllowMappings;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp
new file mode 100644
index 0000000..95cd5d1
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 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 <sys/types.h>
+
+#include "SmallAreaDetectionAllowMappings.h"
+
+namespace android::scheduler {
+void SmallAreaDetectionAllowMappings::update(
+        std::vector<std::pair<uid_t, float>>& uidThresholdMappings) {
+    std::lock_guard lock(mLock);
+    mMap.clear();
+    for (std::pair<uid_t, float> row : uidThresholdMappings) {
+        if (!isValidThreshold(row.second)) continue;
+
+        mMap.emplace(row.first, row.second);
+    }
+}
+
+void SmallAreaDetectionAllowMappings::setThesholdForUid(uid_t uid, float threshold) {
+    if (!isValidThreshold(threshold)) return;
+
+    std::lock_guard lock(mLock);
+    mMap.emplace(uid, threshold);
+}
+
+std::optional<float> SmallAreaDetectionAllowMappings::getThresholdForUid(uid_t uid) {
+    std::lock_guard lock(mLock);
+    const auto iter = mMap.find(uid);
+    if (iter != mMap.end()) {
+        return iter->second;
+    }
+    return std::nullopt;
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h
new file mode 100644
index 0000000..cbab690
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <sys/types.h>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+namespace android::scheduler {
+class SmallAreaDetectionAllowMappings {
+    using UidThresholdMap = std::unordered_map<uid_t, float>;
+
+public:
+    void update(std::vector<std::pair<uid_t, float>>& uidThresholdMappings);
+    void setThesholdForUid(uid_t uid, float threshold) EXCLUDES(mLock);
+    std::optional<float> getThresholdForUid(uid_t uid) EXCLUDES(mLock);
+
+private:
+    static bool isValidThreshold(float threshold) { return threshold >= 0.0f && threshold <= 1.0f; }
+    mutable std::mutex mLock;
+    UidThresholdMap mMap GUARDED_BY(mLock);
+};
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 940a4c6..bc626f3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -8200,6 +8200,17 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::updateSmallAreaDetection(
+        std::vector<std::pair<uid_t, float>>& uidThresholdMappings) {
+    mScheduler->updateSmallAreaDetection(uidThresholdMappings);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setSmallAreaDetectionThreshold(uid_t uid, float threshold) {
+    mScheduler->setSmallAreaDetectionThreshold(uid, threshold);
+    return NO_ERROR;
+}
+
 void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
     bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
     for (const auto& [id, display] : mPhysicalDisplays) {
@@ -9532,6 +9543,40 @@
     return binder::Status::ok();
 }
 
+binder::Status SurfaceComposerAIDL::updateSmallAreaDetection(const std::vector<int32_t>& uids,
+                                                             const std::vector<float>& thresholds) {
+    status_t status;
+    const int c_uid = IPCThreadState::self()->getCallingUid();
+    if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
+        if (uids.size() != thresholds.size()) return binderStatusFromStatusT(BAD_VALUE);
+
+        std::vector<std::pair<uid_t, float>> mappings;
+        const size_t size = uids.size();
+        mappings.reserve(size);
+        for (int i = 0; i < size; i++) {
+            auto row = std::make_pair(static_cast<uid_t>(uids[i]), thresholds[i]);
+            mappings.push_back(row);
+        }
+        status = mFlinger->updateSmallAreaDetection(mappings);
+    } else {
+        ALOGE("updateSmallAreaDetection() permission denied for uid: %d", c_uid);
+        status = PERMISSION_DENIED;
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setSmallAreaDetectionThreshold(int32_t uid, float threshold) {
+    status_t status;
+    const int c_uid = IPCThreadState::self()->getCallingUid();
+    if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
+        status = mFlinger->setSmallAreaDetectionThreshold(uid, threshold);
+    } else {
+        ALOGE("setSmallAreaDetectionThreshold() permission denied for uid: %d", c_uid);
+        status = PERMISSION_DENIED;
+    }
+    return binderStatusFromStatusT(status);
+}
+
 binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) {
     *outPriority = mFlinger->getGpuContextPriority();
     return binder::Status::ok();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6ff9fd1..79dcd0d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -607,6 +607,10 @@
 
     status_t setOverrideFrameRate(uid_t uid, float frameRate);
 
+    status_t updateSmallAreaDetection(std::vector<std::pair<uid_t, float>>& uidThresholdMappings);
+
+    status_t setSmallAreaDetectionThreshold(uid_t uid, float threshold);
+
     int getGpuContextPriority();
 
     status_t getMaxAcquiredBufferCount(int* buffers) const;
@@ -1557,6 +1561,9 @@
     binder::Status setDebugFlash(int delay) override;
     binder::Status scheduleComposite() override;
     binder::Status scheduleCommit() override;
+    binder::Status updateSmallAreaDetection(const std::vector<int32_t>& uids,
+                                            const std::vector<float>& thresholds) override;
+    binder::Status setSmallAreaDetectionThreshold(int32_t uid, float threshold) override;
     binder::Status getGpuContextPriority(int32_t* outPriority) override;
     binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
     binder::Status addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener,
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7d8796f..8deff85 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -101,6 +101,7 @@
         "LayerTestUtils.cpp",
         "MessageQueueTest.cpp",
         "PowerAdvisorTest.cpp",
+        "SmallAreaDetectionAllowMappingsTest.cpp",
         "SurfaceFlinger_CreateDisplayTest.cpp",
         "SurfaceFlinger_DestroyDisplayTest.cpp",
         "SurfaceFlinger_DisplayModeSwitching.cpp",
diff --git a/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp b/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp
new file mode 100644
index 0000000..b910485
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 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 "SmallAreaDetectionAllowMappingsTest"
+
+#include <gtest/gtest.h>
+
+#include "Scheduler/SmallAreaDetectionAllowMappings.h"
+
+namespace android::scheduler {
+
+class SmallAreaDetectionMappingsAllowTest : public testing::Test {
+protected:
+    SmallAreaDetectionAllowMappings mMappings;
+};
+
+namespace {
+TEST_F(SmallAreaDetectionMappingsAllowTest, testUpdate) {
+    const uid_t uid1 = 10100;
+    const uid_t uid2 = 10101;
+    const float threshold1 = 0.05f;
+    const float threshold2 = 0.07f;
+    std::vector<std::pair<uid_t, float>> mappings;
+    mappings.reserve(2);
+    mappings.push_back(std::make_pair(uid1, threshold1));
+    mappings.push_back(std::make_pair(uid2, threshold2));
+
+    mMappings.update(mappings);
+    ASSERT_EQ(mMappings.getThresholdForUid(uid1).value(), threshold1);
+    ASSERT_EQ(mMappings.getThresholdForUid(uid2).value(), threshold2);
+}
+
+TEST_F(SmallAreaDetectionMappingsAllowTest, testSetThesholdForUid) {
+    const uid_t uid = 10111;
+    const float threshold = 0.05f;
+
+    mMappings.setThesholdForUid(uid, threshold);
+    ASSERT_EQ(mMappings.getThresholdForUid(uid), threshold);
+}
+
+TEST_F(SmallAreaDetectionMappingsAllowTest, testUidNotInTheMappings) {
+    const uid_t uid = 10222;
+    ASSERT_EQ(mMappings.getThresholdForUid(uid), std::nullopt);
+}
+
+} // namespace
+} // namespace android::scheduler