enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
+ DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
};
struct Event {
bool connected;
};
+ struct Config {
+ int32_t configId;
+ };
+
Header header;
union {
VSync vsync;
Hotplug hotplug;
+ Config config;
};
};
return event;
}
+DisplayEventReceiver::Event makeConfigChanged(uint32_t displayId, int32_t configId) {
+ DisplayEventReceiver::Event event;
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
+ event.config.configId = configId;
+ return event;
+}
+
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread,
mCondition.notify_all();
}
+void EventThread::onConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mPendingEvents.push_back(makeConfigChanged(displayId, configId));
+ mCondition.notify_all();
+}
+
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
const sp<EventThreadConnection>& connection) const {
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
return true;
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
+ // called when SF changes the active config and apps needs to be notified about the change
+ virtual void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) = 0;
+
virtual void dump(std::string& result) const = 0;
virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
+ void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) override;
+
void dump(std::string& result) const override;
void setPhaseOffset(nsecs_t phaseOffset) override;
mConnections[handle->id]->thread->onScreenReleased();
}
+void Scheduler::onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
+ int32_t configId) {
+ RETURN_IF_INVALID();
+ mConnections[handle->id]->thread->onConfigChanged(displayId, configId);
+}
+
void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const {
RETURN_IF_INVALID();
mConnections.at(handle->id)->thread->dump(result);
// Should be called before the screen is turned off.
void onScreenReleased(const sp<ConnectionHandle>& handle);
+ // Should be called when display config changed
+ void onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
+ int32_t configId);
+
// Should be called when dumpsys command is received.
void dump(const sp<ConnectionHandle>& handle, std::string& result) const;
if (mUse90Hz) {
mPhaseOffsets->setRefreshRateType(
scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
- setRefreshRateTo(RefreshRateType::PERFORMANCE);
+ setRefreshRateTo(RefreshRateType::PERFORMANCE, ConfigEvent::None);
} else {
mPhaseOffsets->setRefreshRateType(
scheduler::RefreshRateConfigs::RefreshRateType::DEFAULT);
- setRefreshRateTo(RefreshRateType::DEFAULT);
+ setRefreshRateTo(RefreshRateType::DEFAULT, ConfigEvent::None);
}
}));
}
if (mUse90Hz) {
mScheduler->setExpiredIdleTimerCallback([this] {
Mutex::Autolock lock(mStateLock);
- setRefreshRateTo(RefreshRateType::DEFAULT);
+ setRefreshRateTo(RefreshRateType::DEFAULT, ConfigEvent::None);
});
mScheduler->setResetIdleTimerCallback([this] {
Mutex::Autolock lock(mStateLock);
- setRefreshRateTo(RefreshRateType::PERFORMANCE);
+ setRefreshRateTo(RefreshRateType::PERFORMANCE, ConfigEvent::None);
});
}
mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs(
return display->getActiveConfig();
}
-void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) {
+void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode,
+ ConfigEvent event) {
ATRACE_CALL();
Vector<DisplayInfo> configs;
// Don't check against the current mode yet. Worst case we set the desired
// config twice.
std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
+ mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken, event};
if (!mDesiredActiveConfigChanged) {
// This is the first time we set the desired
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+ if (mUpcomingActiveConfig.event != ConfigEvent::None) {
+ mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
+ mUpcomingActiveConfig.configId);
+ }
}
bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS {
return mAllowedConfigs[displayId]->isConfigAllowed(config);
}
-void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate) {
+void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent event) {
mPhaseOffsets->setRefreshRateType(refreshRate);
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
// TODO(b/113612090): There should be a better way at determining which config
// has the right refresh rate.
if (std::abs(fps - newFps) <= 1) {
- setDesiredActiveConfig(getInternalDisplayTokenLocked(), i);
+ setDesiredActiveConfig(getInternalDisplayTokenLocked(), i, event);
}
}
}
// we may want to enhance this logic to pick a similar config
// to the current one
ALOGV("Old config is not allowed - switching to config %d", i);
- setDesiredActiveConfig(displayToken, i);
+ setDesiredActiveConfig(displayToken, i, ConfigEvent::Changed);
break;
}
}
void signalLayerUpdate();
void signalRefresh();
+ enum class ConfigEvent { None, Changed };
+
// called on the main thread in response to initializeDisplays()
void onInitializeDisplays() REQUIRES(mStateLock);
// Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
- void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
+ void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode, ConfigEvent event)
+ REQUIRES(mStateLock);
// Once HWC has returned the present fence, this sets the active config and a new refresh
// rate in SF. It also triggers HWC vsync.
void setActiveConfigInternal() REQUIRES(mStateLock);
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType) REQUIRES(mStateLock);
+ void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType, ConfigEvent event)
+ REQUIRES(mStateLock);
bool isConfigAllowed(const DisplayId& displayId, int32_t config);
struct ActiveConfigInfo {
int configId;
sp<IBinder> displayToken;
+ ConfigEvent event;
bool operator!=(const ActiveConfigInfo& other) const {
if (configId != other.configId) {
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
+ void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
+ int32_t expectedConfigId);
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
EXPECT_EQ(expectedConnected, event.hotplug.connected);
}
+void EventThreadTest::expectConfigChangedEventReceivedByConnection(
+ PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, event.header.type);
+ EXPECT_EQ(expectedDisplayId, event.header.displayId);
+ EXPECT_EQ(expectedConfigId, event.config.configId);
+}
+
namespace {
/* ------------------------------------------------------------------------
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true);
}
+TEST_F(EventThreadTest, postConfigChangedPrimary) {
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 7);
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7);
+}
+
+TEST_F(EventThreadTest, postConfigChangedExternal) {
+ mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, 5);
+ expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5);
+}
+
} // namespace
} // namespace android
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
+ MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, int32_t));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
MOCK_METHOD1(registerDisplayEventConnection,