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

Commit d5207878 authored by Dave Mankoff's avatar Dave Mankoff Committed by Android (Google) Code Review
Browse files

Merge changes from topic "revert-15560568-b194781951-flags-8-ZUNMWRYIZP" into sc-v2-dev

* changes:
  Revert "2/N Rename PluginInstanceManager to PluginActionManager."
  Revert "3/N Remove #getPrivilegedPlugins from PluginInitializer."
  Revert "4/N Remove #handleWtfs from PluginInitializer."
  Revert "5/N Remove PluginInitializer entirely."
parents 768b3fb7 be86cc2d
Loading
Loading
Loading
Loading
+34 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.android.systemui.shared.plugins;

import android.content.Context;

/**
 * Provides necessary components for initializing {@link PluginManagerImpl}.
 */
public interface PluginInitializer {

    /**
     * Return a list of plugins that don't get disabled when an exception occurs.
     */
    String[] getPrivilegedPlugins(Context context);


    /**
     * Called from {@link PluginInstanceManager}.
     */
    void handleWtfs();
}
+0 −228
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.shared.plugins;

import android.app.LoadedApk;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginFragment;
import com.android.systemui.plugins.PluginListener;

import dalvik.system.PathClassLoader;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Contains a single instantiation of a Plugin.
 *
 * This class and its related Factory are in charge of actually instantiating a plugin and
 * managing any state related to it.
 *
 * @param <T> The type of plugin that this contains.
 */
public class PluginInstance<T extends Plugin> {
    private static final String TAG = "PluginInstance";
    private static final Map<String, ClassLoader> sClassLoaders = new ArrayMap<>();

    private final Context mPluginContext;
    private final VersionInfo mVersionInfo;
    private final ComponentName mComponentName;
    private final T mPlugin;

    /** */
    public PluginInstance(ComponentName componentName, T plugin, Context pluginContext,
            VersionInfo versionInfo) {
        mComponentName = componentName;
        mPlugin = plugin;
        mPluginContext = pluginContext;
        mVersionInfo = versionInfo;
    }

    /** Alerts listener and plugin that the plugin has been created. */
    public void onCreate(Context appContext, PluginListener<T> listener) {
        if (!(mPlugin instanceof PluginFragment)) {
            // Only call onCreate for plugins that aren't fragments, as fragments
            // will get the onCreate as part of the fragment lifecycle.
            mPlugin.onCreate(appContext, mPluginContext);
        }
        listener.onPluginConnected(mPlugin, mPluginContext);
    }

    /** Alerts listener and plugin that the plugin is being shutdown. */
    public void onDestroy(PluginListener<T> listener) {
        listener.onPluginDisconnected(mPlugin);
        if (!(mPlugin instanceof PluginFragment)) {
            // Only call onDestroy for plugins that aren't fragments, as fragments
            // will get the onDestroy as part of the fragment lifecycle.
            mPlugin.onDestroy();
        }
    }

    /**
     * Returns if the contained plugin matches the passed in class name.
     *
     * It does this by string comparison of the class names.
     **/
    public boolean containsPluginClass(Class pluginClass) {
        return mPlugin.getClass().getName().equals(pluginClass.getName());
    }

    public ComponentName getComponentName() {
        return mComponentName;
    }

    public String getPackage() {
        return mComponentName.getPackageName();
    }

    public VersionInfo getVersionInfo() {
        return mVersionInfo;
    }

    @VisibleForTesting
    Context getPluginContext() {
        return mPluginContext;
    }

    /** Used to create new {@link PluginInstance}s. */
    public static class Factory {
        private final ClassLoader mBaseClassLoader;
        private final InstanceFactory<?> mInstanceFactory;
        private final VersionChecker mVersionChecker;
        private final boolean mIsDebug;
        private final List<String> mPrivilegedPlugins;

        /** Factory used to construct {@link PluginInstance}s. */
        public Factory(ClassLoader classLoader, InstanceFactory<?> instanceFactory,
                VersionChecker versionChecker,
                List<String> privilegedPlugins,
                boolean isDebug) {
            mPrivilegedPlugins = privilegedPlugins;
            mBaseClassLoader = classLoader;
            mInstanceFactory = instanceFactory;
            mVersionChecker = versionChecker;
            mIsDebug = isDebug;
        }

        /** Construct a new PluginInstance. */
        public <T extends Plugin> PluginInstance<T> create(
                Context context,
                ApplicationInfo appInfo,
                ComponentName componentName,
                Class<T> pluginClass)
                throws PackageManager.NameNotFoundException, ClassNotFoundException,
                InstantiationException, IllegalAccessException {

            ClassLoader classLoader = getClassLoader(appInfo, mBaseClassLoader);
            Context pluginContext = new PluginActionManager.PluginContextWrapper(
                    context.createApplicationContext(appInfo, 0), classLoader);
            Class<T> instanceClass = (Class<T>) Class.forName(
                    componentName.getClassName(), true, classLoader);
            // TODO: Only create the plugin before version check if we need it for
            // legacy version check.
            T instance = (T) mInstanceFactory.create(instanceClass);
            VersionInfo version = mVersionChecker.checkVersion(
                    instanceClass, pluginClass, instance);
            return new PluginInstance<T>(componentName, instance, pluginContext, version);
        }

