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

Commit 97d98132 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add one-shot plugin support"

parents 3c6f28aa 59d86ed2
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -221,6 +221,15 @@
            </intent-filter>
        </receiver>

        <receiver android:name=".SysuiRestartReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="com.android.systemui.action.RESTART" />

                <data android:scheme="package" />
            </intent-filter>
        </receiver>

        <service android:name=".ImageWallpaper"
                android:permission="android.permission.BIND_WALLPAPER"
                android:exported="true" />
+18 −3
Original line number Diff line number Diff line
@@ -87,6 +87,21 @@ public class PluginInstanceManager<T extends Plugin> {
        isDebuggable = debuggable;
    }

    public PluginInfo<T> getPlugin() {
        if (Looper.myLooper() != Looper.getMainLooper()) {
            throw new RuntimeException("Must be called from UI thread");
        }
        mPluginHandler.handleQueryPlugins(null /* All packages */);
        if (mPluginHandler.mPlugins.size() > 0) {
            mMainHandler.removeMessages(MainHandler.PLUGIN_CONNECTED);
            PluginInfo<T> info = mPluginHandler.mPlugins.get(0);
            PluginPrefs.setHasPlugins(mContext);
            info.mPlugin.onCreate(mContext, info.mPluginContext);
            return info;
        }
        return null;
    }

    public void loadAll() {
        if (DEBUG) Log.d(TAG, "startListening");
        mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
@@ -366,11 +381,11 @@ public class PluginInstanceManager<T extends Plugin> {
        }
    }

    private static class PluginInfo<T> {
    static class PluginInfo<T> {
        private final Context mPluginContext;
        private T mPlugin;
        private String mClass;
        private String mPackage;
        T mPlugin;
        String mPackage;

        public PluginInfo(String pkg, String cls, T plugin, Context pluginContext) {
            mPlugin = plugin;
+66 −6
Original line number Diff line number Diff line
@@ -14,7 +14,10 @@

package com.android.systemui.plugins;

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;
@@ -23,16 +26,20 @@ 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.HandlerThread;
import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;

import dalvik.system.PathClassLoader;

@@ -54,11 +61,14 @@ public class PluginManager extends BroadcastReceiver {
    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 Context mContext;
    private final PluginInstanceManagerFactory mFactory;
    private final boolean isDebuggable;
    private final PluginPrefs mPluginPrefs;
    private ClassLoaderFilter mParentClassLoader;
    private boolean mListening;
    private boolean mHasOneShot;

    private PluginManager(Context context) {
        this(context, new PluginInstanceManagerFactory(),
@@ -80,6 +90,27 @@ public class PluginManager extends BroadcastReceiver {
        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
    }

    public <T extends Plugin> T getOneShotPlugin(String action, int version) {
        if (!isDebuggable) {
            // Never ever ever allow these on production builds, they are only for prototyping.
            return null;
        }
        if (Looper.myLooper() != Looper.getMainLooper()) {
            throw new RuntimeException("Must be called from UI thread");
        }
        PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
                false, mBackgroundThread.getLooper(), version, this);
        mPluginPrefs.addAction(action);
        PluginInfo<T> info = p.getPlugin();
        if (info != null) {
            mOneShotPackages.add(info.mPackage);
            mHasOneShot = true;
            startListening();
            return info.mPlugin;
        }
        return null;
    }

    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
            int version) {
        addPluginListener(action, listener, version, false);
@@ -96,10 +127,8 @@ public class PluginManager extends BroadcastReceiver {
                allowMultiple, mBackgroundThread.getLooper(), version, this);
        p.loadAll();
        mPluginMap.put(listener, p);
        if (mPluginMap.size() == 1) {
        startListening();
    }
    }

    public void removePluginListener(PluginListener<?> listener) {
        if (!isDebuggable) {
@@ -108,12 +137,12 @@ public class PluginManager extends BroadcastReceiver {
        }
        if (!mPluginMap.containsKey(listener)) return;
        mPluginMap.remove(listener).destroy();
        if (mPluginMap.size() == 0) {
        stopListening();
    }
    }

    private void startListening() {
        if (mListening) return;
        mListening = true;
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -126,6 +155,9 @@ public class PluginManager extends BroadcastReceiver {
    }

    private void stopListening() {
        // Never stop listening if a one-shot is present.
        if (!mListening || mHasOneShot) return;
        mListening = false;
        mContext.unregisterReceiver(this);
    }

@@ -147,6 +179,34 @@ public class PluginManager extends BroadcastReceiver {
        } else {
            Uri data = intent.getData();
            String pkg = data.getEncodedSchemeSpecificPart();
            if (mOneShotPackages.contains(pkg)) {
                int icon = mContext.getResources().getIdentifier("tuner", "drawable",
                        mContext.getPackageName());
                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)
                        .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, 0);
                nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
                mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
                        SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
            }
            clearClassLoader(pkg);
            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                for (PluginInstanceManager manager : mPluginMap.values()) {
+0 −1
Original line number Diff line number Diff line
@@ -80,7 +80,6 @@ public class SystemUIApplication extends Application {
            ShortcutKeyDispatcher.class,
            VendorServices.class,
            LatencyTester.class,
            DozeFactory.Initializer.class,
    };

    /**
+37 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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;

import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Process;

import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;

public class SysuiRestartReceiver extends BroadcastReceiver {

    public static String ACTION = "com.android.systemui.action.RESTART";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (ACTION.equals(intent.getAction())) {
            String pkg = intent.getData().toString().substring(10);
            NotificationManager.from(context).cancel(pkg, SystemMessage.NOTE_PLUGIN);
            Process.killProcess(Process.myPid());
        }
    }
}
Loading