Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1878759 authored by Dave Mankoff's avatar Dave Mankoff
Browse files

1/N Move plugin loading into PluginInstanceManager.

The PluginInstanceManager now manages plugin instances. It loads
them, when instructed to do so by the PluginManager.

This is an imperfect cl - the first of a few to come. Right now,
the PluginInstanceManager is not focused on only loading its specific
instance. This will change.

Bug: 194781951
Test: manual && atest SystemUITests
Change-Id: I2006d623c9d9d2d1020097c567fc7af56a06a83b
parent bf2fd66e
Loading
Loading
Loading
Loading
+216 −134
Original line number Diff line number Diff line
@@ -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.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;
@@ -39,9 +42,12 @@ 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.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

public class PluginInstanceManager<T extends Plugin> {
@@ -56,33 +62,42 @@ public class PluginInstanceManager<T extends Plugin> {
    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 @@ public class PluginInstanceManager<T extends Plugin> {
        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 @@ public class PluginInstanceManager<T extends Plugin> {
        // 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;
    }
@@ -264,12 +293,12 @@ public class PluginInstanceManager<T extends Plugin> {
    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)) {
        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 (!mManager.getPluginEnabler().isEnabled(component)) {
        if (!mPluginEnabler.isEnabled(component)) {
            if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
            return null;
        }
@@ -284,13 +313,13 @@ public class PluginInstanceManager<T extends Plugin> {
                return null;
            }
            // Create our own ClassLoader so we can use our own code as the parent.
                ClassLoader classLoader = mManager.getClassLoader(info);
            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 = (T) pluginClass.newInstance();
            T plugin = mInstanceFactory.create(pluginClass);
            try {
                VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
                if (DEBUG) Log.d(TAG, "createPlugin");
@@ -329,8 +358,7 @@ public class PluginInstanceManager<T extends Plugin> {
                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());
                mNotificationManager.notify(SystemMessage.NOTE_PLUGIN, nb.build());
                // TODO: Warn user.
                Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
                        + ", expected " + mVersion);
@@ -357,6 +385,37 @@ public class PluginInstanceManager<T extends Plugin> {
        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<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 @@ public class PluginInstanceManager<T extends Plugin> {
        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 @@ public class PluginInstanceManager<T extends Plugin> {
            mVersion = info;
        }
    }

    static class InstanceFactory<T extends Plugin> {
        T create(Class cls) throws IllegalAccessException, InstantiationException {
            return (T) cls.newInstance();
        }
    }
}
+8 −105
Original line number Diff line number Diff line
@@ -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.internal.messages.nano.SystemMessageProto.SystemMessage;
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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
    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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
            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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
        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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
            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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
    }

    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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
                // 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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
            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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
        }
    }

    /** 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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
        }
    }

    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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage

    // 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;

+11 −5
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.plugins;

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.shared.plugins.PluginPrefs;
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 @@ public abstract class PluginsModule {
    @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 @@ public abstract class PluginsModule {
                    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 @@ public abstract class PluginsModule {

    @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));
    }
}
+58 −53

File changed.

Preview size limit exceeded, changes collapsed.

+24 −22

File changed.

Preview size limit exceeded, changes collapsed.