Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unity: add methods for setting autoNotify and autoDetectAnrs #1233

Merged
merged 4 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## TBD

### Enhancements

* Unity: add methods for setting autoNotify and autoDetectAnrs
[#1233](https://github.com/bugsnag/bugsnag-android/pull/1233)

## 5.9.1 (2021-04-22)

### Bug fixes
Expand Down
46 changes: 36 additions & 10 deletions bugsnag-android-core/src/main/java/com/bugsnag/android/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
final Logger logger;
final DeliveryDelegate deliveryDelegate;

final ClientObservable clientObservable = new ClientObservable();
final ClientObservable clientObservable;
private PluginClient pluginClient;

final Notifier notifier = new Notifier();
Expand All @@ -88,6 +88,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
final LastRunInfoStore lastRunInfoStore;
final LaunchCrashTracker launchCrashTracker;
final BackgroundTaskService bgTaskService = new BackgroundTaskService();
private final ExceptionHandler exceptionHandler;

/**
* Initialize a Bugsnag client
Expand Down Expand Up @@ -137,6 +138,7 @@ public Unit invoke(Boolean hasConnection, String networkState) {
immutableConfig = sanitiseConfiguration(appContext, configuration, connectivity);
logger = immutableConfig.getLogger();
warnIfNotAppContext(androidContext);
clientObservable = new ClientObservable();

// Set up breadcrumbs
callbackState = configuration.impl.callbackState.copy();
Expand Down Expand Up @@ -209,8 +211,9 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
immutableConfig, breadcrumbState, notifier, bgTaskService);

// Install a default exception handler with this client
exceptionHandler = new ExceptionHandler(this, logger);
if (immutableConfig.getEnabledErrorTypes().getUnhandledExceptions()) {
new ExceptionHandler(this, logger);
exceptionHandler.install();
}

// register a receiver for automatic breadcrumbs
Expand Down Expand Up @@ -245,6 +248,7 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
ContextState contextState,
CallbackState callbackState,
UserState userState,
ClientObservable clientObservable,
Context appContext,
@NonNull DeviceDataCollector deviceDataCollector,
@NonNull AppDataCollector appDataCollector,
Expand All @@ -260,13 +264,15 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
Logger logger,
DeliveryDelegate deliveryDelegate,
LastRunInfoStore lastRunInfoStore,
LaunchCrashTracker launchCrashTracker
LaunchCrashTracker launchCrashTracker,
ExceptionHandler exceptionHandler
) {
this.immutableConfig = immutableConfig;
this.metadataState = metadataState;
this.contextState = contextState;
this.callbackState = callbackState;
this.userState = userState;
this.clientObservable = clientObservable;
this.appContext = appContext;
this.deviceDataCollector = deviceDataCollector;
this.appDataCollector = appDataCollector;
Expand All @@ -284,6 +290,7 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
this.lastRunInfoStore = lastRunInfoStore;
this.launchCrashTracker = launchCrashTracker;
this.lastRunInfo = null;
this.exceptionHandler = exceptionHandler;
}

private LastRunInfo loadLastRunInfo() {
Expand Down Expand Up @@ -365,6 +372,17 @@ void registerObserver(Observer observer) {
launchCrashTracker.addObserver(observer);
}

void unregisterObserver(Observer observer) {
metadataState.deleteObserver(observer);
breadcrumbState.deleteObserver(observer);
sessionTracker.deleteObserver(observer);
clientObservable.deleteObserver(observer);
userState.deleteObserver(observer);
contextState.deleteObserver(observer);
deliveryDelegate.deleteObserver(observer);
launchCrashTracker.deleteObserver(observer);
}

/**
* Sends initial state values for Metadata/User/Context to any registered observers.
*/
Expand Down Expand Up @@ -985,13 +1003,7 @@ Logger getLogger() {
@SuppressWarnings("rawtypes")
@Nullable
Plugin getPlugin(@NonNull Class clz) {
Set<Plugin> plugins = pluginClient.getPlugins();
for (Plugin plugin : plugins) {
if (plugin.getClass().equals(clz)) {
return plugin;
}
}
return null;
return pluginClient.findPlugin(clz);
}

Notifier getNotifier() {
Expand All @@ -1001,4 +1013,18 @@ Notifier getNotifier() {
MetadataState getMetadataState() {
return metadataState;
}

void setAutoNotify(boolean autoNotify) {
pluginClient.setAutoNotify(this, autoNotify);

if (autoNotify) {
exceptionHandler.install();
} else {
exceptionHandler.uninstall();
}
}

void setAutoDetectAnrs(boolean autoDetectAnrs) {
pluginClient.setAutoDetectAnrs(this, autoDetectAnrs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,16 @@ class ExceptionHandler implements UncaughtExceptionHandler {
this.client = client;
this.logger = logger;
this.originalHandler = Thread.getDefaultUncaughtExceptionHandler();
}

void install() {
Thread.setDefaultUncaughtExceptionHandler(this);
}

void uninstall() {
Thread.setDefaultUncaughtExceptionHandler(originalHandler);
}

@Override
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
boolean strictModeThrowable = strictModeHandler.isStrictModeThrowable(throwable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

class LibraryLoader {

private AtomicBoolean attemptedLoad = new AtomicBoolean();
private final AtomicBoolean attemptedLoad = new AtomicBoolean();
private boolean loaded = false;

/**
* Attempts to load a native library, returning false if the load was unsuccessful.
Expand All @@ -21,11 +22,16 @@ boolean loadLibrary(String name, Client client, OnErrorCallback callback) {
if (!attemptedLoad.getAndSet(true)) {
try {
System.loadLibrary(name);
loaded = true;
return true;
} catch (UnsatisfiedLinkError error) {
client.notify(error, callback);
}
}
return false;
}

boolean isLoaded() {
return loaded;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,23 @@ public static Logger getLogger() {
return getClient().getConfig().getLogger();
}

/**
* Switches automatic error detection on/off after Bugsnag has initialized.
* This is required to support legacy functionality in Unity.
*
* @param autoNotify whether errors should be automatically detected.
*/
public static void setAutoNotify(boolean autoNotify) {
// TODO implement me
getClient().setAutoNotify(autoNotify);
}

/**
* Switches automatic ANR detection on/off after Bugsnag has initialized.
* This is required to support legacy functionality in Unity.
*
* @param autoDetectAnrs whether ANRs should be automatically detected.
*/
public static void setAutoDetectAnrs(boolean autoDetectAnrs) {
// TODO implement me
getClient().setAutoDetectAnrs(autoDetectAnrs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,31 @@ package com.bugsnag.android

internal class PluginClient(
userPlugins: Set<Plugin>,
immutableConfig: ImmutableConfig,
private val immutableConfig: ImmutableConfig,
private val logger: Logger
) {

protected val plugins: Set<Plugin>
companion object {
private const val NDK_PLUGIN = "com.bugsnag.android.NdkPlugin"
private const val ANR_PLUGIN = "com.bugsnag.android.AnrPlugin"
private const val RN_PLUGIN = "com.bugsnag.android.BugsnagReactNativePlugin"
}

private val plugins: Set<Plugin>

private val ndkPlugin = instantiatePlugin(NDK_PLUGIN)
private val anrPlugin = instantiatePlugin(ANR_PLUGIN)
private val rnPlugin = instantiatePlugin(RN_PLUGIN)

init {
val set = mutableSetOf<Plugin>()
set.addAll(userPlugins)

// instantiate ANR + NDK plugins by reflection as bugsnag-android-core has no
// direct dependency on the artefacts
if (immutableConfig.enabledErrorTypes.ndkCrashes) {
instantiatePlugin("com.bugsnag.android.NdkPlugin")?.let { set.add(it) }
}
if (immutableConfig.enabledErrorTypes.anrs) {
instantiatePlugin("com.bugsnag.android.AnrPlugin")?.let { set.add(it) }
}
instantiatePlugin("com.bugsnag.android.BugsnagReactNativePlugin")?.let { set.add(it) }
ndkPlugin?.let(set::add)
anrPlugin?.let(set::add)
rnPlugin?.let(set::add)
plugins = set.toSet()
}

Expand All @@ -37,11 +43,51 @@ internal class PluginClient(
}
}

fun loadPlugins(client: Client) = plugins.forEach {
try {
it.load(client)
} catch (exc: Throwable) {
logger.e("Failed to load plugin $it, continuing with initialisation.", exc)
fun loadPlugins(client: Client) {
plugins.forEach { plugin ->
try {
loadPluginInternal(plugin, client)
} catch (exc: Throwable) {
logger.e("Failed to load plugin $plugin, continuing with initialisation.", exc)
}
}
}

fun setAutoNotify(client: Client, autoNotify: Boolean) {
setAutoDetectAnrs(client, autoNotify)

if (autoNotify) {
ndkPlugin?.load(client)
} else {
ndkPlugin?.unload()
}
}

fun setAutoDetectAnrs(client: Client, autoDetectAnrs: Boolean) {
if (autoDetectAnrs) {
anrPlugin?.load(client)
} else {
anrPlugin?.unload()
}
}

fun findPlugin(clz: Class<*>): Plugin? = plugins.find { it.javaClass == clz }

private fun loadPluginInternal(plugin: Plugin, client: Client) {
val name = plugin.javaClass.name
val errorTypes = immutableConfig.enabledErrorTypes

// only initialize NDK/ANR plugins if automatic detection enabled
if (name == NDK_PLUGIN) {
if (errorTypes.ndkCrashes) {
plugin.load(client)
}
} else if (name == ANR_PLUGIN) {
if (errorTypes.anrs) {
plugin.load(client)
}
} else {
plugin.load(client)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,9 @@ public void metadataValid() {
public void dateValid() {
assertEquals(new Date(0).getTime(), crumb.getTimestamp().getTime());
}

@Test
public void stringDateValid() {
assertEquals("1970-01-01T00:00:00.000Z", crumb.getStringTimestamp());
}
}
Loading