Merge "Refactor code to PluginEnabler"
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.ComponentName;
|
||||
|
||||
/**
|
||||
* Enables and disables plugins.
|
||||
*/
|
||||
public interface PluginEnabler {
|
||||
void setEnabled(ComponentName component, boolean enabled);
|
||||
boolean isEnabled(ComponentName component);
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.android.systemui.shared.plugins;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
|
||||
@@ -26,9 +27,10 @@ public interface PluginInitializer {
|
||||
|
||||
/**
|
||||
* This Runnable is run on the bg looper during initialization of {@link PluginManagerImpl}.
|
||||
* It can be null.
|
||||
*/
|
||||
Runnable getBgInitCallback();
|
||||
@Nullable Runnable getBgInitCallback();
|
||||
|
||||
String[] getWhitelistedPlugins(Context context);
|
||||
|
||||
PluginEnabler getPluginEnabler(Context context);
|
||||
}
|
||||
|
||||
@@ -159,10 +159,8 @@ public class PluginInstanceManager<T extends Plugin> {
|
||||
// plugin, if the plugin causing a crash cannot be identified, they are all disabled
|
||||
// assuming one of them must be bad.
|
||||
Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass);
|
||||
mPm.setComponentEnabledSetting(
|
||||
new ComponentName(info.mPackage, info.mClass),
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP);
|
||||
mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass),
|
||||
false);
|
||||
}
|
||||
|
||||
public <T> boolean dependsOn(Plugin p, Class<T> cls) {
|
||||
@@ -280,8 +278,7 @@ public class PluginInstanceManager<T extends Plugin> {
|
||||
if (pkgName != null) {
|
||||
intent.setPackage(pkgName);
|
||||
}
|
||||
List<ResolveInfo> result =
|
||||
mPm.queryIntentServices(intent, 0);
|
||||
List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
|
||||
if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
|
||||
if (result.size() > 1 && !mAllowMultiple) {
|
||||
// TODO: Show warning.
|
||||
@@ -306,6 +303,10 @@ public class PluginInstanceManager<T extends Plugin> {
|
||||
Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
|
||||
return null;
|
||||
}
|
||||
if (!mManager.getPluginEnabler().isEnabled(component)) {
|
||||
if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
|
||||
return null;
|
||||
}
|
||||
String pkg = component.getPackageName();
|
||||
String cls = component.getClassName();
|
||||
try {
|
||||
|
||||
@@ -41,12 +41,11 @@ import android.widget.Toast;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
|
||||
import com.android.systemui.plugins.Plugin;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper;
|
||||
import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
|
||||
import dalvik.system.PathClassLoader;
|
||||
|
||||
@@ -74,6 +73,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
private final PluginInstanceManagerFactory mFactory;
|
||||
private final boolean isDebuggable;
|
||||
private final PluginPrefs mPluginPrefs;
|
||||
private final PluginEnabler mPluginEnabler;
|
||||
private ClassLoaderFilter mParentClassLoader;
|
||||
private boolean mListening;
|
||||
private boolean mHasOneShot;
|
||||
@@ -94,6 +94,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
isDebuggable = debuggable;
|
||||
mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
|
||||
mPluginPrefs = new PluginPrefs(mContext);
|
||||
mPluginEnabler = initializer.getPluginEnabler(mContext);
|
||||
|
||||
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
|
||||
defaultHandler);
|
||||
@@ -109,6 +110,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
return mWhitelistedPlugins.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public PluginEnabler getPluginEnabler() {
|
||||
return mPluginEnabler;
|
||||
}
|
||||
|
||||
public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
|
||||
ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
|
||||
if (info == null) {
|
||||
@@ -202,9 +207,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
||||
Uri uri = intent.getData();
|
||||
ComponentName component = ComponentName.unflattenFromString(
|
||||
uri.toString().substring(10));
|
||||
mContext.getPackageManager().setComponentEnabledSetting(component,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP);
|
||||
getPluginEnabler().setEnabled(component, false);
|
||||
mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
|
||||
SystemMessage.NOTE_PLUGIN);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.plugins;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.shared.plugins.PluginEnabler;
|
||||
|
||||
public class PluginEnablerImpl implements PluginEnabler {
|
||||
|
||||
final private PackageManager mPm;
|
||||
|
||||
public PluginEnablerImpl(Context context) {
|
||||
this(context.getPackageManager());
|
||||
}
|
||||
|
||||
@VisibleForTesting public PluginEnablerImpl(PackageManager pm) {
|
||||
mPm = pm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(ComponentName component, boolean enabled) {
|
||||
final int desiredState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
|
||||
mPm.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(ComponentName component) {
|
||||
return mPm.getComponentEnabledSetting(component)
|
||||
!= PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import android.content.Context;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.shared.plugins.PluginEnabler;
|
||||
import com.android.systemui.shared.plugins.PluginInitializer;
|
||||
import com.android.systemui.R;
|
||||
|
||||
@@ -44,4 +45,8 @@ public class PluginInitializerImpl implements PluginInitializer {
|
||||
public String[] getWhitelistedPlugins(Context context) {
|
||||
return context.getResources().getStringArray(R.array.config_pluginWhitelist);
|
||||
}
|
||||
|
||||
public PluginEnabler getPluginEnabler(Context context) {
|
||||
return new PluginEnablerImpl(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,13 @@ import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import androidx.preference.PreferenceFragment;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.PluginEnablerImpl;
|
||||
import com.android.systemui.shared.plugins.PluginEnabler;
|
||||
import com.android.systemui.shared.plugins.PluginInstanceManager;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.shared.plugins.PluginPrefs;
|
||||
@@ -41,12 +39,18 @@ import com.android.systemui.shared.plugins.PluginPrefs;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.preference.PreferenceFragment;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
public class PluginFragment extends PreferenceFragment {
|
||||
|
||||
public static final String ACTION_PLUGIN_SETTINGS
|
||||
= "com.android.systemui.action.PLUGIN_SETTINGS";
|
||||
|
||||
private PluginPrefs mPluginPrefs;
|
||||
private PluginEnabler mPluginEnabler;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -68,6 +72,7 @@ public class PluginFragment extends PreferenceFragment {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
mPluginEnabler = new PluginEnablerImpl(getContext());
|
||||
loadPrefs();
|
||||
}
|
||||
|
||||
@@ -98,7 +103,7 @@ public class PluginFragment extends PreferenceFragment {
|
||||
PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
|
||||
apps.forEach(app -> {
|
||||
if (!plugins.containsKey(app.packageName)) return;
|
||||
SwitchPreference pref = new PluginPreference(prefContext, app);
|
||||
SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler);
|
||||
pref.setSummary("Plugins: " + toString(plugins.get(app.packageName)));
|
||||
screen.addPreference(pref);
|
||||
});
|
||||
@@ -139,15 +144,16 @@ public class PluginFragment extends PreferenceFragment {
|
||||
private static class PluginPreference extends SwitchPreference {
|
||||
private final boolean mHasSettings;
|
||||
private final PackageInfo mInfo;
|
||||
private final PackageManager mPm;
|
||||
private final PluginEnabler mPluginEnabler;
|
||||
|
||||
public PluginPreference(Context prefContext, PackageInfo info) {
|
||||
public PluginPreference(Context prefContext, PackageInfo info, PluginEnabler pluginEnabler) {
|
||||
super(prefContext);
|
||||
mPm = prefContext.getPackageManager();
|
||||
mHasSettings = mPm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
|
||||
PackageManager pm = prefContext.getPackageManager();
|
||||
mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
|
||||
.setPackage(info.packageName), 0) != null;
|
||||
mInfo = info;
|
||||
setTitle(info.applicationInfo.loadLabel(mPm));
|
||||
mPluginEnabler = pluginEnabler;
|
||||
setTitle(info.applicationInfo.loadLabel(pm));
|
||||
setChecked(isPluginEnabled());
|
||||
setWidgetLayoutResource(R.layout.tuner_widget_settings_switch);
|
||||
}
|
||||
@@ -156,8 +162,7 @@ public class PluginFragment extends PreferenceFragment {
|
||||
for (int i = 0; i < mInfo.services.length; i++) {
|
||||
ComponentName componentName = new ComponentName(mInfo.packageName,
|
||||
mInfo.services[i].name);
|
||||
if (mPm.getComponentEnabledSetting(componentName)
|
||||
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
|
||||
if (!mPluginEnabler.isEnabled(componentName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -165,17 +170,14 @@ public class PluginFragment extends PreferenceFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean persistBoolean(boolean value) {
|
||||
final int desiredState = value ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
|
||||
protected boolean persistBoolean(boolean isEnabled) {
|
||||
boolean shouldSendBroadcast = false;
|
||||
for (int i = 0; i < mInfo.services.length; i++) {
|
||||
ComponentName componentName = new ComponentName(mInfo.packageName,
|
||||
mInfo.services[i].name);
|
||||
|
||||
if (mPm.getComponentEnabledSetting(componentName) != desiredState) {
|
||||
mPm.setComponentEnabledSetting(componentName, desiredState,
|
||||
PackageManager.DONT_KILL_APP);
|
||||
if (mPluginEnabler.isEnabled(componentName) != isEnabled) {
|
||||
mPluginEnabler.setEnabled(componentName, isEnabled);
|
||||
shouldSendBroadcast = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.when;
|
||||
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.Plugin;
|
||||
import com.android.systemui.plugins.PluginEnablerImpl;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
|
||||
import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
|
||||
@@ -88,6 +89,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
|
||||
mMockManager = mock(PluginManagerImpl.class);
|
||||
when(mMockManager.getClassLoader(any(), any()))
|
||||
.thenReturn(getClass().getClassLoader());
|
||||
when(mMockManager.getPluginEnabler()).thenReturn(new PluginEnablerImpl(mMockPm));
|
||||
mMockVersionInfo = mock(VersionInfo.class);
|
||||
mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
|
||||
mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.plugins.Plugin;
|
||||
import com.android.systemui.plugins.PluginEnablerImpl;
|
||||
import com.android.systemui.plugins.PluginInitializerImpl;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
@@ -62,6 +63,7 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
private PluginInstanceManager mMockPluginInstance;
|
||||
private PluginManagerImpl mPluginManager;
|
||||
private PluginListener mMockListener;
|
||||
private PackageManager mMockPackageManager;
|
||||
|
||||
private UncaughtExceptionHandler mRealExceptionHandler;
|
||||
private UncaughtExceptionHandler mMockExceptionHandler;
|
||||
@@ -79,12 +81,18 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
|
||||
.thenReturn(mMockPluginInstance);
|
||||
|
||||
mMockPackageManager = mock(PackageManager.class);
|
||||
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
|
||||
mMockExceptionHandler, new PluginInitializerImpl() {
|
||||
@Override
|
||||
public String[] getWhitelistedPlugins(Context context) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginEnabler getPluginEnabler(Context context) {
|
||||
return new PluginEnablerImpl(mMockPackageManager);
|
||||
}
|
||||
});
|
||||
resetExceptionHandler();
|
||||
mMockListener = mock(PluginListener.class);
|
||||
@@ -182,9 +190,8 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void testDisableIntent() {
|
||||
NotificationManager nm = mock(NotificationManager.class);
|
||||
PackageManager pm = mock(PackageManager.class);
|
||||
mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, nm);
|
||||
mContext.setMockPackageManager(pm);
|
||||
mContext.setMockPackageManager(mMockPackageManager);
|
||||
|
||||
ComponentName testComponent = new ComponentName(getContext().getPackageName(),
|
||||
PluginManagerTest.class.getName());
|
||||
@@ -192,7 +199,7 @@ public class PluginManagerTest extends SysuiTestCase {
|
||||
intent.setData(Uri.parse("package://" + testComponent.flattenToString()));
|
||||
mPluginManager.onReceive(mContext, intent);
|
||||
verify(nm).cancel(eq(testComponent.getClassName()), eq(SystemMessage.NOTE_PLUGIN));
|
||||
verify(pm).setComponentEnabledSetting(eq(testComponent),
|
||||
verify(mMockPackageManager).setComponentEnabledSetting(eq(testComponent),
|
||||
eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
|
||||
eq(PackageManager.DONT_KILL_APP));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user