Merge "Wrap all exceptions/crashes while plugins are active" into oc-mr1-dev
This commit is contained in:
@@ -136,11 +136,12 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
return disableAny;
|
return disableAny;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableAll() {
|
public boolean disableAll() {
|
||||||
ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
|
ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
|
||||||
for (int i = 0; i < plugins.size(); i++) {
|
for (int i = 0; i < plugins.size(); i++) {
|
||||||
disable(plugins.get(i));
|
disable(plugins.get(i));
|
||||||
}
|
}
|
||||||
|
return plugins.size() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disable(PluginInfo info) {
|
private void disable(PluginInfo info) {
|
||||||
@@ -182,6 +183,7 @@ public class PluginInstanceManager<T extends Plugin> {
|
|||||||
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;
|
PluginInfo<T> info = (PluginInfo<T>) msg.obj;
|
||||||
|
mManager.handleWtfs();
|
||||||
if (!(msg.obj instanceof PluginFragment)) {
|
if (!(msg.obj 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 onCreate as part of the fragment lifecycle.
|
// will get the onCreate as part of the fragment lifecycle.
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ import android.os.UserHandle;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Log.TerribleFailure;
|
||||||
|
import android.util.Log.TerribleFailureHandler;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
@@ -71,10 +74,11 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
|||||||
private boolean mListening;
|
private boolean mListening;
|
||||||
private boolean mHasOneShot;
|
private boolean mHasOneShot;
|
||||||
private Looper mLooper;
|
private Looper mLooper;
|
||||||
|
private boolean mWtfsSet;
|
||||||
|
|
||||||
public PluginManagerImpl(Context context) {
|
public PluginManagerImpl(Context context) {
|
||||||
this(context, new PluginInstanceManagerFactory(),
|
this(context, new PluginInstanceManagerFactory(),
|
||||||
Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
|
Build.IS_DEBUGGABLE, Thread.getUncaughtExceptionPreHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -88,7 +92,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
|||||||
|
|
||||||
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
|
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
|
||||||
defaultHandler);
|
defaultHandler);
|
||||||
Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
|
Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
|
||||||
if (isDebuggable) {
|
if (isDebuggable) {
|
||||||
new Handler(mLooper).post(() -> {
|
new Handler(mLooper).post(() -> {
|
||||||
// Plugin dependencies that don't have another good home can go here, but
|
// Plugin dependencies that don't have another good home can go here, but
|
||||||
@@ -290,6 +294,15 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handleWtfs() {
|
||||||
|
if (!mWtfsSet) {
|
||||||
|
mWtfsSet = true;
|
||||||
|
Log.setWtfHandler((tag, what, system) -> {
|
||||||
|
throw new CrashWhilePluginActiveException(what);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static class PluginInstanceManagerFactory {
|
public static class PluginInstanceManagerFactory {
|
||||||
public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
|
public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
|
||||||
@@ -339,9 +352,12 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
|||||||
// disable all the plugins, so we can be sure that SysUI is running as
|
// disable all the plugins, so we can be sure that SysUI is running as
|
||||||
// best as possible.
|
// best as possible.
|
||||||
for (PluginInstanceManager manager : mPluginMap.values()) {
|
for (PluginInstanceManager manager : mPluginMap.values()) {
|
||||||
manager.disableAll();
|
disabledAny |= manager.disableAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (disabledAny) {
|
||||||
|
throwable = new CrashWhilePluginActiveException(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
// Run the normal exception handler so we can crash and cleanup our state.
|
// Run the normal exception handler so we can crash and cleanup our state.
|
||||||
mHandler.uncaughtException(thread, throwable);
|
mHandler.uncaughtException(thread, throwable);
|
||||||
@@ -358,4 +374,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
|
|||||||
return disabledAny | checkStack(throwable.getCause());
|
return disabledAny | checkStack(throwable.getCause());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class CrashWhilePluginActiveException extends RuntimeException {
|
||||||
|
public CrashWhilePluginActiveException(Throwable throwable) {
|
||||||
|
super(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class PluginManagerTest extends SysuiTestCase {
|
|||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
mDependency.injectTestDependency(Dependency.BG_LOOPER,
|
mDependency.injectTestDependency(Dependency.BG_LOOPER,
|
||||||
TestableLooper.get(this).getLooper());
|
TestableLooper.get(this).getLooper());
|
||||||
mRealExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
mRealExceptionHandler = Thread.getUncaughtExceptionPreHandler();
|
||||||
mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
|
mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
|
||||||
mMockFactory = mock(PluginInstanceManagerFactory.class);
|
mMockFactory = mock(PluginInstanceManagerFactory.class);
|
||||||
mMockPluginInstance = mock(PluginInstanceManager.class);
|
mMockPluginInstance = mock(PluginInstanceManager.class);
|
||||||
@@ -167,9 +167,9 @@ public class PluginManagerTest extends SysuiTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resetExceptionHandler() {
|
private void resetExceptionHandler() {
|
||||||
mPluginExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
mPluginExceptionHandler = Thread.getUncaughtExceptionPreHandler();
|
||||||
// Set back the real exception handler so the test can crash if it wants to.
|
// Set back the real exception handler so the test can crash if it wants to.
|
||||||
Thread.setDefaultUncaughtExceptionHandler(mRealExceptionHandler);
|
Thread.setUncaughtExceptionPreHandler(mRealExceptionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
|
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
|
||||||
|
|||||||
Reference in New Issue
Block a user