Add frame rate flexibility token
Add support for temporarily relaxing frame rate restrictions in surface
flinger. This is used by CTS tests to get a consistent device state
while running frame rate tests.
Bug: 148033900
Test: - On a Pixel 4, I turned the brightness down and covered the
ambient light sensor, causing the display manager to set a frame rate
restriction. I ran the frame rate CTS test without these CLs applied,
and confirmed the test failed because surface flinger couldn't switch
frame rates, as expected. Then I ran the tests with the CLs applied, and
confirmed the tests pass.
- I confirmed that, without adopting shell permission identity, the CTS
test is denied the request to acquire a frame rate flexibility token. So
normal apps won't be able to access this.
Change-Id: I6685edc4bc07c7888b79a9dd72a90f56b74e7604
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 8d79cf8..bd4d62c 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1145,6 +1145,42 @@
ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
return err;
}
+
+ return reply.readInt32();
+ }
+
+ virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) {
+ if (!outToken) return BAD_VALUE;
+
+ Parcel data, reply;
+ status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ ALOGE("acquireFrameRateFlexibilityToken: failed writing interface token: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+
+ err = remote()->transact(BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, data,
+ &reply);
+ if (err != NO_ERROR) {
+ ALOGE("acquireFrameRateFlexibilityToken: failed to transact: %s (%d)", strerror(-err),
+ err);
+ return err;
+ }
+
+ err = reply.readInt32();
+ if (err != NO_ERROR) {
+ ALOGE("acquireFrameRateFlexibilityToken: call failed: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ err = reply.readStrongBinder(outToken);
+ if (err != NO_ERROR) {
+ ALOGE("acquireFrameRateFlexibilityToken: failed reading binder token: %s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+
return NO_ERROR;
}
};
@@ -1945,6 +1981,16 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> token;
+ status_t result = acquireFrameRateFlexibilityToken(&token);
+ reply->writeInt32(result);
+ if (result == NO_ERROR) {
+ reply->writeStrongBinder(token);
+ }
+ return NO_ERROR;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 09487ea..3cef256 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -508,6 +508,14 @@
*/
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
int8_t compatibility) = 0;
+
+ /*
+ * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
+ * surface flinger will freely switch between frame rates in any way it sees fit, regardless of
+ * the current restrictions applied by DisplayManager. This is useful to get consistent behavior
+ * for tests. Release the token by releasing the returned IBinder reference.
+ */
+ virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
};
// ----------------------------------------------------------------------------
@@ -566,6 +574,7 @@
GET_GAME_CONTENT_TYPE_SUPPORT,
SET_GAME_CONTENT_TYPE,
SET_FRAME_RATE,
+ ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
// Always append new enum to the end.
};
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 8c0f8f8..ef1fd02 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -859,6 +859,8 @@
return NO_ERROR;
}
+ status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) { return NO_ERROR; }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 02d0b53..14ef733 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -301,7 +301,7 @@
mCurrentRefreshRate) != mAvailableRefreshRates.end()) {
return *mCurrentRefreshRate;
}
- return *mRefreshRates.at(mDefaultConfig);
+ return *mRefreshRates.at(getCurrentPolicyLocked()->defaultConfig);
}
void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
@@ -326,38 +326,59 @@
init(inputConfigs, currentConfigId);
}
-status_t RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
- float maxRefreshRate, bool* outPolicyChanged) {
+bool RefreshRateConfigs::isPolicyValid(const Policy& policy) {
+ // defaultConfig must be a valid config, and within the given refresh rate range.
+ auto iter = mRefreshRates.find(policy.defaultConfig);
+ if (iter == mRefreshRates.end()) {
+ return false;
+ }
+ const RefreshRate& refreshRate = *iter->second;
+ if (!refreshRate.inPolicy(policy.minRefreshRate, policy.maxRefreshRate)) {
+ return false;
+ }
+ return true;
+}
+
+status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
std::lock_guard lock(mLock);
- bool policyChanged = defaultConfigId != mDefaultConfig ||
- minRefreshRate != mMinRefreshRateFps || maxRefreshRate != mMaxRefreshRateFps;
- if (outPolicyChanged) {
- *outPolicyChanged = policyChanged;
- }
- if (!policyChanged) {
- return NO_ERROR;
- }
- // defaultConfigId must be a valid config ID, and within the given refresh rate range.
- if (mRefreshRates.count(defaultConfigId) == 0) {
+ if (!isPolicyValid(policy)) {
return BAD_VALUE;
}
- const RefreshRate& refreshRate = *mRefreshRates.at(defaultConfigId);
- if (!refreshRate.inPolicy(minRefreshRate, maxRefreshRate)) {
- return BAD_VALUE;
+ Policy previousPolicy = *getCurrentPolicyLocked();
+ mDisplayManagerPolicy = policy;
+ if (*getCurrentPolicyLocked() == previousPolicy) {
+ return CURRENT_POLICY_UNCHANGED;
}
- mDefaultConfig = defaultConfigId;
- mMinRefreshRateFps = minRefreshRate;
- mMaxRefreshRateFps = maxRefreshRate;
constructAvailableRefreshRates();
return NO_ERROR;
}
-void RefreshRateConfigs::getPolicy(HwcConfigIndexType* defaultConfigId, float* minRefreshRate,
- float* maxRefreshRate) const {
+status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
std::lock_guard lock(mLock);
- *defaultConfigId = mDefaultConfig;
- *minRefreshRate = mMinRefreshRateFps;
- *maxRefreshRate = mMaxRefreshRateFps;
+ if (policy && !isPolicyValid(*policy)) {
+ return BAD_VALUE;
+ }
+ Policy previousPolicy = *getCurrentPolicyLocked();
+ mOverridePolicy = policy;
+ if (*getCurrentPolicyLocked() == previousPolicy) {
+ return CURRENT_POLICY_UNCHANGED;
+ }
+ constructAvailableRefreshRates();
+ return NO_ERROR;
+}
+
+const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const {
+ return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy;
+}
+
+RefreshRateConfigs::Policy RefreshRateConfigs::getCurrentPolicy() const {
+ std::lock_guard lock(mLock);
+ return *getCurrentPolicyLocked();
+}
+
+RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const {
+ std::lock_guard lock(mLock);
+ return mDisplayManagerPolicy;
}
bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const {
@@ -385,19 +406,25 @@
std::sort(outRefreshRates->begin(), outRefreshRates->end(),
[](const auto refreshRate1, const auto refreshRate2) {
- return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ if (refreshRate1->vsyncPeriod != refreshRate2->vsyncPeriod) {
+ return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ } else {
+ return refreshRate1->configGroup > refreshRate2->configGroup;
+ }
});
}
void RefreshRateConfigs::constructAvailableRefreshRates() {
// Filter configs based on current policy and sort based on vsync period
- HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig)->configGroup;
+ const Policy* policy = getCurrentPolicyLocked();
+ HwcConfigGroupType group = mRefreshRates.at(policy->defaultConfig)->configGroup;
ALOGV("constructAvailableRefreshRates: default %d group %d min %.2f max %.2f",
- mDefaultConfig.value(), group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
+ policy->defaultConfig.value(), group.value(), policy->minRefreshRate,
+ policy->maxRefreshRate);
getSortedRefreshRateList(
[&](const RefreshRate& refreshRate) REQUIRES(mLock) {
- return refreshRate.configGroup == group &&
- refreshRate.inPolicy(mMinRefreshRateFps, mMaxRefreshRateFps);
+ return (policy->allowGroupSwitching || refreshRate.configGroup == group) &&
+ refreshRate.inPolicy(policy->minRefreshRate, policy->maxRefreshRate);
},
&mAvailableRefreshRates);
@@ -409,7 +436,8 @@
ALOGV("Available refresh rates: %s", availableRefreshRates.c_str());
LOG_ALWAYS_FATAL_IF(mAvailableRefreshRates.empty(),
"No compatible display configs for default=%d min=%.0f max=%.0f",
- mDefaultConfig.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
+ policy->defaultConfig.value(), policy->minRefreshRate,
+ policy->maxRefreshRate);
}
// NO_THREAD_SAFETY_ANALYSIS since this is called from the constructor
@@ -432,7 +460,7 @@
std::vector<const RefreshRate*> sortedConfigs;
getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
- mDefaultConfig = currentHwcConfig;
+ mDisplayManagerPolicy.defaultConfig = currentHwcConfig;
mMinSupportedRefreshRate = sortedConfigs.front();
mMaxSupportedRefreshRate = sortedConfigs.back();
constructAvailableRefreshRates();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 87d4389..e749f8f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -20,6 +20,7 @@
#include <algorithm>
#include <numeric>
+#include <optional>
#include <type_traits>
#include "DisplayHardware/HWComposer.h"
@@ -90,14 +91,47 @@
using AllRefreshRatesMapType =
std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
- // Sets the current policy to choose refresh rates. Returns NO_ERROR if the requested policy is
- // valid, or a negative error value otherwise. policyChanged, if non-null, will be set to true
- // if the new policy is different from the old policy.
- status_t setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
- float maxRefreshRate, bool* policyChanged) EXCLUDES(mLock);
- // Gets the current policy.
- void getPolicy(HwcConfigIndexType* defaultConfigId, float* minRefreshRate,
- float* maxRefreshRate) const EXCLUDES(mLock);
+ struct Policy {
+ // The default config, used to ensure we only initiate display config switches within the
+ // same config group as defaultConfigId's group.
+ HwcConfigIndexType defaultConfig;
+ // The min and max FPS allowed by the policy.
+ float minRefreshRate = 0;
+ float maxRefreshRate = std::numeric_limits<float>::max();
+ // Whether or not we switch config groups to get the best frame rate. Only used by tests.
+ bool allowGroupSwitching = false;
+
+ bool operator==(const Policy& other) const {
+ return defaultConfig == other.defaultConfig && minRefreshRate == other.minRefreshRate &&
+ maxRefreshRate == other.maxRefreshRate &&
+ allowGroupSwitching == other.allowGroupSwitching;
+ }
+
+ bool operator!=(const Policy& other) const { return !(*this == other); }
+ };
+
+ // Return code set*Policy() to indicate the current policy is unchanged.
+ static constexpr int CURRENT_POLICY_UNCHANGED = 1;
+
+ // We maintain the display manager policy and the override policy separately. The override
+ // policy is used by CTS tests to get a consistent device state for testing. While the override
+ // policy is set, it takes precedence over the display manager policy. Once the override policy
+ // is cleared, we revert to using the display manager policy.
+
+ // Sets the display manager policy to choose refresh rates. The return value will be:
+ // - A negative value if the policy is invalid or another error occurred.
+ // - NO_ERROR if the policy was successfully updated, and the current policy is different from
+ // what it was before the call.
+ // - CURRENT_POLICY_UNCHANGED if the policy was successfully updated, but the current policy
+ // is the same as it was before the call.
+ status_t setDisplayManagerPolicy(const Policy& policy) EXCLUDES(mLock);
+ // Sets the override policy. See setDisplayManagerPolicy() for the meaning of the return value.
+ status_t setOverridePolicy(const std::optional<Policy>& policy) EXCLUDES(mLock);
+ // Gets the current policy, which will be the override policy if active, and the display manager
+ // policy otherwise.
+ Policy getCurrentPolicy() const EXCLUDES(mLock);
+ // Gets the display manager policy, regardless of whether an override policy is active.
+ Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
// Returns true if config is allowed by the current policy.
bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
@@ -208,6 +242,9 @@
// the policy.
const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
+ const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
+ bool isPolicyValid(const Policy& policy);
+
// The list of refresh rates, indexed by display config ID. This must not change after this
// object is initialized.
AllRefreshRatesMapType mRefreshRates;
@@ -220,14 +257,10 @@
// the main thread, and read by the Scheduler (and other objects) on other threads.
const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
- // The default config. This will change at runtime. This is set by SurfaceFlinger on
- // the main thread, and read by the Scheduler (and other objects) on other threads.
- HwcConfigIndexType mDefaultConfig GUARDED_BY(mLock);
-
- // The min and max FPS allowed by the policy. This will change at runtime and set by
- // SurfaceFlinger on the main thread.
- float mMinRefreshRateFps GUARDED_BY(mLock) = 0;
- float mMaxRefreshRateFps GUARDED_BY(mLock) = std::numeric_limits<float>::max();
+ // The policy values will change at runtime. They're set by SurfaceFlinger on the main thread,
+ // and read by the Scheduler (and other objects) on other threads.
+ Policy mDisplayManagerPolicy GUARDED_BY(mLock);
+ std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
// The min and max refresh rates supported by the device.
// This will not change at runtime.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dd2be7c..c47bf94 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -200,6 +200,15 @@
return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
}
+class FrameRateFlexibilityToken : public BBinder {
+public:
+ FrameRateFlexibilityToken(std::function<void()> callback) : mCallback(callback) {}
+ virtual ~FrameRateFlexibilityToken() { mCallback(); }
+
+private:
+ std::function<void()> mCallback;
+};
+
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -977,8 +986,11 @@
} else {
HwcConfigIndexType config(mode);
const auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(config);
- result = setDesiredDisplayConfigSpecsInternal(display, config, refreshRate.fps,
- refreshRate.fps);
+ result = setDesiredDisplayConfigSpecsInternal(display,
+ scheduler::RefreshRateConfigs::
+ Policy{config, refreshRate.fps,
+ refreshRate.fps},
+ /*overridePolicy=*/false);
}
}));
@@ -3496,12 +3508,13 @@
return flags;
}
-bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess() {
+bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&
- !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
+ (usePermissionCache ? !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)
+ : !checkPermission(sAccessSurfaceFlinger, pid, uid))) {
return false;
}
return true;
@@ -4373,13 +4386,11 @@
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriod());
- HwcConfigIndexType defaultConfig;
- float minFps, maxFps;
- mRefreshRateConfigs->getPolicy(&defaultConfig, &minFps, &maxFps);
+ scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
StringAppendF(&result,
"DesiredDisplayConfigSpecs: default config ID: %d"
", min: %.2f Hz, max: %.2f Hz",
- defaultConfig.value(), minFps, maxFps);
+ policy.defaultConfig.value(), policy.minRefreshRate, policy.maxRefreshRate);
StringAppendF(&result, "(config override by backdoor: %s)\n\n",
mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
@@ -4818,8 +4829,12 @@
case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
case GET_DISPLAYED_CONTENT_SAMPLE:
case NOTIFY_POWER_HINT:
- case SET_GLOBAL_SHADOW_SETTINGS: {
- if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
+ case SET_GLOBAL_SHADOW_SETTINGS:
+ case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
+ // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
+ // necessary permission dynamically. Don't use the permission cache for this check.
+ bool usePermissionCache = code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN;
+ if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
ipc->getCallingPid(), ipc->getCallingUid());
@@ -5908,12 +5923,15 @@
}
}
-status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(const sp<DisplayDevice>& display,
- HwcConfigIndexType defaultConfig,
- float minRefreshRate,
- float maxRefreshRate) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(
+ const sp<DisplayDevice>& display,
+ const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
Mutex::Autolock lock(mStateLock);
+ LOG_ALWAYS_FATAL_IF(!display->isPrimary() && overridePolicy,
+ "Can only set override policy on the primary display");
+ LOG_ALWAYS_FATAL_IF(!policy && !overridePolicy, "Can only clear the override policy");
+
if (!display->isPrimary()) {
// TODO(b/144711714): For non-primary displays we should be able to set an active config
// as well. For now, just call directly to setActiveConfigWithConstraints but ideally
@@ -5927,7 +5945,8 @@
constraints.seamlessRequired = false;
HWC2::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
- if (getHwComposer().setActiveConfigWithConstraints(*displayId, defaultConfig.value(),
+ if (getHwComposer().setActiveConfigWithConstraints(*displayId,
+ policy->defaultConfig.value(),
constraints, &timeline) < 0) {
return BAD_VALUE;
}
@@ -5935,11 +5954,12 @@
repaintEverythingForHWC();
}
- display->setActiveConfig(defaultConfig);
- const nsecs_t vsyncPeriod =
- getHwComposer().getConfigs(*displayId)[defaultConfig.value()]->getVsyncPeriod();
- mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, defaultConfig,
- vsyncPeriod);
+ display->setActiveConfig(policy->defaultConfig);
+ const nsecs_t vsyncPeriod = getHwComposer()
+ .getConfigs(*displayId)[policy->defaultConfig.value()]
+ ->getVsyncPeriod();
+ mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
+ policy->defaultConfig, vsyncPeriod);
return NO_ERROR;
}
@@ -5948,17 +5968,20 @@
return NO_ERROR;
}
- bool policyChanged;
- if (mRefreshRateConfigs->setPolicy(defaultConfig, minRefreshRate, maxRefreshRate,
- &policyChanged) < 0) {
+ status_t setPolicyResult = overridePolicy
+ ? mRefreshRateConfigs->setOverridePolicy(policy)
+ : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
+ if (setPolicyResult < 0) {
return BAD_VALUE;
}
- if (!policyChanged) {
+ if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) {
return NO_ERROR;
}
+ scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
ALOGV("Setting desired display config specs: defaultConfig: %d min: %.f max: %.f",
- defaultConfig.value(), minRefreshRate, maxRefreshRate);
+ currentPolicy.defaultConfig.value(), currentPolicy.minRefreshRate,
+ currentPolicy.maxRefreshRate);
// TODO(b/140204874): This hack triggers a notification that something has changed, so
// that listeners that care about a change in allowed configs can get the notification.
@@ -5972,7 +5995,7 @@
auto& preferredRefreshRate = configId
? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
// NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
- : mRefreshRateConfigs->getRefreshRateFromConfigId(defaultConfig);
+ : mRefreshRateConfigs->getRefreshRateFromConfigId(currentPolicy.defaultConfig);
ALOGV("trying to switch to Scheduler preferred config %d (%s)",
preferredRefreshRate.configId.value(), preferredRefreshRate.name.c_str());
@@ -6007,9 +6030,13 @@
result = BAD_VALUE;
ALOGW("Attempt to set desired display configs for virtual display");
} else {
- result =
- setDesiredDisplayConfigSpecsInternal(display, HwcConfigIndexType(defaultConfig),
- minRefreshRate, maxRefreshRate);
+ result = setDesiredDisplayConfigSpecsInternal(display,
+ scheduler::RefreshRateConfigs::
+ Policy{HwcConfigIndexType(
+ defaultConfig),
+ minRefreshRate,
+ maxRefreshRate},
+ /*overridePolicy=*/false);
}
}));
@@ -6033,9 +6060,11 @@
}
if (display->isPrimary()) {
- HwcConfigIndexType defaultConfig;
- mRefreshRateConfigs->getPolicy(&defaultConfig, outMinRefreshRate, outMaxRefreshRate);
- *outDefaultConfig = defaultConfig.value();
+ scheduler::RefreshRateConfigs::Policy policy =
+ mRefreshRateConfigs->getDisplayManagerPolicy();
+ *outDefaultConfig = policy.defaultConfig.value();
+ *outMinRefreshRate = policy.minRefreshRate;
+ *outMaxRefreshRate = policy.maxRefreshRate;
return NO_ERROR;
} else if (display->isVirtual()) {
return BAD_VALUE;
@@ -6155,6 +6184,68 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) {
+ if (!outToken) {
+ return BAD_VALUE;
+ }
+ status_t result = NO_ERROR;
+ postMessageSync(new LambdaMessage([&]() {
+ if (mFrameRateFlexibilityTokenCount == 0) {
+ // |mStateLock| not needed as we are on the main thread
+ const auto display = getDefaultDisplayDeviceLocked();
+
+ // This is a little racy, but not in a way that hurts anything. As we grab the
+ // defaultConfig from the display manager policy, we could be setting a new display
+ // manager policy, leaving us using a stale defaultConfig. The defaultConfig doesn't
+ // matter for the override policy though, since we set allowGroupSwitching to true, so
+ // it's not a problem.
+ scheduler::RefreshRateConfigs::Policy overridePolicy;
+ overridePolicy.defaultConfig =
+ mRefreshRateConfigs->getDisplayManagerPolicy().defaultConfig;
+ overridePolicy.allowGroupSwitching = true;
+ result = setDesiredDisplayConfigSpecsInternal(display, overridePolicy,
+ /*overridePolicy=*/true);
+ }
+
+ if (result == NO_ERROR) {
+ mFrameRateFlexibilityTokenCount++;
+ // Handing out a reference to the SurfaceFlinger object, as we're doing in the line
+ // below, is something to consider carefully. The lifetime of the
+ // FrameRateFlexibilityToken isn't tied to SurfaceFlinger object lifetime, so if this
+ // SurfaceFlinger object were to be destroyed while the token still exists, the token
+ // destructor would be accessing a stale SurfaceFlinger reference, and crash. This is ok
+ // in this case, for two reasons:
+ // 1. Once SurfaceFlinger::run() is called by main_surfaceflinger.cpp, the only way
+ // the program exits is via a crash. So we won't have a situation where the
+ // SurfaceFlinger object is dead but the process is still up.
+ // 2. The frame rate flexibility token is acquired/released only by CTS tests, so even
+ // if condition 1 were changed, the problem would only show up when running CTS tests,
+ // not on end user devices, so we could spot it and fix it without serious impact.
+ *outToken = new FrameRateFlexibilityToken(
+ [this]() { onFrameRateFlexibilityTokenReleased(); });
+ ALOGD("Frame rate flexibility token acquired. count=%d",
+ mFrameRateFlexibilityTokenCount);
+ }
+ }));
+ return result;
+}
+
+void SurfaceFlinger::onFrameRateFlexibilityTokenReleased() {
+ postMessageAsync(new LambdaMessage([&]() {
+ LOG_ALWAYS_FATAL_IF(mFrameRateFlexibilityTokenCount == 0,
+ "Failed tracking frame rate flexibility tokens");
+ mFrameRateFlexibilityTokenCount--;
+ ALOGD("Frame rate flexibility token released. count=%d", mFrameRateFlexibilityTokenCount);
+ if (mFrameRateFlexibilityTokenCount == 0) {
+ // |mStateLock| not needed as we are on the main thread
+ const auto display = getDefaultDisplayDeviceLocked();
+ status_t result =
+ setDesiredDisplayConfigSpecsInternal(display, {}, /*overridePolicy=*/true);
+ LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
+ }
+ }));
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 60904f6..3997633 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -407,7 +407,8 @@
*/
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
- bool callingThreadHasUnscopedSurfaceFlingerAccess() EXCLUDES(mStateLock);
+ bool callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache = true)
+ EXCLUDES(mStateLock);
/* ------------------------------------------------------------------------
* ISurfaceComposer interface
@@ -503,6 +504,7 @@
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
int8_t compatibility) override;
+ status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
/* ------------------------------------------------------------------------
* DeathRecipient interface
*/
@@ -574,9 +576,9 @@
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
// Sets the desired display configs.
- status_t setDesiredDisplayConfigSpecsInternal(const sp<DisplayDevice>& display,
- HwcConfigIndexType defaultConfig,
- float minRefreshRate, float maxRefreshRate)
+ status_t setDesiredDisplayConfigSpecsInternal(
+ const sp<DisplayDevice>& display,
+ const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
EXCLUDES(mStateLock);
// called on the main thread in response to setAutoLowLatencyMode()
@@ -968,6 +970,8 @@
return doDump(fd, args, asProto);
}
+ void onFrameRateFlexibilityTokenReleased();
+
/* ------------------------------------------------------------------------
* VrFlinger
*/
@@ -1275,6 +1279,8 @@
std::atomic<bool> mInputDirty = true;
void dirtyInput() { mInputDirty = true; }
bool inputDirty() { return mInputDirty; }
+
+ int mFrameRateFlexibilityTokenCount = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index dd04076..ce41291 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -83,8 +83,8 @@
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
- ASSERT_LT(refreshRateConfigs->setPolicy(HwcConfigIndexType(10), 60, 60, nullptr), 0);
- ASSERT_LT(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 20, 40, nullptr), 0);
+ ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HwcConfigIndexType(10), 60, 60}), 0);
+ ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 20, 40}), 0);
}
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
@@ -126,7 +126,7 @@
ASSERT_EQ(expectedDefaultConfig, minRate60);
ASSERT_EQ(expectedDefaultConfig, performanceRate60);
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 60, 90}), 0);
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
@@ -155,7 +155,7 @@
90};
ASSERT_EQ(expectedPerformanceConfig, performanceRate);
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 60, 60}), 0);
auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -180,7 +180,7 @@
EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
}
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 90, 90}), 0);
{
auto& current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
@@ -212,7 +212,7 @@
EXPECT_EQ(expected60Config,
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 60, 60}), 0);
EXPECT_EQ(expected60Config,
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
EXPECT_EQ(expected60Config,
@@ -224,7 +224,7 @@
EXPECT_EQ(expected60Config,
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 90, 90}), 0);
EXPECT_EQ(expected90Config,
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
EXPECT_EQ(expected90Config,
@@ -235,7 +235,7 @@
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
EXPECT_EQ(expected90Config,
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 0, 120}), 0);
EXPECT_EQ(expected90Config,
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
EXPECT_EQ(expected60Config,
@@ -332,7 +332,7 @@
&ignored));
lr.name = "";
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 60, 60}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(expected60Config,
@@ -370,7 +370,7 @@
refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
&ignored));
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 90, 90}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(expected90Config,
@@ -408,7 +408,7 @@
refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
&ignored));
- ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 0, 120}), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(expected60Config,
refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
@@ -1218,6 +1218,33 @@
}
}
+TEST_F(RefreshRateConfigsTest, groupSwitching) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_1, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = 90.0f;
+ layer.name = "90Hz ExplicitDefault";
+
+ bool touchConsidered;
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, false, &touchConsidered)
+ .configId);
+
+ RefreshRateConfigs::Policy policy;
+ policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+ ASSERT_EQ(HWC_CONFIG_ID_90,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, false, &touchConsidered)
+ .configId);
+}
+
} // namespace
} // namespace scheduler
} // namespace android