diff options
9 files changed, 153 insertions, 13 deletions
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index b9ce8c93dbde..373287d76442 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -245,6 +245,12 @@ public class DisplayManagerFlags { Flags.FLAG_ENABLE_PLUGIN_MANAGER, Flags::enablePluginManager ); + + private final FlagState mEnableHdrOverridePluginTypeFlagState = new FlagState( + Flags.FLAG_ENABLE_HDR_OVERRIDE_PLUGIN_TYPE, + Flags::enableHdrOverridePluginType + ); + private final FlagState mDisplayListenerPerformanceImprovementsFlagState = new FlagState( Flags.FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS, Flags::displayListenerPerformanceImprovements @@ -550,6 +556,10 @@ public class DisplayManagerFlags { return mEnablePluginManagerFlagState.isEnabled(); } + public boolean isHdrOverrideEnabled() { + return mEnableHdrOverridePluginTypeFlagState.isEnabled(); + } + /** * @return {@code true} if the flag for display listener performance improvements is enabled */ diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 63cd2d73336a..bbd0e41e9af7 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -454,6 +454,14 @@ flag { } flag { + name: "enable_hdr_override_plugin_type" + namespace: "display_manager" + description: "Enable hdr override plugin type" + bug: "389873155" + is_fixed_read_only: true +} + +flag { name: "enable_display_content_mode_management" namespace: "lse_desktop_experience" description: "Enable switching the content mode of connected displays between mirroring and extened. Also change the default content mode to extended mode." diff --git a/services/core/java/com/android/server/display/plugin/PluginManager.java b/services/core/java/com/android/server/display/plugin/PluginManager.java index cb0a4574361a..a8c4e7eaeb66 100644 --- a/services/core/java/com/android/server/display/plugin/PluginManager.java +++ b/services/core/java/com/android/server/display/plugin/PluginManager.java @@ -30,7 +30,9 @@ import dalvik.system.PathClassLoader; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Responsible for loading Plugins. Plugins and PluginSupplier are loaded from @@ -43,7 +45,6 @@ public class PluginManager { "com.android.server.display.plugin.PluginsProviderImpl"; private static final String TAG = "PluginManager"; - private final DisplayManagerFlags mFlags; private final PluginStorage mPluginStorage; private final List<Plugin> mPlugins; @@ -53,10 +54,11 @@ public class PluginManager { @VisibleForTesting PluginManager(Context context, DisplayManagerFlags flags, Injector injector) { - mFlags = flags; - mPluginStorage = injector.getPluginStorage(); - if (mFlags.isPluginManagerEnabled()) { - mPlugins = Collections.unmodifiableList(injector.loadPlugins(context, mPluginStorage)); + Set<PluginType<?>> enabledTypes = injector.getEnabledPluginTypes(flags); + mPluginStorage = injector.getPluginStorage(enabledTypes); + if (flags.isPluginManagerEnabled()) { + mPlugins = Collections.unmodifiableList(injector.loadPlugins( + context, mPluginStorage, enabledTypes)); Slog.d(TAG, "loaded Plugins:" + mPlugins); } else { mPlugins = List.of(); @@ -110,11 +112,21 @@ public class PluginManager { } static class Injector { - PluginStorage getPluginStorage() { - return new PluginStorage(); + + Set<PluginType<?>> getEnabledPluginTypes(DisplayManagerFlags flags) { + Set<PluginType<?>> enabledTypes = new HashSet<>(); + if (flags.isHdrOverrideEnabled()) { + enabledTypes.add(PluginType.HDR_BOOST_OVERRIDE); + } + return enabledTypes; + } + + PluginStorage getPluginStorage(Set<PluginType<?>> enabledTypes) { + return new PluginStorage(enabledTypes); } - List<Plugin> loadPlugins(Context context, PluginStorage storage) { + List<Plugin> loadPlugins(Context context, PluginStorage storage, + Set<PluginType<?>> enabledTypes) { String providerJarPath = context .getString(com.android.internal.R.string.config_pluginsProviderJarPath); Slog.d(TAG, "loading plugins from:" + providerJarPath); @@ -129,7 +141,7 @@ public class PluginManager { Class<? extends PluginsProvider> cp = pathClassLoader.loadClass(PROVIDER_IMPL_CLASS) .asSubclass(PluginsProvider.class); PluginsProvider provider = cp.getDeclaredConstructor().newInstance(); - return provider.getPlugins(context, storage); + return provider.getPlugins(context, storage, enabledTypes); } catch (ClassNotFoundException e) { Slog.e(TAG, "loading failed: " + PROVIDER_IMPL_CLASS + " is not found in" + providerJarPath, e); diff --git a/services/core/java/com/android/server/display/plugin/PluginStorage.java b/services/core/java/com/android/server/display/plugin/PluginStorage.java index 5102c2709329..d17fbe21deeb 100644 --- a/services/core/java/com/android/server/display/plugin/PluginStorage.java +++ b/services/core/java/com/android/server/display/plugin/PluginStorage.java @@ -24,6 +24,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.tools.r8.keepanno.annotations.KeepForApi; import java.io.PrintWriter; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -62,6 +63,12 @@ public class PluginStorage { updateValue(type, GLOBAL_ID, value); } + private final Set<PluginType<?>> mEnabledTypes; + + PluginStorage(Set<PluginType<?>> enabledTypes) { + mEnabledTypes = Collections.unmodifiableSet(enabledTypes); + } + /** * Updates value in storage and forwards it to corresponding listeners for specific display. * Should be called by OEM Plugin implementation in order to communicate with Framework @@ -71,6 +78,10 @@ public class PluginStorage { */ @KeepForApi public <T> void updateValue(PluginType<T> type, String uniqueDisplayId, @Nullable T value) { + if (isTypeDisabled(type)) { + Slog.d(TAG, "updateValue ignored for disabled type=" + type.mName); + return; + } Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value + "; displayId=" + uniqueDisplayId); Set<PluginManager.PluginChangeListener<T>> localListeners; @@ -119,6 +130,10 @@ public class PluginStorage { */ <T> void addListener(PluginType<T> type, String uniqueDisplayId, PluginManager.PluginChangeListener<T> listener) { + if (isTypeDisabled(type)) { + Slog.d(TAG, "addListener ignored for disabled type=" + type.mName); + return; + } if (GLOBAL_ID.equals(uniqueDisplayId)) { Slog.d(TAG, "addListener ignored for GLOBAL_ID, type=" + type.mName); return; @@ -141,6 +156,10 @@ public class PluginStorage { */ <T> void removeListener(PluginType<T> type, String uniqueDisplayId, PluginManager.PluginChangeListener<T> listener) { + if (isTypeDisabled(type)) { + Slog.d(TAG, "removeListener ignored for disabled type=" + type.mName); + return; + } if (GLOBAL_ID.equals(uniqueDisplayId)) { Slog.d(TAG, "removeListener ignored for GLOBAL_ID, type=" + type.mName); return; @@ -183,6 +202,10 @@ public class PluginStorage { } } + private boolean isTypeDisabled(PluginType<?> type) { + return !mEnabledTypes.contains(type); + } + @GuardedBy("mLock") @SuppressWarnings("unchecked") private <T> ListenersContainer<T> getListenersContainerLocked(PluginType<T> type) { diff --git a/services/core/java/com/android/server/display/plugin/PluginType.java b/services/core/java/com/android/server/display/plugin/PluginType.java index fb60833d259e..e4d2f854ea48 100644 --- a/services/core/java/com/android/server/display/plugin/PluginType.java +++ b/services/core/java/com/android/server/display/plugin/PluginType.java @@ -18,6 +18,7 @@ package com.android.server.display.plugin; import com.android.internal.annotations.Keep; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.plugin.types.HdrBoostOverride; /** * Represent customisation entry point to Framework. OEM and Framework team should define @@ -28,6 +29,15 @@ import com.android.internal.annotations.VisibleForTesting; */ @Keep public class PluginType<T> { + /* + * PluginType for HDR boost override. If set, system will use overridden value instead + * system default parameters. To switch back to default system behaviour, Plugin should set + * this type value to null. + * Value change will trigger whole power state recalculation, so plugins should not update + * value for this type too often. + */ + public static final PluginType<HdrBoostOverride> HDR_BOOST_OVERRIDE = new PluginType<>( + HdrBoostOverride.class, "hdr_boost_override"); final Class<T> mType; final String mName; diff --git a/services/core/java/com/android/server/display/plugin/PluginsProvider.java b/services/core/java/com/android/server/display/plugin/PluginsProvider.java index 9ad85f67bc8b..ec74c860ca58 100644 --- a/services/core/java/com/android/server/display/plugin/PluginsProvider.java +++ b/services/core/java/com/android/server/display/plugin/PluginsProvider.java @@ -22,6 +22,7 @@ import android.content.Context; import com.android.tools.r8.keepanno.annotations.KeepForApi; import java.util.List; +import java.util.Set; /** * Interface that OEMs should implement in order to supply Plugins to PluginManager @@ -32,5 +33,6 @@ public interface PluginsProvider { * Provides list of Plugins to PluginManager */ @NonNull - List<Plugin> getPlugins(Context context, PluginStorage storage); + List<Plugin> getPlugins( + Context context, PluginStorage storage, Set<PluginType<?>> enabledTypes); } diff --git a/services/core/java/com/android/server/display/plugin/types/HdrBoostOverride.java b/services/core/java/com/android/server/display/plugin/types/HdrBoostOverride.java new file mode 100644 index 000000000000..22e024da5f52 --- /dev/null +++ b/services/core/java/com/android/server/display/plugin/types/HdrBoostOverride.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2025 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. + */ + +package com.android.server.display.plugin.types; + +import android.annotation.FloatRange; +import android.os.PowerManager; + +import com.android.server.display.DisplayBrightnessState; + +/** + * HDR boost override value. + * @param sdrHdrRatio - HDR to SDR multiplier, if < 1 HDR boost is off. + * @param maxHdrBrightness - Brightness max when boosted. Value in range from BRIGHTNESS_MIN to + * BRIGHTNESS_MAX. If not used should be set to PowerManager.BRIGHTNESS_MAX + * @param customTransitionRate - Custom transition rate for transitioning to new HDR brightness. + * If not used should be set to + * DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET + */ +public record HdrBoostOverride( + @FloatRange(from = 0) + float sdrHdrRatio, + @FloatRange(from = PowerManager.BRIGHTNESS_MIN, to = PowerManager.BRIGHTNESS_MAX) + float maxHdrBrightness, + float customTransitionRate) { + /** + * Constant for HDR boost off. Plugins should use this constant instead of creating new objects + */ + private static final HdrBoostOverride HDR_OFF = new HdrBoostOverride(0, + PowerManager.BRIGHTNESS_MAX, DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET); + + /** + * Create HdrBoostOverride for HDR boost off + */ + public static HdrBoostOverride forHdrOff() { + return HDR_OFF; + } + + /** + * Create HdrBoostOverride for sdr-hdr ration override + */ + public static HdrBoostOverride forSdrHdrRatio(float sdrHdrRatio) { + return new HdrBoostOverride(sdrHdrRatio, + PowerManager.BRIGHTNESS_MAX, DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt index d9224eaf66ca..0466e7572c2e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt @@ -87,11 +87,15 @@ class PluginManagerTest { val mockPlugin1 = mock<Plugin>() val mockPlugin2 = mock<Plugin>() - override fun getPluginStorage(): PluginStorage { + override fun getPluginStorage(enabledTypes: Set<PluginType<*>>): PluginStorage { return mockStorage } - override fun loadPlugins(context: Context?, storage: PluginStorage?): List<Plugin> { + override fun loadPlugins( + context: Context?, + storage: PluginStorage?, + enabledTypes: Set<PluginType<*>> + ): List<Plugin> { return listOf(mockPlugin1, mockPlugin2) } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt index 8eb3e9fbf9b0..2b06126588d6 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt @@ -29,7 +29,7 @@ private val DISPLAY_ID_2 = "display_2" @SmallTest class PluginStorageTest { - val storage = PluginStorage() + var storage = PluginStorage(setOf(TEST_PLUGIN_TYPE1, TEST_PLUGIN_TYPE2)) @Test fun testUpdateValue() { @@ -93,6 +93,18 @@ class PluginStorageTest { } @Test + fun testDisabledPluginType() { + storage = PluginStorage(setOf(TEST_PLUGIN_TYPE2)) + val type1Value = "value1" + val testChangeListener = TestPluginChangeListener<String>() + storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value) + + storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener) + + assertThat(testChangeListener.receivedValue).isNull() + } + + @Test fun testUpdateGlobal_noDisplaySpecificValue() { val type1Value = "value1" val testChangeListener1 = TestPluginChangeListener<String>() |