Loading packages/SystemUI/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -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" /> Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java +18 −3 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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; Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java +66 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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(), Loading @@ -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); Loading @@ -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) { Loading @@ -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); Loading @@ -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); } Loading @@ -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()) { Loading packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +0 −1 Original line number Diff line number Diff line Loading @@ -80,7 +80,6 @@ public class SystemUIApplication extends Application { ShortcutKeyDispatcher.class, VendorServices.class, LatencyTester.class, DozeFactory.Initializer.class, }; /** Loading packages/SystemUI/src/com/android/systemui/SysuiRestartReceiver.java 0 → 100644 +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
packages/SystemUI/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -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" /> Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java +18 −3 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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; Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java +66 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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(), Loading @@ -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); Loading @@ -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) { Loading @@ -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); Loading @@ -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); } Loading @@ -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()) { Loading
packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +0 −1 Original line number Diff line number Diff line Loading @@ -80,7 +80,6 @@ public class SystemUIApplication extends Application { ShortcutKeyDispatcher.class, VendorServices.class, LatencyTester.class, DozeFactory.Initializer.class, }; /** Loading
packages/SystemUI/src/com/android/systemui/SysuiRestartReceiver.java 0 → 100644 +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()); } } }