From 7ce6d3b50efff5a827fdbba6e7d10f3cb7fc544d Mon Sep 17 00:00:00 2001 From: M66B Date: Wed, 19 May 2021 19:43:50 +0200 Subject: [PATCH] Bugsnag 5.9.3 --- .../com/bugsnag/android/BreadcrumbInternal.kt | 2 +- .../main/java/com/bugsnag/android/Client.java | 12 ++++++---- .../com/bugsnag/android/ConnectivityCompat.kt | 22 ++++++++++++++++- .../com/bugsnag/android/ContextExtensions.kt | 24 +++++++++++++++++++ .../com/bugsnag/android/DeviceWithState.kt | 2 +- .../java/com/bugsnag/android/ErrorInternal.kt | 20 ++++++++-------- .../java/com/bugsnag/android/EventInternal.kt | 2 +- .../bugsnag/android/ForegroundDetector.java | 11 +++++++-- .../android/InternalReportDelegate.java | 9 ++++--- .../main/java/com/bugsnag/android/Notifier.kt | 2 +- .../com/bugsnag/android/ObjectJsonStreamer.kt | 2 ++ .../java/com/bugsnag/android/Session.java | 2 +- .../bugsnag/android/StrictModeHandler.java | 11 ++++----- .../java/com/bugsnag/android/ThreadState.kt | 6 ++--- .../bugsnag/android/ThrowableExtensions.kt | 19 +++++++++++++++ 15 files changed, 111 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/com/bugsnag/android/ThrowableExtensions.kt diff --git a/app/src/main/java/com/bugsnag/android/BreadcrumbInternal.kt b/app/src/main/java/com/bugsnag/android/BreadcrumbInternal.kt index 147ac324ed..0bc68fb349 100644 --- a/app/src/main/java/com/bugsnag/android/BreadcrumbInternal.kt +++ b/app/src/main/java/com/bugsnag/android/BreadcrumbInternal.kt @@ -25,7 +25,7 @@ internal class BreadcrumbInternal internal constructor( @Throws(IOException::class) override fun toStream(writer: JsonStream) { writer.beginObject() - writer.name("timestamp").value(DateUtils.toIso8601(timestamp)) + writer.name("timestamp").value(timestamp) writer.name("name").value(message) writer.name("type").value(type.toString()) writer.name("metaData") diff --git a/app/src/main/java/com/bugsnag/android/Client.java b/app/src/main/java/com/bugsnag/android/Client.java index 9723eb0f03..590bd54f3c 100644 --- a/app/src/main/java/com/bugsnag/android/Client.java +++ b/app/src/main/java/com/bugsnag/android/Client.java @@ -1,5 +1,7 @@ package com.bugsnag.android; +import static com.bugsnag.android.ContextExtensionsKt.getActivityManagerFrom; +import static com.bugsnag.android.ContextExtensionsKt.getStorageManagerFrom; import static com.bugsnag.android.ImmutableConfigKt.sanitiseConfiguration; import static com.bugsnag.android.SeverityReason.REASON_HANDLED_EXCEPTION; @@ -74,7 +76,10 @@ public class Client implements MetadataAware, CallbackAware, UserAware { private final SessionLifecycleCallback sessionLifecycleCallback; private final Connectivity connectivity; + + @Nullable private final StorageManager storageManager; + final Logger logger; final DeliveryDelegate deliveryDelegate; @@ -143,7 +148,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware { int maxBreadcrumbs = immutableConfig.getMaxBreadcrumbs(); breadcrumbState = new BreadcrumbState(maxBreadcrumbs, callbackState, logger); - storageManager = (StorageManager) appContext.getSystemService(Context.STORAGE_SERVICE); + storageManager = getStorageManagerFrom(appContext); contextState = new ContextState(); contextState.setContext(configuration.getContext()); @@ -153,8 +158,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware { sessionStore, logger, bgTaskService); metadataState = copyMetadataState(configuration); - ActivityManager am = - (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager am = getActivityManagerFrom(appContext); launchCrashTracker = new LaunchCrashTracker(immutableConfig); appDataCollector = new AppDataCollector(appContext, appContext.getPackageManager(), @@ -256,7 +260,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware { ActivityBreadcrumbCollector activityBreadcrumbCollector, SessionLifecycleCallback sessionLifecycleCallback, Connectivity connectivity, - StorageManager storageManager, + @Nullable StorageManager storageManager, Logger logger, DeliveryDelegate deliveryDelegate, LastRunInfoStore lastRunInfoStore, diff --git a/app/src/main/java/com/bugsnag/android/ConnectivityCompat.kt b/app/src/main/java/com/bugsnag/android/ConnectivityCompat.kt index bbd501d499..739a71cfb2 100644 --- a/app/src/main/java/com/bugsnag/android/ConnectivityCompat.kt +++ b/app/src/main/java/com/bugsnag/android/ConnectivityCompat.kt @@ -24,9 +24,11 @@ internal class ConnectivityCompat( callback: NetworkChangeCallback? ) : Connectivity { - private val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + private val cm = context.getConnectivityManager() + private val connectivity: Connectivity = when { + cm == null -> UnknownConnectivity Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> ConnectivityApi24(cm, callback) else -> ConnectivityLegacy(context, cm, callback) } @@ -125,3 +127,21 @@ internal class ConnectivityApi24( } } } + +/** + * Connectivity used in cases where we cannot access the system ConnectivityManager. + * We assume that there is some sort of network and do not attempt to report any network changes. + */ +internal object UnknownConnectivity : Connectivity { + override fun registerForNetworkChanges() {} + + override fun unregisterForNetworkChanges() {} + + override fun hasNetworkConnection(): Boolean { + return true + } + + override fun retrieveNetworkAccessState(): String { + return "unknown" + } +} diff --git a/app/src/main/java/com/bugsnag/android/ContextExtensions.kt b/app/src/main/java/com/bugsnag/android/ContextExtensions.kt index a1d8a52ce8..667d536e8a 100644 --- a/app/src/main/java/com/bugsnag/android/ContextExtensions.kt +++ b/app/src/main/java/com/bugsnag/android/ContextExtensions.kt @@ -1,10 +1,14 @@ package com.bugsnag.android +import android.app.ActivityManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.net.ConnectivityManager import android.os.RemoteException +import android.os.storage.StorageManager +import java.lang.RuntimeException /** * Calls [Context.registerReceiver] but swallows [SecurityException] and [RemoteException] @@ -45,3 +49,23 @@ internal fun Context.unregisterReceiverSafe( logger?.w("Failed to register receiver", exc) } } + +private inline fun Context.safeGetSystemService(name: String): T? { + return try { + getSystemService(name) as? T + } catch (exc: RuntimeException) { + null + } +} + +@JvmName("getActivityManagerFrom") +internal fun Context.getActivityManager(): ActivityManager? = + safeGetSystemService(Context.ACTIVITY_SERVICE) + +@JvmName("getConnectivityManagerFrom") +internal fun Context.getConnectivityManager(): ConnectivityManager? = + safeGetSystemService(Context.CONNECTIVITY_SERVICE) + +@JvmName("getStorageManagerFrom") +internal fun Context.getStorageManager(): StorageManager? = + safeGetSystemService(Context.STORAGE_SERVICE) diff --git a/app/src/main/java/com/bugsnag/android/DeviceWithState.kt b/app/src/main/java/com/bugsnag/android/DeviceWithState.kt index 2dc2d5ab05..eccb745a27 100644 --- a/app/src/main/java/com/bugsnag/android/DeviceWithState.kt +++ b/app/src/main/java/com/bugsnag/android/DeviceWithState.kt @@ -42,7 +42,7 @@ class DeviceWithState internal constructor( writer.name("orientation").value(orientation) if (time != null) { - writer.name("time").value(DateUtils.toIso8601(time!!)) + writer.name("time").value(time) } } } diff --git a/app/src/main/java/com/bugsnag/android/ErrorInternal.kt b/app/src/main/java/com/bugsnag/android/ErrorInternal.kt index 6fcfa81800..77a19659ef 100644 --- a/app/src/main/java/com/bugsnag/android/ErrorInternal.kt +++ b/app/src/main/java/com/bugsnag/android/ErrorInternal.kt @@ -11,17 +11,17 @@ internal class ErrorInternal @JvmOverloads internal constructor( internal companion object { fun createError(exc: Throwable, projectPackages: Collection, logger: Logger): MutableList { - val errors = mutableListOf() + return exc.safeUnrollCauses() + .mapTo(mutableListOf()) { currentEx -> + // Somehow it's possible for stackTrace to be null in rare cases + val stacktrace = currentEx.stackTrace ?: arrayOf() + val trace = + Stacktrace.stacktraceFromJavaTrace(stacktrace, projectPackages, logger) + val errorInternal = + ErrorInternal(currentEx.javaClass.name, currentEx.localizedMessage, trace) - var currentEx: Throwable? = exc - while (currentEx != null) { - // Somehow it's possible for stackTrace to be null in rare cases - val stacktrace = currentEx.stackTrace ?: arrayOf() - val trace = Stacktrace.stacktraceFromJavaTrace(stacktrace, projectPackages, logger) - errors.add(ErrorInternal(currentEx.javaClass.name, currentEx.localizedMessage, trace)) - currentEx = currentEx.cause - } - return errors.map { Error(it, logger) }.toMutableList() + return@mapTo Error(errorInternal, logger) + } } } diff --git a/app/src/main/java/com/bugsnag/android/EventInternal.kt b/app/src/main/java/com/bugsnag/android/EventInternal.kt index 06d23aa63f..f367444efd 100644 --- a/app/src/main/java/com/bugsnag/android/EventInternal.kt +++ b/app/src/main/java/com/bugsnag/android/EventInternal.kt @@ -109,7 +109,7 @@ internal class EventInternal @JvmOverloads internal constructor( val copy = Session.copySession(session) writer.name("session").beginObject() writer.name("id").value(copy.id) - writer.name("startedAt").value(DateUtils.toIso8601(copy.startedAt)) + writer.name("startedAt").value(copy.startedAt) writer.name("events").beginObject() writer.name("handled").value(copy.handledCount.toLong()) writer.name("unhandled").value(copy.unhandledCount.toLong()) diff --git a/app/src/main/java/com/bugsnag/android/ForegroundDetector.java b/app/src/main/java/com/bugsnag/android/ForegroundDetector.java index 1f534e967f..5205f7e491 100644 --- a/app/src/main/java/com/bugsnag/android/ForegroundDetector.java +++ b/app/src/main/java/com/bugsnag/android/ForegroundDetector.java @@ -1,20 +1,23 @@ package com.bugsnag.android; +import static com.bugsnag.android.ContextExtensionsKt.getActivityManagerFrom; + import android.app.ActivityManager; import android.content.Context; import android.os.Build; import android.os.Process; + import androidx.annotation.Nullable; import java.util.List; class ForegroundDetector { + @Nullable private final ActivityManager activityManager; ForegroundDetector(Context context) { - this.activityManager = - (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + this.activityManager = getActivityManagerFrom(context); } /** @@ -56,6 +59,10 @@ class ForegroundDetector { @Nullable private ActivityManager.RunningAppProcessInfo getProcessInfoPreApi16() { + if (activityManager == null) { + return null; + } + List appProcesses = activityManager.getRunningAppProcesses(); diff --git a/app/src/main/java/com/bugsnag/android/InternalReportDelegate.java b/app/src/main/java/com/bugsnag/android/InternalReportDelegate.java index b49c2a7503..53ab5a66fa 100644 --- a/app/src/main/java/com/bugsnag/android/InternalReportDelegate.java +++ b/app/src/main/java/com/bugsnag/android/InternalReportDelegate.java @@ -9,6 +9,7 @@ import android.os.Build; import android.os.storage.StorageManager; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.io.File; import java.io.IOException; @@ -22,6 +23,8 @@ class InternalReportDelegate implements EventStore.Delegate { final Logger logger; final ImmutableConfig config; + + @Nullable final StorageManager storageManager; final AppDataCollector appDataCollector; @@ -34,7 +37,7 @@ class InternalReportDelegate implements EventStore.Delegate { InternalReportDelegate(Context context, Logger logger, ImmutableConfig immutableConfig, - StorageManager storageManager, + @Nullable StorageManager storageManager, AppDataCollector appDataCollector, DeviceDataCollector deviceDataCollector, SessionTracker sessionTracker, @@ -72,7 +75,7 @@ class InternalReportDelegate implements EventStore.Delegate { } void recordStorageCacheBehavior(Event event) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (storageManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { File cacheDir = appContext.getCacheDir(); File errDir = new File(cacheDir, "bugsnag-errors"); @@ -113,7 +116,7 @@ class InternalReportDelegate implements EventStore.Delegate { // can only modify headers if DefaultDelivery is in use if (delivery instanceof DefaultDelivery) { Map headers = params.getHeaders(); - headers.put(HEADER_INTERNAL_ERROR, "true"); + headers.put(HEADER_INTERNAL_ERROR, "bugsnag-android"); headers.remove(DeliveryHeadersKt.HEADER_API_KEY); DefaultDelivery defaultDelivery = (DefaultDelivery) delivery; defaultDelivery.deliver(params.getEndpoint(), payload, headers); diff --git a/app/src/main/java/com/bugsnag/android/Notifier.kt b/app/src/main/java/com/bugsnag/android/Notifier.kt index b56bea150f..339a01786b 100644 --- a/app/src/main/java/com/bugsnag/android/Notifier.kt +++ b/app/src/main/java/com/bugsnag/android/Notifier.kt @@ -7,7 +7,7 @@ import java.io.IOException */ class Notifier @JvmOverloads constructor( var name: String = "Android Bugsnag Notifier", - var version: String = "5.9.2", + var version: String = "5.9.3", var url: String = "https://bugsnag.com" ) : JsonStream.Streamable { diff --git a/app/src/main/java/com/bugsnag/android/ObjectJsonStreamer.kt b/app/src/main/java/com/bugsnag/android/ObjectJsonStreamer.kt index 36933818b8..146aba9b65 100644 --- a/app/src/main/java/com/bugsnag/android/ObjectJsonStreamer.kt +++ b/app/src/main/java/com/bugsnag/android/ObjectJsonStreamer.kt @@ -2,6 +2,7 @@ package com.bugsnag.android import java.io.IOException import java.lang.reflect.Array +import java.util.Date internal class ObjectJsonStreamer { @@ -21,6 +22,7 @@ internal class ObjectJsonStreamer { obj is Number -> writer.value(obj) obj is Boolean -> writer.value(obj) obj is JsonStream.Streamable -> obj.toStream(writer) + obj is Date -> writer.value(DateUtils.toIso8601(obj)) obj is Map<*, *> -> mapToStream(writer, obj, shouldRedactKeys) obj is Collection<*> -> collectionToStream(writer, obj) obj.javaClass.isArray -> arrayToStream(writer, obj) diff --git a/app/src/main/java/com/bugsnag/android/Session.java b/app/src/main/java/com/bugsnag/android/Session.java index 2211b4636d..fd3515680b 100644 --- a/app/src/main/java/com/bugsnag/android/Session.java +++ b/app/src/main/java/com/bugsnag/android/Session.java @@ -232,7 +232,7 @@ public final class Session implements JsonStream.Streamable, UserAware { void serializeSessionInfo(@NonNull JsonStream writer) throws IOException { writer.beginObject(); writer.name("id").value(id); - writer.name("startedAt").value(DateUtils.toIso8601(startedAt)); + writer.name("startedAt").value(startedAt); writer.name("user").value(user); writer.endObject(); } diff --git a/app/src/main/java/com/bugsnag/android/StrictModeHandler.java b/app/src/main/java/com/bugsnag/android/StrictModeHandler.java index cc27508371..002466b85b 100644 --- a/app/src/main/java/com/bugsnag/android/StrictModeHandler.java +++ b/app/src/main/java/com/bugsnag/android/StrictModeHandler.java @@ -2,9 +2,11 @@ package com.bugsnag.android; import android.annotation.SuppressLint; import android.text.TextUtils; + import androidx.annotation.Nullable; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; @@ -87,12 +89,7 @@ class StrictModeHandler { * @return the root cause of the throwable */ private Throwable getRootCause(Throwable throwable) { - Throwable cause = throwable.getCause(); - - if (cause == null) { - return throwable; - } else { - return getRootCause(cause); - } + List causes = ThrowableUtils.safeUnrollCauses(throwable); + return causes.get(causes.size() - 1); } } diff --git a/app/src/main/java/com/bugsnag/android/ThreadState.kt b/app/src/main/java/com/bugsnag/android/ThreadState.kt index 739a6b7a2f..8839badaec 100644 --- a/app/src/main/java/com/bugsnag/android/ThreadState.kt +++ b/app/src/main/java/com/bugsnag/android/ThreadState.kt @@ -5,14 +5,14 @@ import java.io.IOException /** * Capture and serialize the state of all threads at the time of an exception. */ -internal class ThreadState @JvmOverloads constructor( +internal class ThreadState @Suppress("LongParameterList") @JvmOverloads constructor( exc: Throwable?, isUnhandled: Boolean, sendThreads: ThreadSendPolicy, projectPackages: Collection, logger: Logger, currentThread: java.lang.Thread = java.lang.Thread.currentThread(), - stackTraces: MutableMap> = java.lang.Thread.getAllStackTraces() + stackTraces: MutableMap>? = null ) : JsonStream.Streamable { internal constructor( @@ -29,7 +29,7 @@ internal class ThreadState @JvmOverloads constructor( threads = when { recordThreads -> captureThreadTrace( - stackTraces, + stackTraces ?: java.lang.Thread.getAllStackTraces(), currentThread, exc, isUnhandled, diff --git a/app/src/main/java/com/bugsnag/android/ThrowableExtensions.kt b/app/src/main/java/com/bugsnag/android/ThrowableExtensions.kt new file mode 100644 index 0000000000..cf56fd51c7 --- /dev/null +++ b/app/src/main/java/com/bugsnag/android/ThrowableExtensions.kt @@ -0,0 +1,19 @@ +@file:JvmName("ThrowableUtils") +package com.bugsnag.android + +/** + * Unroll the list of causes for this Throwable, handling any recursion that may appear within + * the chain. The first element returned will be this Throwable, and the last will be the root + * cause or last non-recursive Throwable. + */ +internal fun Throwable.safeUnrollCauses(): List { + val causes = LinkedHashSet() + var currentEx: Throwable? = this + + // Set.add will return false if we have already "seen" currentEx + while (currentEx != null && causes.add(currentEx)) { + currentEx = currentEx.cause + } + + return causes.toList() +}