diff --git a/CHANGELOG.md b/CHANGELOG.md index 42901e967d..83e0cf2fd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 6.6.1 (2024-07-03) + +### Bug fixes + +* Allow ReactNative applications to set the `correlation` property on Events + [#2050](https://github.com/bugsnag/bugsnag-android/pull/2050) +* Avoid possible crashes in apps the use custom proguard rules that don't include protection for `Enum.values()` + [#2049](https://github.com/bugsnag/bugsnag-android/pull/2049) + ## 6.6.0 (2024-06-19) ### Enhancements diff --git a/bugsnag-android-core/proguard-rules.pro b/bugsnag-android-core/proguard-rules.pro index 2f381c1cdc..a0cedc6cbb 100644 --- a/bugsnag-android-core/proguard-rules.pro +++ b/bugsnag-android-core/proguard-rules.pro @@ -5,3 +5,9 @@ -keep class com.bugsnag.android.BreadcrumbState { *; } -keep class com.bugsnag.android.BreadcrumbType { *; } -keep class com.bugsnag.android.Severity { *; } +-keepclassmembers enum com.bugsnag.android.Telemetry { + public static com.bugsnag.android.Telemetry[] values(); + } +-keepclassmembers enum com.bugsnag.android.ErrorType { + public static com.bugsnag.android.Telemetry[] values(); + } \ No newline at end of file diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt index f89f73b880..d2da04b9ed 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt +++ b/bugsnag-android-core/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 = "6.6.0", + var version: String = "6.6.1", var url: String = "https://bugsnag.com" ) : JsonStream.Streamable { diff --git a/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/EventDeserializer.kt b/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/EventDeserializer.kt index a8ce243e02..c44f738fd0 100644 --- a/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/EventDeserializer.kt +++ b/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/EventDeserializer.kt @@ -1,5 +1,7 @@ package com.bugsnag.android +import java.util.UUID + internal class EventDeserializer( private val client: Client, private val projectPackages: Collection @@ -86,17 +88,61 @@ internal class EventDeserializer( metadata.forEach { event.addMetadata(it.key, it.value as Map) } + + val correlation = map["correlation"] as? Map + correlation?.let { + deserializeCorrelation(it, event) + } + return event } + private fun deserializeCorrelation( + correlation: Map, + event: Event + ) { + val traceId = (correlation["traceId"] as? String) + ?.takeIf { it.length == TRACE_ID_LENGTH } + ?.let { + val mostSigBits = it.substring(0, HEX_LONG_LENGTH).hexToLong() + val leastSigBits = it.substring(HEX_LONG_LENGTH).hexToLong() + + if (mostSigBits != null && leastSigBits != null) { + UUID(mostSigBits, leastSigBits) + } else { + null + } + } + val spanId = (correlation["spanId"] as? String) + ?.takeIf { it.length == HEX_LONG_LENGTH } + ?.hexToLong() + + if (traceId != null && spanId != null) { + event.setTraceCorrelation(traceId, spanId) + } + } + private fun getOriginalUnhandled( map: Map, unhandled: Boolean ): Boolean { - val unhandledOverridden = map.getOrElse("unhandledOverridden", { false }) as Boolean + val unhandledOverridden = (map.getOrElse("unhandledOverridden") { false }) as Boolean return when { unhandledOverridden -> !unhandled else -> unhandled } } + + @Suppress("MagicNumber") + private fun String.hexToLong(): Long? { + if (length != HEX_LONG_LENGTH || this[0] == '-' || this[3] == '-') return null + val firstByte = this.substring(0, 2).toLongOrNull(HEX_LONG_LENGTH) ?: return null + val remaining = this.substring(2).toLongOrNull(HEX_LONG_LENGTH) ?: return null + return (firstByte shl 56) or remaining + } + + companion object { + private const val TRACE_ID_LENGTH = 32 + private const val HEX_LONG_LENGTH = 16 + } } diff --git a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/EventDeserializerTest.kt b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/EventDeserializerTest.kt index ee5d83e999..57635cc80a 100644 --- a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/EventDeserializerTest.kt +++ b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/EventDeserializerTest.kt @@ -12,6 +12,7 @@ import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner import java.util.Date +import java.util.UUID @RunWith(MockitoJUnitRunner::class) class EventDeserializerTest { @@ -39,6 +40,10 @@ class EventDeserializerTest { map["app"] = mapOf(Pair("id", "app-id")) map["device"] = mapOf(Pair("id", "device-id"), Pair("runtimeVersions", mutableMapOf())) + map["correlation"] = mapOf( + "traceId" to "b39e53513eec3c68b5e5c34dc43611e0", + "spanId" to "51d886b3a693a406" + ) `when`(client.config).thenReturn(TestData.generateConfig()) `when`(client.getLogger()).thenReturn(object : Logger {}) @@ -91,6 +96,13 @@ class EventDeserializerTest { assertEquals("device-id", event.device.id) assertEquals("123", event.getMetadata("custom", "id")) assertEquals(TestData.generateConfig().apiKey, event.apiKey) + + assertEquals( + UUID(-5503870086187041688L, -5339647044406079008L), + TestHooks.getCorrelatedTraceId(event) + ) + + assertEquals(5897611818193626118L, TestHooks.getCorrelatedSpanId(event)) } @Test diff --git a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/TestHooks.java b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/TestHooks.java index e9b628b612..344d1a9703 100644 --- a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/TestHooks.java +++ b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/TestHooks.java @@ -1,10 +1,26 @@ package com.bugsnag.android; +import androidx.annotation.Nullable; + +import java.util.UUID; + class TestHooks { static boolean getUnhandledOverridden(Event event) { return event.getImpl().getUnhandledOverridden(); } + @Nullable + static UUID getCorrelatedTraceId(Event event) { + TraceCorrelation traceCorrelation = event.getImpl().getTraceCorrelation(); + return traceCorrelation != null ? traceCorrelation.getTraceId() : null; + } + + @Nullable + static Long getCorrelatedSpanId(Event event) { + TraceCorrelation traceCorrelation = event.getImpl().getTraceCorrelation(); + return traceCorrelation != null ? traceCorrelation.getSpanId() : null; + } + static MetadataState generateMetadataState() { return new MetadataState(); } diff --git a/dockerfiles/Dockerfile.android-publisher b/dockerfiles/Dockerfile.android-publisher index edb4297bf0..f3bc0fabff 100644 --- a/dockerfiles/Dockerfile.android-publisher +++ b/dockerfiles/Dockerfile.android-publisher @@ -5,7 +5,7 @@ WORKDIR /app # Copy gradle files COPY gradlew gradle.properties /app/ COPY gradle/ /app/gradle/ -COPY build.gradle settings.gradle /app/ +COPY build.gradle settings.gradle.kts /app/ COPY buildSrc/ buildSrc/ # Copy sdk source files diff --git a/examples/sdk-app-example/app/build.gradle b/examples/sdk-app-example/app/build.gradle index 5def256fa5..8db9eba255 100644 --- a/examples/sdk-app-example/app/build.gradle +++ b/examples/sdk-app-example/app/build.gradle @@ -42,8 +42,8 @@ android { } dependencies { - implementation "com.bugsnag:bugsnag-android:6.6.0" - implementation "com.bugsnag:bugsnag-plugin-android-okhttp:6.6.0" + implementation "com.bugsnag:bugsnag-android:6.6.1" + implementation "com.bugsnag:bugsnag-plugin-android-okhttp:6.6.1" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "androidx.appcompat:appcompat:1.6.1" implementation "com.google.android.material:material:1.11.0" diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OomScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OomScenario.kt index e45d8066ec..2faa08600d 100644 --- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OomScenario.kt +++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OomScenario.kt @@ -12,9 +12,12 @@ internal class OomScenario( context: Context, eventMetadata: String ) : Scenario(config, context, eventMetadata) { - private val queue = LinkedList>() + init { + config.enabledErrorTypes.anrs = false + } + override fun startScenario() { super.startScenario() while (true) { diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/UnhandledJavaLoadedConfigScenario.java b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/UnhandledJavaLoadedConfigScenario.java index ff167c6b0b..63736c71c1 100644 --- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/UnhandledJavaLoadedConfigScenario.java +++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/UnhandledJavaLoadedConfigScenario.java @@ -17,6 +17,7 @@ public UnhandledJavaLoadedConfigScenario(@NonNull Configuration config, @NonNull Context context, @Nullable String eventMetadata) { super(config, context, eventMetadata); + config.getEnabledErrorTypes().setAnrs(false); } @Override diff --git a/gradle.properties b/gradle.properties index a3007565d8..4581a3adf9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.jvmargs=-Xmx4096m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects org.gradle.parallel=true -VERSION_NAME=6.6.0 +VERSION_NAME=6.6.1 GROUP=com.bugsnag POM_SCM_URL=https://github.com/bugsnag/bugsnag-android POM_SCM_CONNECTION=scm:git@github.com:bugsnag/bugsnag-android.git