1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.display.plugin; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.text.TextUtils; 22 import android.util.Slog; 23 24 import com.android.internal.annotations.VisibleForTesting; 25 import com.android.internal.os.SystemServerClassLoaderFactory; 26 import com.android.server.display.feature.DisplayManagerFlags; 27 28 import dalvik.system.PathClassLoader; 29 30 import java.io.PrintWriter; 31 import java.lang.reflect.InvocationTargetException; 32 import java.util.Collections; 33 import java.util.HashSet; 34 import java.util.List; 35 import java.util.Set; 36 37 /** 38 * Responsible for loading Plugins. Plugins and PluginSupplier are loaded from 39 * standalone system jar. 40 * Plugin manager will look for PROVIDER_IMPL_CLASS in configured jar. 41 * After device booted, PluginManager will delegate this call to each Plugin 42 */ 43 public class PluginManager { 44 private static final String PROVIDER_IMPL_CLASS = 45 "com.android.server.display.plugin.PluginsProviderImpl"; 46 private static final String TAG = "PluginManager"; 47 48 private final PluginStorage mPluginStorage; 49 private final List<Plugin> mPlugins; 50 PluginManager(Context context, DisplayManagerFlags flags)51 public PluginManager(Context context, DisplayManagerFlags flags) { 52 this(context, flags, new Injector()); 53 } 54 55 @VisibleForTesting PluginManager(Context context, DisplayManagerFlags flags, Injector injector)56 PluginManager(Context context, DisplayManagerFlags flags, Injector injector) { 57 Set<PluginType<?>> enabledTypes = injector.getEnabledPluginTypes(flags); 58 mPluginStorage = injector.getPluginStorage(enabledTypes); 59 if (flags.isPluginManagerEnabled()) { 60 mPlugins = Collections.unmodifiableList(injector.loadPlugins( 61 context, mPluginStorage, enabledTypes)); 62 Slog.d(TAG, "loaded Plugins:" + mPlugins); 63 } else { 64 mPlugins = List.of(); 65 Slog.d(TAG, "PluginManager disabled"); 66 } 67 } 68 69 /** 70 * Forwards boot completed event to Plugins 71 */ onBootCompleted()72 public void onBootCompleted() { 73 mPlugins.forEach(Plugin::onBootCompleted); 74 } 75 76 /** 77 * Adds change listener for particular plugin type 78 */ subscribe(PluginType<T> type, String uniqueDisplayId, PluginChangeListener<T> listener)79 public <T> void subscribe(PluginType<T> type, String uniqueDisplayId, 80 PluginChangeListener<T> listener) { 81 mPluginStorage.addListener(type, uniqueDisplayId, listener); 82 } 83 84 /** 85 * Removes change listener 86 */ unsubscribe(PluginType<T> type, String uniqueDisplayId, PluginChangeListener<T> listener)87 public <T> void unsubscribe(PluginType<T> type, String uniqueDisplayId, 88 PluginChangeListener<T> listener) { 89 mPluginStorage.removeListener(type, uniqueDisplayId, listener); 90 } 91 92 /** 93 * Print the object's state and debug information into the given stream. 94 */ dump(PrintWriter pw)95 public void dump(PrintWriter pw) { 96 pw.println("PluginManager:"); 97 mPluginStorage.dump(pw); 98 for (Plugin plugin : mPlugins) { 99 plugin.dump(pw); 100 } 101 } 102 103 /** 104 * Listens for changes in PluginStorage for a particular type 105 * @param <T> plugin value type 106 */ 107 public interface PluginChangeListener<T> { 108 /** 109 * Called when Plugin value changed 110 */ onChanged(@ullable T value)111 void onChanged(@Nullable T value); 112 } 113 114 static class Injector { 115 getEnabledPluginTypes(DisplayManagerFlags flags)116 Set<PluginType<?>> getEnabledPluginTypes(DisplayManagerFlags flags) { 117 Set<PluginType<?>> enabledTypes = new HashSet<>(); 118 if (flags.isHdrOverrideEnabled()) { 119 enabledTypes.add(PluginType.HDR_BOOST_OVERRIDE); 120 } 121 return enabledTypes; 122 } 123 getPluginStorage(Set<PluginType<?>> enabledTypes)124 PluginStorage getPluginStorage(Set<PluginType<?>> enabledTypes) { 125 return new PluginStorage(enabledTypes); 126 } 127 loadPlugins(Context context, PluginStorage storage, Set<PluginType<?>> enabledTypes)128 List<Plugin> loadPlugins(Context context, PluginStorage storage, 129 Set<PluginType<?>> enabledTypes) { 130 String providerJarPath = context 131 .getString(com.android.internal.R.string.config_pluginsProviderJarPath); 132 Slog.d(TAG, "loading plugins from:" + providerJarPath); 133 if (TextUtils.isEmpty(providerJarPath)) { 134 return List.of(); 135 } 136 try { 137 PathClassLoader pathClassLoader = 138 SystemServerClassLoaderFactory.getOrCreateClassLoader( 139 providerJarPath, getClass().getClassLoader(), false); 140 @SuppressWarnings("PrivateApi") 141 Class<? extends PluginsProvider> cp = pathClassLoader.loadClass(PROVIDER_IMPL_CLASS) 142 .asSubclass(PluginsProvider.class); 143 PluginsProvider provider = cp.getDeclaredConstructor().newInstance(); 144 return provider.getPlugins(context, storage, enabledTypes); 145 } catch (ClassNotFoundException e) { 146 Slog.e(TAG, "loading failed: " + PROVIDER_IMPL_CLASS + " is not found in" 147 + providerJarPath, e); 148 } catch (InvocationTargetException | InstantiationException | IllegalAccessException 149 | NoSuchMethodException e) { 150 Slog.e(TAG, "Class instantiation failed", e); 151 } 152 return List.of(); 153 } 154 } 155 } 156