Merge "Daggerize Plugin code." into sc-v2-dev am: 6b201d2575 am: a553714a4d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15477511 Change-Id: I83b55b084eebfd9d751d0ac35fb2ded94ff1661f
This commit is contained in:
@@ -15,31 +15,20 @@
|
||||
package com.android.systemui.shared.plugins;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* Provides necessary components for initializing {@link PluginManagerImpl}.
|
||||
*/
|
||||
public interface PluginInitializer {
|
||||
|
||||
Looper getBgLooper();
|
||||
|
||||
/**
|
||||
* Called from the bg looper during initialization of {@link PluginManagerImpl}.
|
||||
* Return a list of plugins that don't get disabled when an exception occurs.
|
||||
*/
|
||||
void onPluginManagerInit();
|
||||
String[] getPrivilegedPlugins(Context context);
|
||||
|
||||
String[] getWhitelistedPlugins(Context context);
|
||||
|
||||
PluginEnabler getPluginEnabler(Context context);
|
||||
|
||||
/**
|
||||
* Called from {@link PluginManagerImpl#handleWtfs()}.
|
||||
* Called from {@link PluginInstanceManager}.
|
||||
*/
|
||||
void handleWtfs();
|
||||
|
||||
/**
|
||||
* Returns if pluging manager should run in debug mode.
|
||||
*/
|
||||
boolean isDebuggable();
|
||||
}
|
||||
|
||||
@@ -67,17 +67,13 @@ public class PluginInstanceManager<T extends Plugin> {
|
||||
private final PackageManager mPm;
|
||||
private final PluginManagerImpl mManager;
|
||||
private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
|
||||
private final PluginInitializer mInitializer;
|
||||
|
||||
PluginInstanceManager(Context context, String action, PluginListener<T> listener,
|
||||
boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
|
||||
this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
|
||||
manager, manager.isDebuggable(), manager.getWhitelistedPlugins());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
PluginInstanceManager(Context context, PackageManager pm, String action,
|
||||
PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
|
||||
PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
|
||||
PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist,
|
||||
PluginInitializer initializer) {
|
||||
mInitializer = initializer;
|
||||
mMainHandler = new MainHandler(Looper.getMainLooper());
|
||||
mPluginHandler = new PluginHandler(looper);
|
||||
mManager = manager;
|
||||
@@ -214,7 +210,7 @@ public class PluginInstanceManager<T extends Plugin> {
|
||||
if (DEBUG) Log.d(TAG, "onPluginConnected");
|
||||
PluginPrefs.setHasPlugins(mContext);
|
||||
PluginInfo<T> info = (PluginInfo<T>) msg.obj;
|
||||
mManager.handleWtfs();
|
||||
mInitializer.handleWtfs();
|
||||
if (!(msg.obj instanceof PluginFragment)) {
|
||||
// Only call onDestroy for plugins that aren't fragments, as fragments
|
||||
// will get the onCreate as part of the fragment lifecycle.
|
||||
@@ -417,6 +413,33 @@ public class PluginInstanceManager<T extends Plugin> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link PluginInstanceManager}
|
||||
*/
|
||||
public static class Factory {
|
||||
private final Context mContext;
|
||||
private final PackageManager mPackageManager;
|
||||
private final Looper mLooper;
|
||||
private final PluginInitializer mInitializer;
|
||||
|
||||
public Factory(Context context, PackageManager packageManager, Looper looper,
|
||||
PluginInitializer initializer) {
|
||||
mContext = context;
|
||||
mPackageManager = packageManager;
|
||||
mLooper = looper;
|
||||
mInitializer = initializer;
|
||||
}
|
||||
|
||||
<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, mLooper, version, manager, debuggable, pluginWhitelist,
|
||||
mInitializer);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PluginContextWrapper extends ContextWrapper {
|
||||
private final ClassLoader mClassLoader;
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
@@ -27,7 +27,8 @@ public interface PluginManager {
|
||||
// must be one of the channels created in NotificationChannels.java
|
||||
String NOTIFICATION_CHANNEL_ID = "ALR";
|
||||
|
||||
String[] getWhitelistedPlugins();
|
||||
/** Returns plugins that don't get disabled when an exceptoin occurs. */
|
||||
String[] getPrivilegedPlugins();
|
||||
|
||||
<T extends Plugin> T getOneShotPlugin(Class<T> cls);
|
||||
<T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
|
||||
@@ -38,7 +39,7 @@ public interface PluginManager {
|
||||
<T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
|
||||
Class<?> cls);
|
||||
<T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
|
||||
Class cls, boolean allowMultiple);
|
||||
Class<?> cls, boolean allowMultiple);
|
||||
|
||||
void removePluginListener(PluginListener<?> listener);
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemProperties;
|
||||
import android.text.TextUtils;
|
||||
@@ -39,7 +38,6 @@ import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
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;
|
||||
@@ -56,6 +54,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @see Plugin
|
||||
*/
|
||||
@@ -64,57 +64,45 @@ 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<PluginListener<?>, PluginInstanceManager> mPluginMap
|
||||
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> mWhitelistedPlugins = new ArraySet<>();
|
||||
private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
|
||||
private final Context mContext;
|
||||
private final PluginInstanceManagerFactory mFactory;
|
||||
private final PluginInstanceManager.Factory mInstanceManagerFactory;
|
||||
private final boolean mIsDebuggable;
|
||||
private final PluginPrefs mPluginPrefs;
|
||||
private final PluginEnabler mPluginEnabler;
|
||||
private final PluginInitializer mPluginInitializer;
|
||||
private ClassLoaderFilter mParentClassLoader;
|
||||
private boolean mListening;
|
||||
private boolean mHasOneShot;
|
||||
private Looper mLooper;
|
||||
|
||||
public PluginManagerImpl(Context context, PluginInitializer initializer) {
|
||||
this(context, new PluginInstanceManagerFactory(), initializer.isDebuggable(),
|
||||
Thread.getUncaughtExceptionPreHandler(), initializer);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
|
||||
UncaughtExceptionHandler defaultHandler, final PluginInitializer initializer) {
|
||||
public PluginManagerImpl(Context context,
|
||||
PluginInstanceManager.Factory instanceManagerFactory,
|
||||
boolean debuggable,
|
||||
Optional<UncaughtExceptionHandler> defaultHandlerOptional,
|
||||
PluginEnabler pluginEnabler,
|
||||
PluginPrefs pluginPrefs,
|
||||
String[] privilegedPlugins) {
|
||||
mContext = context;
|
||||
mFactory = factory;
|
||||
mLooper = initializer.getBgLooper();
|
||||
mInstanceManagerFactory = instanceManagerFactory;
|
||||
mIsDebuggable = debuggable;
|
||||
mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
|
||||
mPluginPrefs = new PluginPrefs(mContext);
|
||||
mPluginEnabler = initializer.getPluginEnabler(mContext);
|
||||
mPluginInitializer = initializer;
|
||||
mPrivilegedPlugins.addAll(Arrays.asList(privilegedPlugins));
|
||||
mPluginPrefs = pluginPrefs;
|
||||
mPluginEnabler = pluginEnabler;
|
||||
|
||||
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
|
||||
defaultHandler);
|
||||
defaultHandlerOptional);
|
||||
Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
|
||||
|
||||
new Handler(mLooper).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
initializer.onPluginManagerInit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isDebuggable() {
|
||||
return mIsDebuggable;
|
||||
}
|
||||
|
||||
public String[] getWhitelistedPlugins() {
|
||||
return mWhitelistedPlugins.toArray(new String[0]);
|
||||
public String[] getPrivilegedPlugins() {
|
||||
return mPrivilegedPlugins.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public PluginEnabler getPluginEnabler() {
|
||||
@@ -138,9 +126,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
throw new RuntimeException("Must be called from UI thread");
|
||||
}
|
||||
// Passing null causes compiler to complain about incompatible (generic) types.
|
||||
PluginListener<Plugin> dummy = null;
|
||||
PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy,
|
||||
false, mLooper, cls, this);
|
||||
PluginListener<T> dummy = null;
|
||||
PluginInstanceManager<T> p = mInstanceManagerFactory.create(
|
||||
action, dummy, false, new VersionInfo().addClass(cls), this,
|
||||
isDebuggable(), getPrivilegedPlugins());
|
||||
mPluginPrefs.addAction(action);
|
||||
PluginInfo<T> info = p.getPlugin();
|
||||
if (info != null) {
|
||||
@@ -167,10 +156,11 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
}
|
||||
|
||||
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
|
||||
Class cls, boolean allowMultiple) {
|
||||
Class<?> cls, boolean allowMultiple) {
|
||||
mPluginPrefs.addAction(action);
|
||||
PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
|
||||
allowMultiple, mLooper, cls, this);
|
||||
PluginInstanceManager<T> p = mInstanceManagerFactory.create(action, listener, allowMultiple,
|
||||
new VersionInfo().addClass(cls), this, isDebuggable(),
|
||||
getPrivilegedPlugins());
|
||||
p.loadAll();
|
||||
synchronized (this) {
|
||||
mPluginMap.put(listener, p);
|
||||
@@ -218,7 +208,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 (PluginInstanceManager<?> manager : mPluginMap.values()) {
|
||||
manager.loadAll();
|
||||
}
|
||||
}
|
||||
@@ -226,8 +216,8 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
Uri uri = intent.getData();
|
||||
ComponentName component = ComponentName.unflattenFromString(
|
||||
uri.toString().substring(10));
|
||||
if (isPluginWhitelisted(component)) {
|
||||
// Don't disable whitelisted plugins as they are a part of the OS.
|
||||
if (isPluginPrivileged(component)) {
|
||||
// Don't disable privileged plugins as they are a part of the OS.
|
||||
return;
|
||||
}
|
||||
getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
|
||||
@@ -287,11 +277,11 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
|
||||
for (PluginInstanceManager manager : mPluginMap.values()) {
|
||||
for (PluginInstanceManager<?> manager : mPluginMap.values()) {
|
||||
manager.onPackageChange(pkg);
|
||||
}
|
||||
} else {
|
||||
for (PluginInstanceManager manager : mPluginMap.values()) {
|
||||
for (PluginInstanceManager<?> manager : mPluginMap.values()) {
|
||||
manager.onPackageRemoved(pkg);
|
||||
}
|
||||
}
|
||||
@@ -301,8 +291,8 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
|
||||
/** Returns class loader specific for the given plugin. */
|
||||
public ClassLoader getClassLoader(ApplicationInfo appInfo) {
|
||||
if (!mIsDebuggable && !isPluginPackageWhitelisted(appInfo.packageName)) {
|
||||
Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:"
|
||||
if (!mIsDebuggable && !isPluginPackagePrivileged(appInfo.packageName)) {
|
||||
Log.w(TAG, "Cannot get class loader for non-privileged plugin. Src:"
|
||||
+ appInfo.sourceDir + ", pkg: " + appInfo.packageName);
|
||||
return null;
|
||||
}
|
||||
@@ -345,32 +335,18 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
return false;
|
||||
}
|
||||
|
||||
public void handleWtfs() {
|
||||
mPluginInitializer.handleWtfs();
|
||||
}
|
||||
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
synchronized (this) {
|
||||
pw.println(String.format(" plugin map (%d):", mPluginMap.size()));
|
||||
for (PluginListener listener : mPluginMap.keySet()) {
|
||||
for (PluginListener<?> listener : mPluginMap.keySet()) {
|
||||
pw.println(String.format(" %s -> %s",
|
||||
listener, mPluginMap.get(listener)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static class PluginInstanceManagerFactory {
|
||||
public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
|
||||
String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
|
||||
Class<?> cls, PluginManagerImpl manager) {
|
||||
return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
|
||||
new VersionInfo().addClass(cls), manager);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPluginPackageWhitelisted(String packageName) {
|
||||
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)) {
|
||||
@@ -383,8 +359,8 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isPluginWhitelisted(ComponentName pluginName) {
|
||||
for (String componentNameOrPackage : mWhitelistedPlugins) {
|
||||
private boolean isPluginPrivileged(ComponentName pluginName) {
|
||||
for (String componentNameOrPackage : mPrivilegedPlugins) {
|
||||
ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
|
||||
if (componentName != null) {
|
||||
if (componentName.equals(pluginName)) {
|
||||
@@ -417,16 +393,20 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
}
|
||||
|
||||
private class PluginExceptionHandler implements UncaughtExceptionHandler {
|
||||
private final UncaughtExceptionHandler mHandler;
|
||||
private final Optional<UncaughtExceptionHandler> mExceptionHandlerOptional;
|
||||
|
||||
private PluginExceptionHandler(UncaughtExceptionHandler handler) {
|
||||
mHandler = handler;
|
||||
private PluginExceptionHandler(
|
||||
Optional<UncaughtExceptionHandler> exceptionHandlerOptional) {
|
||||
mExceptionHandlerOptional = exceptionHandlerOptional;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||
if (SystemProperties.getBoolean("plugin.debugging", false)) {
|
||||
mHandler.uncaughtException(thread, throwable);
|
||||
Throwable finalThrowable = throwable;
|
||||
mExceptionHandlerOptional.ifPresent(
|
||||
handler -> handler.uncaughtException(thread, finalThrowable));
|
||||
|
||||
return;
|
||||
}
|
||||
// Search for and disable plugins that may have been involved in this crash.
|
||||
@@ -436,7 +416,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 (PluginInstanceManager<?> manager : mPluginMap.values()) {
|
||||
disabledAny |= manager.disableAll();
|
||||
}
|
||||
}
|
||||
@@ -446,7 +426,9 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
}
|
||||
|
||||
// Run the normal exception handler so we can crash and cleanup our state.
|
||||
mHandler.uncaughtException(thread, throwable);
|
||||
Throwable finalThrowable = throwable;
|
||||
mExceptionHandlerOptional.ifPresent(
|
||||
handler -> handler.uncaughtException(thread, finalThrowable));
|
||||
}
|
||||
|
||||
private boolean checkStack(Throwable throwable) {
|
||||
@@ -454,7 +436,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
boolean disabledAny = false;
|
||||
synchronized (this) {
|
||||
for (StackTraceElement element : throwable.getStackTrace()) {
|
||||
for (PluginInstanceManager manager : mPluginMap.values()) {
|
||||
for (PluginInstanceManager<?> manager : mPluginMap.values()) {
|
||||
disabledAny |= manager.checkAndDisable(element.getClassName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,21 +68,15 @@ import com.android.systemui.navigationbar.NavigationBarController;
|
||||
import com.android.systemui.navigationbar.NavigationBarOverlayController;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.navigationbar.TaskbarDelegate;
|
||||
import com.android.systemui.plugins.PluginInitializerImpl;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.qs.ReduceBrightColorsController;
|
||||
import com.android.systemui.recents.OverviewProxyService;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.shared.plugins.PluginManagerImpl;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
import com.android.unfold.UnfoldTransitionFactory;
|
||||
import com.android.unfold.UnfoldTransitionProgressProvider;
|
||||
import com.android.unfold.config.UnfoldTransitionConfig;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController;
|
||||
@@ -98,6 +92,9 @@ import com.android.systemui.statusbar.policy.NetworkController;
|
||||
import com.android.systemui.theme.ThemeOverlayApplier;
|
||||
import com.android.systemui.util.leak.LeakDetector;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
import com.android.unfold.UnfoldTransitionFactory;
|
||||
import com.android.unfold.UnfoldTransitionProgressProvider;
|
||||
import com.android.unfold.config.UnfoldTransitionConfig;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
|
||||
@@ -195,13 +192,6 @@ public class DependencyProvider {
|
||||
return new MetricsLogger();
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
public PluginManager providePluginManager(Context context) {
|
||||
return new PluginManagerImpl(context, new PluginInitializerImpl());
|
||||
}
|
||||
|
||||
/** */
|
||||
@SysUISingleton
|
||||
@Provides
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.util.DisplayMetrics;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.internal.logging.UiEventLoggerImpl;
|
||||
import com.android.systemui.dagger.qualifiers.TestHarness;
|
||||
import com.android.systemui.plugins.PluginsModule;
|
||||
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
@@ -47,7 +48,9 @@ import dagger.Provides;
|
||||
*/
|
||||
@Module(includes = {
|
||||
FrameworkServicesModule.class,
|
||||
GlobalConcurrencyModule.class})
|
||||
GlobalConcurrencyModule.class,
|
||||
PluginsModule.class,
|
||||
})
|
||||
public class GlobalModule {
|
||||
|
||||
/** */
|
||||
|
||||
@@ -18,8 +18,6 @@ package com.android.systemui.dagger;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.systemui.util.concurrency.ThreadFactory;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
@@ -55,9 +53,4 @@ public interface GlobalRootComponent {
|
||||
* Builder for a SysUIComponent.
|
||||
*/
|
||||
SysUIComponent.Builder getSysUIComponent();
|
||||
|
||||
/**
|
||||
* Build a {@link ThreadFactory}.
|
||||
*/
|
||||
ThreadFactory createThreadFactory();
|
||||
}
|
||||
|
||||
@@ -17,25 +17,27 @@ package com.android.systemui.plugins;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.plugins.PluginDependency.DependencyProvider;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
/**
|
||||
*/
|
||||
@SysUISingleton
|
||||
@Singleton
|
||||
public class PluginDependencyProvider extends DependencyProvider {
|
||||
|
||||
private final ArrayMap<Class<?>, Object> mDependencies = new ArrayMap<>();
|
||||
private final PluginManager mManager;
|
||||
private final Lazy<PluginManager> mManagerLazy;
|
||||
|
||||
/**
|
||||
*/
|
||||
@Inject
|
||||
public PluginDependencyProvider(PluginManager manager) {
|
||||
mManager = manager;
|
||||
public PluginDependencyProvider(Lazy<PluginManager> managerLazy) {
|
||||
mManagerLazy = managerLazy;
|
||||
PluginDependency.sProvider = this;
|
||||
}
|
||||
|
||||
@@ -51,7 +53,7 @@ public class PluginDependencyProvider extends DependencyProvider {
|
||||
|
||||
@Override
|
||||
<T> T get(Plugin p, Class<T> cls) {
|
||||
if (!mManager.dependsOn(p, cls)) {
|
||||
if (!mManagerLazy.get().dependsOn(p, cls)) {
|
||||
throw new IllegalArgumentException(p.getClass() + " does not depend on " + cls);
|
||||
}
|
||||
synchronized (mDependencies) {
|
||||
|
||||
@@ -22,16 +22,22 @@ import android.content.pm.PackageManager;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.shared.plugins.PluginEnabler;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/** */
|
||||
@Singleton
|
||||
public class PluginEnablerImpl implements PluginEnabler {
|
||||
private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs";
|
||||
|
||||
private PackageManager mPm;
|
||||
private final PackageManager mPm;
|
||||
private final SharedPreferences mAutoDisabledPrefs;
|
||||
|
||||
public PluginEnablerImpl(Context context) {
|
||||
this(context, context.getPackageManager());
|
||||
}
|
||||
|
||||
@Inject
|
||||
@VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) {
|
||||
mAutoDisabledPrefs = context.getSharedPreferences(
|
||||
CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE);
|
||||
|
||||
@@ -15,16 +15,17 @@
|
||||
package com.android.systemui.plugins;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.shared.plugins.PluginEnabler;
|
||||
import com.android.systemui.shared.plugins.PluginInitializer;
|
||||
import com.android.systemui.shared.plugins.PluginManagerImpl;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/** */
|
||||
@Singleton
|
||||
public class PluginInitializerImpl implements PluginInitializer {
|
||||
|
||||
/**
|
||||
@@ -33,44 +34,24 @@ public class PluginInitializerImpl implements PluginInitializer {
|
||||
private static final boolean WTFS_SHOULD_CRASH = false;
|
||||
private boolean mWtfsSet;
|
||||
|
||||
@Override
|
||||
public Looper getBgLooper() {
|
||||
return Dependency.get(Dependency.BG_LOOPER);
|
||||
@Inject
|
||||
public PluginInitializerImpl(PluginDependencyProvider dependencyProvider) {
|
||||
dependencyProvider.allowPluginDependency(ActivityStarter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginManagerInit() {
|
||||
// Plugin dependencies that don't have another good home can go here, but
|
||||
// dependencies that have better places to init can happen elsewhere.
|
||||
Dependency.get(PluginDependencyProvider.class)
|
||||
.allowPluginDependency(ActivityStarter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getWhitelistedPlugins(Context context) {
|
||||
public String[] getPrivilegedPlugins(Context context) {
|
||||
return context.getResources().getStringArray(R.array.config_pluginWhitelist);
|
||||
}
|
||||
|
||||
public PluginEnabler getPluginEnabler(Context context) {
|
||||
return new PluginEnablerImpl(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleWtfs() {
|
||||
if (WTFS_SHOULD_CRASH && !mWtfsSet) {
|
||||
mWtfsSet = true;
|
||||
Log.setWtfHandler(new Log.TerribleFailureHandler() {
|
||||
@Override
|
||||
public void onTerribleFailure(String tag, Log.TerribleFailure what,
|
||||
boolean system) {
|
||||
throw new PluginManagerImpl.CrashWhilePluginActiveException(what);
|
||||
}
|
||||
Log.setWtfHandler((tag, what, system) -> {
|
||||
throw new PluginManagerImpl.CrashWhilePluginActiveException(what);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebuggable() {
|
||||
return Build.IS_DEBUGGABLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.plugins;
|
||||
|
||||
import static com.android.systemui.util.concurrency.GlobalConcurrencyModule.PRE_HANDLER;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
|
||||
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.PluginManager;
|
||||
import com.android.systemui.shared.plugins.PluginManagerImpl;
|
||||
import com.android.systemui.shared.plugins.PluginPrefs;
|
||||
import com.android.systemui.util.concurrency.ThreadFactory;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
/**
|
||||
* Dagger Module for code related to plugins.
|
||||
*
|
||||
* Covers code both in com.android.systemui.plugins and code in
|
||||
* com.android.systemui.shared.plugins.
|
||||
*/
|
||||
@Module
|
||||
public abstract class PluginsModule {
|
||||
public static final String PLUGIN_THREAD = "plugin_thread";
|
||||
public static final String PLUGIN_DEBUG = "plugin_debug";
|
||||
public static final String PLUGIN_PRIVILEGED = "plugin_privileged";
|
||||
|
||||
@Provides
|
||||
@Named(PLUGIN_DEBUG)
|
||||
static boolean providesPluginDebug() {
|
||||
return Build.IS_DEBUGGABLE;
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract PluginEnabler bindsPluginEnablerImpl(PluginEnablerImpl impl);
|
||||
|
||||
@Binds
|
||||
abstract PluginInitializer bindsPluginInitializerImpl(PluginInitializerImpl impl);
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context,
|
||||
PackageManager packageManager, @Named(PLUGIN_THREAD) Looper pluginLooper,
|
||||
PluginInitializer initializer) {
|
||||
return new PluginInstanceManager.Factory(
|
||||
context, packageManager, pluginLooper, initializer);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(PLUGIN_THREAD)
|
||||
static Looper providesPluginLooper(ThreadFactory threadFactory) {
|
||||
return threadFactory.buildLooperOnNewThread("plugin");
|
||||
}
|
||||
|
||||
@Provides
|
||||
static PluginManager providesPluginManager(
|
||||
Context context,
|
||||
PluginInstanceManager.Factory instanceManagerFactory,
|
||||
@Named(PLUGIN_DEBUG) boolean debug,
|
||||
@Named(PRE_HANDLER)
|
||||
Optional<Thread.UncaughtExceptionHandler> uncaughtExceptionHandlerOptional,
|
||||
PluginEnabler pluginEnabler,
|
||||
PluginPrefs pluginPrefs,
|
||||
@Named(PLUGIN_PRIVILEGED) String[] privilegedPlugins) {
|
||||
return new PluginManagerImpl(context, instanceManagerFactory, debug,
|
||||
uncaughtExceptionHandlerOptional, pluginEnabler, pluginPrefs,
|
||||
privilegedPlugins);
|
||||
}
|
||||
|
||||
@Provides
|
||||
static PluginPrefs providesPluginPrefs(Context context) {
|
||||
return new PluginPrefs(context);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(PLUGIN_PRIVILEGED)
|
||||
static String[] providesPrivilegedPlugins(PluginInitializer initializer, Context context) {
|
||||
return initializer.getPrivilegedPlugins(context);
|
||||
}
|
||||
}
|
||||
@@ -106,8 +106,8 @@ public class PluginFragment extends PreferenceFragment {
|
||||
PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
|
||||
apps.forEach(app -> {
|
||||
if (!plugins.containsKey(app.packageName)) return;
|
||||
if (ArrayUtils.contains(manager.getWhitelistedPlugins(), app.packageName)) {
|
||||
// Don't manage whitelisted plugins, they are part of the OS.
|
||||
if (ArrayUtils.contains(manager.getPrivilegedPlugins(), app.packageName)) {
|
||||
// Don't manage privileged plugins, they are part of the OS.
|
||||
return;
|
||||
}
|
||||
SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler);
|
||||
|
||||
@@ -22,8 +22,10 @@ import android.os.Looper;
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Binds;
|
||||
@@ -35,6 +37,7 @@ import dagger.Provides;
|
||||
*/
|
||||
@Module
|
||||
public abstract class GlobalConcurrencyModule {
|
||||
public static final String PRE_HANDLER = "pre_handler";
|
||||
|
||||
/**
|
||||
* Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
|
||||
@@ -73,4 +76,11 @@ public abstract class GlobalConcurrencyModule {
|
||||
@Binds
|
||||
@Singleton
|
||||
public abstract Execution provideExecution(ExecutionImpl execution);
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@Named(PRE_HANDLER)
|
||||
public static Optional<Thread.UncaughtExceptionHandler> providesUncaughtExceptionHandler() {
|
||||
return Optional.ofNullable(Thread.getUncaughtExceptionPreHandler());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
||||
private PluginEnabler mMockEnabler;
|
||||
ComponentName mTestPluginComponentName =
|
||||
new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
|
||||
private PluginInitializer mInitializer;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
@@ -95,9 +96,10 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
||||
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, mHandlerThread.getLooper(), mMockVersionInfo,
|
||||
mMockManager, true, new String[0]);
|
||||
mMockManager, true, new String[0], mInitializer);
|
||||
sMockPlugin = mock(Plugin.class);
|
||||
when(sMockPlugin.getVersion()).thenReturn(1);
|
||||
}
|
||||
@@ -194,7 +196,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
||||
// Create a version that thinks the build is not debuggable.
|
||||
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
||||
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
|
||||
mMockManager, false, new String[0]);
|
||||
mMockManager, false, new String[0], mInitializer);
|
||||
setupFakePmQuery();
|
||||
|
||||
mPluginInstanceManager.loadAll();
|
||||
@@ -211,7 +213,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
||||
// Create a version that thinks the build is not debuggable.
|
||||
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
||||
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
|
||||
mMockManager, false, new String[] {WHITELISTED_PACKAGE});
|
||||
mMockManager, false, new String[] {WHITELISTED_PACKAGE}, mInitializer);
|
||||
setupFakePmQuery();
|
||||
|
||||
mPluginInstanceManager.loadAll();
|
||||
@@ -255,7 +257,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
||||
public void testDisableWhitelisted() throws Exception {
|
||||
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
||||
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
|
||||
mMockManager, false, new String[] {WHITELISTED_PACKAGE});
|
||||
mMockManager, false, new String[] {WHITELISTED_PACKAGE}, mInitializer);
|
||||
createPlugin(); // Get into valid created state.
|
||||
|
||||
mPluginInstanceManager.disableAll();
|
||||
|
||||
@@ -30,19 +30,14 @@ import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.Plugin;
|
||||
import com.android.systemui.plugins.PluginEnablerImpl;
|
||||
import com.android.systemui.plugins.PluginInitializerImpl;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
|
||||
import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -51,6 +46,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.util.Optional;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@@ -59,11 +55,13 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
|
||||
private static final String WHITELISTED_PACKAGE = "com.android.systemui";
|
||||
|
||||
private PluginInstanceManagerFactory mMockFactory;
|
||||
private PluginInstanceManager.Factory mMockFactory;
|
||||
private PluginInstanceManager mMockPluginInstance;
|
||||
private PluginManagerImpl mPluginManager;
|
||||
private PluginListener mMockListener;
|
||||
private PluginListener<?> mMockListener;
|
||||
private PackageManager mMockPackageManager;
|
||||
private PluginEnabler mPluginEnabler;
|
||||
private PluginPrefs mPluginPrefs;
|
||||
|
||||
private UncaughtExceptionHandler mRealExceptionHandler;
|
||||
private UncaughtExceptionHandler mMockExceptionHandler;
|
||||
@@ -71,30 +69,23 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mDependency.injectTestDependency(Dependency.BG_LOOPER,
|
||||
TestableLooper.get(this).getLooper());
|
||||
mRealExceptionHandler = Thread.getUncaughtExceptionPreHandler();
|
||||
mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
|
||||
mMockFactory = mock(PluginInstanceManagerFactory.class);
|
||||
mMockFactory = mock(PluginInstanceManager.Factory.class);
|
||||
mMockPluginInstance = mock(PluginInstanceManager.class);
|
||||
when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
|
||||
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()))
|
||||
.thenReturn(mMockPluginInstance);
|
||||
|
||||
mMockPackageManager = mock(PackageManager.class);
|
||||
mPluginManager = new PluginManagerImpl(
|
||||
getContext(), mMockFactory, true,
|
||||
mMockExceptionHandler, new PluginInitializerImpl() {
|
||||
@Override
|
||||
public String[] getWhitelistedPlugins(Context context) {
|
||||
return new String[0];
|
||||
}
|
||||
Optional.of(mMockExceptionHandler), mPluginEnabler,
|
||||
mPluginPrefs, new String[0]);
|
||||
|
||||
@Override
|
||||
public PluginEnabler getPluginEnabler(Context context) {
|
||||
return new PluginEnablerImpl(context, mMockPackageManager);
|
||||
}
|
||||
});
|
||||
resetExceptionHandler();
|
||||
mMockListener = mock(PluginListener.class);
|
||||
}
|
||||
@@ -127,13 +118,10 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
@Test
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
public void testNonDebuggable_noWhitelist() {
|
||||
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
|
||||
mMockExceptionHandler, new PluginInitializerImpl() {
|
||||
@Override
|
||||
public String[] getWhitelistedPlugins(Context context) {
|
||||
return new String[0];
|
||||
}
|
||||
});
|
||||
mPluginManager = new PluginManagerImpl(
|
||||
getContext(), mMockFactory, false,
|
||||
Optional.of(mMockExceptionHandler), mPluginEnabler,
|
||||
mPluginPrefs, new String[0]);
|
||||
resetExceptionHandler();
|
||||
|
||||
String sourceDir = "myPlugin";
|
||||
@@ -148,13 +136,10 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
@Test
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
public void testNonDebuggable_whitelistedPkg() {
|
||||
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
|
||||
mMockExceptionHandler, new PluginInitializerImpl() {
|
||||
@Override
|
||||
public String[] getWhitelistedPlugins(Context context) {
|
||||
return new String[] {WHITELISTED_PACKAGE};
|
||||
}
|
||||
});
|
||||
mPluginManager = new PluginManagerImpl(
|
||||
getContext(), mMockFactory, false,
|
||||
Optional.of(mMockExceptionHandler), mPluginEnabler,
|
||||
mPluginPrefs, new String[] {WHITELISTED_PACKAGE});
|
||||
resetExceptionHandler();
|
||||
|
||||
String sourceDir = "myPlugin";
|
||||
@@ -211,9 +196,7 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
intent.setData(Uri.parse("package://" + testComponent.flattenToString()));
|
||||
mPluginManager.onReceive(mContext, intent);
|
||||
verify(nm).cancel(eq(testComponent.getClassName()), eq(SystemMessage.NOTE_PLUGIN));
|
||||
verify(mMockPackageManager).setComponentEnabledSetting(eq(testComponent),
|
||||
eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
|
||||
eq(PackageManager.DONT_KILL_APP));
|
||||
verify(mPluginEnabler).setDisabled(testComponent, PluginEnabler.DISABLED_INVALID_VERSION);
|
||||
}
|
||||
|
||||
private void resetExceptionHandler() {
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.android.systemui.shared.plugins.PluginManager;
|
||||
|
||||
public class FakePluginManager implements PluginManager {
|
||||
|
||||
private final BaseLeakChecker<PluginListener> mLeakChecker;
|
||||
private final BaseLeakChecker<PluginListener<?>> mLeakChecker;
|
||||
|
||||
public FakePluginManager(LeakCheck test) {
|
||||
mLeakChecker = new BaseLeakChecker<>(test, "Plugin");
|
||||
@@ -30,7 +30,7 @@ public class FakePluginManager implements PluginManager {
|
||||
|
||||
@Override
|
||||
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
|
||||
Class cls, boolean allowMultiple) {
|
||||
Class<?> cls, boolean allowMultiple) {
|
||||
mLeakChecker.addCallback(listener);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class FakePluginManager implements PluginManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getWhitelistedPlugins() {
|
||||
public String[] getPrivilegedPlugins() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user