Remove custom PluginHandler from PluginInstanceManager

Use an executor instead.

Bug: 194781951
Test: manual && atest SystemUITests
Change-Id: Iab6bdae1802e9723c2206aabe31a08bd0a596dad
This commit is contained in:
Dave Mankoff
2021-08-05 22:33:58 -04:00
parent 33ce3e1f67
commit bf2fd66e55
3 changed files with 49 additions and 83 deletions

View File

@@ -28,9 +28,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -61,21 +58,22 @@ public class PluginInstanceManager<T extends Plugin> {
private final VersionInfo mVersion;
@VisibleForTesting
final PluginHandler mPluginHandler;
private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
private final boolean isDebuggable;
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,
PluginListener<T> listener, boolean allowMultiple, Executor mainExecutor,
Looper looper, VersionInfo version, PluginManagerImpl manager, boolean debuggable,
Executor bgExecutor, VersionInfo version, PluginManagerImpl manager, boolean debuggable,
String[] pluginWhitelist, PluginInitializer initializer) {
mInitializer = initializer;
mMainExecutor = mainExecutor;
mPluginHandler = new PluginHandler(looper);
mBgExecutor = bgExecutor;
mManager = manager;
mContext = context;
mPm = pm;
@@ -89,29 +87,29 @@ public class PluginInstanceManager<T extends Plugin> {
public void loadAll() {
if (DEBUG) Log.d(TAG, "startListening");
mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
mBgExecutor.execute(this::queryAll);
}
public void destroy() {
if (DEBUG) Log.d(TAG, "stopListening");
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPluginHandler.mPlugins);
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
for (PluginInfo<T> pluginInfo : plugins) {
mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
}
}
public void onPackageRemoved(String pkg) {
mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
mBgExecutor.execute(() -> removePkg(pkg));
}
public void onPackageChange(String pkg) {
mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkg).sendToTarget();
mBgExecutor.execute(() -> removePkg(pkg));
mBgExecutor.execute(() -> queryPkg(pkg));
}
public boolean checkAndDisable(String className) {
boolean disableAny = false;
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPluginHandler.mPlugins);
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
for (PluginInfo<T> info : plugins) {
if (className.startsWith(info.mPackage)) {
disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
@@ -121,7 +119,7 @@ public class PluginInstanceManager<T extends Plugin> {
}
public boolean disableAll() {
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPluginHandler.mPlugins);
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
boolean disabledAny = false;
for (int i = 0; i < plugins.size(); i++) {
disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
@@ -164,7 +162,7 @@ public class PluginInstanceManager<T extends Plugin> {
}
<C> boolean dependsOn(Plugin p, Class<C> cls) {
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPluginHandler.mPlugins);
ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
for (PluginInfo<T> info : plugins) {
if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
return info.mVersion != null && info.mVersion.hasClass(cls);
@@ -201,52 +199,34 @@ public class PluginInstanceManager<T extends Plugin> {
}
}
private class PluginHandler extends Handler {
private static final int QUERY_ALL = 1;
private static final int QUERY_PKG = 2;
private static final int REMOVE_PKG = 3;
private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
public PluginHandler(Looper looper) {
super(looper);
private void queryAll() {
if (DEBUG) Log.d(TAG, "queryAll " + mAction);
for (int i = mPlugins.size() - 1; i >= 0; i--) {
PluginInfo<T> pluginInfo = mPlugins.get(i);
mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
}
mPlugins.clear();
handleQueryPlugins(null);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case QUERY_ALL:
if (DEBUG) Log.d(TAG, "queryAll " + mAction);
for (int i = mPlugins.size() - 1; i >= 0; i--) {
PluginInfo<T> pluginInfo = mPlugins.get(i);
mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
}
mPlugins.clear();
handleQueryPlugins(null);
break;
case REMOVE_PKG:
String pkg = (String) msg.obj;
for (int i = mPlugins.size() - 1; i >= 0; i--) {
final PluginInfo<T> pluginInfo = mPlugins.get(i);
if (pluginInfo.mPackage.equals(pkg)) {
mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
mPlugins.remove(i);
}
}
break;
case QUERY_PKG:
String p = (String) msg.obj;
if (DEBUG) Log.d(TAG, "queryPkg " + p + " " + mAction);
if (mAllowMultiple || (mPlugins.size() == 0)) {
handleQueryPlugins(p);
} else {
if (DEBUG) Log.d(TAG, "Too many of " + mAction);
}
break;
default:
super.handleMessage(msg);
private void removePkg(String pkg) {
for (int i = mPlugins.size() - 1; i >= 0; i--) {
final PluginInfo<T> pluginInfo = mPlugins.get(i);
if (pluginInfo.mPackage.equals(pkg)) {
mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
mPlugins.remove(i);
}
}
}
private void queryPkg(String pkg) {
if (DEBUG) Log.d(TAG, "queryPkg " + pkg + " " + mAction);
if (mAllowMultiple || (mPlugins.size() == 0)) {
handleQueryPlugins(pkg);
} else {
if (DEBUG) Log.d(TAG, "Too many of " + mAction);
}
}
private void handleQueryPlugins(String pkgName) {
// This isn't actually a service and shouldn't ever be started, but is
@@ -376,7 +356,6 @@ public class PluginInstanceManager<T extends Plugin> {
}
return pv;
}
}
/**
* Construct a {@link PluginInstanceManager}
@@ -385,15 +364,15 @@ public class PluginInstanceManager<T extends Plugin> {
private final Context mContext;
private final PackageManager mPackageManager;
private final Executor mMainExecutor;
private final Looper mLooper;
private final Executor mBgExecutor;
private final PluginInitializer mInitializer;
public Factory(Context context, PackageManager packageManager,
Executor mainExecutor, Looper looper, PluginInitializer initializer) {
Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer) {
mContext = context;
mPackageManager = packageManager;
mMainExecutor = mainExecutor;
mLooper = looper;
mBgExecutor = bgExecutor;
mInitializer = initializer;
}
@@ -402,7 +381,7 @@ public class PluginInstanceManager<T extends Plugin> {
PluginListener<T> listener, boolean allowMultiple, VersionInfo version,
PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
return new PluginInstanceManager<>(mContext, mPackageManager, action, listener,
allowMultiple, mMainExecutor, mLooper, version, manager, debuggable,
allowMultiple, mMainExecutor, mBgExecutor, version, manager, debuggable,
pluginWhitelist, mInitializer);
}
}

View File

@@ -21,7 +21,6 @@ import static com.android.systemui.util.concurrency.GlobalConcurrencyModule.PRE_
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Looper;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.plugins.PluginEnabler;
@@ -71,16 +70,16 @@ public abstract class PluginsModule {
@Singleton
static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context,
PackageManager packageManager, @Main Executor mainExecutor,
@Named(PLUGIN_THREAD) Looper pluginLooper, PluginInitializer initializer) {
@Named(PLUGIN_THREAD) Executor pluginExecutor, PluginInitializer initializer) {
return new PluginInstanceManager.Factory(
context, packageManager, mainExecutor, pluginLooper, initializer);
context, packageManager, mainExecutor, pluginExecutor, initializer);
}
@Provides
@Singleton
@Named(PLUGIN_THREAD)
static Looper providesPluginLooper(ThreadFactory threadFactory) {
return threadFactory.buildLooperOnNewThread("plugin");
static Executor providesPluginExecutor(ThreadFactory threadFactory) {
return threadFactory.buildExecutorOnNewThread("plugin");
}
@Provides

View File

@@ -38,7 +38,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.HandlerThread;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -71,7 +70,6 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
// Static since the plugin needs to be generated by the PluginInstanceManager using newInstance.
private static Plugin sMockPlugin;
private HandlerThread mHandlerThread;
private Context mContextWrapper;
private PackageManager mMockPm;
private PluginListener mMockListener;
@@ -86,8 +84,6 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
@Before
public void setup() throws Exception {
mHandlerThread = new HandlerThread("test_thread");
mHandlerThread.start();
mContextWrapper = new MyContextWrapper(getContext());
mMockPm = mock(PackageManager.class);
mMockListener = mock(PluginListener.class);
@@ -98,15 +94,14 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mMockVersionInfo = mock(VersionInfo.class);
mInitializer = mock(PluginInitializer.class);
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
mMockListener, true, mFakeExecutor, mHandlerThread.getLooper(),
mMockListener, true, mFakeExecutor, mFakeExecutor,
mMockVersionInfo, mMockManager, true, new String[0], mInitializer);
sMockPlugin = mock(Plugin.class);
when(sMockPlugin.getVersion()).thenReturn(1);
}
@After
public void tearDown() throws Exception {
mHandlerThread.quit();
public void tearDown() {
sMockPlugin = null;
}
@@ -116,7 +111,6 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
Collections.emptyList());
mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
mFakeExecutor.runAllReady();
verify(mMockListener, never()).onPluginConnected(any(), any());
@@ -138,7 +132,6 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mPluginInstanceManager.destroy();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
mFakeExecutor.runAllReady();
@@ -156,7 +149,6 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
mFakeExecutor.runAllReady();
@@ -171,7 +163,6 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mPluginInstanceManager.onPackageChange("com.android.systemui");
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
mFakeExecutor.runAllReady();
// Verify the old one was destroyed.
@@ -188,13 +179,12 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
public void testNonDebuggable() throws Exception {
// Create a version that thinks the build is not debuggable.
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
mMockListener, true, mFakeExecutor, mHandlerThread.getLooper(),
mMockListener, true, mFakeExecutor, mFakeExecutor,
mMockVersionInfo, mMockManager, false, new String[0], mInitializer);
setupFakePmQuery();
mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
mFakeExecutor.runAllReady();
// Non-debuggable build should receive no plugins.
@@ -205,14 +195,13 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
public void testNonDebuggable_whitelist() throws Exception {
// Create a version that thinks the build is not debuggable.
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
mMockListener, true, mFakeExecutor, mHandlerThread.getLooper(),
mMockListener, true, mFakeExecutor, mFakeExecutor,
mMockVersionInfo, mMockManager, false,
new String[] {WHITELISTED_PACKAGE}, mInitializer);
setupFakePmQuery();
mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
mFakeExecutor.runAllReady();
// Verify startup lifecycle
@@ -250,7 +239,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
@Test
public void testDisableWhitelisted() throws Exception {
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
mMockListener, true, mFakeExecutor, mHandlerThread.getLooper(),
mMockListener, true, mFakeExecutor, mFakeExecutor,
mMockVersionInfo, mMockManager, false, new String[] {WHITELISTED_PACKAGE},
mInitializer);
createPlugin(); // Get into valid created state.
@@ -288,7 +277,6 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mPluginInstanceManager.loadAll();
waitForIdleSync(mPluginInstanceManager.mPluginHandler);
mFakeExecutor.runAllReady();
}