Merge "1/N Move plugin loading into PluginInstanceManager." into sc-v2-dev am: 498d436d54 am: 875d917932
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15499818
Change-Id: I9ea7f2299d8ac728b4b2b97edc3bf2e8298e61ae
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
index e4610ba..dcd3b3e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -14,6 +14,7 @@
package com.android.systemui.shared.plugins;
+import android.app.LoadedApk;
import android.app.Notification;
import android.app.Notification.Action;
import android.app.NotificationManager;
@@ -28,6 +29,8 @@
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;
@@ -39,9 +42,12 @@
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.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
public class PluginInstanceManager<T extends Plugin> {
@@ -56,33 +62,42 @@
private final String mAction;
private final boolean mAllowMultiple;
private final VersionInfo mVersion;
+ private final NotificationManager mNotificationManager;
+ private final PluginEnabler mPluginEnabler;
+ private final InstanceFactory<T> mInstanceFactory;
+ private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
+ private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
@VisibleForTesting
private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
- private final boolean isDebuggable;
+ private final boolean mIsDebuggable;
private final PackageManager mPm;
- private final PluginManagerImpl mManager;
- private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
private final PluginInitializer mInitializer;
private final Executor mMainExecutor;
private final Executor mBgExecutor;
- PluginInstanceManager(Context context, PackageManager pm, String action,
+ private PluginManagerImpl.ClassLoaderFilter mParentClassLoader;
+
+ private PluginInstanceManager(Context context, PackageManager pm, String action,
PluginListener<T> listener, boolean allowMultiple, Executor mainExecutor,
- Executor bgExecutor, VersionInfo version, PluginManagerImpl manager, boolean debuggable,
- String[] pluginWhitelist, PluginInitializer initializer) {
+ Executor bgExecutor, VersionInfo version, boolean debuggable,
+ PluginInitializer initializer, NotificationManager notificationManager,
+ PluginEnabler pluginEnabler, List<String> privilegedPlugins,
+ InstanceFactory<T> instanceFactory) {
mInitializer = initializer;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
- mManager = manager;
mContext = context;
mPm = pm;
mAction = action;
mListener = listener;
mAllowMultiple = allowMultiple;
mVersion = version;
- mWhitelistedPlugins.addAll(Arrays.asList(pluginWhitelist));
- isDebuggable = debuggable;
+ mNotificationManager = notificationManager;
+ mPluginEnabler = pluginEnabler;
+ mInstanceFactory = instanceFactory;
+ mPrivilegedPlugins.addAll(privilegedPlugins);
+ mIsDebuggable = debuggable;
}
public void loadAll() {
@@ -127,8 +142,22 @@
return disabledAny;
}
- private boolean isPluginWhitelisted(ComponentName pluginName) {
- for (String componentNameOrPackage : mWhitelistedPlugins) {
+ 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())) {
@@ -151,12 +180,12 @@
// 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 (isPluginWhitelisted(pluginComponent)) {
+ 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());
- mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
+ mPluginEnabler.setDisabled(pluginComponent, reason);
return true;
}
@@ -228,134 +257,164 @@
}
}
- 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<ResolveInfo> 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<T> pluginInfo = handleLoadPlugin(name);
- if (pluginInfo == null) continue;
-
- // add plugin before sending PLUGIN_CONNECTED message
- mPlugins.add(pluginInfo);
- mMainExecutor.execute(() -> onPluginConnected(pluginInfo));
- }
+ 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<ResolveInfo> 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<T> pluginInfo = handleLoadPlugin(name);
+ if (pluginInfo == null) continue;
- protected PluginInfo<T> 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 (!isDebuggable && !isPluginWhitelisted(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);
+ // add plugin before sending PLUGIN_CONNECTED message
+ mPlugins.add(pluginInfo);
+ mMainExecutor.execute(() -> onPluginConnected(pluginInfo));
+ }
+ }
+
+ protected PluginInfo<T> 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;
}
- if (!mManager.getPluginEnabler().isEnabled(component)) {
- if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
- return null;
- }
- String pkg = component.getPackageName();
- String cls = component.getClassName();
+ // 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 {
- 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 = mManager.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 = (T) pluginClass.newInstance();
+ 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 {
- 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());
- mContext.getSystemService(NotificationManager.class)
- .notify(SystemMessage.NOTE_PLUGIN, nb.build());
- // TODO: Warn user.
- Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
- + ", expected " + mVersion);
- return null;
+ label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString();
+ } catch (NameNotFoundException e2) {
}
- } catch (Throwable e) {
- Log.w(TAG, "Couldn't load plugin: " + pkg, e);
+ 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);
}
- 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;
+ List<String> zipPaths = new ArrayList<>();
+ List<String> 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}
@@ -366,23 +425,40 @@
private final Executor mMainExecutor;
private final Executor mBgExecutor;
private final PluginInitializer mInitializer;
+ private final NotificationManager mNotificationManager;
+ private final PluginEnabler mPluginEnabler;
+ private final List<String> mPrivilegedPlugins;
+ private InstanceFactory<?> mInstanceFactory;
public Factory(Context context, PackageManager packageManager,
- Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer) {
+ Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer,
+ NotificationManager notificationManager, PluginEnabler pluginEnabler,
+ List<String> privilegedPlugins) {
mContext = context;
mPackageManager = packageManager;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
mInitializer = initializer;
+ mNotificationManager = notificationManager;
+ mPluginEnabler = pluginEnabler;
+ mPrivilegedPlugins = privilegedPlugins;
+
+ mInstanceFactory = new InstanceFactory<>();
+ }
+
+ @VisibleForTesting
+ <T extends Plugin> Factory setInstanceFactory(InstanceFactory<T> instanceFactory) {
+ mInstanceFactory = instanceFactory;
+ return this;
}
<T extends Plugin> PluginInstanceManager<T> create(
- String action,
- PluginListener<T> listener, boolean allowMultiple, VersionInfo version,
- PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
- return new PluginInstanceManager<>(mContext, mPackageManager, action, listener,
- allowMultiple, mMainExecutor, mBgExecutor, version, manager, debuggable,
- pluginWhitelist, mInitializer);
+ String action, PluginListener<T> listener, boolean allowMultiple,
+ VersionInfo version, boolean debuggable) {
+ return new PluginInstanceManager<T>(mContext, mPackageManager, action, listener,
+ allowMultiple, mMainExecutor, mBgExecutor, version, debuggable,
+ mInitializer, mNotificationManager, mPluginEnabler,
+ mPrivilegedPlugins, (InstanceFactory<T>) mInstanceFactory);
}
}
@@ -428,4 +504,10 @@
mVersion = info;
}
}
+
+ static class InstanceFactory<T extends Plugin> {
+ T create(Class cls) throws IllegalAccessException, InstantiationException {
+ return (T) cls.newInstance();
+ }
+ }
}
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 cc37be5..ea7b0c3 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
@@ -14,24 +14,15 @@
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.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -41,14 +32,9 @@
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import dalvik.system.PathClassLoader;
-
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -64,16 +50,13 @@
private final ArrayMap<PluginListener<?>, PluginInstanceManager<?>> mPluginMap
= new ArrayMap<>();
private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
- private final ArraySet<String> mOneShotPackages = new ArraySet<>();
private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
private final Context mContext;
private final PluginInstanceManager.Factory mInstanceManagerFactory;
private final boolean mIsDebuggable;
private final PluginPrefs mPluginPrefs;
private final PluginEnabler mPluginEnabler;
- private ClassLoaderFilter mParentClassLoader;
private boolean mListening;
- private boolean mHasOneShot;
public PluginManagerImpl(Context context,
PluginInstanceManager.Factory instanceManagerFactory,
@@ -81,11 +64,11 @@
Optional<UncaughtExceptionHandler> defaultHandlerOptional,
PluginEnabler pluginEnabler,
PluginPrefs pluginPrefs,
- String[] privilegedPlugins) {
+ List<String> privilegedPlugins) {
mContext = context;
mInstanceManagerFactory = instanceManagerFactory;
mIsDebuggable = debuggable;
- mPrivilegedPlugins.addAll(Arrays.asList(privilegedPlugins));
+ mPrivilegedPlugins.addAll(privilegedPlugins);
mPluginPrefs = pluginPrefs;
mPluginEnabler = pluginEnabler;
@@ -102,10 +85,6 @@
return mPrivilegedPlugins.toArray(new String[0]);
}
- public PluginEnabler getPluginEnabler() {
- return mPluginEnabler;
- }
-
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
addPluginListener(listener, cls, false);
}
@@ -124,8 +103,7 @@
Class<?> cls, boolean allowMultiple) {
mPluginPrefs.addAction(action);
PluginInstanceManager<T> p = mInstanceManagerFactory.create(action, listener, allowMultiple,
- new VersionInfo().addClass(cls), this, isDebuggable(),
- getPrivilegedPlugins());
+ new VersionInfo().addClass(cls), isDebuggable());
p.loadAll();
synchronized (this) {
mPluginMap.put(listener, p);
@@ -163,8 +141,7 @@
}
private void stopListening() {
- // Never stop listening if a one-shot is present.
- if (!mListening || mHasOneShot) return;
+ if (!mListening) return;
mListening = false;
mContext.unregisterReceiver(this);
}
@@ -185,42 +162,13 @@
// Don't disable privileged plugins as they are a part of the OS.
return;
}
- getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
+ mPluginEnabler.setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
SystemMessage.NOTE_PLUGIN);
} else {
Uri data = intent.getData();
String pkg = data.getEncodedSchemeSpecificPart();
ComponentName componentName = ComponentName.unflattenFromString(pkg);
- if (mOneShotPackages.contains(pkg)) {
- int icon = Resources.getSystem().getIdentifier(
- "stat_sys_warning", "drawable", "android");
- int color = Resources.getSystem().getIdentifier(
- "system_notification_accent_color", "color", "android");
- String label = pkg;
- try {
- PackageManager pm = mContext.getPackageManager();
- label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
- } catch (NameNotFoundException e) {
- }
- // Localization not required as this will never ever appear in a user build.
- final Notification.Builder nb =
- new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
- .setSmallIcon(icon)
- .setWhen(0)
- .setShowWhen(false)
- .setPriority(Notification.PRIORITY_MAX)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(mContext.getColor(color))
- .setContentTitle("Plugin \"" + label + "\" has updated")
- .setContentText("Restart SysUI for changes to take effect.");
- Intent i = new Intent("com.android.systemui.action.RESTART").setData(
- Uri.parse("package://" + pkg));
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, PendingIntent.FLAG_MUTABLE_UNAUDITED);
- nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
- mContext.getSystemService(NotificationManager.class)
- .notify(SystemMessage.NOTE_PLUGIN, nb.build());
- }
if (clearClassLoader(pkg)) {
if (Build.IS_ENG) {
Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
@@ -231,13 +179,13 @@
if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())
&& componentName != null) {
@PluginEnabler.DisableReason int disableReason =
- getPluginEnabler().getDisableReason(componentName);
+ mPluginEnabler.getDisableReason(componentName);
if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH
|| disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH
|| disableReason == PluginEnabler.DISABLED_INVALID_VERSION) {
Log.i(TAG, "Re-enabling previously disabled plugin that has been "
+ "updated: " + componentName.flattenToShortString());
- getPluginEnabler().setEnabled(componentName);
+ mPluginEnabler.setEnabled(componentName);
}
}
synchronized (this) {
@@ -254,41 +202,10 @@
}
}
- /** 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<String> zipPaths = new ArrayList<>();
- List<String> 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 boolean clearClassLoader(String pkg) {
return mClassLoaders.remove(pkg) != null;
}
- ClassLoader getParentClassLoader() {
- if (mParentClassLoader == null) {
- // Lazily load this so it doesn't have any effect on devices without plugins.
- mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
- "com.android.systemui.plugin");
- }
- return mParentClassLoader;
- }
-
public <T> boolean dependsOn(Plugin p, Class<T> cls) {
synchronized (this) {
for (int i = 0; i < mPluginMap.size(); i++) {
@@ -310,20 +227,6 @@
}
}
- 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);
@@ -340,7 +243,7 @@
// This allows plugins to include any libraries or copied code they want by only including
// classes from the plugin library.
- private static class ClassLoaderFilter extends ClassLoader {
+ static class ClassLoaderFilter extends ClassLoader {
private final String mPackage;
private final ClassLoader mBase;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
index 3a5b1f7..1ea9b3c 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -18,6 +18,7 @@
import static com.android.systemui.util.concurrency.GlobalConcurrencyModule.PRE_HANDLER;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
@@ -32,6 +33,8 @@
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
import com.android.systemui.util.concurrency.ThreadFactory;
+import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -70,9 +73,12 @@
@Singleton
static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context,
PackageManager packageManager, @Main Executor mainExecutor,
- @Named(PLUGIN_THREAD) Executor pluginExecutor, PluginInitializer initializer) {
+ @Named(PLUGIN_THREAD) Executor pluginExecutor, PluginInitializer initializer,
+ NotificationManager notificationManager, PluginEnabler pluginEnabler,
+ @Named(PLUGIN_PRIVILEGED) List<String> privilegedPlugins) {
return new PluginInstanceManager.Factory(
- context, packageManager, mainExecutor, pluginExecutor, initializer);
+ context, packageManager, mainExecutor, pluginExecutor, initializer,
+ notificationManager, pluginEnabler, privilegedPlugins);
}
@Provides
@@ -91,7 +97,7 @@
Optional<Thread.UncaughtExceptionHandler> uncaughtExceptionHandlerOptional,
PluginEnabler pluginEnabler,
PluginPrefs pluginPrefs,
- @Named(PLUGIN_PRIVILEGED) String[] privilegedPlugins) {
+ @Named(PLUGIN_PRIVILEGED) List<String> privilegedPlugins) {
return new PluginManagerImpl(context, instanceManagerFactory, debug,
uncaughtExceptionHandlerOptional, pluginEnabler, pluginPrefs,
privilegedPlugins);
@@ -104,7 +110,7 @@
@Provides
@Named(PLUGIN_PRIVILEGED)
- static String[] providesPrivilegedPlugins(PluginInitializer initializer, Context context) {
- return initializer.getPrivilegedPlugins(context);
+ static List<String> providesPrivilegedPlugins(PluginInitializer initializer, Context context) {
+ return Arrays.asList(initializer.getPrivilegedPlugins(context));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index ad2cbbd..790b4dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -31,7 +31,6 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -44,6 +43,7 @@
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;
@@ -51,12 +51,12 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Collections;
@@ -66,43 +66,46 @@
@RunWith(AndroidJUnit4.class)
public class PluginInstanceManagerTest extends SysuiTestCase {
- private static final String WHITELISTED_PACKAGE = "com.android.systemui";
- // Static since the plugin needs to be generated by the PluginInstanceManager using newInstance.
- private static Plugin sMockPlugin;
+ private static final String PRIVILEGED_PACKAGE = "com.android.systemui.shared.plugins";
+ private TestPlugin mMockPlugin;
- private Context mContextWrapper;
private PackageManager mMockPm;
- private PluginListener mMockListener;
- private PluginInstanceManager mPluginInstanceManager;
- private PluginManagerImpl mMockManager;
+ private PluginListener<Plugin> mMockListener;
+ private PluginInstanceManager<Plugin> mPluginInstanceManager;
private VersionInfo mMockVersionInfo;
private PluginEnabler mMockEnabler;
ComponentName mTestPluginComponentName =
- new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
+ new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName());
private PluginInitializer mInitializer;
private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ NotificationManager mNotificationManager;
+ private PluginInstanceManager.Factory mInstanceManagerFactory;
+ private final PluginInstanceManager.InstanceFactory<Plugin> mPluginInstanceFactory =
+ new PluginInstanceManager.InstanceFactory<Plugin>() {
+ @Override
+ Plugin create(Class cls) {
+ return mMockPlugin;
+ }
+ };
@Before
public void setup() throws Exception {
- mContextWrapper = new MyContextWrapper(getContext());
+ mContext = new MyContextWrapper(mContext);
mMockPm = mock(PackageManager.class);
mMockListener = mock(PluginListener.class);
- mMockManager = mock(PluginManagerImpl.class);
- when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader());
mMockEnabler = mock(PluginEnabler.class);
- when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
mMockVersionInfo = mock(VersionInfo.class);
mInitializer = mock(PluginInitializer.class);
- mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mFakeExecutor, mFakeExecutor,
- mMockVersionInfo, mMockManager, true, new String[0], mInitializer);
- sMockPlugin = mock(Plugin.class);
- when(sMockPlugin.getVersion()).thenReturn(1);
- }
+ mNotificationManager = mock(NotificationManager.class);
+ mMockPlugin = mock(TestPlugin.class);
+ mInstanceManagerFactory = new PluginInstanceManager.Factory(getContext(), mMockPm,
+ mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, mMockEnabler,
+ new ArrayList<>())
+ .setInstanceFactory(mPluginInstanceFactory);
- @After
- public void tearDown() {
- sMockPlugin = null;
+ mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener,
+ true, mMockVersionInfo, true);
+ when(mMockPlugin.getVersion()).thenReturn(1);
}
@Test
@@ -121,7 +124,7 @@
createPlugin();
// Verify startup lifecycle
- verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
+ verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
ArgumentCaptor.forClass(Context.class).capture());
verify(mMockListener).onPluginConnected(any(), any());
}
@@ -137,13 +140,11 @@
// Verify shutdown lifecycle
verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
- verify(sMockPlugin).onDestroy();
+ verify(mMockPlugin).onDestroy();
}
@Test
public void testIncorrectVersion() throws Exception {
- NotificationManager nm = mock(NotificationManager.class);
- mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, nm);
setupFakePmQuery();
doThrow(new InvalidVersionException("", false)).when(mMockVersionInfo).checkVersion(any());
@@ -151,25 +152,24 @@
mFakeExecutor.runAllReady();
-
// Plugin shouldn't be connected because it is the wrong version.
verify(mMockListener, never()).onPluginConnected(any(), any());
- verify(nm).notify(eq(SystemMessage.NOTE_PLUGIN), any());
+ verify(mNotificationManager).notify(eq(SystemMessage.NOTE_PLUGIN), any());
}
@Test
public void testReloadOnChange() throws Exception {
createPlugin(); // Get into valid created state.
- mPluginInstanceManager.onPackageChange("com.android.systemui");
+ mPluginInstanceManager.onPackageChange(PRIVILEGED_PACKAGE);
mFakeExecutor.runAllReady();
// Verify the old one was destroyed.
verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
- verify(sMockPlugin).onDestroy();
+ verify(mMockPlugin).onDestroy();
// Also verify we got a second onCreate.
- verify(sMockPlugin, Mockito.times(2)).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());
@@ -178,9 +178,8 @@
@Test
public void testNonDebuggable() throws Exception {
// Create a version that thinks the build is not debuggable.
- mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mFakeExecutor, mFakeExecutor,
- mMockVersionInfo, mMockManager, false, new String[0], mInitializer);
+ mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener,
+ true, mMockVersionInfo, false);
setupFakePmQuery();
mPluginInstanceManager.loadAll();
@@ -192,12 +191,14 @@
}
@Test
- public void testNonDebuggable_whitelist() throws Exception {
+ public void testNonDebuggable_privileged() throws Exception {
// Create a version that thinks the build is not debuggable.
- mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mFakeExecutor, mFakeExecutor,
- mMockVersionInfo, mMockManager, false,
- new String[] {WHITELISTED_PACKAGE}, mInitializer);
+ PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(),
+ mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager,
+ mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE));
+ factory.setInstanceFactory(mPluginInstanceFactory);
+ mPluginInstanceManager = factory.create("myAction", mMockListener,
+ true, mMockVersionInfo, false);
setupFakePmQuery();
mPluginInstanceManager.loadAll();
@@ -205,7 +206,7 @@
mFakeExecutor.runAllReady();
// Verify startup lifecycle
- verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
+ verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
ArgumentCaptor.forClass(Context.class).capture());
verify(mMockListener).onPluginConnected(any(), any());
}
@@ -238,10 +239,13 @@
@Test
public void testDisableWhitelisted() throws Exception {
- mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mFakeExecutor, mFakeExecutor,
- mMockVersionInfo, mMockManager, false, new String[] {WHITELISTED_PACKAGE},
- mInitializer);
+ PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(),
+ mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager,
+ mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE));
+ factory.setInstanceFactory(mPluginInstanceFactory);
+ mPluginInstanceManager = factory.create("myAction", mMockListener,
+ true, mMockVersionInfo, false);
+
createPlugin(); // Get into valid created state.
mPluginInstanceManager.disableAll();
@@ -266,9 +270,12 @@
when(mMockPm.checkPermission(Mockito.anyString(), Mockito.anyString())).thenReturn(
PackageManager.PERMISSION_GRANTED);
- ApplicationInfo appInfo = getContext().getApplicationInfo();
- when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn(
- appInfo);
+ when(mMockPm.getApplicationInfo(Mockito.anyString(), anyInt())).thenAnswer(
+ (Answer<ApplicationInfo>) invocation -> {
+ ApplicationInfo appInfo = getContext().getApplicationInfo();
+ appInfo.packageName = invocation.getArgument(0);
+ return appInfo;
+ });
when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true);
}
@@ -281,7 +288,7 @@
}
// Real context with no registering/unregistering of receivers.
- private static class MyContextWrapper extends ContextWrapper {
+ private static class MyContextWrapper extends SysuiTestableContext {
public MyContextWrapper(Context base) {
super(base);
}
@@ -307,17 +314,15 @@
public static class TestPlugin implements Plugin {
@Override
public int getVersion() {
- return sMockPlugin.getVersion();
+ return 1;
}
@Override
public void onCreate(Context sysuiContext, Context pluginContext) {
- sMockPlugin.onCreate(sysuiContext, pluginContext);
}
@Override
public void onDestroy() {
- sMockPlugin.onDestroy();
}
}
}
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 8b92066..4590dd8 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,9 +13,8 @@
*/
package com.android.systemui.shared.plugins;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,6 +43,8 @@
import org.mockito.Mockito;
import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Optional;
@SmallTest
@@ -51,10 +52,10 @@
@RunWithLooper
public class PluginManagerTest extends SysuiTestCase {
- private static final String WHITELISTED_PACKAGE = "com.android.systemui";
+ private static final String PRIVILEGED_PACKAGE = "com.android.systemui";
private PluginInstanceManager.Factory mMockFactory;
- private PluginInstanceManager mMockPluginInstance;
+ private PluginInstanceManager<Plugin> mMockPluginInstance;
private PluginManagerImpl mPluginManager;
private PluginListener<?> mMockListener;
private PackageManager mMockPackageManager;
@@ -73,16 +74,14 @@
mMockPluginInstance = mock(PluginInstanceManager.class);
mPluginEnabler = mock(PluginEnabler.class);
mPluginPrefs = mock(PluginPrefs.class);
- when(mMockFactory.create(Mockito.any(), Mockito.any(),
- Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.anyBoolean(),
- Mockito.any()))
+ when(mMockFactory.create(any(), any(), Mockito.anyBoolean(), any(), Mockito.anyBoolean()))
.thenReturn(mMockPluginInstance);
mMockPackageManager = mock(PackageManager.class);
mPluginManager = new PluginManagerImpl(
getContext(), mMockFactory, true,
Optional.of(mMockExceptionHandler), mPluginEnabler,
- mPluginPrefs, new String[0]);
+ mPluginPrefs, new ArrayList<>());
resetExceptionHandler();
mMockListener = mock(PluginListener.class);
@@ -105,46 +104,49 @@
@Test
@RunWithLooper(setAsMainLooper = true)
- public void testNonDebuggable_noWhitelist() {
+ public void testNonDebuggable_nonPrivileged() {
mPluginManager = new PluginManagerImpl(
getContext(), mMockFactory, false,
Optional.of(mMockExceptionHandler), mPluginEnabler,
- mPluginPrefs, new String[0]);
+ mPluginPrefs, new ArrayList<>());
resetExceptionHandler();
String sourceDir = "myPlugin";
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.sourceDir = sourceDir;
- applicationInfo.packageName = WHITELISTED_PACKAGE;
+ applicationInfo.packageName = PRIVILEGED_PACKAGE;
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- assertNull(mPluginManager.getClassLoader(applicationInfo));
+ verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false),
+ any(VersionInfo.class), eq(false));
+ verify(mMockPluginInstance).loadAll();
}
@Test
@RunWithLooper(setAsMainLooper = true)
- public void testNonDebuggable_whitelistedPkg() {
+ public void testNonDebuggable_privilegedPackage() {
mPluginManager = new PluginManagerImpl(
getContext(), mMockFactory, false,
Optional.of(mMockExceptionHandler), mPluginEnabler,
- mPluginPrefs, new String[] {WHITELISTED_PACKAGE});
+ mPluginPrefs, Collections.singletonList(PRIVILEGED_PACKAGE));
resetExceptionHandler();
String sourceDir = "myPlugin";
- ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo();
- whiteListedApplicationInfo.sourceDir = sourceDir;
- whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE;
+ ApplicationInfo privilegedApplicationInfo = new ApplicationInfo();
+ privilegedApplicationInfo.sourceDir = sourceDir;
+ privilegedApplicationInfo.packageName = PRIVILEGED_PACKAGE;
ApplicationInfo invalidApplicationInfo = new ApplicationInfo();
invalidApplicationInfo.sourceDir = sourceDir;
invalidApplicationInfo.packageName = "com.android.invalidpackage";
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo));
- assertNull(mPluginManager.getClassLoader(invalidApplicationInfo));
+ verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false),
+ any(VersionInfo.class), eq(false));
+ verify(mMockPluginInstance).loadAll();
}
@Test
public void testExceptionHandler_foundPlugin() {
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(true);
+ when(mMockPluginInstance.checkAndDisable(any())).thenReturn(true);
mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable());
@@ -159,7 +161,7 @@
@Test
public void testExceptionHandler_noFoundPlugin() {
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(false);
+ when(mMockPluginInstance.checkAndDisable(any())).thenReturn(false);
mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable());