From cb33c243d84076b03ab760ce52aa260e882a4994 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Fri, 10 Sep 2021 14:04:59 +0000 Subject: [PATCH 1/4] Revert "Revert "2/N Rename PluginInstanceManager to PluginAction..." Revert submission 15798157-revert-15560568-b194781951-flags-8-ZUNMWRYIZP Reason for revert: Restoring with a fix. Reverted Changes: I417dd4edd:Revert "5/N Remove PluginInitializer entirely." Ia5f864646:Revert "4/N Remove #handleWtfs from PluginInitiali... I70f22b6a5:Revert "3/N Remove #getPrivilegedPlugins from Plug... I07a26c9ff:Revert "2/N Rename PluginInstanceManager to Plugin... Change-Id: I84f52b4222efc9bf406512e45a4ff523681ab77b --- .../shared/plugins/PluginActionManager.java | 438 +++++++++++++++ .../shared/plugins/PluginInitializer.java | 2 +- .../shared/plugins/PluginInstance.java | 228 ++++++++ .../shared/plugins/PluginInstanceManager.java | 513 ------------------ .../shared/plugins/PluginManager.java | 10 +- .../shared/plugins/PluginManagerImpl.java | 40 +- .../systemui/shared/plugins/VersionInfo.java | 12 + .../systemui/PluginInflateContainer.java | 4 +- .../systemui/plugins/PluginsModule.java | 28 +- .../systemui/tuner/PluginFragment.java | 4 +- ...Test.java => PluginActionManagerTest.java} | 136 ++--- .../shared/plugins/PluginInstanceTest.java | 138 +++++ .../shared/plugins/PluginManagerTest.java | 21 +- .../utils/leaks/FakePluginManager.java | 8 +- 14 files changed, 945 insertions(+), 637 deletions(-) create mode 100644 packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java create mode 100644 packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java delete mode 100644 packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java rename packages/SystemUI/tests/src/com/android/systemui/shared/plugins/{PluginInstanceManagerTest.java => PluginActionManagerTest.java} (65%) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java new file mode 100644 index 0000000000000..af541f07fb68a --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2016 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.systemui.shared.plugins; + +import android.app.Notification; +import android.app.Notification.Action; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.net.Uri; +import android.util.ArraySet; +import android.util.Log; +import android.view.LayoutInflater; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Coordinates all the available plugins for a given action. + * + * The available plugins are queried from the {@link PackageManager} via an an {@link Intent} + * action. + * + * @param The type of plugin that this contains. + */ +public class PluginActionManager { + + private static final boolean DEBUG = false; + + private static final String TAG = "PluginInstanceManager"; + public static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN"; + + private final Context mContext; + private final PluginListener mListener; + private final String mAction; + private final boolean mAllowMultiple; + private final NotificationManager mNotificationManager; + private final PluginEnabler mPluginEnabler; + private final PluginInstance.Factory mPluginInstanceFactory; + private final ArraySet mPrivilegedPlugins = new ArraySet<>(); + + @VisibleForTesting + private final ArrayList> mPluginInstances = new ArrayList<>(); + private final boolean mIsDebuggable; + private final PackageManager mPm; + private final Class mPluginClass; + private final PluginInitializer mInitializer; + private final Executor mMainExecutor; + private final Executor mBgExecutor; + + private PluginActionManager( + Context context, + PackageManager pm, + String action, + PluginListener listener, + Class pluginClass, + boolean allowMultiple, + Executor mainExecutor, + Executor bgExecutor, + boolean debuggable, + PluginInitializer initializer, + NotificationManager notificationManager, + PluginEnabler pluginEnabler, + List privilegedPlugins, + PluginInstance.Factory pluginInstanceFactory) { + mPluginClass = pluginClass; + mInitializer = initializer; + mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; + mContext = context; + mPm = pm; + mAction = action; + mListener = listener; + mAllowMultiple = allowMultiple; + mNotificationManager = notificationManager; + mPluginEnabler = pluginEnabler; + mPluginInstanceFactory = pluginInstanceFactory; + mPrivilegedPlugins.addAll(privilegedPlugins); + mIsDebuggable = debuggable; + } + + /** Load all plugins matching this instance's action. */ + public void loadAll() { + if (DEBUG) Log.d(TAG, "startListening"); + mBgExecutor.execute(this::queryAll); + } + + /** Unload all plugins managed by this instance. */ + public void destroy() { + if (DEBUG) Log.d(TAG, "stopListening"); + ArrayList> plugins = new ArrayList<>(mPluginInstances); + for (PluginInstance plugInstance : plugins) { + mMainExecutor.execute(() -> onPluginDisconnected(plugInstance)); + } + } + + /** Unload all matching plugins managed by this instance. */ + public void onPackageRemoved(String pkg) { + mBgExecutor.execute(() -> removePkg(pkg)); + } + + /** Unload and then reload all matching plugins managed by this instance. */ + public void reloadPackage(String pkg) { + mBgExecutor.execute(() -> { + removePkg(pkg); + queryPkg(pkg); + }); + } + + /** Disable a specific plugin managed by this instance. */ + public boolean checkAndDisable(String className) { + boolean disableAny = false; + ArrayList> plugins = new ArrayList<>(mPluginInstances); + for (PluginInstance info : plugins) { + if (className.startsWith(info.getPackage())) { + disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH); + } + } + return disableAny; + } + + /** Disable all plugins managed by this instance. */ + public boolean disableAll() { + ArrayList> plugins = new ArrayList<>(mPluginInstances); + boolean disabledAny = false; + for (int i = 0; i < plugins.size(); i++) { + disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH); + } + return disabledAny; + } + + boolean isPluginPrivileged(ComponentName pluginName) { + for (String componentNameOrPackage : mPrivilegedPlugins) { + ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); + if (componentName == null) { + if (componentNameOrPackage.equals(pluginName.getPackageName())) { + return true; + } + } else { + if (componentName.equals(pluginName)) { + return true; + } + } + } + return false; + } + + private boolean disable( + PluginInstance pluginInstance, @PluginEnabler.DisableReason int reason) { + // Live by the sword, die by the sword. + // Misbehaving plugins get disabled and won't come back until uninstall/reinstall. + + ComponentName pluginComponent = pluginInstance.getComponentName(); + // If a plugin is detected in the stack of a crash then this will be called for that + // plugin, if the plugin causing a crash cannot be identified, they are all disabled + // assuming one of them must be bad. + if (isPluginPrivileged(pluginComponent)) { + // Don't disable privileged plugins as they are a part of the OS. + return false; + } + Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString()); + mPluginEnabler.setDisabled(pluginComponent, reason); + + return true; + } + + boolean dependsOn(Plugin p, Class cls) { + ArrayList> instances = new ArrayList<>(mPluginInstances); + for (PluginInstance instance : instances) { + if (instance.containsPluginClass(p.getClass())) { + return instance.getVersionInfo() != null && instance.getVersionInfo().hasClass(cls); + } + } + return false; + } + + @Override + public String toString() { + return String.format("%s@%s (action=%s)", + getClass().getSimpleName(), hashCode(), mAction); + } + + private void onPluginConnected(PluginInstance pluginInstance) { + if (DEBUG) Log.d(TAG, "onPluginConnected"); + PluginPrefs.setHasPlugins(mContext); + mInitializer.handleWtfs(); + pluginInstance.onCreate(mContext, mListener); + } + + private void onPluginDisconnected(PluginInstance pluginInstance) { + if (DEBUG) Log.d(TAG, "onPluginDisconnected"); + pluginInstance.onDestroy(mListener); + } + + private void queryAll() { + if (DEBUG) Log.d(TAG, "queryAll " + mAction); + for (int i = mPluginInstances.size() - 1; i >= 0; i--) { + PluginInstance pluginInstance = mPluginInstances.get(i); + mMainExecutor.execute(() -> onPluginDisconnected(pluginInstance)); + } + mPluginInstances.clear(); + handleQueryPlugins(null); + } + + private void removePkg(String pkg) { + for (int i = mPluginInstances.size() - 1; i >= 0; i--) { + final PluginInstance pluginInstance = mPluginInstances.get(i); + if (pluginInstance.getPackage().equals(pkg)) { + mMainExecutor.execute(() -> onPluginDisconnected(pluginInstance)); + mPluginInstances.remove(i); + } + } + } + + private void queryPkg(String pkg) { + if (DEBUG) Log.d(TAG, "queryPkg " + pkg + " " + mAction); + if (mAllowMultiple || (mPluginInstances.size() == 0)) { + handleQueryPlugins(pkg); + } else { + if (DEBUG) Log.d(TAG, "Too many of " + mAction); + } + } + + private void handleQueryPlugins(String pkgName) { + // This isn't actually a service and shouldn't ever be started, but is + // a convenient PM based way to manage our plugins. + Intent intent = new Intent(mAction); + if (pkgName != null) { + intent.setPackage(pkgName); + } + List result = mPm.queryIntentServices(intent, 0); + if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins"); + if (result.size() > 1 && !mAllowMultiple) { + // TODO: Show warning. + Log.w(TAG, "Multiple plugins found for " + mAction); + if (DEBUG) { + for (ResolveInfo info : result) { + ComponentName name = new ComponentName(info.serviceInfo.packageName, + info.serviceInfo.name); + Log.w(TAG, " " + name); + } + } + return; + } + for (ResolveInfo info : result) { + ComponentName name = new ComponentName(info.serviceInfo.packageName, + info.serviceInfo.name); + PluginInstance pluginInstance = loadPluginComponent(name); + if (pluginInstance != null) { + // add plugin before sending PLUGIN_CONNECTED message + mPluginInstances.add(pluginInstance); + mMainExecutor.execute(() -> onPluginConnected(pluginInstance)); + } + } + } + + private PluginInstance loadPluginComponent(ComponentName component) { + // This was already checked, but do it again here to make extra extra sure, we don't + // use these on production builds. + if (!mIsDebuggable && !isPluginPrivileged(component)) { + // Never ever ever allow these on production builds, they are only for prototyping. + Log.w(TAG, "Plugin cannot be loaded on production build: " + component); + return null; + } + if (!mPluginEnabler.isEnabled(component)) { + if (DEBUG) { + Log.d(TAG, "Plugin is not enabled, aborting load: " + component); + } + return null; + } + String packageName = component.getPackageName(); + try { + // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on + if (mPm.checkPermission(PLUGIN_PERMISSION, packageName) + != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Plugin doesn't have permission: " + packageName); + return null; + } + + ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0); + // TODO: Only create the plugin before version check if we need it for + // legacy version check. + if (DEBUG) { + Log.d(TAG, "createPlugin"); + } + try { + return mPluginInstanceFactory.create( + mContext, appInfo, component, + mPluginClass); + } catch (InvalidVersionException e) { + reportInvalidVersion(component, component.getClassName(), e); + } + } catch (Throwable e) { + Log.w(TAG, "Couldn't load plugin: " + packageName, e); + return null; + } + + return null; + } + + private void reportInvalidVersion( + ComponentName component, String className, InvalidVersionException e) { + final int icon = Resources.getSystem().getIdentifier( + "stat_sys_warning", "drawable", "android"); + final int color = Resources.getSystem().getIdentifier( + "system_notification_accent_color", "color", "android"); + final Notification.Builder nb = new Notification.Builder(mContext, + PluginManager.NOTIFICATION_CHANNEL_ID) + .setStyle(new Notification.BigTextStyle()) + .setSmallIcon(icon) + .setWhen(0) + .setShowWhen(false) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setColor(mContext.getColor(color)); + String label = className; + try { + label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString(); + } catch (NameNotFoundException e2) { + // no-op + } + if (!e.isTooNew()) { + // Localization not required as this will never ever appear in a user build. + nb.setContentTitle("Plugin \"" + label + "\" is too old") + .setContentText("Contact plugin developer to get an updated" + + " version.\n" + e.getMessage()); + } else { + // Localization not required as this will never ever appear in a user build. + nb.setContentTitle("Plugin \"" + label + "\" is too new") + .setContentText("Check to see if an OTA is available.\n" + + e.getMessage()); + } + Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData( + Uri.parse("package://" + component.flattenToString())); + PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, + PendingIntent.FLAG_IMMUTABLE); + nb.addAction(new Action.Builder(null, "Disable plugin", pi).build()); + mNotificationManager.notify(SystemMessage.NOTE_PLUGIN, nb.build()); + // TODO: Warn user. + Log.w(TAG, "Plugin has invalid interface version " + e.getActualVersion() + + ", expected " + e.getExpectedVersion()); + } + + /** + * Construct a {@link PluginActionManager} + */ + public static class Factory { + private final Context mContext; + private final PackageManager mPackageManager; + private final Executor mMainExecutor; + private final Executor mBgExecutor; + private final PluginInitializer mInitializer; + private final NotificationManager mNotificationManager; + private final PluginEnabler mPluginEnabler; + private final List mPrivilegedPlugins; + private final PluginInstance.Factory mPluginInstanceFactory; + + public Factory(Context context, PackageManager packageManager, + Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer, + NotificationManager notificationManager, PluginEnabler pluginEnabler, + List privilegedPlugins, PluginInstance.Factory pluginInstanceFactory) { + mContext = context; + mPackageManager = packageManager; + mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; + mInitializer = initializer; + mNotificationManager = notificationManager; + mPluginEnabler = pluginEnabler; + mPrivilegedPlugins = privilegedPlugins; + mPluginInstanceFactory = pluginInstanceFactory; + } + + PluginActionManager create( + String action, PluginListener listener, Class pluginClass, + boolean allowMultiple, boolean debuggable) { + return new PluginActionManager<>(mContext, mPackageManager, action, listener, + pluginClass, allowMultiple, mMainExecutor, mBgExecutor, + debuggable, mInitializer, mNotificationManager, mPluginEnabler, + mPrivilegedPlugins, mPluginInstanceFactory); + } + } + + /** */ + public static class PluginContextWrapper extends ContextWrapper { + private final ClassLoader mClassLoader; + private LayoutInflater mInflater; + + public PluginContextWrapper(Context base, ClassLoader classLoader) { + super(base); + mClassLoader = classLoader; + } + + @Override + public ClassLoader getClassLoader() { + return mClassLoader; + } + + @Override + public Object getSystemService(String name) { + if (LAYOUT_INFLATER_SERVICE.equals(name)) { + if (mInflater == null) { + mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this); + } + return mInflater; + } + return getBaseContext().getSystemService(name); + } + } + +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java index 895b6cd96d6fe..f0e0320e165f1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java @@ -28,7 +28,7 @@ public interface PluginInitializer { /** - * Called from {@link PluginInstanceManager}. + * Called from {@link PluginActionManager}. */ void handleWtfs(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java new file mode 100644 index 0000000000000..2f84602089e0e --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2021 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.systemui.shared.plugins; + +import android.app.LoadedApk; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginFragment; +import com.android.systemui.plugins.PluginListener; + +import dalvik.system.PathClassLoader; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Contains a single instantiation of a Plugin. + * + * This class and its related Factory are in charge of actually instantiating a plugin and + * managing any state related to it. + * + * @param The type of plugin that this contains. + */ +public class PluginInstance { + private static final String TAG = "PluginInstance"; + private static final Map sClassLoaders = new ArrayMap<>(); + + private final Context mPluginContext; + private final VersionInfo mVersionInfo; + private final ComponentName mComponentName; + private final T mPlugin; + + /** */ + public PluginInstance(ComponentName componentName, T plugin, Context pluginContext, + VersionInfo versionInfo) { + mComponentName = componentName; + mPlugin = plugin; + mPluginContext = pluginContext; + mVersionInfo = versionInfo; + } + + /** Alerts listener and plugin that the plugin has been created. */ + public void onCreate(Context appContext, PluginListener listener) { + if (!(mPlugin instanceof PluginFragment)) { + // Only call onCreate for plugins that aren't fragments, as fragments + // will get the onCreate as part of the fragment lifecycle. + mPlugin.onCreate(appContext, mPluginContext); + } + listener.onPluginConnected(mPlugin, mPluginContext); + } + + /** Alerts listener and plugin that the plugin is being shutdown. */ + public void onDestroy(PluginListener listener) { + listener.onPluginDisconnected(mPlugin); + if (!(mPlugin instanceof PluginFragment)) { + // Only call onDestroy for plugins that aren't fragments, as fragments + // will get the onDestroy as part of the fragment lifecycle. + mPlugin.onDestroy(); + } + } + + /** + * Returns if the contained plugin matches the passed in class name. + * + * It does this by string comparison of the class names. + **/ + public boolean containsPluginClass(Class pluginClass) { + return mPlugin.getClass().getName().equals(pluginClass.getName()); + } + + public ComponentName getComponentName() { + return mComponentName; + } + + public String getPackage() { + return mComponentName.getPackageName(); + } + + public VersionInfo getVersionInfo() { + return mVersionInfo; + } + + @VisibleForTesting + Context getPluginContext() { + return mPluginContext; + } + + /** Used to create new {@link PluginInstance}s. */ + public static class Factory { + private final ClassLoader mBaseClassLoader; + private final InstanceFactory mInstanceFactory; + private final VersionChecker mVersionChecker; + private final boolean mIsDebug; + private final List mPrivilegedPlugins; + + /** Factory used to construct {@link PluginInstance}s. */ + public Factory(ClassLoader classLoader, InstanceFactory instanceFactory, + VersionChecker versionChecker, + List privilegedPlugins, + boolean isDebug) { + mPrivilegedPlugins = privilegedPlugins; + mBaseClassLoader = classLoader; + mInstanceFactory = instanceFactory; + mVersionChecker = versionChecker; + mIsDebug = isDebug; + } + + /** Construct a new PluginInstance. */ + public PluginInstance create( + Context context, + ApplicationInfo appInfo, + ComponentName componentName, + Class pluginClass) + throws PackageManager.NameNotFoundException, ClassNotFoundException, + InstantiationException, IllegalAccessException { + + ClassLoader classLoader = getClassLoader(appInfo, mBaseClassLoader); + Context pluginContext = new PluginActionManager.PluginContextWrapper( + context.createApplicationContext(appInfo, 0), classLoader); + Class instanceClass = (Class) Class.forName( + componentName.getClassName(), true, classLoader); + // TODO: Only create the plugin before version check if we need it for + // legacy version check. + T instance = (T) mInstanceFactory.create(instanceClass); + VersionInfo version = mVersionChecker.checkVersion( + instanceClass, pluginClass, instance); + return new PluginInstance(componentName, instance, pluginContext, version); + } + + private boolean isPluginPackagePrivileged(String packageName) { + for (String componentNameOrPackage : mPrivilegedPlugins) { + ComponentName componentName = ComponentName.unflattenFromString( + componentNameOrPackage); + if (componentName != null) { + if (componentName.getPackageName().equals(packageName)) { + return true; + } + } else if (componentNameOrPackage.equals(packageName)) { + return true; + } + } + return false; + } + + private ClassLoader getParentClassLoader(ClassLoader baseClassLoader) { + return new PluginManagerImpl.ClassLoaderFilter( + baseClassLoader, "com.android.systemui.plugin"); + } + + /** Returns class loader specific for the given plugin. */ + private ClassLoader getClassLoader(ApplicationInfo appInfo, + ClassLoader baseClassLoader) { + if (!mIsDebug && !isPluginPackagePrivileged(appInfo.packageName)) { + Log.w(TAG, "Cannot get class loader for non-privileged plugin. Src:" + + appInfo.sourceDir + ", pkg: " + appInfo.packageName); + return null; + } + if (sClassLoaders.containsKey(appInfo.packageName)) { + return sClassLoaders.get(appInfo.packageName); + } + + List zipPaths = new ArrayList<>(); + List libPaths = new ArrayList<>(); + LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths); + ClassLoader classLoader = new PathClassLoader( + TextUtils.join(File.pathSeparator, zipPaths), + TextUtils.join(File.pathSeparator, libPaths), + getParentClassLoader(baseClassLoader)); + sClassLoaders.put(appInfo.packageName, classLoader); + return classLoader; + } + } + + /** Class that compares a plugin class against an implementation for version matching. */ + public static class VersionChecker { + /** Compares two plugin classes. */ + public VersionInfo checkVersion( + Class instanceClass, Class pluginClass, Plugin plugin) { + VersionInfo pluginVersion = new VersionInfo().addClass(pluginClass); + VersionInfo instanceVersion = new VersionInfo().addClass(instanceClass); + if (instanceVersion.hasVersionInfo()) { + pluginVersion.checkVersion(instanceVersion); + } else { + int fallbackVersion = plugin.getVersion(); + if (fallbackVersion != pluginVersion.getDefaultVersion()) { + throw new VersionInfo.InvalidVersionException("Invalid legacy version", false); + } + return null; + } + return instanceVersion; + } + } + + /** + * Simple class to create a new instance. Useful for testing. + * + * @param The type of plugin this create. + **/ + public static class InstanceFactory { + T create(Class cls) throws IllegalAccessException, InstantiationException { + return (T) cls.newInstance(); + } + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java deleted file mode 100644 index dcd3b3eb5dd41..0000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright (C) 2016 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.systemui.shared.plugins; - -import android.app.LoadedApk; -import android.app.Notification; -import android.app.Notification.Action; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.net.Uri; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; -import android.view.LayoutInflater; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.systemui.plugins.Plugin; -import com.android.systemui.plugins.PluginFragment; -import com.android.systemui.plugins.PluginListener; -import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; - -import dalvik.system.PathClassLoader; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; - -public class PluginInstanceManager { - - private static final boolean DEBUG = false; - - private static final String TAG = "PluginInstanceManager"; - public static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN"; - - private final Context mContext; - private final PluginListener mListener; - private final String mAction; - private final boolean mAllowMultiple; - private final VersionInfo mVersion; - private final NotificationManager mNotificationManager; - private final PluginEnabler mPluginEnabler; - private final InstanceFactory mInstanceFactory; - private final ArraySet mPrivilegedPlugins = new ArraySet<>(); - private final Map mClassLoaders = new ArrayMap<>(); - - @VisibleForTesting - private final ArrayList> mPlugins = new ArrayList<>(); - private final boolean mIsDebuggable; - private final PackageManager mPm; - private final PluginInitializer mInitializer; - private final Executor mMainExecutor; - private final Executor mBgExecutor; - - private PluginManagerImpl.ClassLoaderFilter mParentClassLoader; - - private PluginInstanceManager(Context context, PackageManager pm, String action, - PluginListener listener, boolean allowMultiple, Executor mainExecutor, - Executor bgExecutor, VersionInfo version, boolean debuggable, - PluginInitializer initializer, NotificationManager notificationManager, - PluginEnabler pluginEnabler, List privilegedPlugins, - InstanceFactory instanceFactory) { - mInitializer = initializer; - mMainExecutor = mainExecutor; - mBgExecutor = bgExecutor; - mContext = context; - mPm = pm; - mAction = action; - mListener = listener; - mAllowMultiple = allowMultiple; - mVersion = version; - mNotificationManager = notificationManager; - mPluginEnabler = pluginEnabler; - mInstanceFactory = instanceFactory; - mPrivilegedPlugins.addAll(privilegedPlugins); - mIsDebuggable = debuggable; - } - - public void loadAll() { - if (DEBUG) Log.d(TAG, "startListening"); - mBgExecutor.execute(this::queryAll); - } - - public void destroy() { - if (DEBUG) Log.d(TAG, "stopListening"); - ArrayList> plugins = new ArrayList<>(mPlugins); - for (PluginInfo pluginInfo : plugins) { - mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin)); - } - } - - public void onPackageRemoved(String pkg) { - mBgExecutor.execute(() -> removePkg(pkg)); - } - - public void onPackageChange(String pkg) { - mBgExecutor.execute(() -> removePkg(pkg)); - mBgExecutor.execute(() -> queryPkg(pkg)); - } - - public boolean checkAndDisable(String className) { - boolean disableAny = false; - ArrayList> plugins = new ArrayList<>(mPlugins); - for (PluginInfo info : plugins) { - if (className.startsWith(info.mPackage)) { - disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH); - } - } - return disableAny; - } - - public boolean disableAll() { - ArrayList> plugins = new ArrayList<>(mPlugins); - boolean disabledAny = false; - for (int i = 0; i < plugins.size(); i++) { - disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH); - } - return disabledAny; - } - - private boolean isPluginPackagePrivileged(String packageName) { - for (String componentNameOrPackage : mPrivilegedPlugins) { - ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); - if (componentName != null) { - if (componentName.getPackageName().equals(packageName)) { - return true; - } - } else if (componentNameOrPackage.equals(packageName)) { - return true; - } - } - return false; - } - - private boolean isPluginPrivileged(ComponentName pluginName) { - for (String componentNameOrPackage : mPrivilegedPlugins) { - ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); - if (componentName == null) { - if (componentNameOrPackage.equals(pluginName.getPackageName())) { - return true; - } - } else { - if (componentName.equals(pluginName)) { - return true; - } - } - } - return false; - } - - private boolean disable(PluginInfo info, @PluginEnabler.DisableReason int reason) { - // Live by the sword, die by the sword. - // Misbehaving plugins get disabled and won't come back until uninstall/reinstall. - - ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass); - // If a plugin is detected in the stack of a crash then this will be called for that - // plugin, if the plugin causing a crash cannot be identified, they are all disabled - // assuming one of them must be bad. - if (isPluginPrivileged(pluginComponent)) { - // Don't disable whitelisted plugins as they are a part of the OS. - return false; - } - Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString()); - mPluginEnabler.setDisabled(pluginComponent, reason); - - return true; - } - - boolean dependsOn(Plugin p, Class cls) { - ArrayList> plugins = new ArrayList<>(mPlugins); - for (PluginInfo info : plugins) { - if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) { - return info.mVersion != null && info.mVersion.hasClass(cls); - } - } - return false; - } - - @Override - public String toString() { - return String.format("%s@%s (action=%s)", - getClass().getSimpleName(), hashCode(), mAction); - } - - private void onPluginConnected(PluginInfo pluginInfo) { - if (DEBUG) Log.d(TAG, "onPluginConnected"); - PluginPrefs.setHasPlugins(mContext); - mInitializer.handleWtfs(); - if (!(pluginInfo.mPlugin instanceof PluginFragment)) { - // Only call onCreate for plugins that aren't fragments, as fragments - // will get the onCreate as part of the fragment lifecycle. - pluginInfo.mPlugin.onCreate(mContext, pluginInfo.mPluginContext); - } - mListener.onPluginConnected(pluginInfo.mPlugin, pluginInfo.mPluginContext); - } - - private void onPluginDisconnected(T plugin) { - if (DEBUG) Log.d(TAG, "onPluginDisconnected"); - mListener.onPluginDisconnected(plugin); - if (!(plugin instanceof PluginFragment)) { - // Only call onDestroy for plugins that aren't fragments, as fragments - // will get the onDestroy as part of the fragment lifecycle. - plugin.onDestroy(); - } - } - - private void queryAll() { - if (DEBUG) Log.d(TAG, "queryAll " + mAction); - for (int i = mPlugins.size() - 1; i >= 0; i--) { - PluginInfo pluginInfo = mPlugins.get(i); - mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin)); - } - mPlugins.clear(); - handleQueryPlugins(null); - } - - private void removePkg(String pkg) { - for (int i = mPlugins.size() - 1; i >= 0; i--) { - final PluginInfo pluginInfo = mPlugins.get(i); - if (pluginInfo.mPackage.equals(pkg)) { - mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin)); - mPlugins.remove(i); - } - } - } - - private void queryPkg(String pkg) { - if (DEBUG) Log.d(TAG, "queryPkg " + pkg + " " + mAction); - if (mAllowMultiple || (mPlugins.size() == 0)) { - handleQueryPlugins(pkg); - } else { - if (DEBUG) Log.d(TAG, "Too many of " + mAction); - } - } - - private void handleQueryPlugins(String pkgName) { - // This isn't actually a service and shouldn't ever be started, but is - // a convenient PM based way to manage our plugins. - Intent intent = new Intent(mAction); - if (pkgName != null) { - intent.setPackage(pkgName); - } - List result = mPm.queryIntentServices(intent, 0); - if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins"); - if (result.size() > 1 && !mAllowMultiple) { - // TODO: Show warning. - Log.w(TAG, "Multiple plugins found for " + mAction); - if (DEBUG) { - for (ResolveInfo info : result) { - ComponentName name = new ComponentName(info.serviceInfo.packageName, - info.serviceInfo.name); - Log.w(TAG, " " + name); - } - } - return; - } - for (ResolveInfo info : result) { - ComponentName name = new ComponentName(info.serviceInfo.packageName, - info.serviceInfo.name); - PluginInfo pluginInfo = handleLoadPlugin(name); - if (pluginInfo == null) continue; - - // add plugin before sending PLUGIN_CONNECTED message - mPlugins.add(pluginInfo); - mMainExecutor.execute(() -> onPluginConnected(pluginInfo)); - } - } - - protected PluginInfo handleLoadPlugin(ComponentName component) { - // This was already checked, but do it again here to make extra extra sure, we don't - // use these on production builds. - if (!mIsDebuggable && !isPluginPrivileged(component)) { - // Never ever ever allow these on production builds, they are only for prototyping. - Log.w(TAG, "Plugin cannot be loaded on production build: " + component); - return null; - } - if (!mPluginEnabler.isEnabled(component)) { - if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component); - return null; - } - String pkg = component.getPackageName(); - String cls = component.getClassName(); - try { - ApplicationInfo info = mPm.getApplicationInfo(pkg, 0); - // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on - if (mPm.checkPermission(PLUGIN_PERMISSION, pkg) - != PackageManager.PERMISSION_GRANTED) { - Log.d(TAG, "Plugin doesn't have permission: " + pkg); - return null; - } - // Create our own ClassLoader so we can use our own code as the parent. - ClassLoader classLoader = getClassLoader(info); - Context pluginContext = new PluginContextWrapper( - mContext.createApplicationContext(info, 0), classLoader); - Class pluginClass = Class.forName(cls, true, classLoader); - // TODO: Only create the plugin before version check if we need it for - // legacy version check. - T plugin = mInstanceFactory.create(pluginClass); - try { - VersionInfo version = checkVersion(pluginClass, plugin, mVersion); - if (DEBUG) Log.d(TAG, "createPlugin"); - return new PluginInfo<>(pkg, cls, plugin, pluginContext, version); - } catch (InvalidVersionException e) { - final int icon = Resources.getSystem().getIdentifier( - "stat_sys_warning", "drawable", "android"); - final int color = Resources.getSystem().getIdentifier( - "system_notification_accent_color", "color", "android"); - final Notification.Builder nb = new Notification.Builder(mContext, - PluginManager.NOTIFICATION_CHANNEL_ID) - .setStyle(new Notification.BigTextStyle()) - .setSmallIcon(icon) - .setWhen(0) - .setShowWhen(false) - .setVisibility(Notification.VISIBILITY_PUBLIC) - .setColor(mContext.getColor(color)); - String label = cls; - try { - label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString(); - } catch (NameNotFoundException e2) { - } - if (!e.isTooNew()) { - // Localization not required as this will never ever appear in a user build. - nb.setContentTitle("Plugin \"" + label + "\" is too old") - .setContentText("Contact plugin developer to get an updated" - + " version.\n" + e.getMessage()); - } else { - // Localization not required as this will never ever appear in a user build. - nb.setContentTitle("Plugin \"" + label + "\" is too new") - .setContentText("Check to see if an OTA is available.\n" - + e.getMessage()); - } - Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData( - Uri.parse("package://" + component.flattenToString())); - PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, - PendingIntent.FLAG_IMMUTABLE); - nb.addAction(new Action.Builder(null, "Disable plugin", pi).build()); - mNotificationManager.notify(SystemMessage.NOTE_PLUGIN, nb.build()); - // TODO: Warn user. - Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion() - + ", expected " + mVersion); - return null; - } - } catch (Throwable e) { - Log.w(TAG, "Couldn't load plugin: " + pkg, e); - return null; - } - } - - private VersionInfo checkVersion(Class pluginClass, T plugin, VersionInfo version) - throws InvalidVersionException { - VersionInfo pv = new VersionInfo().addClass(pluginClass); - if (pv.hasVersionInfo()) { - version.checkVersion(pv); - } else { - int fallbackVersion = plugin.getVersion(); - if (fallbackVersion != version.getDefaultVersion()) { - throw new InvalidVersionException("Invalid legacy version", false); - } - return null; - } - return pv; - } - - /** Returns class loader specific for the given plugin. */ - public ClassLoader getClassLoader(ApplicationInfo appInfo) { - if (!mIsDebuggable && !isPluginPackagePrivileged(appInfo.packageName)) { - Log.w(TAG, "Cannot get class loader for non-privileged plugin. Src:" - + appInfo.sourceDir + ", pkg: " + appInfo.packageName); - return null; - } - if (mClassLoaders.containsKey(appInfo.packageName)) { - return mClassLoaders.get(appInfo.packageName); - } - - List zipPaths = new ArrayList<>(); - List libPaths = new ArrayList<>(); - LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths); - ClassLoader classLoader = new PathClassLoader( - TextUtils.join(File.pathSeparator, zipPaths), - TextUtils.join(File.pathSeparator, libPaths), - getParentClassLoader()); - mClassLoaders.put(appInfo.packageName, classLoader); - return classLoader; - } - - private ClassLoader getParentClassLoader() { - if (mParentClassLoader == null) { - // Lazily load this so it doesn't have any effect on devices without plugins. - mParentClassLoader = new PluginManagerImpl.ClassLoaderFilter( - getClass().getClassLoader(), "com.android.systemui.plugin"); - } - return mParentClassLoader; - } - - /** - * Construct a {@link PluginInstanceManager} - */ - public static class Factory { - private final Context mContext; - private final PackageManager mPackageManager; - private final Executor mMainExecutor; - private final Executor mBgExecutor; - private final PluginInitializer mInitializer; - private final NotificationManager mNotificationManager; - private final PluginEnabler mPluginEnabler; - private final List mPrivilegedPlugins; - private InstanceFactory mInstanceFactory; - - public Factory(Context context, PackageManager packageManager, - Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer, - NotificationManager notificationManager, PluginEnabler pluginEnabler, - List privilegedPlugins) { - mContext = context; - mPackageManager = packageManager; - mMainExecutor = mainExecutor; - mBgExecutor = bgExecutor; - mInitializer = initializer; - mNotificationManager = notificationManager; - mPluginEnabler = pluginEnabler; - mPrivilegedPlugins = privilegedPlugins; - - mInstanceFactory = new InstanceFactory<>(); - } - - @VisibleForTesting - Factory setInstanceFactory(InstanceFactory instanceFactory) { - mInstanceFactory = instanceFactory; - return this; - } - - PluginInstanceManager create( - String action, PluginListener listener, boolean allowMultiple, - VersionInfo version, boolean debuggable) { - return new PluginInstanceManager(mContext, mPackageManager, action, listener, - allowMultiple, mMainExecutor, mBgExecutor, version, debuggable, - mInitializer, mNotificationManager, mPluginEnabler, - mPrivilegedPlugins, (InstanceFactory) mInstanceFactory); - } - } - - public static class PluginContextWrapper extends ContextWrapper { - private final ClassLoader mClassLoader; - private LayoutInflater mInflater; - - public PluginContextWrapper(Context base, ClassLoader classLoader) { - super(base); - mClassLoader = classLoader; - } - - @Override - public ClassLoader getClassLoader() { - return mClassLoader; - } - - @Override - public Object getSystemService(String name) { - if (LAYOUT_INFLATER_SERVICE.equals(name)) { - if (mInflater == null) { - mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this); - } - return mInflater; - } - return getBaseContext().getSystemService(name); - } - } - - static class PluginInfo { - private final Context mPluginContext; - private final VersionInfo mVersion; - private final String mClass; - T mPlugin; - String mPackage; - - public PluginInfo(String pkg, String cls, T plugin, Context pluginContext, - VersionInfo info) { - mPlugin = plugin; - mClass = cls; - mPackage = pkg; - mPluginContext = pluginContext; - mVersion = info; - } - } - - static class InstanceFactory { - T create(Class cls) throws IllegalAccessException, InstantiationException { - return (T) cls.newInstance(); - } - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java index d264bf2fae52c..c89be869115b5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java @@ -30,13 +30,15 @@ public interface PluginManager { /** Returns plugins that don't get disabled when an exceptoin occurs. */ String[] getPrivilegedPlugins(); - void addPluginListener(PluginListener listener, Class cls); - void addPluginListener(PluginListener listener, Class cls, + /** */ + void addPluginListener(PluginListener listener, Class cls); + /** */ + void addPluginListener(PluginListener listener, Class cls, boolean allowMultiple); void addPluginListener(String action, PluginListener listener, - Class cls); + Class cls); void addPluginListener(String action, PluginListener listener, - Class cls, boolean allowMultiple); + Class cls, boolean allowMultiple); void removePluginListener(PluginListener listener); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index ea7b0c34136a9..7539f995dab47 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -47,26 +47,26 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private static final String TAG = PluginManagerImpl.class.getSimpleName(); static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN"; - private final ArrayMap, PluginInstanceManager> mPluginMap + private final ArrayMap, PluginActionManager> mPluginMap = new ArrayMap<>(); private final Map mClassLoaders = new ArrayMap<>(); private final ArraySet mPrivilegedPlugins = new ArraySet<>(); private final Context mContext; - private final PluginInstanceManager.Factory mInstanceManagerFactory; + private final PluginActionManager.Factory mActionManagerFactory; private final boolean mIsDebuggable; private final PluginPrefs mPluginPrefs; private final PluginEnabler mPluginEnabler; private boolean mListening; public PluginManagerImpl(Context context, - PluginInstanceManager.Factory instanceManagerFactory, + PluginActionManager.Factory actionManagerFactory, boolean debuggable, Optional defaultHandlerOptional, PluginEnabler pluginEnabler, PluginPrefs pluginPrefs, List privilegedPlugins) { mContext = context; - mInstanceManagerFactory = instanceManagerFactory; + mActionManagerFactory = actionManagerFactory; mIsDebuggable = debuggable; mPrivilegedPlugins.addAll(privilegedPlugins); mPluginPrefs = pluginPrefs; @@ -85,25 +85,27 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage return mPrivilegedPlugins.toArray(new String[0]); } - public void addPluginListener(PluginListener listener, Class cls) { + /** */ + public void addPluginListener(PluginListener listener, Class cls) { addPluginListener(listener, cls, false); } - public void addPluginListener(PluginListener listener, Class cls, + /** */ + public void addPluginListener(PluginListener listener, Class cls, boolean allowMultiple) { addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple); } public void addPluginListener(String action, PluginListener listener, - Class cls) { + Class cls) { addPluginListener(action, listener, cls, false); } public void addPluginListener(String action, PluginListener listener, - Class cls, boolean allowMultiple) { + Class cls, boolean allowMultiple) { mPluginPrefs.addAction(action); - PluginInstanceManager p = mInstanceManagerFactory.create(action, listener, allowMultiple, - new VersionInfo().addClass(cls), isDebuggable()); + PluginActionManager p = mActionManagerFactory.create(action, listener, cls, + allowMultiple, isDebuggable()); p.loadAll(); synchronized (this) { mPluginMap.put(listener, p); @@ -135,7 +137,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage filter.addAction(PLUGIN_CHANGED); filter.addAction(DISABLE_PLUGIN); filter.addDataScheme("package"); - mContext.registerReceiver(this, filter, PluginInstanceManager.PLUGIN_PERMISSION, null); + mContext.registerReceiver(this, filter, PluginActionManager.PLUGIN_PERMISSION, null); filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiver(this, filter); } @@ -150,7 +152,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage public void onReceive(Context context, Intent intent) { if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { synchronized (this) { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginActionManager manager : mPluginMap.values()) { manager.loadAll(); } } @@ -189,12 +191,14 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } } synchronized (this) { - if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { - for (PluginInstanceManager manager : mPluginMap.values()) { - manager.onPackageChange(pkg); + if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction()) + || Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction()) + || Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) { + for (PluginActionManager actionManager : mPluginMap.values()) { + actionManager.reloadPackage(pkg); } } else { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginActionManager manager : mPluginMap.values()) { manager.onPackageRemoved(pkg); } } @@ -284,7 +288,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage // disable all the plugins, so we can be sure that SysUI is running as // best as possible. synchronized (this) { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginActionManager manager : mPluginMap.values()) { disabledAny |= manager.disableAll(); } } @@ -304,7 +308,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage boolean disabledAny = false; synchronized (this) { for (StackTraceElement element : throwable.getStackTrace()) { - for (PluginInstanceManager manager : mPluginMap.values()) { + for (PluginActionManager manager : mPluginMap.values()) { disabledAny |= manager.checkAndDisable(element.getClassName()); } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java index bb845cd879238..6be3243879d6d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java @@ -119,6 +119,8 @@ public class VersionInfo { public static class InvalidVersionException extends RuntimeException { private final boolean mTooNew; + private int mExpected; + private int mActual; public InvalidVersionException(String str, boolean tooNew) { super(str); @@ -128,11 +130,21 @@ public class VersionInfo { public InvalidVersionException(Class cls, boolean tooNew, int expected, int actual) { super(cls.getSimpleName() + " expected version " + expected + " but had " + actual); mTooNew = tooNew; + mExpected = expected; + mActual = actual; } public boolean isTooNew() { return mTooNew; } + + public int getExpectedVersion() { + return mExpected; + } + + public int getActualVersion() { + return mActual; + } } private static class Version { diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java index f9617cad848e8..c7f1006a40423 100644 --- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java +++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java @@ -53,7 +53,7 @@ public class PluginInflateContainer extends AutoReinflateContainer private static final String TAG = "PluginInflateContainer"; - private Class mClass; + private Class mClass; private View mPluginView; public PluginInflateContainer(Context context, @Nullable AttributeSet attrs) { @@ -61,7 +61,7 @@ public class PluginInflateContainer extends AutoReinflateContainer TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PluginInflateContainer); String viewType = a.getString(R.styleable.PluginInflateContainer_viewType); try { - mClass = Class.forName(viewType); + mClass = (Class) Class.forName(viewType); } catch (Exception e) { Log.d(TAG, "Problem getting class info " + viewType, e); mClass = null; diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java index 1ea9b3c08b1ec..6a93fb7e4861a 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java @@ -23,10 +23,12 @@ import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; +import com.android.systemui.dagger.PluginModule; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.shared.plugins.PluginActionManager; import com.android.systemui.shared.plugins.PluginEnabler; import com.android.systemui.shared.plugins.PluginInitializer; -import com.android.systemui.shared.plugins.PluginInstanceManager; +import com.android.systemui.shared.plugins.PluginInstance; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginManagerImpl; import com.android.systemui.shared.plugins.PluginPrefs; @@ -71,14 +73,28 @@ public abstract class PluginsModule { @Provides @Singleton - static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context, + static PluginInstance.Factory providesPluginInstanceFactory( + @Named(PLUGIN_PRIVILEGED) List privilegedPlugins, + @Named(PLUGIN_DEBUG) boolean isDebug) { + return new PluginInstance.Factory( + PluginModule.class.getClassLoader(), + new PluginInstance.InstanceFactory<>(), + new PluginInstance.VersionChecker(), + privilegedPlugins, + isDebug); + } + + @Provides + @Singleton + static PluginActionManager.Factory providePluginInstanceManagerFactory(Context context, PackageManager packageManager, @Main Executor mainExecutor, @Named(PLUGIN_THREAD) Executor pluginExecutor, PluginInitializer initializer, NotificationManager notificationManager, PluginEnabler pluginEnabler, - @Named(PLUGIN_PRIVILEGED) List privilegedPlugins) { - return new PluginInstanceManager.Factory( + @Named(PLUGIN_PRIVILEGED) List privilegedPlugins, + PluginInstance.Factory pluginInstanceFactory) { + return new PluginActionManager.Factory( context, packageManager, mainExecutor, pluginExecutor, initializer, - notificationManager, pluginEnabler, privilegedPlugins); + notificationManager, pluginEnabler, privilegedPlugins, pluginInstanceFactory); } @Provides @@ -91,7 +107,7 @@ public abstract class PluginsModule { @Provides static PluginManager providesPluginManager( Context context, - PluginInstanceManager.Factory instanceManagerFactory, + PluginActionManager.Factory instanceManagerFactory, @Named(PLUGIN_DEBUG) boolean debug, @Named(PRE_HANDLER) Optional uncaughtExceptionHandlerOptional, diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java index 20857eaba7d48..fe183fc9e872a 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java @@ -38,8 +38,8 @@ import com.android.internal.util.ArrayUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.PluginEnablerImpl; +import com.android.systemui.shared.plugins.PluginActionManager; import com.android.systemui.shared.plugins.PluginEnabler; -import com.android.systemui.shared.plugins.PluginInstanceManager; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginPrefs; @@ -102,7 +102,7 @@ public class PluginFragment extends PreferenceFragment { } List apps = pm.getPackagesHoldingPermissions(new String[]{ - PluginInstanceManager.PLUGIN_PERMISSION}, + PluginActionManager.PLUGIN_PERMISSION}, PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES); apps.forEach(app -> { if (!plugins.containsKey(app.packageName)) return; diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java similarity index 65% rename from packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java index 790b4dd11825b..ce366657b7b24 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java @@ -1,15 +1,17 @@ /* * Copyright (C) 2016 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 + * 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. + * 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.systemui.shared.plugins; @@ -19,8 +21,6 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -41,13 +41,11 @@ import android.test.suitebuilder.annotation.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestableContext; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.annotations.Requires; -import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -64,14 +62,14 @@ import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) -public class PluginInstanceManagerTest extends SysuiTestCase { +public class PluginActionManagerTest extends SysuiTestCase { private static final String PRIVILEGED_PACKAGE = "com.android.systemui.shared.plugins"; private TestPlugin mMockPlugin; private PackageManager mMockPm; - private PluginListener mMockListener; - private PluginInstanceManager mPluginInstanceManager; + private PluginListener mMockListener; + private PluginActionManager mPluginActionManager; private VersionInfo mMockVersionInfo; private PluginEnabler mMockEnabler; ComponentName mTestPluginComponentName = @@ -79,15 +77,20 @@ public class PluginInstanceManagerTest extends SysuiTestCase { private PluginInitializer mInitializer; private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); NotificationManager mNotificationManager; - private PluginInstanceManager.Factory mInstanceManagerFactory; - private final PluginInstanceManager.InstanceFactory mPluginInstanceFactory = - new PluginInstanceManager.InstanceFactory() { + private PluginInstance mPluginInstance; + private PluginInstance.Factory mPluginInstanceFactory = new PluginInstance.Factory( + this.getClass().getClassLoader(), + new PluginInstance.InstanceFactory<>(), new PluginInstance.VersionChecker(), + Collections.emptyList(), false) { @Override - Plugin create(Class cls) { - return mMockPlugin; + public PluginInstance create(Context context, ApplicationInfo appInfo, + ComponentName componentName, Class pluginClass) { + return (PluginInstance) mPluginInstance; } }; + private PluginActionManager.Factory mActionManagerFactory; + @Before public void setup() throws Exception { mContext = new MyContextWrapper(mContext); @@ -98,13 +101,15 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mInitializer = mock(PluginInitializer.class); mNotificationManager = mock(NotificationManager.class); mMockPlugin = mock(TestPlugin.class); - mInstanceManagerFactory = new PluginInstanceManager.Factory(getContext(), mMockPm, + mPluginInstance = mock(PluginInstance.class); + when(mPluginInstance.getComponentName()).thenReturn(mTestPluginComponentName); + when(mPluginInstance.getPackage()).thenReturn(mTestPluginComponentName.getPackageName()); + mActionManagerFactory = new PluginActionManager.Factory(getContext(), mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, mMockEnabler, - new ArrayList<>()) - .setInstanceFactory(mPluginInstanceFactory); + new ArrayList<>(), mPluginInstanceFactory); - mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener, - true, mMockVersionInfo, true); + mPluginActionManager = mActionManagerFactory.create("myAction", mMockListener, + TestPlugin.class, true, true); when(mMockPlugin.getVersion()).thenReturn(1); } @@ -112,7 +117,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { public void testNoPlugins() { when(mMockPm.queryIntentServices(any(), anyInt())).thenReturn( Collections.emptyList()); - mPluginInstanceManager.loadAll(); + mPluginActionManager.loadAll(); mFakeExecutor.runAllReady(); @@ -121,68 +126,47 @@ public class PluginInstanceManagerTest extends SysuiTestCase { @Test public void testPluginCreate() throws Exception { + //Debug.waitForDebugger(); createPlugin(); // Verify startup lifecycle - verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(), - ArgumentCaptor.forClass(Context.class).capture()); - verify(mMockListener).onPluginConnected(any(), any()); + verify(mPluginInstance).onCreate(mContext, mMockListener); } @Test public void testPluginDestroy() throws Exception { createPlugin(); // Get into valid created state. - mPluginInstanceManager.destroy(); + mPluginActionManager.destroy(); mFakeExecutor.runAllReady(); - // Verify shutdown lifecycle - verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture()); - verify(mMockPlugin).onDestroy(); - } - - @Test - public void testIncorrectVersion() throws Exception { - setupFakePmQuery(); - doThrow(new InvalidVersionException("", false)).when(mMockVersionInfo).checkVersion(any()); - - mPluginInstanceManager.loadAll(); - - mFakeExecutor.runAllReady(); - - // Plugin shouldn't be connected because it is the wrong version. - verify(mMockListener, never()).onPluginConnected(any(), any()); - verify(mNotificationManager).notify(eq(SystemMessage.NOTE_PLUGIN), any()); + verify(mPluginInstance).onDestroy(mMockListener); } @Test public void testReloadOnChange() throws Exception { createPlugin(); // Get into valid created state. - mPluginInstanceManager.onPackageChange(PRIVILEGED_PACKAGE); + mPluginActionManager.reloadPackage(PRIVILEGED_PACKAGE); mFakeExecutor.runAllReady(); // Verify the old one was destroyed. - verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture()); - verify(mMockPlugin).onDestroy(); - // Also verify we got a second onCreate. - verify(mMockPlugin, Mockito.times(2)).onCreate( - ArgumentCaptor.forClass(Context.class).capture(), - ArgumentCaptor.forClass(Context.class).capture()); - verify(mMockListener, Mockito.times(2)).onPluginConnected(any(), any()); + verify(mPluginInstance).onDestroy(mMockListener); + verify(mPluginInstance, Mockito.times(2)) + .onCreate(mContext, mMockListener); } @Test public void testNonDebuggable() throws Exception { // Create a version that thinks the build is not debuggable. - mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener, - true, mMockVersionInfo, false); + mPluginActionManager = mActionManagerFactory.create("myAction", mMockListener, + TestPlugin.class, true, false); setupFakePmQuery(); - mPluginInstanceManager.loadAll(); + mPluginActionManager.loadAll(); mFakeExecutor.runAllReady(); @@ -193,22 +177,20 @@ public class PluginInstanceManagerTest extends SysuiTestCase { @Test public void testNonDebuggable_privileged() throws Exception { // Create a version that thinks the build is not debuggable. - PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(), + PluginActionManager.Factory factory = new PluginActionManager.Factory(getContext(), mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, - mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE)); - factory.setInstanceFactory(mPluginInstanceFactory); - mPluginInstanceManager = factory.create("myAction", mMockListener, - true, mMockVersionInfo, false); + mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE), + mPluginInstanceFactory); + mPluginActionManager = factory.create("myAction", mMockListener, + TestPlugin.class, true, false); setupFakePmQuery(); - mPluginInstanceManager.loadAll(); + mPluginActionManager.loadAll(); mFakeExecutor.runAllReady(); // Verify startup lifecycle - verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(), - ArgumentCaptor.forClass(Context.class).capture()); - verify(mMockListener).onPluginConnected(any(), any()); + verify(mPluginInstance).onCreate(mContext, mMockListener); } @Test @@ -216,12 +198,12 @@ public class PluginInstanceManagerTest extends SysuiTestCase { createPlugin(); // Get into valid created state. // Start with an unrelated class. - boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName()); + boolean result = mPluginActionManager.checkAndDisable(Activity.class.getName()); assertFalse(result); verify(mMockEnabler, never()).setDisabled(any(ComponentName.class), anyInt()); // Now hand it a real class and make sure it disables the plugin. - result = mPluginInstanceManager.checkAndDisable(TestPlugin.class.getName()); + result = mPluginActionManager.checkAndDisable(TestPlugin.class.getName()); assertTrue(result); verify(mMockEnabler).setDisabled( mTestPluginComponentName, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH); @@ -231,24 +213,24 @@ public class PluginInstanceManagerTest extends SysuiTestCase { public void testDisableAll() throws Exception { createPlugin(); // Get into valid created state. - mPluginInstanceManager.disableAll(); + mPluginActionManager.disableAll(); verify(mMockEnabler).setDisabled( mTestPluginComponentName, PluginEnabler.DISABLED_FROM_SYSTEM_CRASH); } @Test - public void testDisableWhitelisted() throws Exception { - PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(), + public void testDisablePrivileged() throws Exception { + PluginActionManager.Factory factory = new PluginActionManager.Factory(getContext(), mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, - mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE)); - factory.setInstanceFactory(mPluginInstanceFactory); - mPluginInstanceManager = factory.create("myAction", mMockListener, - true, mMockVersionInfo, false); + mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE), + mPluginInstanceFactory); + mPluginActionManager = factory.create("myAction", mMockListener, + TestPlugin.class, true, false); createPlugin(); // Get into valid created state. - mPluginInstanceManager.disableAll(); + mPluginActionManager.disableAll(); verify(mMockPm, never()).setComponentEnabledSetting( ArgumentCaptor.forClass(ComponentName.class).capture(), @@ -282,14 +264,14 @@ public class PluginInstanceManagerTest extends SysuiTestCase { private void createPlugin() throws Exception { setupFakePmQuery(); - mPluginInstanceManager.loadAll(); + mPluginActionManager.loadAll(); mFakeExecutor.runAllReady(); } // Real context with no registering/unregistering of receivers. private static class MyContextWrapper extends SysuiTestableContext { - public MyContextWrapper(Context base) { + MyContextWrapper(Context base) { super(base); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java new file mode 100644 index 0000000000000..bb9a1e971fd0f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 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.systemui.shared.plugins; + +import static junit.framework.Assert.assertNotNull; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.plugins.annotations.Requires; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Collections; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PluginInstanceTest extends SysuiTestCase { + + private static final String PRIVILEGED_PACKAGE = "com.android.systemui.plugins"; + + @Mock + private TestPluginImpl mMockPlugin; + @Mock + private PluginListener mMockListener; + @Mock + private VersionInfo mVersionInfo; + ComponentName mTestPluginComponentName = + new ComponentName(PRIVILEGED_PACKAGE, TestPluginImpl.class.getName()); + private PluginInstance mPluginInstance; + private PluginInstance.Factory mPluginInstanceFactory; + + private ApplicationInfo mAppInfo; + private Context mPluginContext; + @Mock + private PluginInstance.VersionChecker mVersionChecker; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + mAppInfo = mContext.getApplicationInfo(); + mAppInfo.packageName = mTestPluginComponentName.getPackageName(); + when(mVersionChecker.checkVersion(any(), any(), any())).thenReturn(mVersionInfo); + + mPluginInstanceFactory = new PluginInstance.Factory( + this.getClass().getClassLoader(), + new PluginInstance.InstanceFactory() { + @Override + TestPlugin create(Class cls) { + return mMockPlugin; + } + }, + mVersionChecker, + Collections.singletonList(PRIVILEGED_PACKAGE), + false); + + mPluginInstance = mPluginInstanceFactory.create( + mContext, mAppInfo, mTestPluginComponentName, TestPlugin.class); + mPluginContext = mPluginInstance.getPluginContext(); + } + + @Test + public void testCorrectVersion() { + assertNotNull(mPluginInstance); + } + + @Test(expected = VersionInfo.InvalidVersionException.class) + public void testIncorrectVersion() throws Exception { + + ComponentName wrongVersionTestPluginComponentName = + new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName()); + + when(mVersionChecker.checkVersion(any(), any(), any())).thenThrow( + new VersionInfo.InvalidVersionException("test", true)); + + mPluginInstanceFactory.create( + mContext, mAppInfo, wrongVersionTestPluginComponentName, TestPlugin.class); + } + + @Test + public void testOnCreate() { + mPluginInstance.onCreate(mContext, mMockListener); + verify(mMockPlugin).onCreate(mContext, mPluginContext); + verify(mMockListener).onPluginConnected(mMockPlugin, mPluginContext); + } + + @Test + public void testOnDestroy() { + mPluginInstance.onDestroy(mMockListener); + verify(mMockListener).onPluginDisconnected(mMockPlugin); + verify(mMockPlugin).onDestroy(); + } + + // This target class doesn't matter, it just needs to have a Requires to hit the flow where + // the mock version info is called. + @ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION) + public interface TestPlugin extends Plugin { + int VERSION = 1; + String ACTION = "testAction"; + } + + @Requires(target = TestPlugin.class, version = TestPlugin.VERSION) + public static class TestPluginImpl implements TestPlugin { + @Override + public void onCreate(Context sysuiContext, Context pluginContext) { + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java index 4590dd8295506..1eadd522352e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java @@ -13,6 +13,7 @@ */ package com.android.systemui.shared.plugins; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -54,10 +55,10 @@ public class PluginManagerTest extends SysuiTestCase { private static final String PRIVILEGED_PACKAGE = "com.android.systemui"; - private PluginInstanceManager.Factory mMockFactory; - private PluginInstanceManager mMockPluginInstance; + private PluginActionManager.Factory mMockFactory; + private PluginActionManager mMockPluginInstance; private PluginManagerImpl mPluginManager; - private PluginListener mMockListener; + private PluginListener mMockListener; private PackageManager mMockPackageManager; private PluginEnabler mPluginEnabler; private PluginPrefs mPluginPrefs; @@ -70,11 +71,11 @@ public class PluginManagerTest extends SysuiTestCase { public void setup() throws Exception { mRealExceptionHandler = Thread.getUncaughtExceptionPreHandler(); mMockExceptionHandler = mock(UncaughtExceptionHandler.class); - mMockFactory = mock(PluginInstanceManager.Factory.class); - mMockPluginInstance = mock(PluginInstanceManager.class); + mMockFactory = mock(PluginActionManager.Factory.class); + mMockPluginInstance = mock(PluginActionManager.class); mPluginEnabler = mock(PluginEnabler.class); mPluginPrefs = mock(PluginPrefs.class); - when(mMockFactory.create(any(), any(), Mockito.anyBoolean(), any(), Mockito.anyBoolean())) + when(mMockFactory.create(any(), any(), eq(TestPlugin.class), anyBoolean(), anyBoolean())) .thenReturn(mMockPluginInstance); mMockPackageManager = mock(PackageManager.class); @@ -116,8 +117,8 @@ public class PluginManagerTest extends SysuiTestCase { applicationInfo.sourceDir = sourceDir; applicationInfo.packageName = PRIVILEGED_PACKAGE; mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false), - any(VersionInfo.class), eq(false)); + verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(TestPlugin.class), + eq(false), eq(false)); verify(mMockPluginInstance).loadAll(); } @@ -138,8 +139,8 @@ public class PluginManagerTest extends SysuiTestCase { invalidApplicationInfo.sourceDir = sourceDir; invalidApplicationInfo.packageName = "com.android.invalidpackage"; mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); - verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false), - any(VersionInfo.class), eq(false)); + verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(TestPlugin.class), + eq(false), eq(false)); verify(mMockPluginInstance).loadAll(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java index 8e1c0f7301e1e..d245c727dcf81 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java @@ -30,24 +30,24 @@ public class FakePluginManager implements PluginManager { @Override public void addPluginListener(String action, PluginListener listener, - Class cls, boolean allowMultiple) { + Class cls, boolean allowMultiple) { mLeakChecker.addCallback(listener); } @Override - public void addPluginListener(PluginListener listener, Class cls) { + public void addPluginListener(PluginListener listener, Class cls) { mLeakChecker.addCallback(listener); } @Override - public void addPluginListener(PluginListener listener, Class cls, + public void addPluginListener(PluginListener listener, Class cls, boolean allowMultiple) { mLeakChecker.addCallback(listener); } @Override public void addPluginListener(String action, PluginListener listener, - Class cls) { + Class cls) { mLeakChecker.addCallback(listener); } From 3b44249990e132294730df49883a728bb3dc8f56 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Fri, 10 Sep 2021 14:04:59 +0000 Subject: [PATCH 2/4] Revert "Revert "3/N Remove #getPrivilegedPlugins from PluginInit..." Revert submission 15798157-revert-15560568-b194781951-flags-8-ZUNMWRYIZP Reason for revert: Restoring with a fix. Reverted Changes: I417dd4edd:Revert "5/N Remove PluginInitializer entirely." Ia5f864646:Revert "4/N Remove #handleWtfs from PluginInitiali... I70f22b6a5:Revert "3/N Remove #getPrivilegedPlugins from Plug... I07a26c9ff:Revert "2/N Rename PluginInstanceManager to Plugin... Change-Id: I9719833fe3888636fc56ba741918f87804f10fd7 --- .../systemui/shared/plugins/PluginInitializer.java | 8 -------- .../android/systemui/plugins/PluginInitializerImpl.java | 8 -------- .../src/com/android/systemui/plugins/PluginsModule.java | 5 +++-- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java index f0e0320e165f1..868f22ee4bed6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java @@ -14,19 +14,11 @@ package com.android.systemui.shared.plugins; -import android.content.Context; - /** * Provides necessary components for initializing {@link PluginManagerImpl}. */ public interface PluginInitializer { - /** - * Return a list of plugins that don't get disabled when an exception occurs. - */ - String[] getPrivilegedPlugins(Context context); - - /** * Called from {@link PluginActionManager}. */ diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java index 654d000ca09e9..09af98ea5244e 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java @@ -14,10 +14,8 @@ package com.android.systemui.plugins; -import android.content.Context; import android.util.Log; -import com.android.systemui.R; import com.android.systemui.shared.plugins.PluginInitializer; import com.android.systemui.shared.plugins.PluginManagerImpl; @@ -39,12 +37,6 @@ public class PluginInitializerImpl implements PluginInitializer { dependencyProvider.allowPluginDependency(ActivityStarter.class); } - @Override - public String[] getPrivilegedPlugins(Context context) { - return context.getResources().getStringArray(R.array.config_pluginWhitelist); - } - - @Override public void handleWtfs() { if (WTFS_SHOULD_CRASH && !mWtfsSet) { diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java index 6a93fb7e4861a..11c5efc4ed7db 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; +import com.android.systemui.R; import com.android.systemui.dagger.PluginModule; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.plugins.PluginActionManager; @@ -126,7 +127,7 @@ public abstract class PluginsModule { @Provides @Named(PLUGIN_PRIVILEGED) - static List providesPrivilegedPlugins(PluginInitializer initializer, Context context) { - return Arrays.asList(initializer.getPrivilegedPlugins(context)); + static List providesPrivilegedPlugins(Context context) { + return Arrays.asList(context.getResources().getStringArray(R.array.config_pluginWhitelist)); } } From 2d1f3a7afa21d1db10d513d23fdc5a3cf6ecae4a Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Fri, 10 Sep 2021 14:04:59 +0000 Subject: [PATCH 3/4] Revert^2 "4/N Remove #handleWtfs from PluginInitializer." f3a62151d096970945534097dbe50567091468d0 Change-Id: Id988d944dc028a2f24a42a26c7faa85cb6d078b9 --- .../shared/plugins/PluginActionManager.java | 1 - .../shared/plugins/PluginInitializer.java | 5 ----- .../plugins/PluginInitializerImpl.java | 20 ------------------- 3 files changed, 26 deletions(-) diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java index af541f07fb68a..3275d2d804bef 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java @@ -212,7 +212,6 @@ public class PluginActionManager { private void onPluginConnected(PluginInstance pluginInstance) { if (DEBUG) Log.d(TAG, "onPluginConnected"); PluginPrefs.setHasPlugins(mContext); - mInitializer.handleWtfs(); pluginInstance.onCreate(mContext, mListener); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java index 868f22ee4bed6..5890f81cc6936 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java @@ -18,9 +18,4 @@ package com.android.systemui.shared.plugins; * Provides necessary components for initializing {@link PluginManagerImpl}. */ public interface PluginInitializer { - - /** - * Called from {@link PluginActionManager}. - */ - void handleWtfs(); } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java index 09af98ea5244e..8effd53ef56da 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java @@ -14,10 +14,7 @@ package com.android.systemui.plugins; -import android.util.Log; - import com.android.systemui.shared.plugins.PluginInitializer; -import com.android.systemui.shared.plugins.PluginManagerImpl; import javax.inject.Inject; import javax.inject.Singleton; @@ -25,25 +22,8 @@ import javax.inject.Singleton; /** */ @Singleton public class PluginInitializerImpl implements PluginInitializer { - - /** - * True if WTFs should lead to crashes - */ - private static final boolean WTFS_SHOULD_CRASH = false; - private boolean mWtfsSet; - @Inject public PluginInitializerImpl(PluginDependencyProvider dependencyProvider) { dependencyProvider.allowPluginDependency(ActivityStarter.class); } - - @Override - public void handleWtfs() { - if (WTFS_SHOULD_CRASH && !mWtfsSet) { - mWtfsSet = true; - Log.setWtfHandler((tag, what, system) -> { - throw new PluginManagerImpl.CrashWhilePluginActiveException(what); - }); - } - } } From 570aa53d5aebf49323bb0d714002b690e3276a17 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Fri, 10 Sep 2021 14:04:59 +0000 Subject: [PATCH 4/4] Revert^2 "5/N Remove PluginInitializer entirely." 12d8e155ab07d6e97aa0d09dcbe0633a8bd490e5 Change-Id: I9b98d1e218d590f1c753a6c257e61c6f99a8e6d8 --- .../shared/plugins/PluginActionManager.java | 9 ++---- .../shared/plugins/PluginInitializer.java | 21 ------------- .../android/systemui/dagger/PluginModule.java | 30 +++++++++++-------- .../plugins/PluginInitializerImpl.java | 29 ------------------ .../systemui/plugins/PluginsModule.java | 8 ++--- .../plugins/PluginActionManagerTest.java | 8 ++--- 6 files changed, 25 insertions(+), 80 deletions(-) delete mode 100644 packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java delete mode 100644 packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java index 3275d2d804bef..9ea4b578ad3bf 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java @@ -73,7 +73,6 @@ public class PluginActionManager { private final boolean mIsDebuggable; private final PackageManager mPm; private final Class mPluginClass; - private final PluginInitializer mInitializer; private final Executor mMainExecutor; private final Executor mBgExecutor; @@ -87,13 +86,11 @@ public class PluginActionManager { Executor mainExecutor, Executor bgExecutor, boolean debuggable, - PluginInitializer initializer, NotificationManager notificationManager, PluginEnabler pluginEnabler, List privilegedPlugins, PluginInstance.Factory pluginInstanceFactory) { mPluginClass = pluginClass; - mInitializer = initializer; mMainExecutor = mainExecutor; mBgExecutor = bgExecutor; mContext = context; @@ -376,21 +373,19 @@ public class PluginActionManager { private final PackageManager mPackageManager; private final Executor mMainExecutor; private final Executor mBgExecutor; - private final PluginInitializer mInitializer; private final NotificationManager mNotificationManager; private final PluginEnabler mPluginEnabler; private final List mPrivilegedPlugins; private final PluginInstance.Factory mPluginInstanceFactory; public Factory(Context context, PackageManager packageManager, - Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer, + Executor mainExecutor, Executor bgExecutor, NotificationManager notificationManager, PluginEnabler pluginEnabler, List privilegedPlugins, PluginInstance.Factory pluginInstanceFactory) { mContext = context; mPackageManager = packageManager; mMainExecutor = mainExecutor; mBgExecutor = bgExecutor; - mInitializer = initializer; mNotificationManager = notificationManager; mPluginEnabler = pluginEnabler; mPrivilegedPlugins = privilegedPlugins; @@ -402,7 +397,7 @@ public class PluginActionManager { boolean allowMultiple, boolean debuggable) { return new PluginActionManager<>(mContext, mPackageManager, action, listener, pluginClass, allowMultiple, mMainExecutor, mBgExecutor, - debuggable, mInitializer, mNotificationManager, mPluginEnabler, + debuggable, mNotificationManager, mPluginEnabler, mPrivilegedPlugins, mPluginInstanceFactory); } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java deleted file mode 100644 index 5890f81cc6936..0000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2018 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.systemui.shared.plugins; - -/** - * Provides necessary components for initializing {@link PluginManagerImpl}. - */ -public interface PluginInitializer { -} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java index 406981d0c4adc..67ad3db74f8ad 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java @@ -24,6 +24,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.GlobalActions; +import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarStateControllerImpl; @@ -32,6 +33,7 @@ import com.android.systemui.volume.VolumeDialogControllerImpl; import dagger.Binds; import dagger.Module; +import dagger.Provides; /** * Module for binding Plugin implementations. @@ -39,36 +41,40 @@ import dagger.Module; * TODO(b/166258224): Many of these should be moved closer to their implementations. */ @Module -public interface PluginModule { +public abstract class PluginModule { + + /** */ + @Provides + static ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate, + PluginDependencyProvider dependencyProvider) { + dependencyProvider.allowPluginDependency(ActivityStarter.class, delegate); + return delegate; + } /** */ @Binds - ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate); + abstract DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherImpl controllerImpl); /** */ @Binds - DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherImpl controllerImpl); + abstract FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl); /** */ @Binds - FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl); + abstract GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl); /** */ @Binds - GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl); - - /** */ - @Binds - GlobalActions.GlobalActionsManager provideGlobalActionsManager( + abstract GlobalActions.GlobalActionsManager provideGlobalActionsManager( GlobalActionsComponent controllerImpl); /** */ @Binds - StatusBarStateController provideStatusBarStateController( + abstract StatusBarStateController provideStatusBarStateController( StatusBarStateControllerImpl controllerImpl); /** */ @Binds - VolumeDialogController provideVolumeDialogController(VolumeDialogControllerImpl controllerImpl); - + abstract VolumeDialogController provideVolumeDialogController( + VolumeDialogControllerImpl controllerImpl); } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java deleted file mode 100644 index 8effd53ef56da..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2018 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.systemui.plugins; - -import com.android.systemui.shared.plugins.PluginInitializer; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** */ -@Singleton -public class PluginInitializerImpl implements PluginInitializer { - @Inject - public PluginInitializerImpl(PluginDependencyProvider dependencyProvider) { - dependencyProvider.allowPluginDependency(ActivityStarter.class); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java index 11c5efc4ed7db..a0cf44c8bfdcc 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java @@ -28,7 +28,6 @@ import com.android.systemui.dagger.PluginModule; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.plugins.PluginActionManager; import com.android.systemui.shared.plugins.PluginEnabler; -import com.android.systemui.shared.plugins.PluginInitializer; import com.android.systemui.shared.plugins.PluginInstance; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginManagerImpl; @@ -69,9 +68,6 @@ public abstract class PluginsModule { @Binds abstract PluginEnabler bindsPluginEnablerImpl(PluginEnablerImpl impl); - @Binds - abstract PluginInitializer bindsPluginInitializerImpl(PluginInitializerImpl impl); - @Provides @Singleton static PluginInstance.Factory providesPluginInstanceFactory( @@ -89,12 +85,12 @@ public abstract class PluginsModule { @Singleton static PluginActionManager.Factory providePluginInstanceManagerFactory(Context context, PackageManager packageManager, @Main Executor mainExecutor, - @Named(PLUGIN_THREAD) Executor pluginExecutor, PluginInitializer initializer, + @Named(PLUGIN_THREAD) Executor pluginExecutor, NotificationManager notificationManager, PluginEnabler pluginEnabler, @Named(PLUGIN_PRIVILEGED) List privilegedPlugins, PluginInstance.Factory pluginInstanceFactory) { return new PluginActionManager.Factory( - context, packageManager, mainExecutor, pluginExecutor, initializer, + context, packageManager, mainExecutor, pluginExecutor, notificationManager, pluginEnabler, privilegedPlugins, pluginInstanceFactory); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java index ce366657b7b24..05280fa826ed7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginActionManagerTest.java @@ -74,7 +74,6 @@ public class PluginActionManagerTest extends SysuiTestCase { private PluginEnabler mMockEnabler; ComponentName mTestPluginComponentName = new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName()); - private PluginInitializer mInitializer; private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); NotificationManager mNotificationManager; private PluginInstance mPluginInstance; @@ -98,14 +97,13 @@ public class PluginActionManagerTest extends SysuiTestCase { mMockListener = mock(PluginListener.class); mMockEnabler = mock(PluginEnabler.class); mMockVersionInfo = mock(VersionInfo.class); - mInitializer = mock(PluginInitializer.class); mNotificationManager = mock(NotificationManager.class); mMockPlugin = mock(TestPlugin.class); mPluginInstance = mock(PluginInstance.class); when(mPluginInstance.getComponentName()).thenReturn(mTestPluginComponentName); when(mPluginInstance.getPackage()).thenReturn(mTestPluginComponentName.getPackageName()); mActionManagerFactory = new PluginActionManager.Factory(getContext(), mMockPm, - mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, mMockEnabler, + mFakeExecutor, mFakeExecutor, mNotificationManager, mMockEnabler, new ArrayList<>(), mPluginInstanceFactory); mPluginActionManager = mActionManagerFactory.create("myAction", mMockListener, @@ -178,7 +176,7 @@ public class PluginActionManagerTest extends SysuiTestCase { public void testNonDebuggable_privileged() throws Exception { // Create a version that thinks the build is not debuggable. PluginActionManager.Factory factory = new PluginActionManager.Factory(getContext(), - mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, + mMockPm, mFakeExecutor, mFakeExecutor, mNotificationManager, mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE), mPluginInstanceFactory); mPluginActionManager = factory.create("myAction", mMockListener, @@ -222,7 +220,7 @@ public class PluginActionManagerTest extends SysuiTestCase { @Test public void testDisablePrivileged() throws Exception { PluginActionManager.Factory factory = new PluginActionManager.Factory(getContext(), - mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, + mMockPm, mFakeExecutor, mFakeExecutor, mNotificationManager, mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE), mPluginInstanceFactory); mPluginActionManager = factory.create("myAction", mMockListener,