Merge "Remove custom MainHandler from PluginInstanceManager" into sc-v2-dev am: b325b69f12
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15478379 Change-Id: Ieb36b3dba8ef2ed7a624da81d37814bee6011286
This commit is contained in:
@@ -45,6 +45,7 @@ import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
public class PluginInstanceManager<T extends Plugin> {
|
public class PluginInstanceManager<T extends Plugin> {
|
||||||
|
|
||||||
@@ -59,8 +60,6 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
private final boolean mAllowMultiple;
|
private final boolean mAllowMultiple;
|
||||||
private final VersionInfo mVersion;
|
private final VersionInfo mVersion;
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
final MainHandler mMainHandler;
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final PluginHandler mPluginHandler;
|
final PluginHandler mPluginHandler;
|
||||||
private final boolean isDebuggable;
|
private final boolean isDebuggable;
|
||||||
@@ -68,13 +67,14 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
private final PluginManagerImpl mManager;
|
private final PluginManagerImpl mManager;
|
||||||
private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
|
private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
|
||||||
private final PluginInitializer mInitializer;
|
private final PluginInitializer mInitializer;
|
||||||
|
private final Executor mMainExecutor;
|
||||||
|
|
||||||
PluginInstanceManager(Context context, PackageManager pm, String action,
|
PluginInstanceManager(Context context, PackageManager pm, String action,
|
||||||
PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
|
PluginListener<T> listener, boolean allowMultiple, Executor mainExecutor,
|
||||||
PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist,
|
Looper looper, VersionInfo version, PluginManagerImpl manager, boolean debuggable,
|
||||||
PluginInitializer initializer) {
|
String[] pluginWhitelist, PluginInitializer initializer) {
|
||||||
mInitializer = initializer;
|
mInitializer = initializer;
|
||||||
mMainHandler = new MainHandler(Looper.getMainLooper());
|
mMainExecutor = mainExecutor;
|
||||||
mPluginHandler = new PluginHandler(looper);
|
mPluginHandler = new PluginHandler(looper);
|
||||||
mManager = manager;
|
mManager = manager;
|
||||||
mContext = context;
|
mContext = context;
|
||||||
@@ -87,21 +87,6 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
isDebuggable = debuggable;
|
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() {
|
public void loadAll() {
|
||||||
if (DEBUG) Log.d(TAG, "startListening");
|
if (DEBUG) Log.d(TAG, "startListening");
|
||||||
mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
|
mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
|
||||||
@@ -109,10 +94,9 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if (DEBUG) Log.d(TAG, "stopListening");
|
if (DEBUG) Log.d(TAG, "stopListening");
|
||||||
ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
|
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPluginHandler.mPlugins);
|
||||||
for (PluginInfo plugin : plugins) {
|
for (PluginInfo<T> pluginInfo : plugins) {
|
||||||
mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
|
mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
|
||||||
plugin.mPlugin).sendToTarget();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +111,8 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
|
|
||||||
public boolean checkAndDisable(String className) {
|
public boolean checkAndDisable(String className) {
|
||||||
boolean disableAny = false;
|
boolean disableAny = false;
|
||||||
ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
|
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPluginHandler.mPlugins);
|
||||||
for (PluginInfo info : plugins) {
|
for (PluginInfo<T> info : plugins) {
|
||||||
if (className.startsWith(info.mPackage)) {
|
if (className.startsWith(info.mPackage)) {
|
||||||
disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
|
disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
|
||||||
}
|
}
|
||||||
@@ -137,7 +121,7 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean disableAll() {
|
public boolean disableAll() {
|
||||||
ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
|
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPluginHandler.mPlugins);
|
||||||
boolean disabledAny = false;
|
boolean disabledAny = false;
|
||||||
for (int i = 0; i < plugins.size(); i++) {
|
for (int i = 0; i < plugins.size(); i++) {
|
||||||
disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
|
disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
|
||||||
@@ -161,7 +145,7 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean disable(PluginInfo info, @PluginEnabler.DisableReason int reason) {
|
private boolean disable(PluginInfo<T> info, @PluginEnabler.DisableReason int reason) {
|
||||||
// Live by the sword, die by the sword.
|
// Live by the sword, die by the sword.
|
||||||
// Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
|
// Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
|
||||||
|
|
||||||
@@ -179,9 +163,9 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> boolean dependsOn(Plugin p, Class<T> cls) {
|
<C> boolean dependsOn(Plugin p, Class<C> cls) {
|
||||||
ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
|
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPluginHandler.mPlugins);
|
||||||
for (PluginInfo info : plugins) {
|
for (PluginInfo<T> info : plugins) {
|
||||||
if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
|
if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
|
||||||
return info.mVersion != null && info.mVersion.hasClass(cls);
|
return info.mVersion != null && info.mVersion.hasClass(cls);
|
||||||
}
|
}
|
||||||
@@ -195,42 +179,25 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
getClass().getSimpleName(), hashCode(), mAction);
|
getClass().getSimpleName(), hashCode(), mAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MainHandler extends Handler {
|
private void onPluginConnected(PluginInfo<T> pluginInfo) {
|
||||||
private static final int PLUGIN_CONNECTED = 1;
|
|
||||||
private static final int PLUGIN_DISCONNECTED = 2;
|
|
||||||
|
|
||||||
public MainHandler(Looper looper) {
|
|
||||||
super(looper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case PLUGIN_CONNECTED:
|
|
||||||
if (DEBUG) Log.d(TAG, "onPluginConnected");
|
if (DEBUG) Log.d(TAG, "onPluginConnected");
|
||||||
PluginPrefs.setHasPlugins(mContext);
|
PluginPrefs.setHasPlugins(mContext);
|
||||||
PluginInfo<T> info = (PluginInfo<T>) msg.obj;
|
|
||||||
mInitializer.handleWtfs();
|
mInitializer.handleWtfs();
|
||||||
if (!(msg.obj instanceof PluginFragment)) {
|
if (!(pluginInfo.mPlugin instanceof PluginFragment)) {
|
||||||
// Only call onDestroy for plugins that aren't fragments, as fragments
|
// Only call onCreate for plugins that aren't fragments, as fragments
|
||||||
// will get the onCreate as part of the fragment lifecycle.
|
// will get the onCreate as part of the fragment lifecycle.
|
||||||
info.mPlugin.onCreate(mContext, info.mPluginContext);
|
pluginInfo.mPlugin.onCreate(mContext, pluginInfo.mPluginContext);
|
||||||
}
|
}
|
||||||
mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
|
mListener.onPluginConnected(pluginInfo.mPlugin, pluginInfo.mPluginContext);
|
||||||
break;
|
}
|
||||||
case PLUGIN_DISCONNECTED:
|
|
||||||
|
private void onPluginDisconnected(T plugin) {
|
||||||
if (DEBUG) Log.d(TAG, "onPluginDisconnected");
|
if (DEBUG) Log.d(TAG, "onPluginDisconnected");
|
||||||
mListener.onPluginDisconnected((T) msg.obj);
|
mListener.onPluginDisconnected(plugin);
|
||||||
if (!(msg.obj instanceof PluginFragment)) {
|
if (!(plugin instanceof PluginFragment)) {
|
||||||
// Only call onDestroy for plugins that aren't fragments, as fragments
|
// Only call onDestroy for plugins that aren't fragments, as fragments
|
||||||
// will get the onDestroy as part of the fragment lifecycle.
|
// will get the onDestroy as part of the fragment lifecycle.
|
||||||
((T) msg.obj).onDestroy();
|
plugin.onDestroy();
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
super.handleMessage(msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,8 +219,7 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
if (DEBUG) Log.d(TAG, "queryAll " + mAction);
|
if (DEBUG) Log.d(TAG, "queryAll " + mAction);
|
||||||
for (int i = mPlugins.size() - 1; i >= 0; i--) {
|
for (int i = mPlugins.size() - 1; i >= 0; i--) {
|
||||||
PluginInfo<T> pluginInfo = mPlugins.get(i);
|
PluginInfo<T> pluginInfo = mPlugins.get(i);
|
||||||
mMainHandler.obtainMessage(
|
mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
|
||||||
MainHandler.PLUGIN_DISCONNECTED, pluginInfo.mPlugin).sendToTarget();
|
|
||||||
}
|
}
|
||||||
mPlugins.clear();
|
mPlugins.clear();
|
||||||
handleQueryPlugins(null);
|
handleQueryPlugins(null);
|
||||||
@@ -261,10 +227,9 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
case REMOVE_PKG:
|
case REMOVE_PKG:
|
||||||
String pkg = (String) msg.obj;
|
String pkg = (String) msg.obj;
|
||||||
for (int i = mPlugins.size() - 1; i >= 0; i--) {
|
for (int i = mPlugins.size() - 1; i >= 0; i--) {
|
||||||
final PluginInfo<T> plugin = mPlugins.get(i);
|
final PluginInfo<T> pluginInfo = mPlugins.get(i);
|
||||||
if (plugin.mPackage.equals(pkg)) {
|
if (pluginInfo.mPackage.equals(pkg)) {
|
||||||
mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
|
mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
|
||||||
plugin.mPlugin).sendToTarget();
|
|
||||||
mPlugins.remove(i);
|
mPlugins.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,12 +272,12 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
for (ResolveInfo info : result) {
|
for (ResolveInfo info : result) {
|
||||||
ComponentName name = new ComponentName(info.serviceInfo.packageName,
|
ComponentName name = new ComponentName(info.serviceInfo.packageName,
|
||||||
info.serviceInfo.name);
|
info.serviceInfo.name);
|
||||||
PluginInfo<T> t = handleLoadPlugin(name);
|
PluginInfo<T> pluginInfo = handleLoadPlugin(name);
|
||||||
if (t == null) continue;
|
if (pluginInfo == null) continue;
|
||||||
|
|
||||||
// add plugin before sending PLUGIN_CONNECTED message
|
// add plugin before sending PLUGIN_CONNECTED message
|
||||||
mPlugins.add(t);
|
mPlugins.add(pluginInfo);
|
||||||
mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
|
mMainExecutor.execute(() -> onPluginConnected(pluginInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +314,7 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
try {
|
try {
|
||||||
VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
|
VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
|
||||||
if (DEBUG) Log.d(TAG, "createPlugin");
|
if (DEBUG) Log.d(TAG, "createPlugin");
|
||||||
return new PluginInfo(pkg, cls, plugin, pluginContext, version);
|
return new PluginInfo<>(pkg, cls, plugin, pluginContext, version);
|
||||||
} catch (InvalidVersionException e) {
|
} catch (InvalidVersionException e) {
|
||||||
final int icon = Resources.getSystem().getIdentifier(
|
final int icon = Resources.getSystem().getIdentifier(
|
||||||
"stat_sys_warning", "drawable", "android");
|
"stat_sys_warning", "drawable", "android");
|
||||||
@@ -419,13 +384,15 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
public static class Factory {
|
public static class Factory {
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final PackageManager mPackageManager;
|
private final PackageManager mPackageManager;
|
||||||
|
private final Executor mMainExecutor;
|
||||||
private final Looper mLooper;
|
private final Looper mLooper;
|
||||||
private final PluginInitializer mInitializer;
|
private final PluginInitializer mInitializer;
|
||||||
|
|
||||||
public Factory(Context context, PackageManager packageManager, Looper looper,
|
public Factory(Context context, PackageManager packageManager,
|
||||||
PluginInitializer initializer) {
|
Executor mainExecutor, Looper looper, PluginInitializer initializer) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mPackageManager = packageManager;
|
mPackageManager = packageManager;
|
||||||
|
mMainExecutor = mainExecutor;
|
||||||
mLooper = looper;
|
mLooper = looper;
|
||||||
mInitializer = initializer;
|
mInitializer = initializer;
|
||||||
}
|
}
|
||||||
@@ -435,8 +402,8 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
PluginListener<T> listener, boolean allowMultiple, VersionInfo version,
|
PluginListener<T> listener, boolean allowMultiple, VersionInfo version,
|
||||||
PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
|
PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
|
||||||
return new PluginInstanceManager<>(mContext, mPackageManager, action, listener,
|
return new PluginInstanceManager<>(mContext, mPackageManager, action, listener,
|
||||||
allowMultiple, mLooper, version, manager, debuggable, pluginWhitelist,
|
allowMultiple, mMainExecutor, mLooper, version, manager, debuggable,
|
||||||
mInitializer);
|
pluginWhitelist, mInitializer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,10 +433,10 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class PluginInfo<T> {
|
static class PluginInfo<T extends Plugin> {
|
||||||
private final Context mPluginContext;
|
private final Context mPluginContext;
|
||||||
private final VersionInfo mVersion;
|
private final VersionInfo mVersion;
|
||||||
private String mClass;
|
private final String mClass;
|
||||||
T mPlugin;
|
T mPlugin;
|
||||||
String mPackage;
|
String mPackage;
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ 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> T getOneShotPlugin(Class<T> cls);
|
|
||||||
<T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
|
|
||||||
|
|
||||||
<T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
|
<T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
|
||||||
<T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
|
<T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
|
||||||
boolean allowMultiple);
|
boolean allowMultiple);
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
|||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
@@ -41,8 +40,6 @@ import android.widget.Toast;
|
|||||||
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||||
import com.android.systemui.plugins.Plugin;
|
import com.android.systemui.plugins.Plugin;
|
||||||
import com.android.systemui.plugins.PluginListener;
|
import com.android.systemui.plugins.PluginListener;
|
||||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
|
||||||
import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
|
|
||||||
|
|
||||||
import dalvik.system.PathClassLoader;
|
import dalvik.system.PathClassLoader;
|
||||||
|
|
||||||
@@ -109,38 +106,6 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
|||||||
return mPluginEnabler;
|
return mPluginEnabler;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(mankoff): This appears to be only called from tests. Remove?
|
|
||||||
public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
|
|
||||||
ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
|
|
||||||
if (info == null) {
|
|
||||||
throw new RuntimeException(cls + " doesn't provide an interface");
|
|
||||||
}
|
|
||||||
if (TextUtils.isEmpty(info.action())) {
|
|
||||||
throw new RuntimeException(cls + " doesn't provide an action");
|
|
||||||
}
|
|
||||||
return getOneShotPlugin(info.action(), cls);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
|
|
||||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
|
||||||
throw new RuntimeException("Must be called from UI thread");
|
|
||||||
}
|
|
||||||
// Passing null causes compiler to complain about incompatible (generic) types.
|
|
||||||
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) {
|
|
||||||
mOneShotPackages.add(info.mPackage);
|
|
||||||
mHasOneShot = true;
|
|
||||||
startListening();
|
|
||||||
return info.mPlugin;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
|
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
|
||||||
addPluginListener(listener, cls, false);
|
addPluginListener(listener, cls, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,18 @@ import android.content.pm.PackageManager;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.shared.plugins.PluginEnabler;
|
import com.android.systemui.shared.plugins.PluginEnabler;
|
||||||
import com.android.systemui.shared.plugins.PluginInitializer;
|
import com.android.systemui.shared.plugins.PluginInitializer;
|
||||||
import com.android.systemui.shared.plugins.PluginInstanceManager;
|
import com.android.systemui.shared.plugins.PluginInstanceManager;
|
||||||
import com.android.systemui.shared.plugins.PluginManager;
|
import com.android.systemui.shared.plugins.PluginManager;
|
||||||
import com.android.systemui.shared.plugins.PluginManagerImpl;
|
import com.android.systemui.shared.plugins.PluginManagerImpl;
|
||||||
import com.android.systemui.shared.plugins.PluginPrefs;
|
import com.android.systemui.shared.plugins.PluginPrefs;
|
||||||
|
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
|
||||||
import com.android.systemui.util.concurrency.ThreadFactory;
|
import com.android.systemui.util.concurrency.ThreadFactory;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -46,7 +49,7 @@ import dagger.Provides;
|
|||||||
* Covers code both in com.android.systemui.plugins and code in
|
* Covers code both in com.android.systemui.plugins and code in
|
||||||
* com.android.systemui.shared.plugins.
|
* com.android.systemui.shared.plugins.
|
||||||
*/
|
*/
|
||||||
@Module
|
@Module(includes = {GlobalConcurrencyModule.class})
|
||||||
public abstract class PluginsModule {
|
public abstract class PluginsModule {
|
||||||
public static final String PLUGIN_THREAD = "plugin_thread";
|
public static final String PLUGIN_THREAD = "plugin_thread";
|
||||||
public static final String PLUGIN_DEBUG = "plugin_debug";
|
public static final String PLUGIN_DEBUG = "plugin_debug";
|
||||||
@@ -67,10 +70,10 @@ public abstract class PluginsModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context,
|
static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context,
|
||||||
PackageManager packageManager, @Named(PLUGIN_THREAD) Looper pluginLooper,
|
PackageManager packageManager, @Main Executor mainExecutor,
|
||||||
PluginInitializer initializer) {
|
@Named(PLUGIN_THREAD) Looper pluginLooper, PluginInitializer initializer) {
|
||||||
return new PluginInstanceManager.Factory(
|
return new PluginInstanceManager.Factory(
|
||||||
context, packageManager, pluginLooper, initializer);
|
context, packageManager, mainExecutor, pluginLooper, initializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -67,11 +67,23 @@ public abstract class GlobalConcurrencyModule {
|
|||||||
* Provide a Main-Thread Executor.
|
* Provide a Main-Thread Executor.
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
|
@Singleton
|
||||||
@Main
|
@Main
|
||||||
public static Executor provideMainExecutor(Context context) {
|
public static Executor provideMainExecutor(Context context) {
|
||||||
return context.getMainExecutor();
|
return context.getMainExecutor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a Main-Thread DelayableExecutor.
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Main
|
||||||
|
public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
|
||||||
|
return new ExecutorImpl(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
@Binds
|
@Binds
|
||||||
@Singleton
|
@Singleton
|
||||||
|
|||||||
@@ -119,16 +119,6 @@ public abstract class SysUIConcurrencyModule {
|
|||||||
return new ExecutorImpl(looper);
|
return new ExecutorImpl(looper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide a Main-Thread Executor.
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
@SysUISingleton
|
|
||||||
@Main
|
|
||||||
public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
|
|
||||||
return new ExecutorImpl(looper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a Background-Thread Executor by default.
|
* Provide a Background-Thread Executor by default.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
package com.android.systemui.shared.plugins;
|
package com.android.systemui.shared.plugins;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertFalse;
|
import static junit.framework.Assert.assertFalse;
|
||||||
import static junit.framework.Assert.assertNotNull;
|
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
@@ -42,7 +41,6 @@ import android.content.pm.ServiceInfo;
|
|||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
import androidx.test.annotation.UiThreadTest;
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||||
@@ -50,8 +48,9 @@ import com.android.systemui.SysuiTestCase;
|
|||||||
import com.android.systemui.plugins.Plugin;
|
import com.android.systemui.plugins.Plugin;
|
||||||
import com.android.systemui.plugins.PluginListener;
|
import com.android.systemui.plugins.PluginListener;
|
||||||
import com.android.systemui.plugins.annotations.Requires;
|
import com.android.systemui.plugins.annotations.Requires;
|
||||||
import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
|
|
||||||
import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
|
import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
|
||||||
|
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||||
|
import com.android.systemui.util.time.FakeSystemClock;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -83,6 +82,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
ComponentName mTestPluginComponentName =
|
ComponentName mTestPluginComponentName =
|
||||||
new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
|
new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
|
||||||
private PluginInitializer mInitializer;
|
private PluginInitializer mInitializer;
|
||||||
|
private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
@@ -98,8 +98,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
mMockVersionInfo = mock(VersionInfo.class);
|
mMockVersionInfo = mock(VersionInfo.class);
|
||||||
mInitializer = mock(PluginInitializer.class);
|
mInitializer = mock(PluginInitializer.class);
|
||||||
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
||||||
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
|
mMockListener, true, mFakeExecutor, mHandlerThread.getLooper(),
|
||||||
mMockManager, true, new String[0], mInitializer);
|
mMockVersionInfo, mMockManager, true, new String[0], mInitializer);
|
||||||
sMockPlugin = mock(Plugin.class);
|
sMockPlugin = mock(Plugin.class);
|
||||||
when(sMockPlugin.getVersion()).thenReturn(1);
|
when(sMockPlugin.getVersion()).thenReturn(1);
|
||||||
}
|
}
|
||||||
@@ -110,23 +110,14 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
sMockPlugin = null;
|
sMockPlugin = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThreadTest
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetPlugin() throws Exception {
|
public void testNoPlugins() {
|
||||||
setupFakePmQuery();
|
|
||||||
PluginInfo p = mPluginInstanceManager.getPlugin();
|
|
||||||
assertNotNull(p.mPlugin);
|
|
||||||
verify(sMockPlugin).onCreate(any(), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNoPlugins() throws Exception {
|
|
||||||
when(mMockPm.queryIntentServices(any(), anyInt())).thenReturn(
|
when(mMockPm.queryIntentServices(any(), anyInt())).thenReturn(
|
||||||
Collections.emptyList());
|
Collections.emptyList());
|
||||||
mPluginInstanceManager.loadAll();
|
mPluginInstanceManager.loadAll();
|
||||||
|
|
||||||
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
||||||
waitForIdleSync(mPluginInstanceManager.mMainHandler);
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
verify(mMockListener, never()).onPluginConnected(any(), any());
|
verify(mMockListener, never()).onPluginConnected(any(), any());
|
||||||
}
|
}
|
||||||
@@ -148,7 +139,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
mPluginInstanceManager.destroy();
|
mPluginInstanceManager.destroy();
|
||||||
|
|
||||||
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
||||||
waitForIdleSync(mPluginInstanceManager.mMainHandler);
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
|
|
||||||
// Verify shutdown lifecycle
|
// Verify shutdown lifecycle
|
||||||
verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
|
verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
|
||||||
@@ -165,7 +157,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
mPluginInstanceManager.loadAll();
|
mPluginInstanceManager.loadAll();
|
||||||
|
|
||||||
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
||||||
waitForIdleSync(mPluginInstanceManager.mMainHandler);
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
|
|
||||||
// Plugin shouldn't be connected because it is the wrong version.
|
// Plugin shouldn't be connected because it is the wrong version.
|
||||||
verify(mMockListener, never()).onPluginConnected(any(), any());
|
verify(mMockListener, never()).onPluginConnected(any(), any());
|
||||||
@@ -179,7 +172,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
mPluginInstanceManager.onPackageChange("com.android.systemui");
|
mPluginInstanceManager.onPackageChange("com.android.systemui");
|
||||||
|
|
||||||
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
||||||
waitForIdleSync(mPluginInstanceManager.mMainHandler);
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
// Verify the old one was destroyed.
|
// Verify the old one was destroyed.
|
||||||
verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
|
verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
|
||||||
@@ -195,14 +188,14 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
public void testNonDebuggable() throws Exception {
|
public void testNonDebuggable() throws Exception {
|
||||||
// Create a version that thinks the build is not debuggable.
|
// Create a version that thinks the build is not debuggable.
|
||||||
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
||||||
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
|
mMockListener, true, mFakeExecutor, mHandlerThread.getLooper(),
|
||||||
mMockManager, false, new String[0], mInitializer);
|
mMockVersionInfo, mMockManager, false, new String[0], mInitializer);
|
||||||
setupFakePmQuery();
|
setupFakePmQuery();
|
||||||
|
|
||||||
mPluginInstanceManager.loadAll();
|
mPluginInstanceManager.loadAll();
|
||||||
|
|
||||||
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
||||||
waitForIdleSync(mPluginInstanceManager.mMainHandler);;
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
// Non-debuggable build should receive no plugins.
|
// Non-debuggable build should receive no plugins.
|
||||||
verify(mMockListener, never()).onPluginConnected(any(), any());
|
verify(mMockListener, never()).onPluginConnected(any(), any());
|
||||||
@@ -212,14 +205,15 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
public void testNonDebuggable_whitelist() throws Exception {
|
public void testNonDebuggable_whitelist() throws Exception {
|
||||||
// Create a version that thinks the build is not debuggable.
|
// Create a version that thinks the build is not debuggable.
|
||||||
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
||||||
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
|
mMockListener, true, mFakeExecutor, mHandlerThread.getLooper(),
|
||||||
mMockManager, false, new String[] {WHITELISTED_PACKAGE}, mInitializer);
|
mMockVersionInfo, mMockManager, false,
|
||||||
|
new String[] {WHITELISTED_PACKAGE}, mInitializer);
|
||||||
setupFakePmQuery();
|
setupFakePmQuery();
|
||||||
|
|
||||||
mPluginInstanceManager.loadAll();
|
mPluginInstanceManager.loadAll();
|
||||||
|
|
||||||
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
||||||
waitForIdleSync(mPluginInstanceManager.mMainHandler);
|
mFakeExecutor.runAllReady();
|
||||||
|
|
||||||
// Verify startup lifecycle
|
// Verify startup lifecycle
|
||||||
verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
|
verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
|
||||||
@@ -256,8 +250,9 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testDisableWhitelisted() throws Exception {
|
public void testDisableWhitelisted() throws Exception {
|
||||||
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
||||||
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
|
mMockListener, true, mFakeExecutor, mHandlerThread.getLooper(),
|
||||||
mMockManager, false, new String[] {WHITELISTED_PACKAGE}, mInitializer);
|
mMockVersionInfo, mMockManager, false, new String[] {WHITELISTED_PACKAGE},
|
||||||
|
mInitializer);
|
||||||
createPlugin(); // Get into valid created state.
|
createPlugin(); // Get into valid created state.
|
||||||
|
|
||||||
mPluginInstanceManager.disableAll();
|
mPluginInstanceManager.disableAll();
|
||||||
@@ -294,7 +289,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
|||||||
mPluginInstanceManager.loadAll();
|
mPluginInstanceManager.loadAll();
|
||||||
|
|
||||||
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
|
||||||
waitForIdleSync(mPluginInstanceManager.mMainHandler);
|
mFakeExecutor.runAllReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Real context with no registering/unregistering of receivers.
|
// Real context with no registering/unregistering of receivers.
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ package com.android.systemui.shared.plugins;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertSame;
|
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -37,7 +36,6 @@ import com.android.systemui.SysuiTestCase;
|
|||||||
import com.android.systemui.plugins.Plugin;
|
import com.android.systemui.plugins.Plugin;
|
||||||
import com.android.systemui.plugins.PluginListener;
|
import com.android.systemui.plugins.PluginListener;
|
||||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||||
import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -90,16 +88,6 @@ public class PluginManagerTest extends SysuiTestCase {
|
|||||||
mMockListener = mock(PluginListener.class);
|
mMockListener = mock(PluginListener.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RunWithLooper(setAsMainLooper = true)
|
|
||||||
@Test
|
|
||||||
public void testOneShot() {
|
|
||||||
Plugin mockPlugin = mock(Plugin.class);
|
|
||||||
when(mMockPluginInstance.getPlugin()).thenReturn(new PluginInfo(null, null, mockPlugin,
|
|
||||||
null, null));
|
|
||||||
Plugin result = mPluginManager.getOneShotPlugin("myAction", TestPlugin.class);
|
|
||||||
assertSame(mockPlugin, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddListener() {
|
public void testAddListener() {
|
||||||
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
|
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
|
||||||
@@ -129,7 +117,6 @@ public class PluginManagerTest extends SysuiTestCase {
|
|||||||
applicationInfo.sourceDir = sourceDir;
|
applicationInfo.sourceDir = sourceDir;
|
||||||
applicationInfo.packageName = WHITELISTED_PACKAGE;
|
applicationInfo.packageName = WHITELISTED_PACKAGE;
|
||||||
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
|
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
|
||||||
assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class));
|
|
||||||
assertNull(mPluginManager.getClassLoader(applicationInfo));
|
assertNull(mPluginManager.getClassLoader(applicationInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +193,8 @@ public class PluginManagerTest extends SysuiTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
|
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
|
||||||
public static interface TestPlugin extends Plugin {
|
public interface TestPlugin extends Plugin {
|
||||||
public static final String ACTION = "testAction";
|
String ACTION = "testAction";
|
||||||
public static final int VERSION = 1;
|
int VERSION = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,14 +65,4 @@ public class FakePluginManager implements PluginManager {
|
|||||||
public String[] getPrivilegedPlugins() {
|
public String[] getPrivilegedPlugins() {
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user