summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/gui/ISurfaceComposer.cpp35
-rw-r--r--libs/gui/SurfaceComposerClient.cpp6
-rw-r--r--libs/gui/include/gui/DisplayEventReceiver.h6
-rw-r--r--libs/gui/include/gui/ISurfaceComposer.h10
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h6
-rw-r--r--libs/gui/tests/Surface_test.cpp4
-rw-r--r--services/surfaceflinger/AllowedDisplayConfigs.h62
-rw-r--r--services/surfaceflinger/Scheduler/EventThread.cpp15
-rw-r--r--services/surfaceflinger/Scheduler/EventThread.h5
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.cpp6
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.h4
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp125
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h24
-rw-r--r--services/surfaceflinger/tests/unittests/EventThreadTest.cpp22
-rw-r--r--services/surfaceflinger/tests/unittests/mock/MockEventThread.h1
15 files changed, 316 insertions, 15 deletions
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index d549346276..d2f2539e8e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -807,6 +807,32 @@ public:
}
return error;
}
+
+ virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(displayToken);
+ if (result != NO_ERROR) {
+ ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = data.writeInt32Vector(allowedConfigs);
+ if (result != NO_ERROR) {
+ ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setAllowedDisplayConfigs failed to transact: %d", result);
+ return result;
+ }
+ return reply.readInt32();
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -1319,6 +1345,15 @@ status_t BnSurfaceComposer::onTransact(
}
return removeRegionSamplingListener(listener);
}
+ case SET_ALLOWED_DISPLAY_CONFIGS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken = data.readStrongBinder();
+ std::vector<int32_t> allowedConfigs;
+ data.readInt32Vector(&allowedConfigs);
+ status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs);
+ reply->writeInt32(result);
+ return result;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7770518cf6..d583e6db9d 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1382,6 +1382,12 @@ status_t SurfaceComposerClient::setActiveConfig(const sp<IBinder>& display, int
return ComposerService::getComposerService()->setActiveConfig(display, id);
}
+status_t SurfaceComposerClient::setAllowedDisplayConfigs(
+ const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) {
+ return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken,
+ allowedConfigs);
+}
+
status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
Vector<ColorMode>* outColorModes) {
return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 8c3f46305c..22de751498 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -52,6 +52,7 @@ public:
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 {
@@ -70,10 +71,15 @@ public:
bool connected;
};
+ struct Config {
+ int32_t configId;
+ };
+
Header header;
union {
VSync vsync;
Hotplug hotplug;
+ Config config;
};
};
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 8654f5ef0a..eedd5f583a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -360,6 +360,14 @@ public:
* Removes a listener that was streaming median luma updates from SurfaceFlinger.
*/
virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
+
+ /*
+ * Sets the allowed display configurations to be used.
+ * The allowedConfigs in a vector of indexes corresponding to the configurations
+ * returned from getDisplayConfigs().
+ */
+ virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) = 0;
};
// ----------------------------------------------------------------------------
@@ -407,7 +415,7 @@ public:
GET_PHYSICAL_DISPLAY_IDS,
ADD_REGION_SAMPLING_LISTENER,
REMOVE_REGION_SAMPLING_LISTENER,
-
+ SET_ALLOWED_DISPLAY_CONFIGS,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6d21bff1f7..4621a34781 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -111,6 +111,12 @@ public:
// returned by getDisplayInfo
static status_t setActiveConfig(const sp<IBinder>& display, int id);
+ // Sets the allowed display configurations to be used.
+ // The allowedConfigs in a vector of indexes corresponding to the configurations
+ // returned from getDisplayConfigs().
+ static status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs);
+
// Gets the list of supported color modes for the given display
static status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ui::ColorMode>* outColorModes);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e14c698088..7f1dc84b1c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -679,6 +679,10 @@ public:
const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
+ status_t setAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
+ const std::vector<int32_t>& /*allowedConfigs*/) override {
+ return NO_ERROR;
+ }
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/services/surfaceflinger/AllowedDisplayConfigs.h b/services/surfaceflinger/AllowedDisplayConfigs.h
new file mode 100644
index 0000000000..e3b9c1f1d1
--- /dev/null
+++ b/services/surfaceflinger/AllowedDisplayConfigs.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <log/log.h>
+#include <vector>
+
+/*
+ * Used to represent the Display Configurations allowed to be set by SurfaceFlinger
+ */
+class AllowedDisplayConfigs {
+private:
+ // Defining ConstructorTag as private to prevent instantiating this class from outside
+ // while still allowing it to be constructed by std::make_unique
+ struct ConstructorTag {};
+
+public:
+ AllowedDisplayConfigs(ConstructorTag) {}
+
+ class Builder {
+ public:
+ Builder()
+ : mAllowedDisplayConfigs(std::make_unique<AllowedDisplayConfigs>(ConstructorTag{})) {}
+
+ std::unique_ptr<const AllowedDisplayConfigs> build() {
+ return std::move(mAllowedDisplayConfigs);
+ }
+
+ // add a config to the allowed config set
+ Builder& addConfig(int32_t config) {
+ mAllowedDisplayConfigs->addConfig(config);
+ return *this;
+ }
+
+ private:
+ std::unique_ptr<AllowedDisplayConfigs> mAllowedDisplayConfigs;
+ };
+
+ bool isConfigAllowed(int32_t config) const {
+ return (std::find(mConfigs.begin(), mConfigs.end(), config) != mConfigs.end());
+ }
+
+private:
+ // add a config to the allowed config set
+ void addConfig(int32_t config) { mConfigs.push_back(config); }
+
+ std::vector<int32_t> mConfigs;
+};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 4b500f1180..78bf7c5c49 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -97,6 +97,13 @@ DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t times
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,
@@ -307,6 +314,13 @@ void EventThread::onHotplugReceived(PhysicalDisplayId displayId, bool connected)
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;
@@ -404,6 +418,7 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
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:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 341143823a..d5e33490a5 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -112,6 +112,9 @@ public:
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;
@@ -153,6 +156,8 @@ public:
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;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4f846dbf59..990318a00a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -153,6 +153,12 @@ void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle)
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);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c566922d79..7f113e717b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -105,6 +105,10 @@ public:
// 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;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 68fa04c7a0..daa9ff9047 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -575,11 +575,11 @@ void SurfaceFlinger::bootFinished()
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);
}
}));
}
@@ -706,11 +706,11 @@ void SurfaceFlinger::init() {
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(
@@ -918,7 +918,8 @@ int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) {
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;
@@ -949,7 +950,7 @@ void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int
// 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
@@ -964,9 +965,11 @@ void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int
status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
ATRACE_CALL();
- postMessageSync(new LambdaMessage(
- [&]() NO_THREAD_SAFETY_ANALYSIS { setDesiredActiveConfig(displayToken, mode); }));
- return NO_ERROR;
+
+ std::vector<int32_t> allowedConfig;
+ allowedConfig.push_back(mode);
+
+ return setAllowedDisplayConfigs(displayToken, allowedConfig);
}
void SurfaceFlinger::setActiveConfigInternal() {
@@ -980,6 +983,10 @@ void SurfaceFlinger::setActiveConfigInternal() {
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 {
@@ -1028,7 +1035,14 @@ bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS {
return false;
}
- // Desired active config was set, it is different than the config currently in use. Notify HWC.
+ // Desired active config was set, it is different than the config currently in use, however
+ // allowed configs might have change by the time we process the refresh.
+ // Make sure the desired config is still allowed
+ if (!isConfigAllowed(*display->getId(), desiredActiveConfig.configId)) {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mDesiredActiveConfig.configId = display->getActiveConfig();
+ return false;
+ }
mUpcomingActiveConfig = desiredActiveConfig;
const auto displayId = display->getId();
LOG_ALWAYS_FATAL_IF(!displayId);
@@ -1396,7 +1410,18 @@ void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
*compositorTiming = getBE().mCompositorTiming;
}
-void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate) {
+bool SurfaceFlinger::isConfigAllowed(const DisplayId& displayId, int32_t config) {
+ std::lock_guard lock(mAllowedConfigsLock);
+
+ // if allowed configs are not set yet for this display, every config is considered allowed
+ if (mAllowedConfigs.find(displayId) == mAllowedConfigs.end()) {
+ return true;
+ }
+
+ return mAllowedConfigs[displayId]->isConfigAllowed(config);
+}
+
+void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent event) {
mPhaseOffsets->setRefreshRateType(refreshRate);
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
@@ -1431,6 +1456,11 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate) {
auto configs = getHwComposer().getConfigs(*displayId);
for (int i = 0; i < configs.size(); i++) {
+ if (!isConfigAllowed(*displayId, i)) {
+ ALOGV("Skipping config %d as it is not part of allowed configs", i);
+ continue;
+ }
+
const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod();
if (vsyncPeriod == 0) {
continue;
@@ -1439,7 +1469,7 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate) {
// 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);
}
}
}
@@ -4873,6 +4903,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
case GET_ANIMATION_FRAME_STATS:
case GET_HDR_CAPABILITIES:
case SET_ACTIVE_CONFIG:
+ case SET_ALLOWED_DISPLAY_CONFIGS:
case SET_ACTIVE_COLOR_MODE:
case INJECT_VSYNC:
case SET_POWER_MODE:
@@ -5693,6 +5724,76 @@ void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& disp
}
}
+void SurfaceFlinger::setAllowedDisplayConfigsInternal(
+ const android::sp<android::IBinder>& displayToken,
+ std::unique_ptr<const AllowedDisplayConfigs>&& allowedConfigs) {
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setAllowedDisplayConfigsInternal: getPhysicalDisplayId failed");
+ return;
+ }
+
+ ALOGV("Updating allowed configs");
+ {
+ std::lock_guard lock(mAllowedConfigsLock);
+ mAllowedConfigs[*displayId] = std::move(allowedConfigs);
+ }
+
+ // make sure that the current config is still allowed
+ int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId);
+ if (!isConfigAllowed(*displayId, currentConfigIndex)) {
+ // TODO(b/122906558): stop querying HWC for the available configs and instead use the cached
+ // configs queried on boot
+ auto configs = getHwComposer().getConfigs(*displayId);
+
+ for (int i = 0; i < configs.size(); i++) {
+ if (isConfigAllowed(*displayId, i)) {
+ // TODO: we switch to the first allowed config. In the future
+ // 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, ConfigEvent::Changed);
+ break;
+ }
+ }
+ }
+}
+
+status_t SurfaceFlinger::setAllowedDisplayConfigs(const android::sp<android::IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) {
+ ATRACE_CALL();
+
+ if (!displayToken) {
+ ALOGE("setAllowedDisplayConfigs: displayToken is null");
+ return BAD_VALUE;
+ }
+
+ if (!allowedConfigs.size()) {
+ ALOGE("setAllowedDisplayConfigs: empty config set provided");
+ return BAD_VALUE;
+ }
+
+ {
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setAllowedDisplayConfigs: display not found");
+ return NAME_NOT_FOUND;
+ }
+ }
+
+ auto allowedDisplayConfigsBuilder = AllowedDisplayConfigs::Builder();
+ for (int config : allowedConfigs) {
+ ALOGV("setAllowedDisplayConfigs: Adding config to the allowed configs = %d", config);
+ allowedDisplayConfigsBuilder.addConfig(config);
+ }
+ auto allowedDisplayConfigs = allowedDisplayConfigsBuilder.build();
+ postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
+ setAllowedDisplayConfigsInternal(displayToken, std::move(allowedDisplayConfigs));
+ }));
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
void SetInputWindowsListener::onSetInputWindowsFinished() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6ca9720a32..4fb1c0e258 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -47,6 +47,7 @@
#include <utils/Trace.h>
#include <utils/threads.h>
+#include "AllowedDisplayConfigs.h"
#include "Barrier.h"
#include "BufferStateLayerCache.h"
#include "DisplayDevice.h"
@@ -491,6 +492,9 @@ private:
status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) override;
status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
+ status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
+ const std::vector<int32_t>& allowedConfigs) override;
+
/* ------------------------------------------------------------------------
* DeathRecipient interface
*/
@@ -520,10 +524,13 @@ private:
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);
@@ -535,6 +542,11 @@ private:
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
+ // called on the main thread in response to setAllowedDisplayConfigs()
+ void setAllowedDisplayConfigsInternal(
+ const sp<IBinder>& displayToken,
+ std::unique_ptr<const AllowedDisplayConfigs>&& allowedConfigs) REQUIRES(mStateLock);
+
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -799,7 +811,10 @@ private:
// 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);
/*
* Display identification
@@ -1095,9 +1110,14 @@ private:
sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
+ std::mutex mAllowedConfigsLock;
+ std::unordered_map<DisplayId, std::unique_ptr<const AllowedDisplayConfigs>> mAllowedConfigs
+ GUARDED_BY(mAllowedConfigsLock);
+
struct ActiveConfigInfo {
int configId;
sp<IBinder> displayToken;
+ ConfigEvent event;
bool operator!=(const ActiveConfigInfo& other) const {
if (configId != other.configId) {
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index e499ff58f7..406ec81ce4 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -81,6 +81,8 @@ protected:
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;
@@ -212,6 +214,16 @@ void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId e
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 {
/* ------------------------------------------------------------------------
@@ -448,5 +460,15 @@ TEST_F(EventThreadTest, postHotplugExternalConnect) {
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
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 5edee6e83c..cb4a300611 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,6 +33,7 @@ public:
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,