        private boolean isPluginPackagePrivileged(String packageName) {
            for (String componentNameOrPackage : mPrivilegedPlugins) {
                ComponentName componentName = ComponentName.unflattenFromString(
                        componentNameOrPackage);
                if (componentName != null) {
                    if (componentName.getPackageName().equals(packageName)) {
                        return true;
                    }
                } else if (componentNameOrPackage.equals(packageName)) {
                    return true;
                }
            }
            return false;
        }

        private ClassLoader getParentClassLoader(ClassLoader baseClassLoader) {
            return new PluginManagerImpl.ClassLoaderFilter(
                    baseClassLoader, "com.android.systemui.plugin");
        }

        /** Returns class loader specific for the given plugin. */
        private ClassLoader getClassLoader(ApplicationInfo appInfo,
                ClassLoader baseClassLoader) {
            if (!mIsDebug && !isPluginPackagePrivileged(appInfo.packageName)) {
                Log.w(TAG, "Cannot get class loader for non-privileged plugin. Src:"
                        + appInfo.sourceDir + ", pkg: " + appInfo.packageName);
                return null;
            }
            if (sClassLoaders.containsKey(appInfo.packageName)) {
                return sClassLoaders.get(appInfo.packageName);
            }

            List<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(baseClassLoader));
            sClassLoaders.put(appInfo.packageName, classLoader);
            return classLoader;
        }
    }

    /** Class that compares a plugin class against an implementation for version matching. */
    public static class VersionChecker {
        /** Compares two plugin classes. */
        public <T extends Plugin> VersionInfo checkVersion(
                Class<T> instanceClass, Class<T> pluginClass, Plugin plugin) {
            VersionInfo pluginVersion = new VersionInfo().addClass(pluginClass);
            VersionInfo instanceVersion = new VersionInfo().addClass(instanceClass);
            if (instanceVersion.hasVersionInfo()) {
                pluginVersion.checkVersion(instanceVersion);
            } else {
                int fallbackVersion = plugin.getVersion();
                if (fallbackVersion != pluginVersion.getDefaultVersion()) {
                    throw new VersionInfo.InvalidVersionException("Invalid legacy version", false);
                }
                return null;
            }
            return instanceVersion;
        }
    }

    /**
     *  Simple class to create a new instance. Useful for testing.
     *
     * @param <T> The type of plugin this create.
     **/
    public static class InstanceFactory<T extends Plugin> {
        T create(Class cls) throws IllegalAccessException, InstantiationException {
            return (T) cls.newInstance();
        }
    }
}
+513 −0

File changed and moved.

Preview size limit exceeded, changes collapsed.

+4 −6
Original line number Original line Diff line number Diff line
@@ -30,15 +30,13 @@ public interface PluginManager {
    /** Returns plugins that don't get disabled when an exceptoin occurs. */
    /** Returns plugins that don't get disabled when an exceptoin occurs. */
    String[] getPrivilegedPlugins();
    String[] getPrivilegedPlugins();


    /** */
    <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
    <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<T> cls);
    <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
    /** */
    <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<T> cls,
            boolean allowMultiple);
            boolean allowMultiple);
    <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
    <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
            Class<T> cls);
            Class<?> cls);
    <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
    <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
            Class<T> cls, boolean allowMultiple);
            Class<?> cls, boolean allowMultiple);


    void removePluginListener(PluginListener<?> listener);
    void removePluginListener(PluginListener<?> listener);


