Loading core/res/res/values/config_display.xml +2 −0 Original line number Diff line number Diff line Loading @@ -29,5 +29,7 @@ <!-- Whether even dimmer feature is enabled. --> <bool name="config_evenDimmerEnabled">false</bool> <!-- Jar file path to look for PluginProvider --> <string name="config_pluginsProviderJarPath"/> </resources> core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -5692,6 +5692,7 @@ <!-- DisplayManager configs. --> <java-symbol type="bool" name="config_evenDimmerEnabled" /> <java-symbol type="string" name="config_pluginsProviderJarPath" /> <java-symbol type="bool" name="config_watchlistUseFileHashesCache" /> <java-symbol type="string" name="config_defaultContextualSearchPackageName" /> Loading services/core/java/com/android/server/display/DisplayManagerService.java +9 −3 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.layout.Layout; import com.android.server.display.mode.DisplayModeDirector; import com.android.server.display.notifications.DisplayNotificationManager; import com.android.server.display.plugin.PluginManager; import com.android.server.display.utils.DebugUtils; import com.android.server.display.utils.SensorUtils; import com.android.server.input.InputManagerInternal; Loading Loading @@ -583,6 +584,7 @@ public final class DisplayManagerService extends SystemService { private final DisplayNotificationManager mDisplayNotificationManager; private final ExternalDisplayStatsService mExternalDisplayStatsService; private final PluginManager mPluginManager; // Manages the relative placement of extended displays @Nullable Loading Loading @@ -669,6 +671,7 @@ public final class DisplayManagerService extends SystemService { } else { mDisplayTopologyCoordinator = null; } mPluginManager = new PluginManager(mContext, mFlags); } public void setupSchedulerPolicies() { Loading Loading @@ -739,6 +742,7 @@ public final class DisplayManagerService extends SystemService { mLogicalDisplayMapper.onBootCompleted(); mDisplayNotificationManager.onBootCompleted(); mExternalDisplayPolicy.onBootCompleted(); mPluginManager.onBootCompleted(); } } Loading Loading @@ -3552,6 +3556,9 @@ public final class DisplayManagerService extends SystemService { SparseArray<DisplayPowerController> displayPowerControllersLocal = new SparseArray<>(); int displayPowerControllerCount; IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); synchronized (mSyncRoot) { brightnessTrackerLocal = mBrightnessTracker; Loading Loading @@ -3599,9 +3606,6 @@ public final class DisplayManagerService extends SystemService { pw.println(" Display SdrBrightness=" + brightnessPair.sdrBrightness); } IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); pw.println(); pw.println("Display Adapters: size=" + mDisplayAdapters.size()); pw.println("------------------------"); Loading Loading @@ -3664,6 +3668,8 @@ public final class DisplayManagerService extends SystemService { pw.println(); mDisplayTopologyCoordinator.dump(pw); } pw.println(); mPluginManager.dump(ipw); pw.println(); mFlags.dump(pw); Loading services/core/java/com/android/server/display/plugin/Plugin.java 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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; import com.android.tools.r8.keepanno.annotations.KeepForApi; import java.io.PrintWriter; /** * Interface that OEMs should implement in order to inject custom code to system process. * Communication between OEM Plugin and Framework is implemented via {@link PluginStorage}. * OEM Plugin pushes values to PluginStorage, that are picked up by * {@link PluginManager.PluginChangeListener}, implemented on Framework side. * Avoid calling heavy operations in constructor - it will be called during boot and will * negatively impact boot time. Use onBootComplete and separate thread for long running operations. */ @KeepForApi public interface Plugin { /** * Called when device boot completed */ void onBootCompleted(); /** * Print the object's state and debug information into the given stream. */ void dump(PrintWriter pw); } services/core/java/com/android/server/display/plugin/PluginManager.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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; import android.annotation.Nullable; import android.content.Context; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SystemServerClassLoaderFactory; import com.android.server.display.feature.DisplayManagerFlags; import dalvik.system.PathClassLoader; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; /** * Responsible for loading Plugins. Plugins and PluginSupplier are loaded from * standalone system jar. * Plugin manager will look for PROVIDER_IMPL_CLASS in configured jar. * After device booted, PluginManager will delegate this call to each Plugin */ public class PluginManager { private static final String PROVIDER_IMPL_CLASS = "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; public PluginManager(Context context, DisplayManagerFlags flags) { this(context, flags, new Injector()); } @VisibleForTesting PluginManager(Context context, DisplayManagerFlags flags, Injector injector) { mFlags = flags; mPluginStorage = injector.getPluginStorage(); if (mFlags.isPluginManagerEnabled()) { mPlugins = Collections.unmodifiableList(injector.loadPlugins(context, mPluginStorage)); Slog.d(TAG, "loaded Plugins:" + mPlugins); } else { mPlugins = List.of(); Slog.d(TAG, "PluginManager disabled"); } } /** * Forwards boot completed event to Plugins */ public void onBootCompleted() { mPlugins.forEach(Plugin::onBootCompleted); } /** * Adds change listener for particular plugin type */ public <T> void subscribe(PluginType<T> type, PluginChangeListener<T> listener) { mPluginStorage.addListener(type, listener); } /** * Removes change listener */ public <T> void unsubscribe(PluginType<T> type, PluginChangeListener<T> listener) { mPluginStorage.removeListener(type, listener); } /** * Print the object's state and debug information into the given stream. */ public void dump(PrintWriter pw) { pw.println("PluginManager:"); mPluginStorage.dump(pw); for (Plugin plugin : mPlugins) { plugin.dump(pw); } } /** * Listens for changes in PluginStorage for a particular type * @param <T> plugin value type */ public interface PluginChangeListener<T> { /** * Called when Plugin value changed */ void onChanged(@Nullable T value); } static class Injector { PluginStorage getPluginStorage() { return new PluginStorage(); } List<Plugin> loadPlugins(Context context, PluginStorage storage) { String providerJarPath = context .getString(com.android.internal.R.string.config_pluginsProviderJarPath); Slog.d(TAG, "loading plugins from:" + providerJarPath); if (TextUtils.isEmpty(providerJarPath)) { return List.of(); } try { PathClassLoader pathClassLoader = SystemServerClassLoaderFactory.getOrCreateClassLoader( providerJarPath, getClass().getClassLoader(), false); @SuppressWarnings("PrivateApi") Class<? extends PluginsProvider> cp = pathClassLoader.loadClass(PROVIDER_IMPL_CLASS) .asSubclass(PluginsProvider.class); PluginsProvider provider = cp.getDeclaredConstructor().newInstance(); return provider.getPlugins(context, storage); } catch (ClassNotFoundException e) { Slog.e(TAG, "loading failed: " + PROVIDER_IMPL_CLASS + " is not found in" + providerJarPath, e); } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { Slog.e(TAG, "Class instantiation failed", e); } return List.of(); } } } Loading
core/res/res/values/config_display.xml +2 −0 Original line number Diff line number Diff line Loading @@ -29,5 +29,7 @@ <!-- Whether even dimmer feature is enabled. --> <bool name="config_evenDimmerEnabled">false</bool> <!-- Jar file path to look for PluginProvider --> <string name="config_pluginsProviderJarPath"/> </resources>
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -5692,6 +5692,7 @@ <!-- DisplayManager configs. --> <java-symbol type="bool" name="config_evenDimmerEnabled" /> <java-symbol type="string" name="config_pluginsProviderJarPath" /> <java-symbol type="bool" name="config_watchlistUseFileHashesCache" /> <java-symbol type="string" name="config_defaultContextualSearchPackageName" /> Loading
services/core/java/com/android/server/display/DisplayManagerService.java +9 −3 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.layout.Layout; import com.android.server.display.mode.DisplayModeDirector; import com.android.server.display.notifications.DisplayNotificationManager; import com.android.server.display.plugin.PluginManager; import com.android.server.display.utils.DebugUtils; import com.android.server.display.utils.SensorUtils; import com.android.server.input.InputManagerInternal; Loading Loading @@ -583,6 +584,7 @@ public final class DisplayManagerService extends SystemService { private final DisplayNotificationManager mDisplayNotificationManager; private final ExternalDisplayStatsService mExternalDisplayStatsService; private final PluginManager mPluginManager; // Manages the relative placement of extended displays @Nullable Loading Loading @@ -669,6 +671,7 @@ public final class DisplayManagerService extends SystemService { } else { mDisplayTopologyCoordinator = null; } mPluginManager = new PluginManager(mContext, mFlags); } public void setupSchedulerPolicies() { Loading Loading @@ -739,6 +742,7 @@ public final class DisplayManagerService extends SystemService { mLogicalDisplayMapper.onBootCompleted(); mDisplayNotificationManager.onBootCompleted(); mExternalDisplayPolicy.onBootCompleted(); mPluginManager.onBootCompleted(); } } Loading Loading @@ -3552,6 +3556,9 @@ public final class DisplayManagerService extends SystemService { SparseArray<DisplayPowerController> displayPowerControllersLocal = new SparseArray<>(); int displayPowerControllerCount; IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); synchronized (mSyncRoot) { brightnessTrackerLocal = mBrightnessTracker; Loading Loading @@ -3599,9 +3606,6 @@ public final class DisplayManagerService extends SystemService { pw.println(" Display SdrBrightness=" + brightnessPair.sdrBrightness); } IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); pw.println(); pw.println("Display Adapters: size=" + mDisplayAdapters.size()); pw.println("------------------------"); Loading Loading @@ -3664,6 +3668,8 @@ public final class DisplayManagerService extends SystemService { pw.println(); mDisplayTopologyCoordinator.dump(pw); } pw.println(); mPluginManager.dump(ipw); pw.println(); mFlags.dump(pw); Loading
services/core/java/com/android/server/display/plugin/Plugin.java 0 → 100644 +44 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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; import com.android.tools.r8.keepanno.annotations.KeepForApi; import java.io.PrintWriter; /** * Interface that OEMs should implement in order to inject custom code to system process. * Communication between OEM Plugin and Framework is implemented via {@link PluginStorage}. * OEM Plugin pushes values to PluginStorage, that are picked up by * {@link PluginManager.PluginChangeListener}, implemented on Framework side. * Avoid calling heavy operations in constructor - it will be called during boot and will * negatively impact boot time. Use onBootComplete and separate thread for long running operations. */ @KeepForApi public interface Plugin { /** * Called when device boot completed */ void onBootCompleted(); /** * Print the object's state and debug information into the given stream. */ void dump(PrintWriter pw); }
services/core/java/com/android/server/display/plugin/PluginManager.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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; import android.annotation.Nullable; import android.content.Context; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SystemServerClassLoaderFactory; import com.android.server.display.feature.DisplayManagerFlags; import dalvik.system.PathClassLoader; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; /** * Responsible for loading Plugins. Plugins and PluginSupplier are loaded from * standalone system jar. * Plugin manager will look for PROVIDER_IMPL_CLASS in configured jar. * After device booted, PluginManager will delegate this call to each Plugin */ public class PluginManager { private static final String PROVIDER_IMPL_CLASS = "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; public PluginManager(Context context, DisplayManagerFlags flags) { this(context, flags, new Injector()); } @VisibleForTesting PluginManager(Context context, DisplayManagerFlags flags, Injector injector) { mFlags = flags; mPluginStorage = injector.getPluginStorage(); if (mFlags.isPluginManagerEnabled()) { mPlugins = Collections.unmodifiableList(injector.loadPlugins(context, mPluginStorage)); Slog.d(TAG, "loaded Plugins:" + mPlugins); } else { mPlugins = List.of(); Slog.d(TAG, "PluginManager disabled"); } } /** * Forwards boot completed event to Plugins */ public void onBootCompleted() { mPlugins.forEach(Plugin::onBootCompleted); } /** * Adds change listener for particular plugin type */ public <T> void subscribe(PluginType<T> type, PluginChangeListener<T> listener) { mPluginStorage.addListener(type, listener); } /** * Removes change listener */ public <T> void unsubscribe(PluginType<T> type, PluginChangeListener<T> listener) { mPluginStorage.removeListener(type, listener); } /** * Print the object's state and debug information into the given stream. */ public void dump(PrintWriter pw) { pw.println("PluginManager:"); mPluginStorage.dump(pw); for (Plugin plugin : mPlugins) { plugin.dump(pw); } } /** * Listens for changes in PluginStorage for a particular type * @param <T> plugin value type */ public interface PluginChangeListener<T> { /** * Called when Plugin value changed */ void onChanged(@Nullable T value); } static class Injector { PluginStorage getPluginStorage() { return new PluginStorage(); } List<Plugin> loadPlugins(Context context, PluginStorage storage) { String providerJarPath = context .getString(com.android.internal.R.string.config_pluginsProviderJarPath); Slog.d(TAG, "loading plugins from:" + providerJarPath); if (TextUtils.isEmpty(providerJarPath)) { return List.of(); } try { PathClassLoader pathClassLoader = SystemServerClassLoaderFactory.getOrCreateClassLoader( providerJarPath, getClass().getClassLoader(), false); @SuppressWarnings("PrivateApi") Class<? extends PluginsProvider> cp = pathClassLoader.loadClass(PROVIDER_IMPL_CLASS) .asSubclass(PluginsProvider.class); PluginsProvider provider = cp.getDeclaredConstructor().newInstance(); return provider.getPlugins(context, storage); } catch (ClassNotFoundException e) { Slog.e(TAG, "loading failed: " + PROVIDER_IMPL_CLASS + " is not found in" + providerJarPath, e); } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { Slog.e(TAG, "Class instantiation failed", e); } return List.of(); } } }