Merge "Move KeyCharacterMap to enum class"
diff --git a/data/etc/cec_config.xml b/data/etc/cec_config.xml
index 42a45e4..8e78ad7 100644
--- a/data/etc/cec_config.xml
+++ b/data/etc/cec_config.xml
@@ -1,14 +1,16 @@
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<cec-settings>
<setting name="hdmi_cec_enabled"
+ value-type="int"
user-configurable="true">
<allowed-values>
- <value string-value="0" />
- <value string-value="1" />
+ <value int-value="0" />
+ <value int-value="1" />
</allowed-values>
- <default-value string-value="1" />
+ <default-value int-value="1" />
</setting>
<setting name="send_standby_on_sleep"
+ value-type="string"
user-configurable="true">
<allowed-values>
<value string-value="to_tv" />
@@ -18,6 +20,7 @@
<default-value string-value="to_tv" />
</setting>
<setting name="power_state_change_on_active_source_lost"
+ value-type="string"
user-configurable="false">
<allowed-values>
<value string-value="none" />
@@ -26,11 +29,12 @@
<default-value string-value="none" />
</setting>
<setting name="system_audio_mode_muting"
+ value-type="int"
user-configurable="false">
<allowed-values>
- <value string-value="0" />
- <value string-value="1" />
+ <value int-value="0" />
+ <value int-value="1" />
</allowed-values>
- <default-value string-value="1" />
+ <default-value int-value="1" />
</setting>
</cec-settings>
diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h
index 42cfb12..1d987fe 100644
--- a/include/input/NamedEnum.h
+++ b/include/input/NamedEnum.h
@@ -115,10 +115,10 @@
// Do not specialize it to a large number to avoid performance issues.
// The recommended maximum enum number to specialize is 64.
template <typename E>
- static const std::string string(E val) {
+ static const std::string string(E val, const char* fallbackFormat = "0x%08x") {
std::string result;
std::optional<std::string_view> enumString = enum_name(val);
- result += enumString ? enumString.value() : base::StringPrintf("0x%08x", val);
+ result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
return result;
}
};
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 325e204..2e15e50 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -29,16 +29,12 @@
using AidlServiceManager = android::os::IServiceManager;
-class ClientCounterCallback : public ::android::os::BnClientCallback {
+class ClientCounterCallbackImpl : public ::android::os::BnClientCallback {
public:
- ClientCounterCallback() : mNumConnectedServices(0), mForcePersist(false) {}
+ ClientCounterCallbackImpl() : mNumConnectedServices(0), mForcePersist(false) {}
bool registerService(const sp<IBinder>& service, const std::string& name,
bool allowIsolated, int dumpFlags);
-
- /**
- * Set a flag to prevent services from automatically shutting down
- */
void forcePersist(bool persist);
protected:
@@ -75,7 +71,23 @@
bool mForcePersist;
};
-bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+class ClientCounterCallback {
+public:
+ ClientCounterCallback();
+
+ bool registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags);
+
+ /**
+ * Set a flag to prevent services from automatically shutting down
+ */
+ void forcePersist(bool persist);
+
+private:
+ sp<ClientCounterCallbackImpl> mImpl;
+};
+
+bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, const std::string& name,
bool allowIsolated, int dumpFlags) {
auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
@@ -89,7 +101,7 @@
}
if (!reRegister) {
- if (!manager->registerClientCallback(name, service, this).isOk()) {
+ if(!manager->registerClientCallback(name, service, this).isOk()) {
ALOGE("Failed to add client callback for service %s", name.c_str());
return false;
}
@@ -105,7 +117,7 @@
return true;
}
-std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) {
+std::map<std::string, ClientCounterCallbackImpl::Service>::iterator ClientCounterCallbackImpl::assertRegisteredService(const sp<IBinder>& service) {
LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service");
for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) {
auto const& [name, registered] = *it;
@@ -117,7 +129,7 @@
__builtin_unreachable();
}
-void ClientCounterCallback::forcePersist(bool persist) {
+void ClientCounterCallbackImpl::forcePersist(bool persist) {
mForcePersist = persist;
if(!mForcePersist) {
// Attempt a shutdown in case the number of clients hit 0 while the flag was on
@@ -129,7 +141,7 @@
* onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
* invocations could occur on different threads however.
*/
-Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
+Status ClientCounterCallbackImpl::onClients(const sp<IBinder>& service, bool clients) {
auto & [name, registered] = *assertRegisteredService(service);
if (registered.clients == clients) {
LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
@@ -154,7 +166,7 @@
return Status::ok();
}
-void ClientCounterCallback::tryShutdown() {
+void ClientCounterCallbackImpl::tryShutdown() {
if(mNumConnectedServices > 0) {
// Should only shut down if there are no clients
return;
@@ -175,7 +187,6 @@
bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk();
-
if (!success) {
ALOGI("Failed to unregister service %s", entry.first.c_str());
break;
@@ -200,6 +211,19 @@
}
}
+ClientCounterCallback::ClientCounterCallback() {
+ mImpl = sp<ClientCounterCallbackImpl>::make();
+}
+
+bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ return mImpl->registerService(service, name, allowIsolated, dumpFlags);
+}
+
+void ClientCounterCallback::forcePersist(bool persist) {
+ mImpl->forcePersist(persist);
+}
+
} // namespace internal
LazyServiceRegistrar::LazyServiceRegistrar() {
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 18877af..8d60226 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -313,7 +313,8 @@
/**
* Takes ownership of a.
*/
- explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {}
+ ScopedFileDescriptor() : ScopedFileDescriptor(-1) {}
+ explicit ScopedFileDescriptor(int a) : ScopedAResource(a) {}
~ScopedFileDescriptor() {}
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index ff64d65..678613b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -159,7 +159,7 @@
if (context == nullptr) {
return;
}
- BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context);
+ sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
bq->transactionCallback(latchTime, presentFence, stats);
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3135c19..7094c74 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -204,12 +204,12 @@
return true;
}
-static void dumpRegion(std::string& dump, const Region& region) {
+static std::string dumpRegion(const Region& region) {
if (region.isEmpty()) {
- dump += "<empty>";
- return;
+ return "<empty>";
}
+ std::string dump;
bool first = true;
Region::const_iterator cur = region.begin();
Region::const_iterator const tail = region.end();
@@ -222,6 +222,7 @@
dump += StringPrintf("[%d,%d][%d,%d]", cur->left, cur->top, cur->right, cur->bottom);
cur++;
}
+ return dump;
}
static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
@@ -2271,15 +2272,17 @@
std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
bool isTouchedWindow) const {
- return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", mode=%s, alpha=%.2f, "
- "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
- "], window=%s, applicationInfo=%s, flags=%s\n",
+ return StringPrintf(INDENT2
+ "* %stype=%s, package=%s/%" PRId32 ", mode=%s, alpha=%.2f, "
+ "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+ "], touchableRegion=%s, window=%s, applicationInfo=%s, flags={%s}\n",
(isTouchedWindow) ? "[TOUCHED] " : "",
- NamedEnum::string(info->type).c_str(), info->packageName.c_str(),
- info->ownerUid, toString(info->touchOcclusionMode).c_str(), info->alpha,
- info->frameLeft, info->frameTop, info->frameRight, info->frameBottom,
- info->name.c_str(), info->applicationInfo.name.c_str(),
- info->flags.string().c_str());
+ NamedEnum::string(info->type, "%" PRId32).c_str(),
+ info->packageName.c_str(), info->ownerUid,
+ toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
+ info->frameTop, info->frameRight, info->frameBottom,
+ dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
+ info->applicationInfo.name.c_str(), info->flags.string().c_str());
}
bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
@@ -4484,7 +4487,7 @@
windowInfo->frameRight, windowInfo->frameBottom,
windowInfo->globalScaleFactor,
windowInfo->applicationInfo.name.c_str());
- dumpRegion(dump, windowInfo->touchableRegion);
+ dump += dumpRegion(windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=%s",
windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d302f98..fa75ffa 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -393,6 +393,15 @@
nsecs_t expectedPresentTime) {
ATRACE_CALL();
+ // If this is not a valid vsync for the layer's uid, return and try again later
+ const bool isVsyncValidForUid =
+ mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
+ if (!isVsyncValidForUid) {
+ ATRACE_NAME("!isVsyncValidForUid");
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ return false;
+ }
+
bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
if (refreshRequired) {
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index bf2a509..bf5be47 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -31,6 +31,8 @@
#include <android-base/stringprintf.h>
+#include <binder/IPCThreadState.h>
+
#include <cutils/compiler.h>
#include <cutils/sched_policy.h>
@@ -123,11 +125,12 @@
} // namespace
-EventThreadConnection::EventThreadConnection(EventThread* eventThread,
+EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
ResyncCallback resyncCallback,
ISurfaceComposer::ConfigChanged configChanged)
: resyncCallback(std::move(resyncCallback)),
mConfigChanged(configChanged),
+ mOwnerUid(callingUid),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -170,10 +173,12 @@
EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
android::frametimeline::TokenManager* tokenManager,
- InterceptVSyncsCallback interceptVSyncsCallback)
+ InterceptVSyncsCallback interceptVSyncsCallback,
+ ThrottleVsyncCallback throttleVsyncCallback)
: mVSyncSource(std::move(vsyncSource)),
mTokenManager(tokenManager),
mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
+ mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
mThreadName(mVSyncSource->getName()) {
mVSyncSource->setCallback(this);
@@ -216,8 +221,9 @@
sp<EventThreadConnection> EventThread::createEventConnection(
ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
- return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
- configChanged);
+ return new EventThreadConnection(const_cast<EventThread*>(this),
+ IPCThreadState::self()->getCallingUid(),
+ std::move(resyncCallback), configChanged);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -443,6 +449,11 @@
bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
const sp<EventThreadConnection>& connection) const {
+ const auto throttleVsync = [&] {
+ return mThrottleVsyncCallback &&
+ mThrottleVsyncCallback(event.vsync.expectedVSyncTimestamp, connection->mOwnerUid);
+ };
+
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
return true;
@@ -458,12 +469,22 @@
case VSyncRequest::SingleSuppressCallback:
connection->vsyncRequest = VSyncRequest::None;
return false;
- case VSyncRequest::Single:
+ case VSyncRequest::Single: {
+ if (throttleVsync()) {
+ return false;
+ }
connection->vsyncRequest = VSyncRequest::SingleSuppressCallback;
return true;
+ }
case VSyncRequest::Periodic:
+ if (throttleVsync()) {
+ return false;
+ }
return true;
default:
+ // We don't throttle vsync if the app set a vsync request rate
+ // since there is no easy way to do that and this is a very
+ // rare case
return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index e42ca05..2e2d989 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -81,7 +81,7 @@
class EventThreadConnection : public BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread*, ResyncCallback,
+ EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
ISurfaceComposer::ConfigChanged configChanged);
virtual ~EventThreadConnection();
@@ -98,6 +98,8 @@
const ISurfaceComposer::ConfigChanged mConfigChanged =
ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
+ const uid_t mOwnerUid;
+
private:
virtual void onFirstRef();
EventThread* const mEventThread;
@@ -143,9 +145,10 @@
class EventThread : public android::EventThread, private VSyncSource::Callback {
public:
using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+ using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
- EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*,
- InterceptVSyncsCallback);
+ EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
+ ThrottleVsyncCallback);
~EventThread();
sp<EventThreadConnection> createEventConnection(
@@ -196,6 +199,7 @@
frametimeline::TokenManager* const mTokenManager;
const InterceptVSyncsCallback mInterceptVSyncsCallback;
+ const ThrottleVsyncCallback mThrottleVsyncCallback;
const char* const mThreadName;
std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 7ab49a9..7d97e72 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -625,4 +625,36 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
+void RefreshRateConfigs::setPreferredRefreshRateForUid(uid_t uid, float refreshRateHz) {
+ if (refreshRateHz > 0 && refreshRateHz < 1) {
+ return;
+ }
+
+ std::lock_guard lock(mLock);
+ if (refreshRateHz != 0) {
+ mPreferredRefreshRateForUid[uid] = refreshRateHz;
+ } else {
+ mPreferredRefreshRateForUid.erase(uid);
+ }
+}
+
+int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
+ constexpr float kThreshold = 0.1f;
+ std::lock_guard lock(mLock);
+
+ const auto iter = mPreferredRefreshRateForUid.find(uid);
+ if (iter == mPreferredRefreshRateForUid.end()) {
+ return 1;
+ }
+
+ const auto refreshRateHz = iter->second;
+ const auto numPeriods = mCurrentRefreshRate->getFps() / refreshRateHz;
+ const auto numPeriodsRounded = std::round(numPeriods);
+ if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
+ return 1;
+ }
+
+ return static_cast<int>(numPeriods);
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 280ed62..5cf7d07 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -311,6 +311,13 @@
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
+ // Stores the preferred refresh rate that an app should run at.
+ // refreshRate == 0 means no preference.
+ void setPreferredRefreshRateForUid(uid_t, float refreshRateHz) EXCLUDES(mLock);
+
+ // Returns a divider for the current refresh rate
+ int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+
private:
friend class RefreshRateConfigsTest;
@@ -368,6 +375,8 @@
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
+ std::unordered_map<uid_t, float> mPreferredRefreshRateForUid GUARDED_BY(mLock);
+
// The min and max refresh rates supported by the device.
// This will not change at runtime.
const RefreshRate* mMinSupportedRefreshRate;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7b8448f..a14019e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -212,13 +212,26 @@
readyDuration, traceVsync, name);
}
+bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
+ const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+ if (divider <= 1) {
+ return true;
+ }
+
+ return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider);
+}
+
Scheduler::ConnectionHandle Scheduler::createConnection(
const char* connectionName, frametimeline::TokenManager* tokenManager,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
+ auto throttleVsync = [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+ return !isVsyncValid(expectedVsyncTimestamp, uid);
+ };
auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
- std::move(interceptCallback));
+ std::move(interceptCallback),
+ std::move(throttleVsync));
return createConnection(std::move(eventThread));
}
@@ -379,7 +392,8 @@
auto eventThread =
std::make_unique<impl::EventThread>(std::move(vsyncSource),
/*tokenManager=*/nullptr,
- impl::EventThread::InterceptVSyncsCallback());
+ impl::EventThread::InterceptVSyncsCallback(),
+ impl::EventThread::ThrottleVsyncCallback());
mInjectorConnectionHandle = createConnection(std::move(eventThread));
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 47ce4a4..4c86d26 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -139,6 +139,10 @@
scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+ // Returns true if a given vsync timestamp is considered valid vsync
+ // for a given uid
+ bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
+
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index e90edf7..75d1e6f 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -27,6 +27,9 @@
#include <chrono>
#include <sstream>
+#undef LOG_TAG
+#define LOG_TAG "VSyncPredictor"
+
namespace android::scheduler {
using base::StringAppendF;
@@ -66,7 +69,7 @@
nsecs_t VSyncPredictor::currentPeriod() const {
std::lock_guard lock(mMutex);
- return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+ return mRateMap.find(mIdealPeriod)->second.slope;
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
@@ -118,7 +121,7 @@
// normalizing to the oldest timestamp cuts down on error in calculating the intercept.
auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
auto it = mRateMap.find(mIdealPeriod);
- auto const currentPeriod = std::get<0>(it->second);
+ auto const currentPeriod = it->second.slope;
// TODO (b/144707443): its important that there's some precision in the mean of the ordinals
// for the intercept calculation, so scale the ordinals by 1000 to continue
// fixed point calculation. Explore expanding
@@ -172,10 +175,8 @@
return true;
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
- std::lock_guard lock(mMutex);
-
- auto const [slope, intercept] = getVSyncPredictionModel(lock);
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
+ auto const [slope, intercept] = getVSyncPredictionModelLocked();
if (mTimestamps.empty()) {
traceInt64If("VSP-mode", 1);
@@ -210,13 +211,71 @@
return prediction;
}
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
std::lock_guard lock(mMutex);
- return VSyncPredictor::getVSyncPredictionModel(lock);
+ return nextAnticipatedVSyncTimeFromLocked(timePoint);
}
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
- std::lock_guard<std::mutex> const&) const {
+/*
+ * Returns whether a given vsync timestamp is in phase with a vsync divider.
+ * For example, if the vsync timestamps are (0,16,32,48):
+ * isVSyncInPhase(0, 2) = true
+ * isVSyncInPhase(16, 2) = false
+ * isVSyncInPhase(32, 2) = true
+ */
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const {
+ struct VsyncError {
+ nsecs_t vsyncTimestamp;
+ float error;
+
+ bool operator<(const VsyncError& other) const { return error < other.error; }
+ };
+
+ std::lock_guard lock(mMutex);
+ if (divider <= 1) {
+ return true;
+ }
+
+ const nsecs_t period = mRateMap[mIdealPeriod].slope;
+ const nsecs_t justBeforeTimePoint = timePoint - period / 2;
+ const nsecs_t dividedPeriod = mIdealPeriod / divider;
+
+ // If this is the first time we have asked about this divider with the
+ // current vsync period, it is considered in phase and we store the closest
+ // vsync timestamp
+ const auto knownTimestampIter = mRateDividerKnownTimestampMap.find(dividedPeriod);
+ if (knownTimestampIter == mRateDividerKnownTimestampMap.end()) {
+ const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint);
+ mRateDividerKnownTimestampMap[dividedPeriod] = vsync;
+ return true;
+ }
+
+ // Find the next N vsync timestamp where N is the divider.
+ // One of these vsyncs will be in phase. We return the one which is
+ // the most aligned with the last known in phase vsync
+ std::vector<VsyncError> vsyncs(static_cast<size_t>(divider));
+ const nsecs_t knownVsync = knownTimestampIter->second;
+ nsecs_t point = justBeforeTimePoint;
+ for (size_t i = 0; i < divider; i++) {
+ const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point);
+ const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divider);
+ const auto error = std::abs(std::round(numPeriods) - numPeriods);
+ vsyncs[i] = {vsync, error};
+ point = vsync + 1;
+ }
+
+ const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end());
+ mRateDividerKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
+ return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
+ std::lock_guard lock(mMutex);
+ const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
+ return {model.slope, model.intercept};
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
return mRateMap.find(mIdealPeriod)->second;
}
@@ -269,8 +328,8 @@
for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
StringAppendF(&result,
"\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
- idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
- std::get<1>(periodInterceptTuple));
+ idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f,
+ periodInterceptTuple.intercept);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 5f2ec49..381cf81 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,10 +38,10 @@
uint32_t outlierTolerancePercent);
~VSyncPredictor();
- bool addVsyncTimestamp(nsecs_t timestamp) final;
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
- nsecs_t currentPeriod() const final;
- void resetModel() final;
+ bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
+ nsecs_t currentPeriod() const final EXCLUDES(mMutex);
+ void resetModel() final EXCLUDES(mMutex);
/*
* Inform the model that the period is anticipated to change to a new value.
@@ -50,16 +50,23 @@
*
* \param [in] period The new period that should be used.
*/
- void setPeriod(nsecs_t period) final;
+ void setPeriod(nsecs_t period) final EXCLUDES(mMutex);
/* Query if the model is in need of more samples to make a prediction.
* \return True, if model would benefit from more samples, False if not.
*/
- bool needsMoreSamples() const final;
+ bool needsMoreSamples() const final EXCLUDES(mMutex);
- std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+ struct Model {
+ nsecs_t slope;
+ nsecs_t intercept;
+ };
- void dump(std::string& result) const final;
+ VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
+
+ bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex);
+
+ void dump(std::string& result) const final EXCLUDES(mMutex);
private:
VSyncPredictor(VSyncPredictor const&) = delete;
@@ -76,13 +83,19 @@
std::mutex mutable mMutex;
size_t next(size_t i) const REQUIRES(mMutex);
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
- std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
- REQUIRES(mMutex);
+
+ Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
+
+ nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
nsecs_t mIdealPeriod GUARDED_BY(mMutex);
std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
- std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
+ // Map between ideal vsync period and the calculated model
+ std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);
+
+ // Map between the divided vsync period and the last known vsync timestamp
+ std::unordered_map<nsecs_t, nsecs_t> mutable mRateDividerKnownTimestampMap GUARDED_BY(mMutex);
size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 107c540..2cd9b3d 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -68,6 +68,14 @@
virtual bool needsMoreSamples() const = 0;
+ /*
+ * Checks if a vsync timestamp is in phase for a given divider.
+ *
+ * \param [in] timePoint A vsync timestamp
+ * \param [in] divider The divider to check for
+ */
+ virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fde38c9..1119fce 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4915,7 +4915,7 @@
}
// Numbers from 1000 to 1038 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1038) {
+ if (code >= 1000 && code <= 1039) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5280,6 +5280,13 @@
mFrameTimeline->setMaxDisplayFrames(n);
return NO_ERROR;
}
+ case 1039: {
+ // The first parameter is the uid
+ n = data.readInt32();
+ const float refreshRateHz = data.readFloat();
+ mRefreshRateConfigs->setPreferredRefreshRateForUid(n, refreshRateHz);
+ }
+ return NO_ERROR;
}
}
return err;
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index cab33ae..e530ff9 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -5,6 +5,14 @@
},
{
"name": "libcompositionengine_test"
+ },
+ {
+ "name": "SurfaceFlinger_test"
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "sffakehwc_test"
}
]
}
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 8d094e4..3535fbb 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -14,7 +14,6 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.graphics.composer@2.1-resources",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
@@ -34,6 +33,7 @@
"libutils",
],
static_libs: [
+ "android.hardware.graphics.composer@2.1-resources",
"libcompositionengine",
"libgmock",
"libperfetto_client_experimental",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index a4f7449..0911712 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -134,13 +134,15 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index db05d5a..f0311bd 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -70,12 +70,14 @@
void DisplayTransactionTest::injectMockScheduler() {
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(mEventThread, /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(mSFEventThread, /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index f680bdb..3aafd45 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -59,9 +59,11 @@
protected:
class MockEventThreadConnection : public EventThreadConnection {
public:
- MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
+ MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
+ ResyncCallback&& resyncCallback,
ISurfaceComposer::ConfigChanged configChanged)
- : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
+ : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
+ configChanged) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -73,7 +75,8 @@
void createThread(std::unique_ptr<VSyncSource>);
sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
- ISurfaceComposer::ConfigChanged configChanged);
+ ISurfaceComposer::ConfigChanged configChanged,
+ uid_t ownerUid = mConnectionUid);
void expectVSyncSetEnabledCallReceived(bool expectedState);
void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
@@ -89,6 +92,7 @@
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
int32_t expectedConfigId,
nsecs_t expectedVsyncPeriod);
+ void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t);
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
@@ -96,12 +100,18 @@
mVSyncSetDurationCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
+ ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
MockVSyncSource* mVSyncSource;
VSyncSource::Callback* mCallback = nullptr;
std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
+ sp<MockEventThreadConnection> mThrottledConnection;
+
+ static constexpr uid_t mConnectionUid = 443;
+ static constexpr uid_t mThrottledConnectionUid = 177;
};
EventThreadTest::EventThreadTest() {
@@ -124,6 +134,9 @@
createThread(std::move(vsyncSource));
mConnection = createConnection(mConnectionEventCallRecorder,
ISurfaceComposer::eConfigChangedDispatch);
+ mThrottledConnection =
+ createConnection(mThrottledConnectionEventCallRecorder,
+ ISurfaceComposer::eConfigChangedDispatch, mThrottledConnectionUid);
// A display must be connected for VSYNC events to be delivered.
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -140,9 +153,15 @@
}
void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+ const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+ mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
+ return (uid == mThrottledConnectionUid);
+ };
+
mThread = std::make_unique<impl::EventThread>(std::move(source),
/*tokenManager=*/nullptr,
- mInterceptVSyncCallRecorder.getInvocable());
+ mInterceptVSyncCallRecorder.getInvocable(),
+ throttleVsync);
// EventThread should register itself as VSyncSource callback.
mCallback = expectVSyncSetCallbackCallReceived();
@@ -150,10 +169,11 @@
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
- ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) {
+ ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged,
+ uid_t ownerUid) {
sp<MockEventThreadConnection> connection =
- new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
- configChanged);
+ new MockEventThreadConnection(mThread.get(), ownerUid,
+ mResyncCallRecorder.getInvocable(), configChanged);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -183,6 +203,13 @@
EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
}
+void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
+ auto args = mThrottleVsyncCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
+ EXPECT_EQ(uid, std::get<1>(args.value()));
+}
+
void EventThreadTest::expectVsyncEventReceivedByConnection(
const char* name, ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount) {
@@ -267,13 +294,15 @@
// The interceptor should receive the event, as well as the connection.
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// Use the received callback to signal a second vsync event.
- // The interceptor should receive the event, but the the connection should
+ // The interceptor should receive the event, but the connection should
// not as it was only interested in the first.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// EventThread should also detect that at this point that it does not need
@@ -323,16 +352,19 @@
// interceptor, and the connection.
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// A second event should go to the same places.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
+ expectThrottleVsyncReceived(123, mConnectionUid);
expectVsyncEventReceivedByConnection(456, 2u);
// A third event should go to the same places.
mCallback->onVSyncEvent(789, 777, 111);
expectInterceptCallReceived(789);
+ expectThrottleVsyncReceived(777, mConnectionUid);
expectVsyncEventReceivedByConnection(789, 3u);
}
@@ -346,16 +378,19 @@
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The second event will be seen by the interceptor and the connection.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The third event will be seen by the interceptor, and not the connection.
mCallback->onVSyncEvent(789, 777, 744);
expectInterceptCallReceived(789);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The fourth event will be seen by the interceptor and the connection.
mCallback->onVSyncEvent(101112, 7847, 86);
@@ -408,19 +443,19 @@
}
TEST_F(EventThreadTest, tracksEventConnections) {
- EXPECT_EQ(1, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection =
createConnection(errorConnectionEventRecorder,
ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, errorConnection);
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
createConnection(secondConnectionEventRecorder,
ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, secondConnection);
- EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
// EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
@@ -432,7 +467,7 @@
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
1u);
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
}
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
@@ -514,5 +549,35 @@
ASSERT_FALSE(args.has_value());
}
+TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
+ // Signal that we want the next vsync event to be posted to the throttled connection
+ mThread->requestNextVsync(mThrottledConnection);
+
+ // EventThread should immediately request a resync.
+ EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+
+ // EventThread should enable vsync callbacks.
+ expectVSyncSetEnabledCallReceived(true);
+
+ // Use the received callback to signal a first vsync event.
+ // The interceptor should receive the event, but not the connection.
+ mCallback->onVSyncEvent(123, 456, 789);
+ expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mThrottledConnectionUid);
+ mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
+
+ // Use the received callback to signal a second vsync event.
+ // The interceptor should receive the event, but the connection should
+ // not as it was only interested in the first.
+ mCallback->onVSyncEvent(456, 123, 0);
+ expectInterceptCallReceived(456);
+ expectThrottleVsyncReceived(123, mThrottledConnectionUid);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should not change the vsync state as it didn't send the event
+ // yet
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 1f6f166..4762fd4 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1471,6 +1471,34 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
+TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_30);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234));
+}
+
+TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_30);
+ const uid_t uid = 1234;
+ refreshRateConfigs->setPreferredRefreshRateForUid(uid, 30);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+ EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
+ EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 2c8178e..8cd8372 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -131,12 +131,14 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index eee9400..509858a 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -42,7 +42,7 @@
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, ResyncCallback(),
+ : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress) {}
~MockEventThreadConnection() = default;
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index efee826..e25d501 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -171,12 +171,14 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 760bf65..68cf330 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -66,13 +66,15 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 1e5139c..0af5f30 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -52,6 +52,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, int) const final { return false; }
void dump(std::string&) const final {}
private:
@@ -88,6 +89,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, int) const final { return false; }
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 69731fd..72b5396 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -48,6 +48,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index d4cd11d..3d60479 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -59,16 +59,16 @@
};
TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ auto model = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(mPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ EXPECT_THAT(model.slope, Eq(mPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
auto const changedPeriod = 2000;
tracker.setPeriod(changedPeriod);
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(changedPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(changedPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
}
TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
@@ -264,17 +264,17 @@
}
auto const mMaxRoundingError = 100;
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError));
- EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+ auto model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError));
+ EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
tracker.setPeriod(slowPeriod);
for (auto const& timestamp : simulatedVsyncsSlow) {
tracker.addVsyncTimestamp(timestamp);
}
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError));
- EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, IsCloseTo(slowPeriod, mMaxRoundingError));
+ EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
}
TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) {
@@ -296,9 +296,9 @@
for (auto const& timestamp : simulatedVsyncsFast) {
tracker.addVsyncTimestamp(timestamp);
}
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ auto model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
tracker.setPeriod(slowPeriod);
for (auto const& timestamp : simulatedVsyncsSlow) {
@@ -308,16 +308,16 @@
// we had a model for 100ns mPeriod before, use that until the new samples are
// sufficiently built up
tracker.setPeriod(idealPeriod);
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
for (auto const& timestamp : simulatedVsyncsFast2) {
tracker.addVsyncTimestamp(timestamp);
}
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod2));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod2));
+ EXPECT_THAT(model.intercept, Eq(0));
}
TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
@@ -407,11 +407,9 @@
tracker.addVsyncTimestamp(i * realPeriod);
}
- EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
- IsCloseTo(realPeriod, mMaxRoundingError));
+ EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(realPeriod, mMaxRoundingError));
tracker.resetModel();
- EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
- IsCloseTo(idealPeriod, mMaxRoundingError));
+ EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(idealPeriod, mMaxRoundingError));
}
TEST_F(VSyncPredictorTest, slopeAlwaysValid) {
@@ -450,6 +448,33 @@
EXPECT_THAT(intercept, Eq(0));
}
+TEST_F(VSyncPredictorTest, isVSyncInPhase) {
+ auto last = mNow;
+ auto const bias = 10;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod - bias;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ mNow += bias;
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
+
+ const auto maxDivider = 5;
+ const auto maxPeriods = 15;
+ for (int divider = 1; divider < maxDivider; divider++) {
+ for (int i = 0; i < maxPeriods; i++) {
+ const bool expectedInPhase = (i % divider) == 0;
+ EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider))
+ << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is "
+ << (expectedInPhase ? "not " : "") << "in phase for divider " << divider;
+ }
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 0dcaf26..a7568e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -42,6 +42,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 03ddc85..de98025 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -33,6 +33,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
};