+18 −22
Original line number Original line Diff line number Diff line
@@ -47,26 +47,26 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
    private static final String TAG = PluginManagerImpl.class.getSimpleName();
    private static final String TAG = PluginManagerImpl.class.getSimpleName();
    static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
    static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";


    private final ArrayMap<PluginListener<?>, PluginActionManager<?>> mPluginMap
    private final ArrayMap<PluginListener<?>, PluginInstanceManager<?>> mPluginMap
            = new ArrayMap<>();
            = new ArrayMap<>();
    private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
    private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
    private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
    private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
    private final Context mContext;
    private final Context mContext;
    private final PluginActionManager.Factory mActionManagerFactory;
    private final PluginInstanceManager.Factory mInstanceManagerFactory;
    private final boolean mIsDebuggable;
    private final boolean mIsDebuggable;
    private final PluginPrefs mPluginPrefs;
    private final PluginPrefs mPluginPrefs;
    private final PluginEnabler mPluginEnabler;
    private final PluginEnabler mPluginEnabler;
    private boolean mListening;
    private boolean mListening;


    public PluginManagerImpl(Context context,
    public PluginManagerImpl(Context context,
            PluginActionManager.Factory actionManagerFactory,
            PluginInstanceManager.Factory instanceManagerFactory,
            boolean debuggable,
            boolean debuggable,
            Optional<UncaughtExceptionHandler> defaultHandlerOptional,
            Optional<UncaughtExceptionHandler> defaultHandlerOptional,
            PluginEnabler pluginEnabler,
            PluginEnabler pluginEnabler,
            PluginPrefs pluginPrefs,
            PluginPrefs pluginPrefs,
            List<String> privilegedPlugins) {
            List<String> privilegedPlugins) {
        mContext = context;
        mContext = context;
        mActionManagerFactory = actionManagerFactory;
        mInstanceManagerFactory = instanceManagerFactory;
        mIsDebuggable = debuggable;
        mIsDebuggable = debuggable;
        mPrivilegedPlugins.addAll(privilegedPlugins);
        mPrivilegedPlugins.addAll(privilegedPlugins);
        mPluginPrefs = pluginPrefs;
        mPluginPrefs = pluginPrefs;
@@ -85,27 +85,25 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
        return mPrivilegedPlugins.toArray(new String[0]);
        return mPrivilegedPlugins.toArray(new String[0]);
    }
    }


    /** */
    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<T> cls) {
        addPluginListener(listener, cls, false);
        addPluginListener(listener, cls, false);
    }
    }


    /** */
    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<T> cls,
            boolean allowMultiple) {
            boolean allowMultiple) {
        addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple);
        addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple);
    }
    }


    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
            Class<T> cls) {
            Class<?> cls) {
        addPluginListener(action, listener, cls, false);
        addPluginListener(action, listener, cls, false);
    }
    }


    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
            Class<T> cls, boolean allowMultiple) {
            Class<?> cls, boolean allowMultiple) {
        mPluginPrefs.addAction(action);
        mPluginPrefs.addAction(action);
        PluginActionManager<T> p = mActionManagerFactory.create(action, listener, cls,
        PluginInstanceManager<T> p = mInstanceManagerFactory.create(action, listener, allowMultiple,
                allowMultiple, isDebuggable());
                new VersionInfo().addClass(cls), isDebuggable());
        p.loadAll();
        p.loadAll();
        synchronized (this) {
        synchronized (this) {
            mPluginMap.put(listener, p);
            mPluginMap.put(listener, p);
@@ -137,7 +135,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
        filter.addAction(PLUGIN_CHANGED);
        filter.addAction(PLUGIN_CHANGED);
        filter.addAction(DISABLE_PLUGIN);
        filter.addAction(DISABLE_PLUGIN);
        filter.addDataScheme("package");
        filter.addDataScheme("package");
        mContext.registerReceiver(this, filter, PluginActionManager.PLUGIN_PERMISSION, null);
        mContext.registerReceiver(this, filter, PluginInstanceManager.PLUGIN_PERMISSION, null);
        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
        mContext.registerReceiver(this, filter);
        mContext.registerReceiver(this, filter);
    }
    }
@@ -152,7 +150,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
    public void onReceive(Context context, Intent intent) {
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
            synchronized (this) {
            synchronized (this) {
                for (PluginActionManager<?> manager : mPluginMap.values()) {
                for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                    manager.loadAll();
                    manager.loadAll();
                }
                }
            }
            }
@@ -191,14 +189,12 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
                }
                }
            }
            }
            synchronized (this) {
            synchronized (this) {
                if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
                if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                        || Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
                    for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                        || Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
                        manager.onPackageChange(pkg);
                    for (PluginActionManager<?> actionManager : mPluginMap.values()) {
                        actionManager.reloadPackage(pkg);
                    }
                    }
                } else {
                } else {
                    for (PluginActionManager<?> manager : mPluginMap.values()) {
                    for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                        manager.onPackageRemoved(pkg);
                        manager.onPackageRemoved(pkg);
                    }
                    }
                }
                }
@@ -288,7 +284,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
                // disable all the plugins, so we can be sure that SysUI is running as
                // disable all the plugins, so we can be sure that SysUI is running as
                // best as possible.
                // best as possible.
                synchronized (this) {
                synchronized (this) {
                    for (PluginActionManager<?> manager : mPluginMap.values()) {
                    for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                        disabledAny |= manager.disableAll();
                        disabledAny |= manager.disableAll();
                    }
                    }
                }
                }
@@ -308,7 +304,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
            boolean disabledAny = false;
            boolean disabledAny = false;
            synchronized (this) {
            synchronized (this) {
                for (StackTraceElement element : throwable.getStackTrace()) {
                for (StackTraceElement element : throwable.getStackTrace()) {
                    for (PluginActionManager<?> manager : mPluginMap.values()) {
                    for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                        disabledAny |= manager.checkAndDisable(element.getClassName());
                        disabledAny |= manager.checkAndDisable(element.getClassName());
                    }
                    }
                }
                }
Loading