Merge "Fix nullptr dereference when reporting hung transaction" into udc-qpr-dev
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index c1a04dd..bc9cb16 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -125,6 +125,8 @@
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+
sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 180ed09..861fdc4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2750,6 +2750,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 7e652ac..516d159 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -481,6 +481,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);
+
/**
* Gets priority of the RenderEngine in SurfaceFlinger.
*/
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
index f684874..fd8ffe1 100644
--- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -1172,9 +1172,12 @@
String8 H2BGraphicBufferProducer::getConsumerName() const {
String8 lName;
- mBase->getConsumerName([&lName] (hidl_string const& name) {
- lName = name.c_str();
- });
+ status_t transStatus = toStatusT(
+ mBase->getConsumerName([&lName](hidl_string const& name) { lName = name.c_str(); }));
+ if (transStatus != NO_ERROR) {
+ ALOGE("getConsumerName failed to transact: %d", transStatus);
+ return String8("TransactFailed");
+ }
return lName;
}
diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
index 2f5b73c..ae00a26 100644
--- a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
@@ -437,6 +437,10 @@
[&bName](hidl_string const& name) {
bName = name.c_str();
});
+ if (!transResult.isOk()) {
+ LOG(ERROR) << "getConsumerName: corrupted transaction.";
+ return String8("TransactFailed");
+ }
return bName;
}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index c70197c..67915f9 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -151,6 +151,9 @@
MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
(const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (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 dbcbd3b..7c55100 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 d7910d2..a371daf 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -993,6 +993,15 @@
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 5683a92..0e6d8b1 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -185,6 +185,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 f0e62ae..a186ad4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3178,6 +3178,14 @@
}
mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
+
+ // If the layer had been updated a TextureView, this would make sure the present time could be
+ // same to TextureView update when it's a small dirty, and get the correct heuristic rate.
+ if (mFlinger->mScheduler->supportSmallDirtyDetection()) {
+ if (mDrawingState.useVsyncIdForRefreshRateSelection) {
+ mUsedVsyncIdForRefreshRateSelection = true;
+ }
+ }
return true;
}
@@ -3200,10 +3208,38 @@
mDrawingState.latchedVsyncId);
if (prediction.has_value()) {
ATRACE_FORMAT_INSTANT("predictedPresentTime");
+ mMaxTimeForUseVsyncId = prediction->presentTime +
+ scheduler::LayerHistory::kMaxPeriodForHistory.count();
return prediction->presentTime;
}
}
+ if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
+ return static_cast<nsecs_t>(0);
+ }
+
+ // If the layer is not an application and didn't set an explicit rate or desiredPresentTime,
+ // return "0" to tell the layer history that it will use the max refresh rate without
+ // calculating the adaptive rate.
+ if (mWindowType != WindowInfo::Type::APPLICATION &&
+ mWindowType != WindowInfo::Type::BASE_APPLICATION) {
+ return static_cast<nsecs_t>(0);
+ }
+
+ // Return the valid present time only when the layer potentially updated a TextureView so
+ // LayerHistory could heuristically calculate the rate if the UI is continually updating.
+ if (mUsedVsyncIdForRefreshRateSelection) {
+ const auto prediction =
+ mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
+ mDrawingState.latchedVsyncId);
+ if (prediction.has_value()) {
+ if (mMaxTimeForUseVsyncId >= prediction->presentTime) {
+ return prediction->presentTime;
+ }
+ mUsedVsyncIdForRefreshRateSelection = false;
+ }
+ }
+
return static_cast<nsecs_t>(0);
}();
@@ -3263,6 +3299,7 @@
mDrawingState.surfaceDamageRegion = surfaceDamage;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
+ setIsSmallDirty();
return true;
}
@@ -4310,6 +4347,26 @@
mLastLatchTime = latchTime;
}
+void Layer::setIsSmallDirty() {
+ if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
+ return;
+ }
+
+ if (mWindowType != WindowInfo::Type::APPLICATION &&
+ mWindowType != WindowInfo::Type::BASE_APPLICATION) {
+ return;
+ }
+ Rect bounds = mDrawingState.surfaceDamageRegion.getBounds();
+ if (!bounds.isValid()) {
+ return;
+ }
+
+ // 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(mOwnerUid,
+ bounds.getWidth() * bounds.getHeight());
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2fbbbdc..895d25a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -842,6 +842,14 @@
mutable bool contentDirty{false};
Region surfaceDamageRegion;
+ // True when the surfaceDamageRegion is recognized as a small area update.
+ bool mSmallDirty{false};
+ // Used to check if mUsedVsyncIdForRefreshRateSelection should be expired when it stop updating.
+ nsecs_t mMaxTimeForUseVsyncId = 0;
+ // True when DrawState.useVsyncIdForRefreshRateSelection previously set to true during updating
+ // buffer.
+ bool mUsedVsyncIdForRefreshRateSelection{false};
+
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
// the same.
@@ -904,6 +912,7 @@
.transform = getTransform(),
.setFrameRateVote = getFrameRateForLayerTree(),
.frameRateSelectionPriority = getFrameRateSelectionPriority(),
+ .isSmallDirty = mSmallDirty,
};
};
bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
@@ -917,6 +926,9 @@
// Exposed so SurfaceFlinger can assert that it's held
const sp<SurfaceFlinger> mFlinger;
+ // Check if the damage region is a small dirty.
+ void setIsSmallDirty();
+
protected:
// For unit tests
friend class TestableSurfaceFlinger;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index beaf972..6adffc9 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -320,4 +320,11 @@
return {LayerStatus::NotFound, nullptr};
}
+bool LayerHistory::isSmallDirtyArea(uint32_t dirtyArea, float threshold) const {
+ const float ratio = (float)dirtyArea / mDisplayArea;
+ const bool isSmallDirty = ratio <= threshold;
+ ATRACE_FORMAT_INSTANT("small dirty=%s, ratio=%.3f", isSmallDirty ? "true" : "false", ratio);
+ return isSmallDirty;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 69caf9f..6f07e3b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -43,6 +43,7 @@
class LayerHistory {
public:
using LayerVoteType = RefreshRateSelector::LayerVoteType;
+ static constexpr std::chrono::nanoseconds kMaxPeriodForHistory = 1s;
LayerHistory();
~LayerHistory();
@@ -87,6 +88,8 @@
void attachChoreographer(int32_t layerId,
const sp<EventThreadConnection>& choreographerConnection);
+ bool isSmallDirtyArea(uint32_t dirtyArea, float threshold) const;
+
private:
friend class LayerHistoryTest;
friend class TestableScheduler;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index bae3739..348e2b9 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -63,7 +63,8 @@
case LayerUpdateType::Buffer:
FrameTimeData frameTime = {.presentTime = lastPresentTime,
.queueTime = mLastUpdatedTime,
- .pendingModeChange = pendingModeChange};
+ .pendingModeChange = pendingModeChange,
+ .isSmallDirty = props.isSmallDirty};
mFrameTimes.push_back(frameTime);
if (mFrameTimes.size() > HISTORY_SIZE) {
mFrameTimes.pop_front();
@@ -99,11 +100,15 @@
// classification.
bool isFrequent = true;
bool isInfrequent = true;
+ int32_t smallDirtyCount = 0;
const auto n = mFrameTimes.size() - 1;
for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) {
if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime <
kMaxPeriodForFrequentLayerNs.count()) {
isInfrequent = false;
+ if (mFrameTimes[n - i].presentTime == 0 && mFrameTimes[n - i].isSmallDirty) {
+ smallDirtyCount++;
+ }
} else {
isFrequent = false;
}
@@ -113,7 +118,8 @@
// If the layer was previously inconclusive, we clear
// the history as indeterminate layers changed to frequent,
// and we should not look at the stale data.
- return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true};
+ return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true,
+ /* isSmallDirty */ smallDirtyCount >= kNumSmallDirtyThreshold};
}
// If we can't determine whether the layer is frequent or not, we return
@@ -202,6 +208,7 @@
nsecs_t totalDeltas = 0;
int numDeltas = 0;
+ int32_t smallDirtyCount = 0;
auto prevFrame = mFrameTimes.begin();
for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) {
const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame);
@@ -210,6 +217,13 @@
continue;
}
+ // If this is a small area update, we don't want to consider it for calculating the average
+ // frame time. Instead, we let the bigger frame updates to drive the calculation.
+ if (it->isSmallDirty && currDelta < kMinPeriodBetweenSmallDirtyFrames) {
+ smallDirtyCount++;
+ continue;
+ }
+
prevFrame = it;
if (currDelta > kMaxPeriodBetweenFrames) {
@@ -221,6 +235,10 @@
numDeltas++;
}
+ if (smallDirtyCount > 0) {
+ ATRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount);
+ }
+
if (numDeltas == 0) {
return std::nullopt;
}
@@ -295,6 +313,13 @@
clearHistory(now);
}
+ // Return no vote if the latest frames are small dirty.
+ if (frequent.isSmallDirty && !mLastRefreshRate.reported.isValid()) {
+ ATRACE_FORMAT_INSTANT("NoVote (small dirty)");
+ ALOGV("%s is small dirty", mName.c_str());
+ return {LayerHistory::LayerVoteType::NoVote, Fps()};
+ }
+
auto refreshRate = calculateRefreshRateIfPossible(selector, now);
if (refreshRate.has_value()) {
ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index c5a6057..6a85806 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -57,6 +57,7 @@
static constexpr Fps kMinFpsForFrequentLayer = 10_Hz;
static constexpr auto kMaxPeriodForFrequentLayerNs =
std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms;
+ static constexpr size_t kNumSmallDirtyThreshold = 2;
friend class LayerHistoryTest;
friend class LayerInfoTest;
@@ -195,6 +196,7 @@
nsecs_t presentTime; // desiredPresentTime, if provided
nsecs_t queueTime; // buffer queue time
bool pendingModeChange;
+ bool isSmallDirty;
};
// Holds information about the calculated and reported refresh rate
@@ -259,6 +261,8 @@
bool clearHistory;
// Represents whether we were able to determine isFrequent conclusively
bool isConclusive;
+ // Represents whether the latest frames are small dirty.
+ bool isSmallDirty = false;
};
Frequent isFrequent(nsecs_t now) const;
bool isAnimating(nsecs_t now) const;
@@ -277,6 +281,11 @@
// this period apart from each other, the interval between them won't be
// taken into account when calculating average frame rate.
static constexpr nsecs_t kMaxPeriodBetweenFrames = kMinFpsForFrequentLayer.getPeriodNsecs();
+ // Used for sanitizing the heuristic data. If frames are small dirty updating and are less
+ // than this period apart from each other, the interval between them won't be
+ // taken into account when calculating average frame rate.
+ static constexpr nsecs_t kMinPeriodBetweenSmallDirtyFrames = (60_Hz).getPeriodNsecs();
+
LayerHistory::LayerVoteType mDefaultVote;
LayerVote mLayerVote;
@@ -291,7 +300,7 @@
std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
std::chrono::steady_clock::now();
static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
- static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+ static constexpr std::chrono::nanoseconds HISTORY_DURATION = LayerHistory::kMaxPeriodForHistory;
std::unique_ptr<LayerProps> mLayerProps;
@@ -309,6 +318,7 @@
ui::Transform transform;
LayerInfo::FrameRate setFrameRateVote;
int32_t frameRateSelectionPriority = -1;
+ bool isSmallDirty = false;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0b6ce80..68927bd 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -1048,4 +1048,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 b913700..20cc77f 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"
@@ -289,6 +290,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);
@@ -305,6 +313,11 @@
return mLayerHistory.getLayerFramerate(now, id);
}
+ // Returns true if the small dirty detection is enabled.
+ bool supportSmallDirtyDetection() const {
+ return mFeatures.test(Feature::kSmallDirtyContentDetection);
+ }
+
private:
friend class TestableScheduler;
@@ -529,6 +542,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/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
index 200407d..7c72ac6 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -28,6 +28,7 @@
kContentDetection = 1 << 2,
kTracePredictedVsync = 1 << 3,
kBackpressureGpuComposition = 1 << 4,
+ kSmallDirtyContentDetection = 1 << 5,
};
using FeatureFlags = ftl::Flags<Feature>;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5e5ceb6..4928a5c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3928,6 +3928,9 @@
if (sysprop::use_content_detection_for_refresh_rate(false)) {
features |= Feature::kContentDetection;
+ if (base::GetBoolProperty("debug.sf.enable_small_dirty_detection"s, false)) {
+ features |= Feature::kSmallDirtyContentDetection;
+ }
}
if (base::GetBoolProperty("debug.sf.show_predicted_vsync"s, false)) {
features |= Feature::kTracePredictedVsync;
@@ -7841,6 +7844,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) {
@@ -7968,6 +7982,15 @@
void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
mScheduler->onActiveDisplayAreaChanged(activeDisplay.getWidth() * activeDisplay.getHeight());
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
+
+ // Notify layers to update small dirty flag.
+ if (mScheduler->supportSmallDirtyDetection()) {
+ mCurrentState.traverse([&](Layer* layer) {
+ if (layer->getLayerStack() == activeDisplay.getLayerStack()) {
+ layer->setIsSmallDirty();
+ }
+ });
+ }
}
sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const {
@@ -9107,6 +9130,40 @@
return binderStatusFromStatusT(status);
}
+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 f1989db..427e6bb 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -614,6 +614,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;
@@ -1547,6 +1551,9 @@
const sp<IBinder>& displayToken,
std::optional<gui::DisplayDecorationSupport>* outSupport) override;
binder::Status setOverrideFrameRate(int32_t uid, float frameRate) 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 86af303..ceb69df 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -100,6 +100,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/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 85d86a7..be4b026 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -959,6 +959,77 @@
recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
}
+TEST_F(LayerHistoryTest, smallDirtyLayer) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ LayerHistory::Summary summary;
+
+ // layer is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ auto props = layer->getLayerProps();
+ if (i % 3 == 0) {
+ props.isSmallDirty = false;
+ } else {
+ props.isSmallDirty = true;
+ }
+
+ history().record(layer->getSequence(), props, time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_GE(HI_FPS, summary[0].desiredRefreshRate);
+}
+
+TEST_F(LayerHistoryTest, smallDirtyInMultiLayer) {
+ auto layer1 = createLayer("UI");
+ auto layer2 = createLayer("Video");
+
+ EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer2, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(30_Hz, Layer::FrameRateCompatibility::Default)));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(2, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ LayerHistory::Summary summary;
+
+ // layer1 is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ auto props = layer1->getLayerProps();
+ props.isSmallDirty = true;
+ history().record(layer1->getSequence(), props, 0 /*presentTime*/, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
+}
+
class LayerHistoryTestParameterized : public LayerHistoryTest,
public testing::WithParamInterface<std::chrono::nanoseconds> {
};
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