diff options
| author | 2018-12-12 17:11:30 -0500 | |
|---|---|---|
| committer | 2018-12-14 13:33:03 -0500 | |
| commit | fd2c695e0502286d20574b6fb0fb2b82ff7ec78c (patch) | |
| tree | 092a4cd76e3abee0148d8ca67a56a53b1d49fdc2 | |
| parent | b3c1557892cc65c31a71eeb42f717dd2cd72b0f0 (diff) | |
Track when plugins are disabled due to crashes.
Bug: 120901833
Change-Id: I41e5c54d059b631befac235b1d3929200764860c
Test: atest SystemUITests
7 files changed, 124 insertions, 57 deletions
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java index 74fd13f9564e..01b012d1fc84 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginEnabler.java @@ -14,12 +14,40 @@ package com.android.systemui.shared.plugins; +import android.annotation.IntDef; import android.content.ComponentName; /** * Enables and disables plugins. */ public interface PluginEnabler { - void setEnabled(ComponentName component, boolean enabled); + + int ENABLED = 0; + int DISABLED_MANUALLY = 1; + int DISABLED_INVALID_VERSION = 1; + int DISABLED_FROM_EXPLICIT_CRASH = 2; + int DISABLED_FROM_SYSTEM_CRASH = 3; + + @IntDef({ENABLED, DISABLED_MANUALLY, DISABLED_INVALID_VERSION, DISABLED_FROM_EXPLICIT_CRASH, + DISABLED_FROM_SYSTEM_CRASH}) + @interface DisableReason { + } + + /** Enables plugin via the PackageManager. */ + void setEnabled(ComponentName component); + + /** Disables a plugin via the PackageManager and records the reason for disabling. */ + void setDisabled(ComponentName component, @DisableReason int reason); + + /** Returns true if the plugin is enabled in the PackageManager. */ boolean isEnabled(ComponentName component); + + /** + * Returns the reason that a plugin is disabled, (if it is). + * + * It should return {@link #ENABLED} if the plugin is turned on. + * It should return {@link #DISABLED_MANUALLY} if the plugin is off but the reason is unknown. + */ + @DisableReason + int getDisableReason(ComponentName componentName); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 8e7fadb5c7cb..523720d54eec 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -136,7 +136,7 @@ public class PluginInstanceManager<T extends Plugin> { ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); for (PluginInfo info : plugins) { if (className.startsWith(info.mPackage)) { - disable(info); + disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH); disableAny = true; } } @@ -146,12 +146,13 @@ public class PluginInstanceManager<T extends Plugin> { public boolean disableAll() { ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); for (int i = 0; i < plugins.size(); i++) { - disable(plugins.get(i)); + disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH); } return plugins.size() != 0; } - private void disable(PluginInfo info) { + private void disable(PluginInfo info, + @PluginEnabler.DisableReason int reason) { // Live by the sword, die by the sword. // Misbehaving plugins get disabled and won't come back until uninstall/reinstall. @@ -162,9 +163,9 @@ public class PluginInstanceManager<T extends Plugin> { // Don't disable whitelisted plugins as they are a part of the OS. return; } - Log.w(TAG, "Disabling plugin " + info.mPackage + "/" + info.mClass); - mManager.getPluginEnabler().setEnabled(new ComponentName(info.mPackage, info.mClass), - false); + ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass); + Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString()); + mManager.getPluginEnabler().setDisabled(pluginComponent, reason); } public <T> boolean dependsOn(Plugin p, Class<T> cls) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index dc2a9bd5105b..10b5f1c64d85 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -184,6 +184,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage mListening = true; IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addAction(Intent.ACTION_PACKAGE_REPLACED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(PLUGIN_CHANGED); filter.addAction(DISABLE_PLUGIN); @@ -214,12 +215,13 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage // Don't disable whitelisted plugins as they are a part of the OS. return; } - getPluginEnabler().setEnabled(component, false); + getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION); mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(), SystemMessage.NOTE_PLUGIN); } else { Uri data = intent.getData(); String pkg = data.getEncodedSchemeSpecificPart(); + ComponentName componentName = ComponentName.unflattenFromString(pkg); if (mOneShotPackages.contains(pkg)) { int icon = mContext.getResources().getIdentifier("tuner", "drawable", mContext.getPackageName()); @@ -256,6 +258,17 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage Log.v(TAG, "Reloading " + pkg); } } + if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) { + @PluginEnabler.DisableReason int disableReason = + getPluginEnabler().getDisableReason(componentName); + if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH + || disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH + || disableReason == PluginEnabler.DISABLED_INVALID_VERSION) { + Log.i(TAG, "Re-enabling previously disabled plugin that has been " + + "updated: " + componentName.flattenToShortString()); + getPluginEnabler().setEnabled(componentName); + } + } if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { for (PluginInstanceManager manager : mPluginMap.values()) { manager.onPackageChange(pkg); diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java index e2417f7b6397..63374150adaa 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java @@ -16,28 +16,44 @@ package com.android.systemui.plugins; import android.content.ComponentName; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.shared.plugins.PluginEnabler; public class PluginEnablerImpl implements PluginEnabler { + private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs"; - final private PackageManager mPm; + private PackageManager mPm; + private final SharedPreferences mAutoDisabledPrefs; public PluginEnablerImpl(Context context) { - this(context.getPackageManager()); + this(context, context.getPackageManager()); } - @VisibleForTesting public PluginEnablerImpl(PackageManager pm) { + @VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) { + mAutoDisabledPrefs = context.getSharedPreferences( + CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE); mPm = pm; } @Override - public void setEnabled(ComponentName component, boolean enabled) { + public void setEnabled(ComponentName component) { + setDisabled(component, ENABLED); + } + + @Override + public void setDisabled(ComponentName component, @DisableReason int reason) { + boolean enabled = reason == ENABLED; final int desiredState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; mPm.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP); + if (enabled) { + mAutoDisabledPrefs.edit().remove(component.flattenToString()).apply(); + } else { + mAutoDisabledPrefs.edit().putInt(component.flattenToString(), reason).apply(); + } } @Override @@ -45,4 +61,12 @@ public class PluginEnablerImpl implements PluginEnabler { return mPm.getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED; } + + @Override + public @DisableReason int getDisableReason(ComponentName componentName) { + if (isEnabled(componentName)) { + return ENABLED; + } + return mAutoDisabledPrefs.getInt(componentName.flattenToString(), DISABLED_MANUALLY); + } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java index dae1472c42af..0a29e04ce20f 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java @@ -184,7 +184,11 @@ public class PluginFragment extends PreferenceFragment { mInfo.services[i].name); if (mPluginEnabler.isEnabled(componentName) != isEnabled) { - mPluginEnabler.setEnabled(componentName, isEnabled); + if (isEnabled) { + mPluginEnabler.setEnabled(componentName); + } else { + mPluginEnabler.setDisabled(componentName, PluginEnabler.DISABLED_MANUALLY); + } shouldSendBroadcast = true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java index 5cc3b3c7823b..458377017fb9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.shared.plugins; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; @@ -26,22 +27,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; 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; -import com.android.systemui.plugins.annotations.Requires; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; - import android.app.Activity; import android.app.NotificationManager; import android.content.BroadcastReceiver; @@ -60,6 +45,21 @@ import android.support.test.annotation.UiThreadTest; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.annotations.Requires; +import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; +import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -79,6 +79,9 @@ public class PluginInstanceManagerTest extends SysuiTestCase { private PluginInstanceManager mPluginInstanceManager; private PluginManagerImpl mMockManager; private VersionInfo mMockVersionInfo; + private PluginEnabler mMockEnabler; + ComponentName mTestPluginComponentName = + new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName()); @Before public void setup() throws Exception { @@ -88,9 +91,9 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mMockPm = mock(PackageManager.class); mMockListener = mock(PluginListener.class); mMockManager = mock(PluginManagerImpl.class); - when(mMockManager.getClassLoader(any(), any())) - .thenReturn(getClass().getClassLoader()); - when(mMockManager.getPluginEnabler()).thenReturn(new PluginEnablerImpl(mMockPm)); + when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader()); + mMockEnabler = mock(PluginEnabler.class); + when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler); mMockVersionInfo = mock(VersionInfo.class); mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction", mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo, @@ -230,18 +233,13 @@ public class PluginInstanceManagerTest extends SysuiTestCase { // Start with an unrelated class. boolean result = mPluginInstanceManager.checkAndDisable(Activity.class.getName()); assertFalse(result); - verify(mMockPm, never()).setComponentEnabledSetting( - ArgumentCaptor.forClass(ComponentName.class).capture(), - ArgumentCaptor.forClass(int.class).capture(), - ArgumentCaptor.forClass(int.class).capture()); + verify(mMockEnabler, never()).setDisabled(any(ComponentName.class), anyInt()); // Now hand it a real class and make sure it disables the plugin. result = mPluginInstanceManager.checkAndDisable(TestPlugin.class.getName()); assertTrue(result); - verify(mMockPm).setComponentEnabledSetting( - ArgumentCaptor.forClass(ComponentName.class).capture(), - ArgumentCaptor.forClass(int.class).capture(), - ArgumentCaptor.forClass(int.class).capture()); + verify(mMockEnabler).setDisabled( + mTestPluginComponentName, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH); } @Test @@ -250,10 +248,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mPluginInstanceManager.disableAll(); - verify(mMockPm).setComponentEnabledSetting( - ArgumentCaptor.forClass(ComponentName.class).capture(), - ArgumentCaptor.forClass(int.class).capture(), - ArgumentCaptor.forClass(int.class).capture()); + verify(mMockEnabler).setDisabled( + mTestPluginComponentName, PluginEnabler.DISABLED_FROM_SYSTEM_CRASH); } @Test @@ -275,8 +271,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase { List<ResolveInfo> list = new ArrayList<>(); ResolveInfo info = new ResolveInfo(); info.serviceInfo = mock(ServiceInfo.class); - info.serviceInfo.packageName = "com.android.systemui"; - info.serviceInfo.name = TestPlugin.class.getName(); + info.serviceInfo.packageName = mTestPluginComponentName.getPackageName(); + info.serviceInfo.name = mTestPluginComponentName.getClassName(); when(info.serviceInfo.loadLabel(any())).thenReturn("Test Plugin"); list.add(info); when(mMockPm.queryIntentServices(any(), Mockito.anyInt())).thenReturn(list); @@ -288,6 +284,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { ApplicationInfo appInfo = getContext().getApplicationInfo(); when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn( appInfo); + when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true); } private void createPlugin() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java index ff1bc8abf5d1..76e68f1df724 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java @@ -27,7 +27,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; -import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -82,17 +81,18 @@ public class PluginManagerTest extends SysuiTestCase { .thenReturn(mMockPluginInstance); mMockPackageManager = mock(PackageManager.class); - mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true, + 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); - } + @Override + public String[] getWhitelistedPlugins(Context context) { + return new String[0]; + } + + @Override + public PluginEnabler getPluginEnabler(Context context) { + return new PluginEnablerImpl(context, mMockPackageManager); + } }); resetExceptionHandler(); mMockListener = mock(PluginListener.class); |