From 1c44dd11b70393c11eb31deb93a273686008b953 Mon Sep 17 00:00:00 2001
From: YingYing Chen <40571804+YYChen01988@users.noreply.github.com>
Date: Tue, 16 Apr 2024 10:13:14 +0100
Subject: [PATCH 01/23] fix(test) added more end to end test for validated all
the changeable fields in an error callback (#2010)
---
.../jvm-scenarios/detekt-baseline.xml | 2 +
...ndledExceptionEventDetailChangeScenario.kt | 86 +++++++++++++++++++
.../error_callback_alters_fields.feature | 59 +++++++++++++
3 files changed, 147 insertions(+)
create mode 100644 features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/UnhandledExceptionEventDetailChangeScenario.kt
create mode 100644 features/full_tests/error_callback_alters_fields.feature
diff --git a/features/fixtures/mazerunner/jvm-scenarios/detekt-baseline.xml b/features/fixtures/mazerunner/jvm-scenarios/detekt-baseline.xml
index 1fcda340f9..d18bb02cbd 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/detekt-baseline.xml
+++ b/features/fixtures/mazerunner/jvm-scenarios/detekt-baseline.xml
@@ -24,6 +24,8 @@
MagicNumber:StartupCrashFlushScenario.kt$StartupCrashFlushScenario$6000
MagicNumber:TestHarnessHooks.kt$<no name provided>$500
MagicNumber:TrimmedStacktraceScenario.kt$TrimmedStacktraceScenario$100000
+ MagicNumber:UnhandledExceptionEventDetailChangeScenario.kt$UnhandledExceptionEventDetailChangeScenario$123
+ MagicNumber:UnhandledExceptionEventDetailChangeScenario.kt$UnhandledExceptionEventDetailChangeScenario$123456
ThrowingExceptionsWithoutMessageOrCause:AnrHelper.kt$<no name provided>$IllegalStateException()
ThrowingExceptionsWithoutMessageOrCause:BugsnagInitScenario.kt$BugsnagInitScenario$RuntimeException()
ThrowingExceptionsWithoutMessageOrCause:CustomPluginNotifierDescriptionScenario.kt$CustomPluginNotifierDescriptionScenario$RuntimeException()
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/UnhandledExceptionEventDetailChangeScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/UnhandledExceptionEventDetailChangeScenario.kt
new file mode 100644
index 0000000000..fdacbf4c3e
--- /dev/null
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/UnhandledExceptionEventDetailChangeScenario.kt
@@ -0,0 +1,86 @@
+package com.bugsnag.android.mazerunner.scenarios
+
+import android.content.Context
+import com.bugsnag.android.BreadcrumbType
+import com.bugsnag.android.Bugsnag
+import com.bugsnag.android.Configuration
+import com.bugsnag.android.OnErrorCallback
+import com.bugsnag.android.Severity
+
+/**
+ * Sends an unhandled exception to Bugsnag where the event details are changed in a callback
+ */
+internal class UnhandledExceptionEventDetailChangeScenario(
+ config: Configuration,
+ context: Context,
+ eventMetadata: String?
+) : Scenario(config, context, eventMetadata) {
+
+ init {
+
+ config.addOnError(
+ OnErrorCallback { event ->
+ event.apiKey = "0000111122223333aaaabbbbcccc9999"
+ event.severity = Severity.ERROR
+ event.groupingHash = "groupingHash1"
+ event.context = "new-context"
+ event.setUser("abc", "joe@test.com", "Joe")
+
+ event.clearMetadata("custom_data1")
+ event.clearMetadata("custom_data2", "data")
+ event.addMetadata("custom_data2", "test_data", "this is test")
+
+ event.clearFeatureFlag("test1")
+ event.addFeatureFlag("beta", "b")
+ event.addFeatureFlag("gamma")
+
+ event.isUnhandled = false
+ event.app.binaryArch = "x86"
+ event.app.id = "12345"
+ event.app.releaseStage = "custom"
+ event.app.version = "1.2.3"
+ event.app.buildUuid = "12345678"
+ event.app.type = "android_custom"
+ event.app.versionCode = 123
+ event.app.duration = 123456
+ event.app.durationInForeground = 123456
+ event.app.inForeground = false
+ event.app.isLaunching = false
+
+ event.device.id = "12345"
+ event.device.jailbroken = true
+ event.device.locale = "en-UK"
+ event.device.totalMemory = 123456
+ event.device.runtimeVersions = mutableMapOf("androidApiLevel" to "30")
+ event.device.freeDisk = 123456
+ event.device.freeMemory = 123456
+ event.device.orientation = "portrait"
+
+ event.breadcrumbs.removeLast()
+ event.breadcrumbs.first().type = BreadcrumbType.ERROR
+ event.breadcrumbs.first().message = "new breadcrumb message"
+ event.breadcrumbs[1].type = BreadcrumbType.ERROR
+ event.breadcrumbs[1].message = "Second breadcrumb message"
+ event.breadcrumbs.first().metadata = mapOf("foo" to "data")
+ true
+ }
+ )
+ }
+
+ override fun startScenario() {
+ super.startScenario()
+ Bugsnag.leaveBreadcrumb("Hello1")
+ Bugsnag.leaveBreadcrumb("Hello2")
+ Bugsnag.leaveBreadcrumb("Hello3")
+
+ Bugsnag.addMetadata("custom_data1", "data", "hello")
+ Bugsnag.addMetadata("custom_data2", "data", "hello")
+ Bugsnag.addMetadata("custom_data3", "test data", "divert all available power to the crash reporter")
+
+ Bugsnag.addFeatureFlag("test1")
+ Bugsnag.addFeatureFlag("test2")
+
+ Bugsnag.notify(RuntimeException("UnhandledExceptionEventDetailChangeScenario"))
+ throw NullPointerException("something broke")
+ }
+}
diff --git a/features/full_tests/error_callback_alters_fields.feature b/features/full_tests/error_callback_alters_fields.feature
new file mode 100644
index 0000000000..2748be9376
--- /dev/null
+++ b/features/full_tests/error_callback_alters_fields.feature
@@ -0,0 +1,59 @@
+Feature: When the api key is altered in an Event the JSON payload reflects this
+
+ Background:
+ Given I clear all persistent data
+
+ Scenario: Unhandled exception with altered event details
+ When I run "UnhandledExceptionEventDetailChangeScenario" and relaunch the crashed app
+ And I configure Bugsnag for "UnhandledExceptionApiKeyChangeScenario"
+ And I wait to receive an error
+ And the error payload field "events" is an array with 1 elements
+ And the exception "message" equals "UnhandledExceptionEventDetailChangeScenario"
+ And the error payload field "apiKey" equals "0000111122223333aaaabbbbcccc9999"
+ And the error "Bugsnag-Api-Key" header equals "0000111122223333aaaabbbbcccc9999"
+ And the event "severity" equals "error"
+ And the event "context" equals "new-context"
+ And the event "groupingHash" equals "groupingHash1"
+ And the event "user.id" equals "abc"
+ And the event "user.email" equals "joe@test.com"
+ And the event "user.name" equals "Joe"
+ And the event "metaData.custom_data2.test_data" equals "this is test"
+ And the event "metaData.custom_data1" is null
+ And the event "metaData.custom_data2.data" is null
+ And the event "metaData.custom_data3.test data" equals "divert all available power to the crash reporter"
+ And event 0 contains the feature flag "beta" with variant "b"
+ And event 0 does not contain the feature flag "alpha"
+ And event 0 contains the feature flag "gamma" with no variant
+ And event 0 does not contain the feature flag "test1"
+ And event 0 contains the feature flag "test2" with no variant
+
+ # app fields
+ And the event "unhandled" is false
+ And the event "app.binaryArch" equals "x86"
+ And the event "app.id" equals "12345"
+ And the event "app.releaseStage" equals "custom"
+ And the event "app.version" equals "1.2.3"
+ And the event "app.buildUUID" equals "12345678"
+ And the event "app.type" equals "android_custom"
+ And the event "app.versionCode" equals 123
+ And the event "app.duration" equals 123456
+ And the event "app.durationInForeground" equals 123456
+ And the event "app.inForeground" is false
+ And the event "app.isLaunching" is false
+
+ # device fields
+ And the event "device.id" equals "12345"
+ And the event "device.jailbroken" is true
+ And the event "device.locale" equals "en-UK"
+ And the event "device.totalMemory" equals 123456
+ And the event "device.runtimeVersions.androidApiLevel" equals "30"
+ And the event "device.freeDisk" equals 123456
+ And the event "device.freeMemory" equals 123456
+ And the event "device.orientation" equals "portrait"
+
+ # breadcrumbs fields
+ And the event "breadcrumbs.0.type" equals "error"
+ And the event "breadcrumbs.0.name" equals "new breadcrumb message"
+ And the event "breadcrumbs.0.metaData.foo" equals "data"
+ And the event "breadcrumbs.1.type" equals "error"
+ And the event "breadcrumbs.1.name" equals "Second breadcrumb message"
\ No newline at end of file
From 55311962eaea3cab0741008a4d21e10f84e6bffe Mon Sep 17 00:00:00 2001
From: YingYing Chen <40571804+YYChen01988@users.noreply.github.com>
Date: Tue, 16 Apr 2024 14:35:25 +0100
Subject: [PATCH 02/23] Reduce use of Atomics in Session (#2013)
* feat(session) Change autoCaptured type from AtomicBoolean to volatile boolean since it does not use specific atomic method
* feat(session) Make isPaused private
---
.../java/com/bugsnag/android/Session.java | 28 +++++++++++++------
.../com/bugsnag/android/SessionTracker.java | 8 +++---
.../java/com/bugsnag/android/SessionTest.kt | 5 +++-
3 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Session.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/Session.java
index 408d6fc0f7..5081ad22e7 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Session.java
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/Session.java
@@ -28,11 +28,11 @@ public final class Session implements JsonStream.Streamable, UserAware {
private App app;
private Device device;
- private final AtomicBoolean autoCaptured = new AtomicBoolean(false);
+ private volatile boolean autoCaptured = false;
private final AtomicInteger unhandledCount = new AtomicInteger();
private final AtomicInteger handledCount = new AtomicInteger();
private final AtomicBoolean tracked = new AtomicBoolean(false);
- final AtomicBoolean isPaused = new AtomicBoolean(false);
+ private final AtomicBoolean isPaused = new AtomicBoolean(false);
private String apiKey;
@@ -41,7 +41,7 @@ static Session copySession(Session session) {
session.unhandledCount.get(), session.handledCount.get(), session.notifier,
session.logger, session.getApiKey());
copy.tracked.set(session.tracked.get());
- copy.autoCaptured.set(session.isAutoCaptured());
+ copy.autoCaptured = session.isAutoCaptured();
return copy;
}
@@ -68,7 +68,7 @@ static Session copySession(Session session) {
this.id = id;
this.startedAt = new Date(startedAt.getTime());
this.user = user;
- this.autoCaptured.set(autoCaptured);
+ this.autoCaptured = autoCaptured;
this.apiKey = apiKey;
}
@@ -198,16 +198,28 @@ Session incrementUnhandledAndCopy() {
return copySession(this);
}
- AtomicBoolean isTracked() {
- return tracked;
+ boolean markTracked() {
+ return tracked.compareAndSet(false, true);
+ }
+
+ boolean markResumed() {
+ return isPaused.compareAndSet(true, false);
+ }
+
+ void markPaused() {
+ isPaused.set(true);
+ }
+
+ boolean isPaused() {
+ return isPaused.get();
}
boolean isAutoCaptured() {
- return autoCaptured.get();
+ return autoCaptured;
}
void setAutoCaptured(boolean autoCaptured) {
- this.autoCaptured.set(autoCaptured);
+ this.autoCaptured = autoCaptured;
}
/**
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionTracker.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionTracker.java
index e9e15b2a2e..9fc2747f5b 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionTracker.java
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/SessionTracker.java
@@ -120,7 +120,7 @@ void pauseSession() {
Session session = currentSession;
if (session != null) {
- session.isPaused.set(true);
+ session.markPaused();
updateState(StateEvent.PauseSession.INSTANCE);
}
}
@@ -133,7 +133,7 @@ boolean resumeSession() {
session = startSession(false);
resumed = false;
} else {
- resumed = session.isPaused.compareAndSet(true, false);
+ resumed = session.markResumed();
}
if (session != null) {
@@ -191,7 +191,7 @@ private boolean trackSessionIfNeeded(final Session session) {
session.setDevice(client.getDeviceDataCollector().generateDevice());
boolean deliverSession = callbackState.runOnSessionTasks(session, logger);
- if (deliverSession && session.isTracked().compareAndSet(false, true)) {
+ if (deliverSession && session.markTracked()) {
currentSession = session;
notifySessionStartObserver(session);
flushInMemorySession(session);
@@ -205,7 +205,7 @@ private boolean trackSessionIfNeeded(final Session session) {
Session getCurrentSession() {
Session session = currentSession;
- if (session != null && !session.isPaused.get()) {
+ if (session != null && !session.isPaused()) {
return session;
}
return null;
diff --git a/bugsnag-android-core/src/test/java/com/bugsnag/android/SessionTest.kt b/bugsnag-android-core/src/test/java/com/bugsnag/android/SessionTest.kt
index 73f80449c0..ae7c0f6fcd 100644
--- a/bugsnag-android-core/src/test/java/com/bugsnag/android/SessionTest.kt
+++ b/bugsnag-android-core/src/test/java/com/bugsnag/android/SessionTest.kt
@@ -141,7 +141,10 @@ class SessionTest {
assertEquals(startedAt, copy.startedAt)
assertEquals(getUser(), copy.getUser()) // make a shallow copy
assertEquals(isAutoCaptured, copy.isAutoCaptured)
- assertEquals(isTracked.get(), copy.isTracked.get())
+ assertEquals(markTracked(), copy.markTracked())
+ assertEquals(markResumed(), copy.markResumed())
+ assertEquals(isPaused, copy.isPaused)
+ assertEquals(markPaused(), copy.markPaused())
assertEquals(session.unhandledCount, copy.unhandledCount)
assertEquals(session.handledCount, copy.handledCount)
}
From f1753a4cfb00c6243b4a24ad109ae4838206e347 Mon Sep 17 00:00:00 2001
From: YingYing Chen <40571804+YYChen01988@users.noreply.github.com>
Date: Tue, 16 Apr 2024 15:13:33 +0100
Subject: [PATCH 03/23] fix(gradle) implementation, plugins and ndk version
updated (#2015)
---
examples/sdk-app-example/app/build.gradle | 11 ++++++-----
.../sdk-app-example/app/src/main/AndroidManifest.xml | 3 +--
examples/sdk-app-example/build.gradle | 4 ++--
examples/sdk-app-example/gradle.properties | 3 +++
.../gradle/wrapper/gradle-wrapper.properties | 2 +-
5 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/examples/sdk-app-example/app/build.gradle b/examples/sdk-app-example/app/build.gradle
index d03a5b6c74..9b25b2fd0c 100644
--- a/examples/sdk-app-example/app/build.gradle
+++ b/examples/sdk-app-example/app/build.gradle
@@ -2,13 +2,13 @@ apply plugin: "com.android.application"
apply plugin: "kotlin-android"
android {
- compileSdk 31
+ compileSdk 34
ndkVersion "21.4.7075529"
defaultConfig {
applicationId "com.example.bugsnag.android"
minSdkVersion 16
- targetSdkVersion 31
+ targetSdkVersion 34
versionCode 1
versionName "1.0"
}
@@ -38,15 +38,16 @@ android {
buildFeatures.prefab = true
packagingOptions.jniLibs.pickFirsts += ["**/libbugsnag-ndk.so"]
+ namespace 'com.example.bugsnag.android'
}
dependencies {
implementation "com.bugsnag:bugsnag-android:6.4.0"
implementation "com.bugsnag:bugsnag-plugin-android-okhttp:6.4.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- implementation "androidx.appcompat:appcompat:1.4.0"
- implementation "com.google.android.material:material:1.4.0"
- implementation "com.squareup.okhttp3:okhttp:4.10.0"
+ implementation "androidx.appcompat:appcompat:1.6.1"
+ implementation "com.google.android.material:material:1.11.0"
+ implementation "com.squareup.okhttp3:okhttp:4.12.0"
}
apply plugin: "com.bugsnag.android.gradle"
diff --git a/examples/sdk-app-example/app/src/main/AndroidManifest.xml b/examples/sdk-app-example/app/src/main/AndroidManifest.xml
index 9e3bd6979b..5017380f20 100644
--- a/examples/sdk-app-example/app/src/main/AndroidManifest.xml
+++ b/examples/sdk-app-example/app/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
+ xmlns:tools="http://schemas.android.com/tools">
diff --git a/examples/sdk-app-example/build.gradle b/examples/sdk-app-example/build.gradle
index bd8394d830..bf66353fdf 100644
--- a/examples/sdk-app-example/build.gradle
+++ b/examples/sdk-app-example/build.gradle
@@ -6,9 +6,9 @@ buildscript {
mavenLocal()
}
dependencies {
- classpath "com.android.tools.build:gradle:7.4.2"
+ classpath "com.android.tools.build:gradle:8.3.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath "com.bugsnag:bugsnag-android-gradle-plugin:7.1.0"
+ classpath "com.bugsnag:bugsnag-android-gradle-plugin:8.+"
}
}
diff --git a/examples/sdk-app-example/gradle.properties b/examples/sdk-app-example/gradle.properties
index b7aa9d48f5..fa41b5bf20 100644
--- a/examples/sdk-app-example/gradle.properties
+++ b/examples/sdk-app-example/gradle.properties
@@ -2,3 +2,6 @@ android.useAndroidX=true
org.gradle.jvmargs=-Xmx4096m
org.gradle.parallel=true
kotlin.code.style=official
+android.defaults.buildfeatures.buildconfig=true
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false
diff --git a/examples/sdk-app-example/gradle/wrapper/gradle-wrapper.properties b/examples/sdk-app-example/gradle/wrapper/gradle-wrapper.properties
index 068cdb2dc2..e411586a54 100644
--- a/examples/sdk-app-example/gradle/wrapper/gradle-wrapper.properties
+++ b/examples/sdk-app-example/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From 9f23de0332626dfe6d11b106cc4ac34af04664a3 Mon Sep 17 00:00:00 2001
From: jason
Date: Tue, 16 Apr 2024 11:39:59 +0100
Subject: [PATCH 04/23] feat(ndk): changed the breadcrumb-type transfer type
from a string to an int
---
.../com/bugsnag/android/ndk/NativeBridge.kt | 38 +++++++++++++++++-
.../src/main/jni/bugsnag_ndk.c | 40 ++++++++++++-------
2 files changed, 62 insertions(+), 16 deletions(-)
diff --git a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
index 3ff0b0e87e..988176b230 100644
--- a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
+++ b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
@@ -1,6 +1,7 @@
package com.bugsnag.android.ndk
import android.os.Build
+import com.bugsnag.android.BreadcrumbType
import com.bugsnag.android.NativeInterface
import com.bugsnag.android.StateEvent
import com.bugsnag.android.StateEvent.AddBreadcrumb
@@ -62,7 +63,15 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
)
external fun deliverReportAtPath(filePath: String)
- external fun addBreadcrumb(name: String, type: String, timestamp: String, metadata: Any)
+ fun addBreadcrumb(name: String, type: String, timestamp: String, metadata: Any) {
+ val breadcrumbType = BreadcrumbType.values()
+ .find { it.toString() == type }
+ ?: BreadcrumbType.MANUAL
+
+ addBreadcrumb(name, breadcrumbType.toNativeValue(), timestamp, metadata)
+ }
+
+ private external fun addBreadcrumb(name: String, type: Int, timestamp: String, metadata: Any)
external fun addMetadataString(tab: String, key: String, value: String)
external fun addMetadataDouble(tab: String, key: String, value: Double)
external fun addMetadataBoolean(tab: String, key: String, value: Boolean)
@@ -106,12 +115,14 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
event.section,
event.key ?: ""
)
+
is AddBreadcrumb -> addBreadcrumb(
event.message,
- event.type.toString(),
+ event.type.toNativeValue(),
event.timestamp,
makeSafeMetadata(event.metadata)
)
+
NotifyHandled -> addHandledEvent()
NotifyUnhandled -> addUnhandledEvent()
PauseSession -> pausedSession()
@@ -121,11 +132,13 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
event.handledCount,
event.unhandledCount
)
+
is UpdateContext -> updateContext(event.context ?: "")
is UpdateInForeground -> updateInForeground(
event.inForeground,
event.contextActivity ?: ""
)
+
is StateEvent.UpdateLastRunInfo -> updateLastRunInfo(event.consecutiveLaunchCrashes)
is StateEvent.UpdateIsLaunching -> {
updateIsLaunching(event.isLaunching)
@@ -135,20 +148,24 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
bgTaskService.submitTask(TaskType.DEFAULT, this::refreshSymbolTable)
}
}
+
is UpdateOrientation -> updateOrientation(event.orientation ?: "")
is UpdateUser -> {
updateUserId(event.user.id ?: "")
updateUserName(event.user.name ?: "")
updateUserEmail(event.user.email ?: "")
}
+
is StateEvent.UpdateMemoryTrimEvent -> updateLowMemory(
event.isLowMemory,
event.memoryTrimLevelDescription
)
+
is StateEvent.AddFeatureFlag -> addFeatureFlag(
event.name,
event.variant
)
+
is StateEvent.ClearFeatureFlag -> clearFeatureFlag(event.name)
is StateEvent.ClearFeatureFlags -> clearFeatureFlags()
}
@@ -227,4 +244,21 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
}
}
}
+
+ /**
+ * Convert a [BreadcrumbType] to the value expected by the [addBreadcrumb] implementation. This
+ * is implemented as an exhaustive when so that any changes to the `enum` are picked up and/or
+ * don't directly impact the implementation.
+ */
+ @Suppress("MagicNumber") // introducing consts would reduce readability
+ private fun BreadcrumbType.toNativeValue(): Int = when (this) {
+ BreadcrumbType.ERROR -> 0
+ BreadcrumbType.LOG -> 1
+ BreadcrumbType.MANUAL -> 2
+ BreadcrumbType.NAVIGATION -> 3
+ BreadcrumbType.PROCESS -> 4
+ BreadcrumbType.REQUEST -> 5
+ BreadcrumbType.STATE -> 6
+ BreadcrumbType.USER -> 7
+ }
}
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
index e2fbd419b4..207a254d39 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
@@ -387,7 +387,7 @@ JNIEXPORT void JNICALL Java_com_bugsnag_android_ndk_NativeBridge_pausedSession(
}
JNIEXPORT void JNICALL Java_com_bugsnag_android_ndk_NativeBridge_addBreadcrumb(
- JNIEnv *env, jobject _this, jstring name_, jstring crumb_type,
+ JNIEnv *env, jobject _this, jstring name_, jint crumb_type,
jstring timestamp_, jobject metadata) {
if (!bsg_jni_cache->initialized) {
@@ -395,28 +395,41 @@ JNIEXPORT void JNICALL Java_com_bugsnag_android_ndk_NativeBridge_addBreadcrumb(
return;
}
const char *name = bsg_safe_get_string_utf_chars(env, name_);
- const char *type = bsg_safe_get_string_utf_chars(env, crumb_type);
const char *timestamp = bsg_safe_get_string_utf_chars(env, timestamp_);
- if (name != NULL && type != NULL && timestamp != NULL) {
+ if (name != NULL && timestamp != NULL) {
bugsnag_breadcrumb *crumb = calloc(1, sizeof(bugsnag_breadcrumb));
bsg_strncpy(crumb->name, name, sizeof(crumb->name));
bsg_strncpy(crumb->timestamp, timestamp, sizeof(crumb->timestamp));
- if (strcmp(type, "user") == 0) {
- crumb->type = BSG_CRUMB_USER;
- } else if (strcmp(type, "error") == 0) {
+
+ // the values of crumb_type are defined in
+ // NativeBridge.BreadcrumbType.toNativeValue()
+ switch (crumb_type) {
+ case 0:
crumb->type = BSG_CRUMB_ERROR;
- } else if (strcmp(type, "log") == 0) {
+ break;
+ case 1:
crumb->type = BSG_CRUMB_LOG;
- } else if (strcmp(type, "navigation") == 0) {
+ break;
+ case 2:
+ crumb->type = BSG_CRUMB_MANUAL;
+ break;
+ case 3:
crumb->type = BSG_CRUMB_NAVIGATION;
- } else if (strcmp(type, "request") == 0) {
+ break;
+ case 4:
+ crumb->type = BSG_CRUMB_PROCESS;
+ break;
+ case 5:
crumb->type = BSG_CRUMB_REQUEST;
- } else if (strcmp(type, "state") == 0) {
+ break;
+ case 6:
crumb->type = BSG_CRUMB_STATE;
- } else if (strcmp(type, "process") == 0) {
- crumb->type = BSG_CRUMB_PROCESS;
- } else {
+ break;
+ case 7:
+ crumb->type = BSG_CRUMB_USER;
+ break;
+ default:
crumb->type = BSG_CRUMB_MANUAL;
}
@@ -428,7 +441,6 @@ JNIEXPORT void JNICALL Java_com_bugsnag_android_ndk_NativeBridge_addBreadcrumb(
free(crumb);
}
bsg_safe_release_string_utf_chars(env, name_, name);
- bsg_safe_release_string_utf_chars(env, crumb_type, type);
bsg_safe_release_string_utf_chars(env, timestamp_, timestamp);
}
From 16c3a6e14cd947972b9a1e0a0f1a637a6cd55a8d Mon Sep 17 00:00:00 2001
From: jason
Date: Mon, 22 Apr 2024 16:45:11 +0100
Subject: [PATCH 05/23] feat(ndk): create NDK breadcrumb buffer dynamically
instead of as a static part of the `bugsnag_event` struct
---
CHANGELOG.md | 7 ++++
Makefile | 2 +-
.../api/bugsnag-android-core.api | 3 +-
bugsnag-android-core/detekt-baseline.xml | 2 +-
.../com/bugsnag/android/ClientObservable.kt | 3 +-
.../java/com/bugsnag/android/StateEvent.kt | 3 +-
.../api/bugsnag-plugin-android-ndk.api | 2 +-
.../detekt-baseline.xml | 2 +-
.../com/bugsnag/android/ndk/NativeBridge.kt | 6 ++-
.../src/main/jni/bugsnag_ndk.c | 10 ++++-
.../src/main/jni/event.c | 4 +-
.../src/main/jni/event.h | 12 ++----
.../main/jni/utils/serializer/event_reader.c | 37 +++++++++++++++++--
.../main/jni/utils/serializer/event_writer.c | 11 +++++-
.../main/jni/utils/serializer/json_writer.c | 2 +-
.../src/test/cpp/main.c | 2 +
.../src/test/cpp/test_breadcrumbs.c | 2 +-
.../src/test/cpp/test_serializer.c | 6 +--
18 files changed, 87 insertions(+), 29 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6cecf42f66..6984e8da91 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## TBD
+
+### Enhancements
+
+* `Configuration.maxBreadcrumbs` is now obeyed by `bugsnag-plugin-android-ndk`, so native events will include the correct number of breadcrumbs
+ []()
+
## 6.4.0 (2024-04-15)
### Enhancements
diff --git a/Makefile b/Makefile
index 42696fe2cf..0e5bba7b8f 100644
--- a/Makefile
+++ b/Makefile
@@ -64,7 +64,7 @@ fixture-debug: notifier
example-app:
@./gradlew assembleRelease publishToMavenLocal -x check
# Build Android example app
- @./examples/sdk-app-example/gradlew clean assembleRelease
+ @cd ./examples/sdk-app-example/ && ./gradlew clean assembleRelease
bump:
ifneq ($(shell git diff --staged),)
diff --git a/bugsnag-android-core/api/bugsnag-android-core.api b/bugsnag-android-core/api/bugsnag-android-core.api
index 10b89831f3..ecca487084 100644
--- a/bugsnag-android-core/api/bugsnag-android-core.api
+++ b/bugsnag-android-core/api/bugsnag-android-core.api
@@ -687,9 +687,10 @@ public final class com/bugsnag/android/StateEvent$Install : com/bugsnag/android/
public final field buildUuid Ljava/lang/String;
public final field consecutiveLaunchCrashes I
public final field lastRunInfoPath Ljava/lang/String;
+ public final field maxBreadcrumbs I
public final field releaseStage Ljava/lang/String;
public final field sendThreads Lcom/bugsnag/android/ThreadSendPolicy;
- public fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILcom/bugsnag/android/ThreadSendPolicy;)V
+ public fun (Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILcom/bugsnag/android/ThreadSendPolicy;I)V
}
public final class com/bugsnag/android/StateEvent$NotifyHandled : com/bugsnag/android/StateEvent {
diff --git a/bugsnag-android-core/detekt-baseline.xml b/bugsnag-android-core/detekt-baseline.xml
index 1e44fd786f..a5e99e257b 100644
--- a/bugsnag-android-core/detekt-baseline.xml
+++ b/bugsnag-android-core/detekt-baseline.xml
@@ -18,7 +18,7 @@
LongParameterList:EventInternal.kt$EventInternal$( apiKey: String, logger: Logger, breadcrumbs: MutableList<Breadcrumb> = mutableListOf(), discardClasses: Set<Pattern> = setOf(), errors: MutableList<Error> = mutableListOf(), metadata: Metadata = Metadata(), featureFlags: FeatureFlags = FeatureFlags(), originalError: Throwable? = null, projectPackages: Collection<String> = setOf(), severityReason: SeverityReason = SeverityReason.newInstance(SeverityReason.REASON_HANDLED_EXCEPTION), threads: MutableList<Thread> = mutableListOf(), user: User = User(), redactionKeys: Set<Pattern>? = null )
LongParameterList:EventStorageModule.kt$EventStorageModule$( contextModule: ContextModule, configModule: ConfigModule, dataCollectionModule: DataCollectionModule, bgTaskService: BackgroundTaskService, trackerModule: TrackerModule, systemServiceModule: SystemServiceModule, notifier: Notifier, callbackState: CallbackState )
LongParameterList:NativeStackframe.kt$NativeStackframe$( /** * The name of the method that was being executed */ var method: String?, /** * The location of the source file */ var file: String?, /** * The line number within the source file this stackframe refers to */ var lineNumber: Number?, /** * The address of the instruction where the event occurred. */ var frameAddress: Long?, /** * The address of the function where the event occurred. */ var symbolAddress: Long?, /** * The address of the library where the event occurred. */ var loadAddress: Long?, /** * Whether this frame identifies the program counter */ var isPC: Boolean?, /** * The type of the error */ var type: ErrorType? = null, /** * Identifies the exact build this frame originates from. */ var codeIdentifier: String? = null, )
- LongParameterList:StateEvent.kt$StateEvent.Install$( @JvmField val apiKey: String, @JvmField val autoDetectNdkCrashes: Boolean, @JvmField val appVersion: String?, @JvmField val buildUuid: String?, @JvmField val releaseStage: String?, @JvmField val lastRunInfoPath: String, @JvmField val consecutiveLaunchCrashes: Int, @JvmField val sendThreads: ThreadSendPolicy )
+ LongParameterList:StateEvent.kt$StateEvent.Install$( @JvmField val apiKey: String, @JvmField val autoDetectNdkCrashes: Boolean, @JvmField val appVersion: String?, @JvmField val buildUuid: String?, @JvmField val releaseStage: String?, @JvmField val lastRunInfoPath: String, @JvmField val consecutiveLaunchCrashes: Int, @JvmField val sendThreads: ThreadSendPolicy, @JvmField val maxBreadcrumbs: Int )
LongParameterList:ThreadState.kt$ThreadState$( allThreads: List<JavaThread>, currentThread: JavaThread, exc: Throwable?, isUnhandled: Boolean, maxThreadCount: Int, threadCollectionTimeLimitMillis: Long, projectPackages: Collection<String>, logger: Logger )
MagicNumber:DefaultDelivery.kt$DefaultDelivery$299
MagicNumber:DefaultDelivery.kt$DefaultDelivery$429
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt
index cd6ab1fe5d..b099c7edc3 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/ClientObservable.kt
@@ -22,7 +22,8 @@ internal class ClientObservable : BaseObservable() {
conf.releaseStage,
lastRunInfoPath,
consecutiveLaunchCrashes,
- conf.sendThreads
+ conf.sendThreads,
+ conf.maxBreadcrumbs
)
}
}
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt
index 729c9b9fb7..e16671559f 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/StateEvent.kt
@@ -10,7 +10,8 @@ sealed class StateEvent { // JvmField allows direct field access optimizations
@JvmField val releaseStage: String?,
@JvmField val lastRunInfoPath: String,
@JvmField val consecutiveLaunchCrashes: Int,
- @JvmField val sendThreads: ThreadSendPolicy
+ @JvmField val sendThreads: ThreadSendPolicy,
+ @JvmField val maxBreadcrumbs: Int
) : StateEvent()
object DeliverPending : StateEvent()
diff --git a/bugsnag-plugin-android-ndk/api/bugsnag-plugin-android-ndk.api b/bugsnag-plugin-android-ndk/api/bugsnag-plugin-android-ndk.api
index ab910c524c..cdc5a7496c 100644
--- a/bugsnag-plugin-android-ndk/api/bugsnag-plugin-android-ndk.api
+++ b/bugsnag-plugin-android-ndk/api/bugsnag-plugin-android-ndk.api
@@ -21,7 +21,7 @@ public final class com/bugsnag/android/ndk/NativeBridge : com/bugsnag/android/in
public final fun getCurrentNativeApiCallUsage ()Ljava/util/Map;
public final fun getSignalUnwindStackFunction ()J
public final fun initCallbackCounts (Ljava/util/Map;)V
- public final fun install (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZIZI)V
+ public final fun install (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZIZII)V
public final fun notifyAddCallback (Ljava/lang/String;)V
public final fun notifyRemoveCallback (Ljava/lang/String;)V
public fun onStateChange (Lcom/bugsnag/android/StateEvent;)V
diff --git a/bugsnag-plugin-android-ndk/detekt-baseline.xml b/bugsnag-plugin-android-ndk/detekt-baseline.xml
index dbc3020513..57df3f72f2 100644
--- a/bugsnag-plugin-android-ndk/detekt-baseline.xml
+++ b/bugsnag-plugin-android-ndk/detekt-baseline.xml
@@ -4,7 +4,7 @@
CyclomaticComplexMethod:NativeBridge.kt$NativeBridge$override fun onStateChange(event: StateEvent)
LongMethod:EventOnDiskTests.kt$EventOnDiskTests$@Test fun testEvent()
- LongParameterList:NativeBridge.kt$NativeBridge$( apiKey: String, reportingDirectory: String, lastRunInfoPath: String, consecutiveLaunchCrashes: Int, autoDetectNdkCrashes: Boolean, apiLevel: Int, is32bit: Boolean, threadSendPolicy: Int )
+ LongParameterList:NativeBridge.kt$NativeBridge$( apiKey: String, reportingDirectory: String, lastRunInfoPath: String, consecutiveLaunchCrashes: Int, autoDetectNdkCrashes: Boolean, apiLevel: Int, is32bit: Boolean, threadSendPolicy: Int, maxBreadcrumbs: Int, )
NestedBlockDepth:NativeBridge.kt$NativeBridge$private fun deliverPendingReports()
TooManyFunctions:NativeBridge.kt$NativeBridge : StateObserver
UseCheckOrError:ResourceUtils.kt$throw IllegalStateException("Failed to read JSON from $resourceName")
diff --git a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
index 988176b230..9ff476b93a 100644
--- a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
+++ b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
@@ -52,7 +52,8 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
autoDetectNdkCrashes: Boolean,
apiLevel: Int,
is32bit: Boolean,
- threadSendPolicy: Int
+ threadSendPolicy: Int,
+ maxBreadcrumbs: Int,
)
external fun startedSession(
@@ -226,7 +227,8 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
arg.autoDetectNdkCrashes,
Build.VERSION.SDK_INT,
is32bit,
- arg.sendThreads.ordinal
+ arg.sendThreads.ordinal,
+ arg.maxBreadcrumbs
)
installed.set(true)
}
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
index 207a254d39..8b06c9b17d 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c
@@ -150,7 +150,7 @@ JNIEXPORT void JNICALL Java_com_bugsnag_android_ndk_NativeBridge_install(
JNIEnv *env, jobject _this, jstring _api_key, jstring _event_path,
jstring _last_run_info_path, jint consecutive_launch_crashes,
jboolean auto_detect_ndk_crashes, jint _api_level, jboolean is32bit,
- jint send_threads) {
+ jint send_threads, jint max_breadcrumbs) {
if (!bsg_jni_cache_init(env)) {
BUGSNAG_LOG("Could not init JNI jni_cache.");
@@ -165,6 +165,14 @@ JNIEXPORT void JNICALL Java_com_bugsnag_android_ndk_NativeBridge_install(
bugsnag_env->send_threads = send_threads;
bugsnag_env->handling_crash = ATOMIC_VAR_INIT(false);
+ bugsnag_env->next_event.max_crumb_count = max_breadcrumbs;
+ bugsnag_env->next_event.breadcrumbs =
+ calloc(max_breadcrumbs, sizeof(bugsnag_breadcrumb));
+
+ if (bugsnag_env->next_event.breadcrumbs == NULL) {
+ goto error;
+ }
+
// copy event path to env struct
const char *event_path = bsg_safe_get_string_utf_chars(env, _event_path);
if (event_path == NULL) {
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/event.c b/bugsnag-plugin-android-ndk/src/main/jni/event.c
index 5ef89aea8b..e76618690b 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/event.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/event.c
@@ -353,13 +353,13 @@ void bugsnag_event_set_user(void *event_ptr, const char *id, const char *email,
void bsg_event_add_breadcrumb(bugsnag_event *event, bugsnag_breadcrumb *crumb) {
int crumb_index;
- if (event->crumb_count < BUGSNAG_CRUMBS_MAX) {
+ if (event->crumb_count < event->max_crumb_count) {
crumb_index = event->crumb_count;
event->crumb_count++;
} else {
crumb_index = event->crumb_first_index;
event->crumb_first_index =
- (event->crumb_first_index + 1) % BUGSNAG_CRUMBS_MAX;
+ (event->crumb_first_index + 1) % event->max_crumb_count;
}
bsg_free_opaque_metadata(&(event->breadcrumbs[crumb_index].metadata));
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/event.h b/bugsnag-plugin-android-ndk/src/main/jni/event.h
index c129743439..d92324f394 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/event.h
+++ b/bugsnag-plugin-android-ndk/src/main/jni/event.h
@@ -12,12 +12,6 @@
*/
#define BUGSNAG_METADATA_MAX 128
#endif
-#ifndef BUGSNAG_CRUMBS_MAX
-/**
- * Max number of breadcrumbs in an event. Configures a default if not defined.
- */
-#define BUGSNAG_CRUMBS_MAX 50
-#endif
#ifndef BUGSNAG_DEFAULT_EX_TYPE
/**
* Type assigned to exceptions. Configures a default if not defined.
@@ -34,7 +28,7 @@
/**
* Version of the bugsnag_event struct. Serialized to report header.
*/
-#define BUGSNAG_EVENT_VERSION 13
+#define BUGSNAG_EVENT_VERSION 14
#ifdef __cplusplus
extern "C" {
@@ -229,7 +223,9 @@ typedef struct {
// Breadcrumbs are a ring; the first index moves as the
// structure is filled and replaced.
int crumb_first_index;
- bugsnag_breadcrumb breadcrumbs[BUGSNAG_CRUMBS_MAX];
+ // the maximum number of breadcrumbs that can be placed in the buffer
+ int max_crumb_count;
+ bugsnag_breadcrumb *breadcrumbs;
char context[64];
bugsnag_severity severity;
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/event_reader.c b/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/event_reader.c
index 729ac9b30f..021de3e18e 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/event_reader.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/event_reader.c
@@ -2,6 +2,7 @@
#include "../../event.h"
#include "../string.h"
+#include "utils/logger.h"
#include
#include
@@ -9,7 +10,7 @@
#include
#include
-const int BSG_MIGRATOR_CURRENT_VERSION = 13;
+const int BSG_MIGRATOR_CURRENT_VERSION = 14;
void bsg_read_feature_flags(int fd, bool expect_verification,
bsg_feature_flag **out_feature_flags,
@@ -21,6 +22,8 @@ void bsg_read_opaque_breadcrumb_metadata(int fd,
bugsnag_breadcrumb *breadcrumbs,
int crumb_count);
+bool bsg_read_breadcrumbs(int fd, bugsnag_event *event);
+
bsg_report_header *bsg_report_header_read(int fd) {
bsg_report_header *header = calloc(1, sizeof(bsg_report_header));
ssize_t len = read(fd, header, sizeof(bsg_report_header));
@@ -55,10 +58,13 @@ bugsnag_event *bsg_read_event(char *filepath) {
ssize_t len = read(fd, event, event_size);
if (len != event_size) {
- free(event);
- return NULL;
+ goto error;
}
+ // read the breadcrumbs
+ if (!bsg_read_breadcrumbs(fd, event)) {
+ goto error;
+ }
// read the feature flags, if possible
bsg_read_feature_flags(fd, true, &event->feature_flags,
&event->feature_flag_count);
@@ -67,6 +73,31 @@ bugsnag_event *bsg_read_event(char *filepath) {
event->crumb_count);
return event;
+error:
+ free(event);
+ return NULL;
+}
+
+bool bsg_read_breadcrumbs(int fd, bugsnag_event *event) {
+ bugsnag_breadcrumb *breadcrumbs =
+ calloc(event->max_crumb_count, sizeof(bugsnag_breadcrumb));
+ if (breadcrumbs == NULL) {
+ goto error;
+ }
+ const size_t bytes_to_read =
+ event->max_crumb_count * sizeof(bugsnag_breadcrumb);
+ const ssize_t read_count = read(fd, breadcrumbs, bytes_to_read);
+ if (read_count != bytes_to_read) {
+ goto error;
+ }
+
+ event->breadcrumbs = breadcrumbs;
+ return true;
+error:
+ event->breadcrumbs = NULL;
+ event->crumb_count = 0;
+ event->crumb_first_index = 0;
+ return false;
}
static char *read_string(int fd) {
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/event_writer.c b/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/event_writer.c
index b81987e78a..df787e479d 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/event_writer.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/event_writer.c
@@ -8,11 +8,13 @@
#include "buffered_writer.h"
#include "utils/seqlock.h"
+bool bsg_write_breadcrumbs(bugsnag_event *event, bsg_buffered_writer *writer);
bool bsg_write_feature_flags(bugsnag_event *event, bsg_buffered_writer *writer);
bool bsg_write_opaque_metadata(bugsnag_event *event,
bsg_buffered_writer *writer);
+bool bsg_write_breadcrumbs(bugsnag_event *event, bsg_buffered_writer *writer);
bool bsg_report_header_write(bsg_report_header *header, int fd) {
ssize_t len = write(fd, header, sizeof(bsg_report_header));
@@ -30,7 +32,9 @@ bool bsg_event_write(bsg_environment *env) {
bsg_report_header_write(&env->report_header, writer.fd) &&
// add cached event info
writer.write(&writer, &env->next_event, sizeof(bugsnag_event)) &&
- // append feature flags after event structure
+ // append the breadcrumbs after the event structure
+ bsg_write_breadcrumbs(&env->next_event, &writer) &&
+ // append feature flags
bsg_write_feature_flags(&env->next_event, &writer) &&
// append opaque metadata after the feature flags
bsg_write_opaque_metadata(&env->next_event, &writer);
@@ -85,6 +89,11 @@ static bool write_feature_flag(bsg_buffered_writer *writer,
return true;
}
+bool bsg_write_breadcrumbs(bugsnag_event *event, bsg_buffered_writer *writer) {
+ return writer->write(writer, event->breadcrumbs,
+ sizeof(bugsnag_breadcrumb) * event->max_crumb_count);
+}
+
extern bsg_seqlock_t bsg_feature_flag_lock;
bool bsg_write_feature_flags(bugsnag_event *event,
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/json_writer.c b/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/json_writer.c
index 390439c56d..ad3c356a5b 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/json_writer.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/utils/serializer/json_writer.c
@@ -365,7 +365,7 @@ void bsg_serialize_breadcrumbs(const bugsnag_event *event, JSON_Array *crumbs) {
bsg_crumb_type_string(breadcrumb.type));
bsg_serialize_breadcrumb_metadata(breadcrumb.metadata, crumb);
current_index++;
- if (current_index == BUGSNAG_CRUMBS_MAX) {
+ if (current_index == event->max_crumb_count) {
current_index = 0;
}
}
diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/main.c b/bugsnag-plugin-android-ndk/src/test/cpp/main.c
index 76c03c88f8..68624d369a 100644
--- a/bugsnag-plugin-android-ndk/src/test/cpp/main.c
+++ b/bugsnag-plugin-android-ndk/src/test/cpp/main.c
@@ -191,6 +191,8 @@ JNIEXPORT jstring JNICALL
Java_com_bugsnag_android_ndk_BreadcrumbStateSerializationTest_run(JNIEnv *env,
jobject thiz) {
bugsnag_event *event = calloc(1, sizeof(bugsnag_event));
+ event->max_crumb_count = 50;
+ event->breadcrumbs = calloc(event->max_crumb_count, sizeof(bugsnag_breadcrumb));
loadBreadcrumbsTestCase(event);
JSON_Value *eventVal = json_value_init_array();
JSON_Array *eventAry = json_value_get_array(eventVal);
diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/test_breadcrumbs.c b/bugsnag-plugin-android-ndk/src/test/cpp/test_breadcrumbs.c
index 7ddf7da67f..9c10aee9de 100644
--- a/bugsnag-plugin-android-ndk/src/test/cpp/test_breadcrumbs.c
+++ b/bugsnag-plugin-android-ndk/src/test/cpp/test_breadcrumbs.c
@@ -59,7 +59,7 @@ TEST test_add_breadcrumbs_over_max(void) {
}
// assertions assume that the crumb count is always 50
- ASSERT_EQ(BUGSNAG_CRUMBS_MAX, event->crumb_count);
+ ASSERT_EQ(event->max_crumb_count, event->crumb_count);
ASSERT_EQ(14, event->crumb_first_index);
ASSERT_STR_EQ("crumb: 50", event->breadcrumbs[0].name);
diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c b/bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c
index 69da848637..682b8b2e71 100644
--- a/bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c
+++ b/bugsnag-plugin-android-ndk/src/test/cpp/test_serializer.c
@@ -108,12 +108,12 @@ void loadBreadcrumbsTestCase(bugsnag_event *event) {
bugsnag_breadcrumb *crumb = calloc(1, sizeof(bugsnag_breadcrumb));
memset(crumb, 0, sizeof(bugsnag_breadcrumb));
event->crumb_count = 4;
- event->crumb_first_index = BUGSNAG_CRUMBS_MAX - 2;
+ event->crumb_first_index = event->max_crumb_count - 2;
// ensure that serialization loop is covered by test
// first breadcrumb
- crumb = &event->breadcrumbs[BUGSNAG_CRUMBS_MAX - 2];
+ crumb = &event->breadcrumbs[event->max_crumb_count - 2];
crumb->type = BSG_CRUMB_USER;
strcpy(crumb->name, "Jane");
strcpy(crumb->timestamp, "2018-10-08T12:07:09Z");
@@ -127,7 +127,7 @@ void loadBreadcrumbsTestCase(bugsnag_event *event) {
strcpy(data->values[0].char_value, "Foo");
// second breadcrumb
- crumb = &event->breadcrumbs[BUGSNAG_CRUMBS_MAX - 1];
+ crumb = &event->breadcrumbs[event->max_crumb_count - 1];
crumb->type = BSG_CRUMB_MANUAL;
strcpy(crumb->name, "Something went wrong");
strcpy(crumb->timestamp, "2018-10-08T12:07:11Z");
From 5c93c27ad2d9d781fd3ed6baade4cee1348c682b Mon Sep 17 00:00:00 2001
From: jason
Date: Tue, 23 Apr 2024 11:37:15 +0100
Subject: [PATCH 06/23] test(ndk): end-to-end test with 500 breadcrumbs in a
native crash
---
.buildkite/pipeline.full.yml | 2 ++
.../test/cpp/migrations/EventOnDiskTests.cpp | 7 +++--
.../src/test/cpp/test_breadcrumbs.c | 8 ++++++
.../src/test/cpp/test_utils_serialize.c | 9 ++++++
.../src/main/cpp/cxx-scenarios.cpp | 5 ++++
.../CXXMaxBreadcrumbCrashScenario.kt | 28 +++++++++++++++++++
.../full_tests/native_breadcrumbs.feature | 8 ++++++
7 files changed, 65 insertions(+), 2 deletions(-)
create mode 100644 features/fixtures/mazerunner/cxx-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/CXXMaxBreadcrumbCrashScenario.kt
diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml
index 668b6a9bdd..02fe640f07 100644
--- a/.buildkite/pipeline.full.yml
+++ b/.buildkite/pipeline.full.yml
@@ -11,6 +11,8 @@ steps:
timeout_in_minutes: 5
agents:
queue: macos-14
+ env:
+ JAVA_VERSION: 17
command: 'make example-app'
- label: ':android: Build debug fixture APK'
diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/migrations/EventOnDiskTests.cpp b/bugsnag-plugin-android-ndk/src/test/cpp/migrations/EventOnDiskTests.cpp
index 8ba09f69c2..18a06b4acf 100644
--- a/bugsnag-plugin-android-ndk/src/test/cpp/migrations/EventOnDiskTests.cpp
+++ b/bugsnag-plugin-android-ndk/src/test/cpp/migrations/EventOnDiskTests.cpp
@@ -41,7 +41,9 @@ static void *create_full_event() {
event->app.version_code = 8139512718;
// breadcrumbs
- auto max = 50;
+ event->max_crumb_count = 50;
+ event->breadcrumbs = new bugsnag_breadcrumb[event->max_crumb_count];
+ auto max = event->max_crumb_count;
event->crumb_first_index = 2; // test the circular buffer logic
char name[30];
for (int i = event->crumb_first_index; i < max; i++) {
@@ -132,7 +134,7 @@ static void *create_full_event() {
static const char *write_event(JNIEnv *env, jstring temp_file,
void *(event_generator)()) {
auto event_ctx = (bsg_environment *)calloc(1, sizeof(bsg_environment));
- event_ctx->report_header.version = 13;
+ event_ctx->report_header.version = BUGSNAG_EVENT_VERSION;
const char *path = (*env).GetStringUTFChars(temp_file, nullptr);
sprintf(event_ctx->next_event_path, "%s", path);
@@ -185,6 +187,7 @@ Java_com_bugsnag_android_ndk_migrations_EventOnDiskTests_generateAndStoreEvent(
free(parsed_event->feature_flags[i].name);
free(parsed_event->feature_flags[i].variant);
}
+ free(parsed_event->breadcrumbs);
free(parsed_event->feature_flags);
free(parsed_event);
diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/test_breadcrumbs.c b/bugsnag-plugin-android-ndk/src/test/cpp/test_breadcrumbs.c
index 9c10aee9de..5f5a9588c8 100644
--- a/bugsnag-plugin-android-ndk/src/test/cpp/test_breadcrumbs.c
+++ b/bugsnag-plugin-android-ndk/src/test/cpp/test_breadcrumbs.c
@@ -20,6 +20,9 @@ bugsnag_breadcrumb *init_breadcrumb(const char *name, const char *message, bugsn
TEST test_add_breadcrumb(void) {
bugsnag_event *event = calloc(1, sizeof(bugsnag_event));
+ event->max_crumb_count = 50;
+ event->breadcrumbs =
+ calloc(event->max_crumb_count, sizeof(bugsnag_breadcrumb));
bugsnag_breadcrumb *crumb = init_breadcrumb("stroll", "this is a drill.", BSG_CRUMB_USER);
bsg_event_add_breadcrumb(event, crumb);
ASSERT_EQ(1, event->crumb_count);
@@ -39,6 +42,7 @@ TEST test_add_breadcrumb(void) {
ASSERT(strcmp("message", event->breadcrumbs[1].metadata.values[0].name) == 0);
ASSERT(strcmp("this is not a drill.", event->breadcrumbs[1].metadata.values[0].char_value) == 0);
+ free(event->breadcrumbs);
free(event);
free(crumb2);
PASS();
@@ -46,6 +50,9 @@ TEST test_add_breadcrumb(void) {
TEST test_add_breadcrumbs_over_max(void) {
bugsnag_event *event = calloc(1, sizeof(bugsnag_event));
+ event->max_crumb_count = 50;
+ event->breadcrumbs =
+ calloc(event->max_crumb_count, sizeof(bugsnag_breadcrumb));
int breadcrumb_count = 64;
for (int i=0; i < breadcrumb_count; i++) {
@@ -71,6 +78,7 @@ TEST test_add_breadcrumbs_over_max(void) {
ASSERT_STR_EQ("crumb: 14", event->breadcrumbs[14].name);
ASSERT_STR_EQ("crumb: 15", event->breadcrumbs[15].name);
ASSERT_STR_EQ("crumb: 16", event->breadcrumbs[16].name);
+ free(event->breadcrumbs);
free(event);
PASS();
}
diff --git a/bugsnag-plugin-android-ndk/src/test/cpp/test_utils_serialize.c b/bugsnag-plugin-android-ndk/src/test/cpp/test_utils_serialize.c
index 15953ecd34..cd1835580d 100644
--- a/bugsnag-plugin-android-ndk/src/test/cpp/test_utils_serialize.c
+++ b/bugsnag-plugin-android-ndk/src/test/cpp/test_utils_serialize.c
@@ -87,6 +87,9 @@ void generate_basic_report(bugsnag_event *event) {
bugsnag_event *bsg_generate_event(void) {
bugsnag_event *report = calloc(1, sizeof(bugsnag_event));
+ report->max_crumb_count = 50;
+ report->breadcrumbs =
+ calloc(report->max_crumb_count, sizeof(bugsnag_breadcrumb));
strcpy(report->grouping_hash, "foo-hash");
strcpy(report->api_key, "5d1e5fbd39a74caa1200142706a90b20");
strcpy(report->context, "SomeActivity");
@@ -177,6 +180,7 @@ TEST test_report_to_file(void) {
strcpy(env->report_header.os_build, "macOS Sierra");
strcpy(env->next_event_path, SERIALIZE_TEST_FILE);
ASSERT(bsg_serialize_event_to_file(env));
+ free(report->breadcrumbs);
free(report);
free(env);
PASS();
@@ -193,6 +197,7 @@ TEST test_report_with_feature_flags_to_file(void) {
strcpy(env->report_header.os_build, "macOS Sierra");
strcpy(env->next_event_path, SERIALIZE_TEST_FILE);
ASSERT(bsg_serialize_event_to_file(env));
+ free(report->breadcrumbs);
free(report);
free(env);
PASS();
@@ -212,6 +217,7 @@ TEST test_file_to_report(void) {
ASSERT(report != NULL);
ASSERT(strcmp("SIGBUS", report->error.errorClass) == 0);
ASSERT(strcmp("POSIX is serious about oncoming traffic", report->error.errorMessage) == 0);
+ free(generated_report->breadcrumbs);
free(generated_report);
free(env);
free(report);
@@ -234,6 +240,7 @@ TEST test_report_with_feature_flags_from_file(void) {
ASSERT_EQ(2, event->feature_flag_count);
+ free(report->breadcrumbs);
free(report);
free(env);
free(event);
@@ -259,6 +266,7 @@ TEST test_report_with_opaque_metadata_from_file(void) {
ASSERT_EQ(BSG_METADATA_OPAQUE_VALUE, bugsnag_event_has_metadata(event, "opaque", "map"));
ASSERT_EQ(BSG_METADATA_OPAQUE_VALUE, bugsnag_event_has_metadata(event, "opaque", "list"));
+ free(report->breadcrumbs);
free(report);
free(env);
free(event);
@@ -271,6 +279,7 @@ JSON_Value *bsg_generate_json(void) {
char *json = bsg_serialize_event_to_json_string(event);
JSON_Value *root_value = json_parse_string(json);
free(json);
+ free(event->breadcrumbs);
free(event);
return root_value;
}
diff --git a/features/fixtures/mazerunner/cxx-scenarios/src/main/cpp/cxx-scenarios.cpp b/features/fixtures/mazerunner/cxx-scenarios/src/main/cpp/cxx-scenarios.cpp
index c6fe958d2e..e188aa0864 100644
--- a/features/fixtures/mazerunner/cxx-scenarios/src/main/cpp/cxx-scenarios.cpp
+++ b/features/fixtures/mazerunner/cxx-scenarios/src/main/cpp/cxx-scenarios.cpp
@@ -274,5 +274,10 @@ Java_com_bugsnag_android_mazerunner_scenarios_UnhandledNdkAutoNotifyFalseScenari
abort();
}
+JNIEXPORT void JNICALL
+Java_com_bugsnag_android_mazerunner_scenarios_CXXMaxBreadcrumbCrashScenario_activate(JNIEnv *env,
+ jobject thiz) {
+ abort();
}
+}
\ No newline at end of file
diff --git a/features/fixtures/mazerunner/cxx-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/CXXMaxBreadcrumbCrashScenario.kt b/features/fixtures/mazerunner/cxx-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/CXXMaxBreadcrumbCrashScenario.kt
new file mode 100644
index 0000000000..9dffb9ea54
--- /dev/null
+++ b/features/fixtures/mazerunner/cxx-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/CXXMaxBreadcrumbCrashScenario.kt
@@ -0,0 +1,28 @@
+package com.bugsnag.android.mazerunner.scenarios
+
+import android.content.Context
+import com.bugsnag.android.Bugsnag
+import com.bugsnag.android.Configuration
+
+private const val MAX_BREADCRUMB_COUNT = 500
+
+class CXXMaxBreadcrumbCrashScenario(
+ config: Configuration,
+ context: Context,
+ eventMetadata: String?
+) : Scenario(config, context, eventMetadata) {
+ init {
+ config.maxBreadcrumbs = MAX_BREADCRUMB_COUNT
+ }
+
+ external fun activate()
+
+ override fun startScenario() {
+ super.startScenario()
+ repeat(config.maxBreadcrumbs) { index ->
+ Bugsnag.leaveBreadcrumb("this is breadcrumb $index")
+ }
+
+ activate()
+ }
+}
diff --git a/features/full_tests/native_breadcrumbs.feature b/features/full_tests/native_breadcrumbs.feature
index 14388f3a3d..3775e0cfe1 100644
--- a/features/full_tests/native_breadcrumbs.feature
+++ b/features/full_tests/native_breadcrumbs.feature
@@ -23,3 +23,11 @@ Feature: Native Breadcrumbs API
And the event "severity" equals "warning"
And the event has a "process" breadcrumb named "Rerun field analysis"
And the event "unhandled" is false
+
+ Scenario: Leaving the maximum number of native breadcrumbs
+ When I run "CXXMaxBreadcrumbCrashScenario" and relaunch the crashed app
+ And I configure Bugsnag for "CXXMaxBreadcrumbCrashScenario"
+ And I wait to receive an error
+ And the error payload contains a completed handled native report
+ And the event "unhandled" is true
+ And the event has 500 breadcrumbs
\ No newline at end of file
From 198036f499cbd7ab91835d6d4ad41a136f9b9240 Mon Sep 17 00:00:00 2001
From: jason
Date: Wed, 24 Apr 2024 08:08:08 +0100
Subject: [PATCH 07/23] fix(OpaqueValue): OpaqueValue should not redact keys
when marshalling values
---
.../src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
index 426b5baf83..585fa38ae2 100644
--- a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
+++ b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
@@ -28,7 +28,7 @@ internal class OpaqueValue(val json: String) {
private fun encode(value: Any): String {
val writer = StringWriter()
- writer.use { JsonStream(it).value(value, true) }
+ writer.use { JsonStream(it).value(value, false) }
return writer.toString()
}
From e20e1be34957420248ba8c6daf19d61027b340f9 Mon Sep 17 00:00:00 2001
From: jason
Date: Wed, 24 Apr 2024 08:10:21 +0100
Subject: [PATCH 08/23] fix(OpaqueValue): set StringWriter initial size to 256
chars since most encoded strings are longer than the default 16 characters
---
.../src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
index 585fa38ae2..35dc0cec5c 100644
--- a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
+++ b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
@@ -10,6 +10,7 @@ internal class OpaqueValue(val json: String) {
companion object {
private const val MAX_NDK_STRING_LENGTH = 64
private const val US_ASCII_MAX_CODEPOINT = 127
+ private const val INITIAL_BUFFER_SIZE = 256
private fun isStringNDKSupported(value: String): Boolean {
// anything over 63 characters is definitely not supported
@@ -27,7 +28,7 @@ internal class OpaqueValue(val json: String) {
}
private fun encode(value: Any): String {
- val writer = StringWriter()
+ val writer = StringWriter(INITIAL_BUFFER_SIZE)
writer.use { JsonStream(it).value(value, false) }
return writer.toString()
}
From 3bf0721ae68b0c6b48351bb16798dc268dd1b7fc Mon Sep 17 00:00:00 2001
From: jason
Date: Wed, 24 Apr 2024 13:36:55 +0100
Subject: [PATCH 09/23] chore(build): removed the instrumented test GitHub
workflow pending a ticket to migrate them
---
.github/workflows/build.yml | 49 -------------------
.../com/bugsnag/android/MemoryTrimTest.java | 3 ++
2 files changed, 3 insertions(+), 49 deletions(-)
delete mode 100644 .github/workflows/build.yml
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index 966414a05a..0000000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-name: instrumentation_tests
-
-on: [ push, pull_request ]
-
-env:
- GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.parallel=true"
- ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL: 60
-
-jobs:
- android:
- runs-on: macos-latest
-
- strategy:
- fail-fast: true
- matrix:
- api-level:
- - 29
-
- steps:
- - uses: actions/checkout@v3
- with:
- submodules: recursive
- - uses: gradle/wrapper-validation-action@v1
-
- - uses: actions/setup-java@v3
- with:
- distribution: 'zulu'
- java-version: 11
-
- - name: Gradle cache
- uses: gradle/gradle-build-action@v2
-
- - name: Create AVD and generate snapshot for caching
- if: steps.avd-cache.outputs.cache-hit != 'true'
- uses: reactivecircus/android-emulator-runner@v2
- with:
- api-level: ${{ matrix.api-level }}
- force-avd-creation: false
- emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
- disable-animations: false
- script: echo "Generated AVD snapshot for caching."
-
- - name: Run Tests
- uses: reactivecircus/android-emulator-runner@v2
- with:
- api-level: ${{ matrix.api-level }}
- script: ./gradlew connectedCheck -x :bugsnag-benchmarks:connectedCheck
- env:
- API_LEVEL: ${{ matrix.api-level }}
diff --git a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/MemoryTrimTest.java b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/MemoryTrimTest.java
index 8d8799b65d..838f1de2a5 100644
--- a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/MemoryTrimTest.java
+++ b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/MemoryTrimTest.java
@@ -2,6 +2,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,6 +36,7 @@ public class MemoryTrimTest {
@Test
public void onLowMemoryEvent() {
when(context.getApplicationContext()).thenReturn(context);
+ doNothing().when(context).registerComponentCallbacks(any());
Client client = new Client(context, BugsnagTestUtils.generateConfiguration());
// block until observer is registered
From 2c766a6ff51c9e8111d028fe8c4a84da8db0ce22 Mon Sep 17 00:00:00 2001
From: jason
Date: Thu, 25 Apr 2024 13:34:28 +0100
Subject: [PATCH 10/23] fix(ndk): iterate over the `Map.entrySet` directly
instead of copying the keys to an `ArrayList` when copying the metadata into
the NDK structures
---
CHANGELOG.md | 4 +-
bugsnag-plugin-android-ndk/proguard-rules.pro | 1 +
.../com/bugsnag/android/ndk/NativeBridge.kt | 9 +--
.../com/bugsnag/android/ndk/OpaqueValue.kt | 7 +-
.../src/main/jni/jni_cache.c | 2 +
.../src/main/jni/jni_cache.h | 1 +
.../src/main/jni/metadata.c | 76 +++++++++++--------
7 files changed, 55 insertions(+), 45 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6984e8da91..44c8f5d355 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,9 @@
### Enhancements
* `Configuration.maxBreadcrumbs` is now obeyed by `bugsnag-plugin-android-ndk`, so native events will include the correct number of breadcrumbs
- []()
+ [#2020](https://github.com/bugsnag/bugsnag-android/pull/2020)
+* `bugsnag-plugin-android-ndk` will no longer create an `ArrayList` copy of metadata keys when leaving breadcrumbs
+ [#2022](https://github.com/bugsnag/bugsnag-android/pull/2022)
## 6.4.0 (2024-04-15)
diff --git a/bugsnag-plugin-android-ndk/proguard-rules.pro b/bugsnag-plugin-android-ndk/proguard-rules.pro
index 3fa50bcdb9..eb3469de29 100644
--- a/bugsnag-plugin-android-ndk/proguard-rules.pro
+++ b/bugsnag-plugin-android-ndk/proguard-rules.pro
@@ -1,6 +1,7 @@
-keepattributes LineNumberTable,SourceFile
-keep class com.bugsnag.android.ndk.OpaqueValue {
java.lang.String getJson();
+ static java.lang.Object makeSafe(java.lang.Object);
}
-keep class com.bugsnag.android.ndk.NativeBridge { *; }
-keep class com.bugsnag.android.NdkPlugin { *; }
diff --git a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
index 9ff476b93a..33115f355c 100644
--- a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
+++ b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/NativeBridge.kt
@@ -121,7 +121,7 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
event.message,
event.type.toNativeValue(),
event.timestamp,
- makeSafeMetadata(event.metadata)
+ event.metadata
)
NotifyHandled -> addHandledEvent()
@@ -172,13 +172,6 @@ class NativeBridge(private val bgTaskService: BackgroundTaskService) : StateObse
}
}
- private fun makeSafeMetadata(metadata: Map): Map {
- if (metadata.isEmpty()) return metadata
- return object : Map by metadata {
- override fun get(key: String): Any? = OpaqueValue.makeSafe(metadata[key])
- }
- }
-
private fun isInvalidMessage(msg: Any?): Boolean {
if (msg == null || msg !is StateEvent) {
return true
diff --git a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
index 35dc0cec5c..c4383c1655 100644
--- a/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
+++ b/bugsnag-plugin-android-ndk/src/main/java/com/bugsnag/android/ndk/OpaqueValue.kt
@@ -7,12 +7,12 @@ import java.io.StringWriter
* Marker class for values that are `BSG_METADATA_OPAQUE_VALUE` in the C layer
*/
internal class OpaqueValue(val json: String) {
- companion object {
+ internal companion object {
private const val MAX_NDK_STRING_LENGTH = 64
private const val US_ASCII_MAX_CODEPOINT = 127
private const val INITIAL_BUFFER_SIZE = 256
- private fun isStringNDKSupported(value: String): Boolean {
+ fun isStringNDKSupported(value: String): Boolean {
// anything over 63 characters is definitely not supported
if (value.length >= MAX_NDK_STRING_LENGTH) return false
@@ -27,7 +27,7 @@ internal class OpaqueValue(val json: String) {
return value.toByteArray().size < MAX_NDK_STRING_LENGTH
}
- private fun encode(value: Any): String {
+ fun encode(value: Any): String {
val writer = StringWriter(INITIAL_BUFFER_SIZE)
writer.use { JsonStream(it).value(value, false) }
return writer.toString()
@@ -38,6 +38,7 @@ internal class OpaqueValue(val json: String) {
* is both a compatible type and fits into the available space. This method can return
* any one of: `Boolean`, `Number`, `String`, `OpaqueValue` or `null`.
*/
+ @JvmStatic
fun makeSafe(value: Any?): Any? = when {
value is Boolean -> value
value is Number -> value
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/jni_cache.c b/bugsnag-plugin-android-ndk/src/main/jni/jni_cache.c
index 21b60ec282..b5909e885d 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/jni_cache.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/jni_cache.c
@@ -218,6 +218,8 @@ bool bsg_jni_cache_init(JNIEnv *env) {
CACHE_CLASS(OpaqueValue, "com/bugsnag/android/ndk/OpaqueValue");
CACHE_METHOD(OpaqueValue, OpaqueValue_getJson, "getJson",
"()Ljava/lang/String;");
+ CACHE_STATIC_METHOD(OpaqueValue, OpaqueValue_makeSafe, "makeSafe",
+ "(Ljava/lang/Object;)Ljava/lang/Object;");
CACHE_ENUM_CONSTANT(ErrorType_C, "com/bugsnag/android/ErrorType", "C");
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/jni_cache.h b/bugsnag-plugin-android-ndk/src/main/jni/jni_cache.h
index 9b3200379a..44253a390c 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/jni_cache.h
+++ b/bugsnag-plugin-android-ndk/src/main/jni/jni_cache.h
@@ -89,6 +89,7 @@ typedef struct {
jclass OpaqueValue;
jmethodID OpaqueValue_getJson;
+ jmethodID OpaqueValue_makeSafe;
jobject ErrorType_C;
} bsg_jni_cache_t;
diff --git a/bugsnag-plugin-android-ndk/src/main/jni/metadata.c b/bugsnag-plugin-android-ndk/src/main/jni/metadata.c
index 818e0f7866..f466322c9a 100644
--- a/bugsnag-plugin-android-ndk/src/main/jni/metadata.c
+++ b/bugsnag-plugin-android-ndk/src/main/jni/metadata.c
@@ -343,24 +343,29 @@ static void populate_metadata_value(JNIEnv *env, bugsnag_metadata *dst,
return;
}
- if (bsg_safe_is_instance_of(env, _value, bsg_jni_cache->number)) {
+ jobject safe_value = bsg_safe_call_static_object_method(
+ env, bsg_jni_cache->OpaqueValue, bsg_jni_cache->OpaqueValue_makeSafe,
+ _value);
+
+ if (bsg_safe_is_instance_of(env, safe_value, bsg_jni_cache->number)) {
// add a double metadata value
double value = bsg_safe_call_double_method(
- env, _value, bsg_jni_cache->number_double_value);
+ env, safe_value, bsg_jni_cache->number_double_value);
bsg_add_metadata_value_double(dst, section, name, value);
- } else if (bsg_safe_is_instance_of(env, _value, bsg_jni_cache->Boolean)) {
+ } else if (bsg_safe_is_instance_of(env, safe_value, bsg_jni_cache->Boolean)) {
// add a boolean metadata value
bool value = bsg_safe_call_boolean_method(
- env, _value, bsg_jni_cache->Boolean_booleanValue);
+ env, safe_value, bsg_jni_cache->Boolean_booleanValue);
bsg_add_metadata_value_bool(dst, section, name, value);
- } else if (bsg_safe_is_instance_of(env, _value, bsg_jni_cache->String)) {
- const char *value = bsg_safe_get_string_utf_chars(env, _value);
+ } else if (bsg_safe_is_instance_of(env, safe_value, bsg_jni_cache->String)) {
+ const char *value = bsg_safe_get_string_utf_chars(env, safe_value);
if (value != NULL) {
bsg_add_metadata_value_str(dst, section, name, value);
}
- } else if (bsg_safe_is_instance_of(env, _value, bsg_jni_cache->OpaqueValue)) {
+ } else if (bsg_safe_is_instance_of(env, safe_value,
+ bsg_jni_cache->OpaqueValue)) {
jstring _json = bsg_safe_call_object_method(
- env, _value, bsg_jni_cache->OpaqueValue_getJson);
+ env, safe_value, bsg_jni_cache->OpaqueValue_getJson);
const char *json = bsg_safe_get_string_utf_chars(env, _json);
if (json != NULL) {
@@ -513,8 +518,8 @@ void bsg_populate_metadata(JNIEnv *env, bugsnag_metadata *dst,
void bsg_populate_crumb_metadata(JNIEnv *env, bugsnag_breadcrumb *crumb,
jobject metadata) {
- jobject keyset = NULL;
- jobject keylist = NULL;
+ jobject entryset = NULL;
+ jobject entries = NULL;
if (metadata == NULL) {
goto exit;
@@ -526,43 +531,48 @@ void bsg_populate_crumb_metadata(JNIEnv *env, bugsnag_breadcrumb *crumb,
// get size of metadata map
jint map_size =
bsg_safe_call_int_method(env, metadata, bsg_jni_cache->Map_size);
- if (map_size == -1) {
+ if (map_size <= 0) {
goto exit;
}
// create a list of metadata keys
- keyset =
- bsg_safe_call_object_method(env, metadata, bsg_jni_cache->Map_keySet);
- if (keyset == NULL) {
+ entryset =
+ bsg_safe_call_object_method(env, metadata, bsg_jni_cache->Map_entrySet);
+ if (entryset == NULL) {
goto exit;
}
- keylist = bsg_safe_new_object(env, bsg_jni_cache->ArrayList,
- bsg_jni_cache->ArrayList_constructor_collection,
- keyset);
- if (keylist == NULL) {
+ entries =
+ bsg_safe_call_object_method(env, entryset, bsg_jni_cache->Set_iterator);
+ if (entries == NULL) {
goto exit;
}
- for (int i = 0; i < map_size; i++) {
- jstring _key = bsg_safe_call_object_method(
- env, keylist, bsg_jni_cache->ArrayList_get, (jint)i);
- jobject _value = bsg_safe_call_object_method(env, metadata,
- bsg_jni_cache->Map_get, _key);
-
- if (_key != NULL && _value != NULL) {
- const char *key = bsg_safe_get_string_utf_chars(env, _key);
- if (key != NULL) {
- populate_metadata_value(env, &crumb->metadata, "metaData", key, _value);
- bsg_safe_release_string_utf_chars(env, _key, key);
+ while (bsg_safe_call_boolean_method(env, entries,
+ bsg_jni_cache->Iterator_hasNext)) {
+ (*env)->PushLocalFrame(env, 3);
+ {
+ jobject entry = bsg_safe_call_object_method(env, entries,
+ bsg_jni_cache->Iterator_next);
+ jstring _key = bsg_safe_call_object_method(
+ env, entry, bsg_jni_cache->MapEntry_getKey);
+ jobject _value = bsg_safe_call_object_method(
+ env, entry, bsg_jni_cache->MapEntry_getValue);
+
+ if (_key != NULL && _value != NULL) {
+ const char *key = bsg_safe_get_string_utf_chars(env, _key);
+ if (key != NULL) {
+ populate_metadata_value(env, &crumb->metadata, "metaData", key,
+ _value);
+ bsg_safe_release_string_utf_chars(env, _key, key);
+ }
}
}
- bsg_safe_delete_local_ref(env, _key);
- bsg_safe_delete_local_ref(env, _value);
+ (*env)->PopLocalFrame(env, NULL);
}
exit:
- bsg_safe_delete_local_ref(env, keyset);
- bsg_safe_delete_local_ref(env, keylist);
+ bsg_safe_delete_local_ref(env, entries);
+ bsg_safe_delete_local_ref(env, entryset);
}
void bsg_populate_event(JNIEnv *env, bugsnag_event *event) {
From 278a11cb951ec168ceed54e5fd98bafc2cce14ac Mon Sep 17 00:00:00 2001
From: YYChen01988
Date: Thu, 18 Apr 2024 05:49:11 +0100
Subject: [PATCH 11/23] fix(gradle) convert build.gradle (bugsnag-android) to
KTS
---
build.gradle | 35 -------------------
build.gradle.kts | 34 ++++++++++++++++++
.../sdk-app-example/app/proguard-rules.pro | 2 +-
features/fixtures/minimalapp/build.gradle | 2 +-
4 files changed, 36 insertions(+), 37 deletions(-)
delete mode 100644 build.gradle
create mode 100644 build.gradle.kts
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index c6dcc94a09..0000000000
--- a/build.gradle
+++ /dev/null
@@ -1,35 +0,0 @@
-import com.bugsnag.android.Versions
-
-buildscript {
- repositories {
- google()
- mavenCentral()
- maven { url "https://plugins.gradle.org/m2/" }
- }
- dependencies {
- classpath "com.android.tools.build:gradle:${Versions.androidGradlePlugin}"
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}"
- classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:${Versions.detektPlugin}"
- classpath "org.jetbrains.dokka:dokka-gradle-plugin:${Versions.dokkaPlugin}"
- classpath "org.jlleitschuh.gradle:ktlint-gradle:${Versions.ktlintPlugin}"
- classpath "androidx.benchmark:benchmark-gradle-plugin:${Versions.benchmarkPlugin}"
- }
-}
-
-plugins {
- id "com.github.hierynomus.license" version "0.16.1"
- id "org.jetbrains.kotlinx.binary-compatibility-validator" version "0.13.1" apply false
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- }
-
- gradle.projectsEvaluated {
- tasks.withType(JavaCompile) {
- options.compilerArgs << "-Xlint:all" << "-Werror"
- }
- }
-}
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000000..5ff26b96fc
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,34 @@
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ maven(url = "https://plugins.gradle.org/m2/")
+ }
+ dependencies {
+ classpath("com.android.tools.build:gradle:7.0.4")
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10")
+ classpath("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.1")
+ classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0")
+ classpath("org.jlleitschuh.gradle:ktlint-gradle:10.2.0")
+ classpath("androidx.benchmark:benchmark-gradle-plugin:1.1.1")
+ }
+}
+
+plugins {
+ id("com.github.hierynomus.license") version "0.16.1"
+ id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.13.1" apply false
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ gradle.projectsEvaluated {
+ tasks.withType {
+ options.compilerArgs.addAll(listOf("-Xlint:all", "-Werror"))
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/sdk-app-example/app/proguard-rules.pro b/examples/sdk-app-example/app/proguard-rules.pro
index f1b424510d..2f9dc5a47e 100644
--- a/examples/sdk-app-example/app/proguard-rules.pro
+++ b/examples/sdk-app-example/app/proguard-rules.pro
@@ -1,6 +1,6 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
+# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
diff --git a/features/fixtures/minimalapp/build.gradle b/features/fixtures/minimalapp/build.gradle
index e294e05703..e9c0e57f3d 100644
--- a/features/fixtures/minimalapp/build.gradle
+++ b/features/fixtures/minimalapp/build.gradle
@@ -13,7 +13,7 @@ buildscript {
classpath "com.bugsnag:bugsnag-android-gradle-plugin:7.1.0"
}
// NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ // in the individual module build.gradle.kts files
}
}
From c62096a0a9fc1009744247a9785738b099cda1c8 Mon Sep 17 00:00:00 2001
From: YYChen01988
Date: Thu, 18 Apr 2024 08:45:48 +0100
Subject: [PATCH 12/23] fix(gradle) convert build.gradle
(bugsnag--plugin-react-native) to KTS
---
bugsnag-plugin-react-native/build.gradle | 9 ---------
bugsnag-plugin-react-native/build.gradle.kts | 9 +++++++++
2 files changed, 9 insertions(+), 9 deletions(-)
delete mode 100644 bugsnag-plugin-react-native/build.gradle
create mode 100644 bugsnag-plugin-react-native/build.gradle.kts
diff --git a/bugsnag-plugin-react-native/build.gradle b/bugsnag-plugin-react-native/build.gradle
deleted file mode 100644
index 345adbf3ae..0000000000
--- a/bugsnag-plugin-react-native/build.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-plugins {
- id("bugsnag-build-plugin")
-}
-
-apply plugin: "com.android.library"
-
-dependencies {
- api(project(":bugsnag-android-core"))
-}
diff --git a/bugsnag-plugin-react-native/build.gradle.kts b/bugsnag-plugin-react-native/build.gradle.kts
new file mode 100644
index 0000000000..c9d46adb15
--- /dev/null
+++ b/bugsnag-plugin-react-native/build.gradle.kts
@@ -0,0 +1,9 @@
+plugins {
+ id("bugsnag-build-plugin")
+}
+
+apply(plugin = "com.android.library")
+
+dependencies {
+ add("api", project(":bugsnag-android-core"))
+}
\ No newline at end of file
From 5a4b101fa4c4deb5a915c54656ce6f4f7807915e Mon Sep 17 00:00:00 2001
From: YYChen01988
Date: Thu, 18 Apr 2024 08:49:48 +0100
Subject: [PATCH 13/23] fix(gradle) convert build.gradle (bugsnag-android) to
KTS
---
bugsnag-android/build.gradle | 15 ---------------
bugsnag-android/build.gradle.kts | 15 +++++++++++++++
2 files changed, 15 insertions(+), 15 deletions(-)
delete mode 100644 bugsnag-android/build.gradle
create mode 100644 bugsnag-android/build.gradle.kts
diff --git a/bugsnag-android/build.gradle b/bugsnag-android/build.gradle
deleted file mode 100644
index 3b6d9059b7..0000000000
--- a/bugsnag-android/build.gradle
+++ /dev/null
@@ -1,15 +0,0 @@
-plugins {
- id("bugsnag-build-plugin")
-}
-
-bugsnagBuildOptions {
- compilesCode = false
-}
-
-apply plugin: "com.android.library"
-
-dependencies {
- api(project(":bugsnag-android-core"))
- api(project(":bugsnag-plugin-android-anr"))
- api(project(":bugsnag-plugin-android-ndk"))
-}
diff --git a/bugsnag-android/build.gradle.kts b/bugsnag-android/build.gradle.kts
new file mode 100644
index 0000000000..3f1ecbf8fb
--- /dev/null
+++ b/bugsnag-android/build.gradle.kts
@@ -0,0 +1,15 @@
+plugins {
+ id("bugsnag-build-plugin")
+}
+
+bugsnagBuildOptions {
+ compilesCode = false
+}
+
+apply(plugin = "com.android.library")
+
+dependencies {
+ add("api", project(":bugsnag-android-core"))
+ add("api", project(":bugsnag-plugin-android-anr"))
+ add("api", project(":bugsnag-plugin-android-ndk"))
+}
From a8b4fec6b6f04944dffd83b572e93f8bd83397ee Mon Sep 17 00:00:00 2001
From: YYChen01988
Date: Thu, 18 Apr 2024 09:07:53 +0100
Subject: [PATCH 14/23] fix(gradle) convert build.gradle
(bugsnag-plugin-android-anr) to KTS
---
bugsnag-plugin-android-anr/build.gradle | 15 ---------------
bugsnag-plugin-android-anr/build.gradle.kts | 14 ++++++++++++++
2 files changed, 14 insertions(+), 15 deletions(-)
delete mode 100644 bugsnag-plugin-android-anr/build.gradle
create mode 100644 bugsnag-plugin-android-anr/build.gradle.kts
diff --git a/bugsnag-plugin-android-anr/build.gradle b/bugsnag-plugin-android-anr/build.gradle
deleted file mode 100644
index 169d3dd223..0000000000
--- a/bugsnag-plugin-android-anr/build.gradle
+++ /dev/null
@@ -1,15 +0,0 @@
-plugins {
- id("bugsnag-build-plugin")
-}
-
-bugsnagBuildOptions {
- usesNdk = true
-}
-
-apply plugin: "com.android.library"
-
-dependencies {
- api(project(":bugsnag-android-core"))
-}
-
-apply from: "../gradle/kotlin.gradle"
diff --git a/bugsnag-plugin-android-anr/build.gradle.kts b/bugsnag-plugin-android-anr/build.gradle.kts
new file mode 100644
index 0000000000..221a66cdaa
--- /dev/null
+++ b/bugsnag-plugin-android-anr/build.gradle.kts
@@ -0,0 +1,14 @@
+plugins {
+ id("bugsnag-build-plugin")
+}
+
+bugsnagBuildOptions {
+ usesNdk = true
+}
+
+apply(plugin = "com.android.library")
+
+dependencies {
+ add("api", project(":bugsnag-android-core"))
+}
+apply(from = "../gradle/kotlin.gradle")
From bb20f34d847dc34f5e2282794995b45016b99313 Mon Sep 17 00:00:00 2001
From: YYChen01988
Date: Thu, 18 Apr 2024 09:59:19 +0100
Subject: [PATCH 15/23] fix(gradle) convert build.gradle
(bugsnag-plugin-android-okhttp) to KTS
---
bugsnag-plugin-android-okhttp/build.gradle | 21 -------------------
.../build.gradle.kts | 21 +++++++++++++++++++
bugsnag-plugin-react-native/build.gradle.kts | 2 +-
3 files changed, 22 insertions(+), 22 deletions(-)
delete mode 100644 bugsnag-plugin-android-okhttp/build.gradle
create mode 100644 bugsnag-plugin-android-okhttp/build.gradle.kts
diff --git a/bugsnag-plugin-android-okhttp/build.gradle b/bugsnag-plugin-android-okhttp/build.gradle
deleted file mode 100644
index 0cee87d116..0000000000
--- a/bugsnag-plugin-android-okhttp/build.gradle
+++ /dev/null
@@ -1,21 +0,0 @@
-plugins {
- id("bugsnag-build-plugin")
-}
-
-apply plugin: "com.android.library"
-
-dependencies {
- api(project(":bugsnag-android-core"))
-}
-
-apply from: "../gradle/kotlin.gradle"
-
-dependencies {
- compileOnly("com.squareup.okhttp3:okhttp:4.9.1") {
- exclude group: "org.jetbrains.kotlin"
- }
-
- testImplementation("com.squareup.okhttp3:mockwebserver:4.9.1") {
- exclude group: "org.jetbrains.kotlin"
- }
-}
diff --git a/bugsnag-plugin-android-okhttp/build.gradle.kts b/bugsnag-plugin-android-okhttp/build.gradle.kts
new file mode 100644
index 0000000000..9944bae668
--- /dev/null
+++ b/bugsnag-plugin-android-okhttp/build.gradle.kts
@@ -0,0 +1,21 @@
+plugins {
+ id("bugsnag-build-plugin")
+}
+
+apply(plugin = "com.android.library")
+
+dependencies {
+ add("api", project(":bugsnag-android-core"))
+}
+
+apply(from = "../gradle/kotlin.gradle")
+
+dependencies {
+ "compileOnly"("com.squareup.okhttp3:okhttp:4.9.1") {
+ exclude(group = "org.jetbrains.kotlin")
+ }
+
+ "testImplementation"("com.squareup.okhttp3:mockwebserver:4.9.1") {
+ exclude(group = "org.jetbrains.kotlin")
+ }
+}
diff --git a/bugsnag-plugin-react-native/build.gradle.kts b/bugsnag-plugin-react-native/build.gradle.kts
index c9d46adb15..9c2c33a38e 100644
--- a/bugsnag-plugin-react-native/build.gradle.kts
+++ b/bugsnag-plugin-react-native/build.gradle.kts
@@ -6,4 +6,4 @@ apply(plugin = "com.android.library")
dependencies {
add("api", project(":bugsnag-android-core"))
-}
\ No newline at end of file
+}
From ad6716b2d2b0db759d29b53d9bfde247a9ab7a2e Mon Sep 17 00:00:00 2001
From: jason
Date: Tue, 30 Apr 2024 16:16:47 +0100
Subject: [PATCH 16/23] chore(build): all primary build scripts -> kotlin
---
.../{build.gradle => build.gradle.kts} | 21 +++++-----
bugsnag-android/build.gradle.kts | 3 +-
bugsnag-plugin-android-anr/build.gradle.kts | 4 +-
bugsnag-plugin-android-exitinfo/build.gradle | 39 -------------------
.../build.gradle.kts | 38 ++++++++++++++++++
.../{build.gradle => build.gradle.kts} | 9 ++---
.../build.gradle.kts | 11 ++----
bugsnag-plugin-react-native/build.gradle.kts | 3 +-
build.gradle.kts | 2 +-
settings.gradle => settings.gradle.kts | 2 +-
10 files changed, 63 insertions(+), 69 deletions(-)
rename bugsnag-android-core/{build.gradle => build.gradle.kts} (50%)
delete mode 100644 bugsnag-plugin-android-exitinfo/build.gradle
create mode 100644 bugsnag-plugin-android-exitinfo/build.gradle.kts
rename bugsnag-plugin-android-ndk/{build.gradle => build.gradle.kts} (67%)
rename settings.gradle => settings.gradle.kts (90%)
diff --git a/bugsnag-android-core/build.gradle b/bugsnag-android-core/build.gradle.kts
similarity index 50%
rename from bugsnag-android-core/build.gradle
rename to bugsnag-android-core/build.gradle.kts
index e1d57b95f7..db30980a95 100644
--- a/bugsnag-android-core/build.gradle
+++ b/bugsnag-android-core/build.gradle.kts
@@ -1,15 +1,17 @@
+import org.jetbrains.dokka.gradle.DokkaTask
+
plugins {
id("bugsnag-build-plugin")
+ id("com.android.library")
}
bugsnagBuildOptions {
usesNdk = true
}
-apply plugin: "com.android.library"
-apply plugin: "org.jetbrains.dokka"
+apply(plugin = "org.jetbrains.dokka")
-dokkaHtml.configure {
+tasks.getByName("dokkaHtml") {
dokkaSourceSets {
named("main") {
noAndroidSdkLink.set(false)
@@ -21,19 +23,18 @@ dokkaHtml.configure {
}
}
-apply from: "../gradle/kotlin.gradle"
+apply(from = "../gradle/kotlin.gradle")
// pick up dsl-json by adding to the default sourcesets
android {
sourceSets {
- main {
- java.srcDirs += ["dsl-json/library/src/main/java"]
+ named("main") {
+ java.srcDirs("dsl-json/library/src/main/java")
}
- test {
- java.srcDirs += ["dsl-json/library/src/test/java"]
+ named("test") {
+ java.srcDirs("dsl-json/library/src/test/java")
}
}
}
-apiValidation.ignoredPackages += ["com.bugsnag.android.repackaged.dslplatform.json"]
-
+apiValidation.ignoredPackages.add("com.bugsnag.android.repackaged.dslplatform.json")
diff --git a/bugsnag-android/build.gradle.kts b/bugsnag-android/build.gradle.kts
index 3f1ecbf8fb..bd5c5a6162 100644
--- a/bugsnag-android/build.gradle.kts
+++ b/bugsnag-android/build.gradle.kts
@@ -1,13 +1,12 @@
plugins {
id("bugsnag-build-plugin")
+ id("com.android.library")
}
bugsnagBuildOptions {
compilesCode = false
}
-apply(plugin = "com.android.library")
-
dependencies {
add("api", project(":bugsnag-android-core"))
add("api", project(":bugsnag-plugin-android-anr"))
diff --git a/bugsnag-plugin-android-anr/build.gradle.kts b/bugsnag-plugin-android-anr/build.gradle.kts
index 221a66cdaa..1574b67ef9 100644
--- a/bugsnag-plugin-android-anr/build.gradle.kts
+++ b/bugsnag-plugin-android-anr/build.gradle.kts
@@ -1,14 +1,14 @@
plugins {
id("bugsnag-build-plugin")
+ id("com.android.library")
}
bugsnagBuildOptions {
usesNdk = true
}
-apply(plugin = "com.android.library")
-
dependencies {
add("api", project(":bugsnag-android-core"))
}
+
apply(from = "../gradle/kotlin.gradle")
diff --git a/bugsnag-plugin-android-exitinfo/build.gradle b/bugsnag-plugin-android-exitinfo/build.gradle
deleted file mode 100644
index 835102de1c..0000000000
--- a/bugsnag-plugin-android-exitinfo/build.gradle
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("bugsnag-build-plugin")
- id "com.google.protobuf" version "0.9.4"
-}
-
-apply plugin: "com.android.library"
-
-dependencies {
- api(project(":bugsnag-android-core"))
-}
-
-apply from: "../gradle/kotlin.gradle"
-
-dependencies {
- implementation 'com.google.protobuf:protobuf-javalite:3.24.2'
-}
-
-android.libraryVariants.configureEach { variant ->
- variant.processJavaResourcesProvider.configure {
- exclude('**/*.proto')
- }
-}
-
-protobuf {
- protoc {
- artifact = 'com.google.protobuf:protoc:3.24.2'
- }
- generateProtoTasks {
- all().configureEach { task ->
- task.builtins {
- java {
- option "lite"
- }
- }
- }
- }
-}
-
-apiValidation.ignoredPackages += ["com.bugsnag.android.repackaged.server.os"]
diff --git a/bugsnag-plugin-android-exitinfo/build.gradle.kts b/bugsnag-plugin-android-exitinfo/build.gradle.kts
new file mode 100644
index 0000000000..c0b736c803
--- /dev/null
+++ b/bugsnag-plugin-android-exitinfo/build.gradle.kts
@@ -0,0 +1,38 @@
+plugins {
+ id("bugsnag-build-plugin")
+ id("com.android.library")
+ id("com.google.protobuf") version "0.9.4"
+}
+
+dependencies {
+ api(project(":bugsnag-android-core"))
+}
+
+apply(from = "../gradle/kotlin.gradle")
+
+dependencies {
+ implementation("com.google.protobuf:protobuf-javalite:3.24.2")
+}
+
+android.libraryVariants.configureEach {
+ processJavaResourcesProvider {
+ exclude("**/*.proto")
+ }
+}
+
+protobuf {
+ protoc {
+ artifact = "com.google.protobuf:protoc:3.24.2"
+ }
+ generateProtoTasks {
+ all().configureEach {
+ builtins {
+ create("java") {
+ option("lite")
+ }
+ }
+ }
+ }
+}
+
+apiValidation.ignoredPackages += "com.bugsnag.android.repackaged.server.os"
diff --git a/bugsnag-plugin-android-ndk/build.gradle b/bugsnag-plugin-android-ndk/build.gradle.kts
similarity index 67%
rename from bugsnag-plugin-android-ndk/build.gradle
rename to bugsnag-plugin-android-ndk/build.gradle.kts
index 47dcec3f26..bc576cc3a3 100644
--- a/bugsnag-plugin-android-ndk/build.gradle
+++ b/bugsnag-plugin-android-ndk/build.gradle.kts
@@ -1,5 +1,6 @@
plugins {
id("bugsnag-build-plugin")
+ id("com.android.library")
}
bugsnagBuildOptions {
@@ -7,21 +8,19 @@ bugsnagBuildOptions {
publishesPrefab = "bugsnag-ndk"
}
-apply plugin: "com.android.library"
-
dependencies {
api(project(":bugsnag-android-core"))
}
-apply from: "../gradle/kotlin.gradle"
+apply(from = "../gradle/kotlin.gradle")
afterEvaluate {
- tasks.named("prefabReleasePackage") {
+ tasks.create("prefabReleasePackage") {
doLast {
project.fileTree("build/intermediates/prefab_package/") {
include("**/abi.json")
}.forEach { file ->
- file.text = file.text.replace("c++_static", "none")
+ file.writeText(file.readText().replace("c++_static", "none"))
}
}
}
diff --git a/bugsnag-plugin-android-okhttp/build.gradle.kts b/bugsnag-plugin-android-okhttp/build.gradle.kts
index 9944bae668..e01e18be95 100644
--- a/bugsnag-plugin-android-okhttp/build.gradle.kts
+++ b/bugsnag-plugin-android-okhttp/build.gradle.kts
@@ -1,21 +1,18 @@
plugins {
id("bugsnag-build-plugin")
+ id("com.android.library")
}
-apply(plugin = "com.android.library")
+apply(from = "../gradle/kotlin.gradle")
dependencies {
add("api", project(":bugsnag-android-core"))
-}
-apply(from = "../gradle/kotlin.gradle")
-
-dependencies {
- "compileOnly"("com.squareup.okhttp3:okhttp:4.9.1") {
+ add("compileOnly", "com.squareup.okhttp3:okhttp:4.9.1") {
exclude(group = "org.jetbrains.kotlin")
}
- "testImplementation"("com.squareup.okhttp3:mockwebserver:4.9.1") {
+ add("testImplementation", "com.squareup.okhttp3:mockwebserver:4.9.1") {
exclude(group = "org.jetbrains.kotlin")
}
}
diff --git a/bugsnag-plugin-react-native/build.gradle.kts b/bugsnag-plugin-react-native/build.gradle.kts
index 9c2c33a38e..da00b5fbe3 100644
--- a/bugsnag-plugin-react-native/build.gradle.kts
+++ b/bugsnag-plugin-react-native/build.gradle.kts
@@ -1,9 +1,8 @@
plugins {
id("bugsnag-build-plugin")
+ id("com.android.library")
}
-apply(plugin = "com.android.library")
-
dependencies {
add("api", project(":bugsnag-android-core"))
}
diff --git a/build.gradle.kts b/build.gradle.kts
index 5ff26b96fc..07f756315d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,10 +1,10 @@
-
buildscript {
repositories {
google()
mavenCentral()
maven(url = "https://plugins.gradle.org/m2/")
}
+
dependencies {
classpath("com.android.tools.build:gradle:7.0.4")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10")
diff --git a/settings.gradle b/settings.gradle.kts
similarity index 90%
rename from settings.gradle
rename to settings.gradle.kts
index a561e56bf8..a093eafc48 100644
--- a/settings.gradle
+++ b/settings.gradle.kts
@@ -1,5 +1,5 @@
plugins {
- id "com.gradle.enterprise" version "3.5"
+ id("com.gradle.enterprise") version "3.5"
}
gradleEnterprise {
From 048c0a8759e42e9f08ef540ff256310b7891d25c Mon Sep 17 00:00:00 2001
From: jason
Date: Tue, 30 Apr 2024 17:11:57 +0100
Subject: [PATCH 17/23] chore(build): moved kotlin compiler options into
BugsnagBuildPlugin
---
bugsnag-android-core/build.gradle.kts | 2 --
bugsnag-plugin-android-anr/build.gradle.kts | 2 --
.../build.gradle.kts | 5 -----
bugsnag-plugin-android-ndk/build.gradle.kts | 2 --
bugsnag-plugin-android-okhttp/build.gradle.kts | 2 --
buildSrc/build.gradle.kts | 1 +
.../com/bugsnag/android/BugsnagBuildPlugin.kt | 18 ++++++++++++++++++
.../kotlin/com/bugsnag/android/Versions.kt | 1 +
gradle/kotlin.gradle | 13 -------------
9 files changed, 20 insertions(+), 26 deletions(-)
delete mode 100644 gradle/kotlin.gradle
diff --git a/bugsnag-android-core/build.gradle.kts b/bugsnag-android-core/build.gradle.kts
index db30980a95..6640936437 100644
--- a/bugsnag-android-core/build.gradle.kts
+++ b/bugsnag-android-core/build.gradle.kts
@@ -23,8 +23,6 @@ tasks.getByName("dokkaHtml") {
}
}
-apply(from = "../gradle/kotlin.gradle")
-
// pick up dsl-json by adding to the default sourcesets
android {
sourceSets {
diff --git a/bugsnag-plugin-android-anr/build.gradle.kts b/bugsnag-plugin-android-anr/build.gradle.kts
index 1574b67ef9..12457bd329 100644
--- a/bugsnag-plugin-android-anr/build.gradle.kts
+++ b/bugsnag-plugin-android-anr/build.gradle.kts
@@ -10,5 +10,3 @@ bugsnagBuildOptions {
dependencies {
add("api", project(":bugsnag-android-core"))
}
-
-apply(from = "../gradle/kotlin.gradle")
diff --git a/bugsnag-plugin-android-exitinfo/build.gradle.kts b/bugsnag-plugin-android-exitinfo/build.gradle.kts
index c0b736c803..6a5fdc1b5a 100644
--- a/bugsnag-plugin-android-exitinfo/build.gradle.kts
+++ b/bugsnag-plugin-android-exitinfo/build.gradle.kts
@@ -6,11 +6,6 @@ plugins {
dependencies {
api(project(":bugsnag-android-core"))
-}
-
-apply(from = "../gradle/kotlin.gradle")
-
-dependencies {
implementation("com.google.protobuf:protobuf-javalite:3.24.2")
}
diff --git a/bugsnag-plugin-android-ndk/build.gradle.kts b/bugsnag-plugin-android-ndk/build.gradle.kts
index bc576cc3a3..10104bb5ae 100644
--- a/bugsnag-plugin-android-ndk/build.gradle.kts
+++ b/bugsnag-plugin-android-ndk/build.gradle.kts
@@ -12,8 +12,6 @@ dependencies {
api(project(":bugsnag-android-core"))
}
-apply(from = "../gradle/kotlin.gradle")
-
afterEvaluate {
tasks.create("prefabReleasePackage") {
doLast {
diff --git a/bugsnag-plugin-android-okhttp/build.gradle.kts b/bugsnag-plugin-android-okhttp/build.gradle.kts
index e01e18be95..9bfba99091 100644
--- a/bugsnag-plugin-android-okhttp/build.gradle.kts
+++ b/bugsnag-plugin-android-okhttp/build.gradle.kts
@@ -3,8 +3,6 @@ plugins {
id("com.android.library")
}
-apply(from = "../gradle/kotlin.gradle")
-
dependencies {
add("api", project(":bugsnag-android-core"))
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index f34c558161..901628b472 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -20,4 +20,5 @@ repositories {
dependencies {
compileOnly(gradleApi())
implementation("com.android.tools.build:gradle:7.0.2")
+ implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10")
}
diff --git a/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt b/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
index dc7571d57d..74a3e05ed6 100644
--- a/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
+++ b/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
@@ -6,6 +6,7 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies
+import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import java.io.File
/**
@@ -65,6 +66,8 @@ class BugsnagBuildPlugin : Plugin {
project.apply(from = project.file("../gradle/license-check.gradle"))
if (bugsnag.compilesCode) {
+ project.configureKotlinOptions()
+
project.apply(from = project.file("../gradle/detekt.gradle"))
project.apply(from = project.file("../gradle/checkstyle.gradle"))
}
@@ -175,6 +178,21 @@ class BugsnagBuildPlugin : Plugin {
}
}
+ private fun Project.configureKotlinOptions() {
+ tasks.withType(KotlinCompile::class.java).configureEach {
+ kotlinOptions {
+ allWarningsAsErrors = true
+ apiVersion = Versions.kotlinLang
+ languageVersion = Versions.kotlinLang
+ freeCompilerArgs += listOf(
+ "-Xno-call-assertions",
+ "-Xno-receiver-assertions",
+ "-Xno-param-assertions"
+ )
+ }
+ }
+ }
+
/**
* Configures Android project defaults such as minSdkVersion.
*/
diff --git a/buildSrc/src/main/kotlin/com/bugsnag/android/Versions.kt b/buildSrc/src/main/kotlin/com/bugsnag/android/Versions.kt
index 6e672819bb..ae6b8ecc45 100644
--- a/buildSrc/src/main/kotlin/com/bugsnag/android/Versions.kt
+++ b/buildSrc/src/main/kotlin/com/bugsnag/android/Versions.kt
@@ -12,6 +12,7 @@ object Versions {
val ndk = "23.1.7779620"
val java = JavaVersion.VERSION_1_8
val kotlin = "1.5.10"
+ val kotlinLang = "1.5"
// plugins
val androidGradlePlugin = "7.0.4"
diff --git a/gradle/kotlin.gradle b/gradle/kotlin.gradle
deleted file mode 100644
index 2d3be612fd..0000000000
--- a/gradle/kotlin.gradle
+++ /dev/null
@@ -1,13 +0,0 @@
-android {
- kotlinOptions {
- allWarningsAsErrors = true
- jvmTarget = "1.8"
- apiVersion = "1.5"
- languageVersion = "1.5"
- freeCompilerArgs += [
- '-Xno-call-assertions',
- '-Xno-receiver-assertions',
- '-Xno-param-assertions'
- ]
- }
-}
From 2fca9e08b78bfe342918afcc7214fa4397093cad Mon Sep 17 00:00:00 2001
From: jason
Date: Wed, 1 May 2024 08:47:32 +0100
Subject: [PATCH 18/23] chore(build): moved checkstyle options into
BugsnagBuildPlugin
---
.../com/bugsnag/android/BugsnagBuildPlugin.kt | 3 ++-
.../kotlin/com/bugsnag/android/Checkstyle.kt | 23 +++++++++++++++++++
features/fixtures/mazerunner/app/build.gradle | 2 +-
.../cxx-scenarios-bugsnag/build.gradle | 2 +-
.../mazerunner/cxx-scenarios/build.gradle | 2 +-
.../mazerunner/gradle}/checkstyle.gradle | 0
.../mazerunner/jvm-scenarios/build.gradle | 2 +-
7 files changed, 29 insertions(+), 5 deletions(-)
create mode 100644 buildSrc/src/main/kotlin/com/bugsnag/android/Checkstyle.kt
rename {gradle => features/fixtures/mazerunner/gradle}/checkstyle.gradle (100%)
diff --git a/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt b/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
index 74a3e05ed6..5026b29086 100644
--- a/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
+++ b/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
@@ -67,9 +67,9 @@ class BugsnagBuildPlugin : Plugin {
if (bugsnag.compilesCode) {
project.configureKotlinOptions()
+ project.configureCheckstyle()
project.apply(from = project.file("../gradle/detekt.gradle"))
- project.apply(from = project.file("../gradle/checkstyle.gradle"))
}
}
@@ -215,6 +215,7 @@ class BugsnagBuildPlugin : Plugin {
plugins.apply("com.github.hierynomus.license")
if (bugsnag.compilesCode) {
+ plugins.apply("checkstyle")
plugins.apply("kotlin-android")
plugins.apply("io.gitlab.arturbosch.detekt")
plugins.apply("org.jlleitschuh.gradle.ktlint")
diff --git a/buildSrc/src/main/kotlin/com/bugsnag/android/Checkstyle.kt b/buildSrc/src/main/kotlin/com/bugsnag/android/Checkstyle.kt
new file mode 100644
index 0000000000..989a9ae6ad
--- /dev/null
+++ b/buildSrc/src/main/kotlin/com/bugsnag/android/Checkstyle.kt
@@ -0,0 +1,23 @@
+package com.bugsnag.android
+
+import org.gradle.api.Project
+import org.gradle.api.plugins.quality.Checkstyle
+import org.gradle.api.plugins.quality.CheckstyleExtension
+import org.gradle.kotlin.dsl.get
+
+fun Project.configureCheckstyle() {
+ extensions.getByType(CheckstyleExtension::class.java).apply {
+ toolVersion = "8.18"
+ }
+
+ val checkstyle = tasks.register("checkstyle", Checkstyle::class.java) {
+ configFile = rootProject.file("config/checkstyle/checkstyle.xml")
+ source = fileTree("src/") {
+ include("**/*.java")
+ exclude("**/external/**/*.java")
+ }
+ classpath = files()
+ }
+
+ tasks["check"].dependsOn(checkstyle)
+}
\ No newline at end of file
diff --git a/features/fixtures/mazerunner/app/build.gradle b/features/fixtures/mazerunner/app/build.gradle
index 79eab90a7a..d63778abf7 100644
--- a/features/fixtures/mazerunner/app/build.gradle
+++ b/features/fixtures/mazerunner/app/build.gradle
@@ -94,4 +94,4 @@ dependencies {
apply from: "../bugsnag-dependency.gradle"
apply from: "../../../../gradle/detekt.gradle"
-apply from: "../../../../gradle/checkstyle.gradle"
+apply from: "../gradle/checkstyle.gradle"
diff --git a/features/fixtures/mazerunner/cxx-scenarios-bugsnag/build.gradle b/features/fixtures/mazerunner/cxx-scenarios-bugsnag/build.gradle
index 341504a053..3d2486d0c1 100644
--- a/features/fixtures/mazerunner/cxx-scenarios-bugsnag/build.gradle
+++ b/features/fixtures/mazerunner/cxx-scenarios-bugsnag/build.gradle
@@ -46,4 +46,4 @@ dependencies {
}
apply from: "../../../../gradle/detekt.gradle"
-apply from: "../../../../gradle/checkstyle.gradle"
+apply from: "../gradle/checkstyle.gradle"
diff --git a/features/fixtures/mazerunner/cxx-scenarios/build.gradle b/features/fixtures/mazerunner/cxx-scenarios/build.gradle
index 9e5c2da16b..ca3600068b 100644
--- a/features/fixtures/mazerunner/cxx-scenarios/build.gradle
+++ b/features/fixtures/mazerunner/cxx-scenarios/build.gradle
@@ -43,4 +43,4 @@ dependencies {
apply from: "../bugsnag-dependency.gradle"
apply from: "../../../../gradle/detekt.gradle"
-apply from: "../../../../gradle/checkstyle.gradle"
+apply from: "../gradle/checkstyle.gradle"
diff --git a/gradle/checkstyle.gradle b/features/fixtures/mazerunner/gradle/checkstyle.gradle
similarity index 100%
rename from gradle/checkstyle.gradle
rename to features/fixtures/mazerunner/gradle/checkstyle.gradle
diff --git a/features/fixtures/mazerunner/jvm-scenarios/build.gradle b/features/fixtures/mazerunner/jvm-scenarios/build.gradle
index 411e5d1294..79f5df6035 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/build.gradle
+++ b/features/fixtures/mazerunner/jvm-scenarios/build.gradle
@@ -48,4 +48,4 @@ private boolean useLegacyOkHttp() {
apply from: "../bugsnag-dependency.gradle"
apply from: "../../../../gradle/detekt.gradle"
-apply from: "../../../../gradle/checkstyle.gradle"
+apply from: "../gradle/checkstyle.gradle"
From cfc20061a6f51782e54b058e1c82355a6b36690f Mon Sep 17 00:00:00 2001
From: jason
Date: Wed, 1 May 2024 09:00:26 +0100
Subject: [PATCH 19/23] test(mazerunner): fixed use of Handler() constructor
---
.../src/main/java/com/bugsnag/android/mazerunner/AnrHelper.kt | 3 ++-
.../android/mazerunner/scenarios/StartupCrashFlushScenario.kt | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/AnrHelper.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/AnrHelper.kt
index 71e5af0b66..f60934082c 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/AnrHelper.kt
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/AnrHelper.kt
@@ -1,6 +1,7 @@
package com.bugsnag.android.mazerunner
import android.os.Handler
+import android.os.Looper
val mutex = Any()
@@ -21,7 +22,7 @@ fun createDeadlock() {
}
).start()
- Handler().postDelayed(
+ Handler(Looper.getMainLooper()).postDelayed(
object : java.lang.Runnable {
override fun run() {
synchronized(mutex) { throw java.lang.IllegalStateException() }
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/StartupCrashFlushScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/StartupCrashFlushScenario.kt
index 531890eb0f..faf9f675bc 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/StartupCrashFlushScenario.kt
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/StartupCrashFlushScenario.kt
@@ -2,6 +2,7 @@ package com.bugsnag.android.mazerunner.scenarios
import android.content.Context
import android.os.Handler
+import android.os.Looper
import com.bugsnag.android.Configuration
import com.bugsnag.android.mazerunner.disableAllDelivery
@@ -35,7 +36,7 @@ internal class StartupCrashFlushScenario(
override fun startScenario() {
super.startScenario()
if ("CrashOfflineWithDelay" == eventMetadata) {
- Handler().postDelayed(
+ Handler(Looper.getMainLooper()).postDelayed(
Runnable {
throw RuntimeException("Regular crash")
},
From 2cf873c11ed2aeed1b4b41dd4a73443b5c575f8c Mon Sep 17 00:00:00 2001
From: jason
Date: Wed, 1 May 2024 09:18:44 +0100
Subject: [PATCH 20/23] chore(build): corrected the build plugin apply order
---
bugsnag-android-core/build.gradle.kts | 29 ++++++++++---------
bugsnag-plugin-android-anr/build.gradle.kts | 3 +-
bugsnag-plugin-android-ndk/build.gradle.kts | 7 +++--
.../com/bugsnag/android/BugsnagBuildPlugin.kt | 10 ++++---
.../android/BugsnagBuildPluginExtension.kt | 6 ++++
5 files changed, 34 insertions(+), 21 deletions(-)
diff --git a/bugsnag-android-core/build.gradle.kts b/bugsnag-android-core/build.gradle.kts
index 6640936437..d6774d451a 100644
--- a/bugsnag-android-core/build.gradle.kts
+++ b/bugsnag-android-core/build.gradle.kts
@@ -1,14 +1,27 @@
+import kotlinx.validation.ApiValidationExtension
import org.jetbrains.dokka.gradle.DokkaTask
plugins {
id("bugsnag-build-plugin")
- id("com.android.library")
}
bugsnagBuildOptions {
usesNdk = true
+
+ // pick up dsl-json by adding to the default sourcesets
+ android {
+ sourceSets {
+ named("main") {
+ java.srcDirs("dsl-json/library/src/main/java")
+ }
+ named("test") {
+ java.srcDirs("dsl-json/library/src/test/java")
+ }
+ }
+ }
}
+apply(plugin = "com.android.library")
apply(plugin = "org.jetbrains.dokka")
tasks.getByName("dokkaHtml") {
@@ -23,16 +36,6 @@ tasks.getByName("dokkaHtml") {
}
}
-// pick up dsl-json by adding to the default sourcesets
-android {
- sourceSets {
- named("main") {
- java.srcDirs("dsl-json/library/src/main/java")
- }
- named("test") {
- java.srcDirs("dsl-json/library/src/test/java")
- }
- }
+plugins.withId("org.jetbrains.kotlinx.binary-compatibility-validator") {
+ project.extensions.getByType(ApiValidationExtension::class.java).ignoredPackages.add("com.bugsnag.android.repackaged.dslplatform.json")
}
-
-apiValidation.ignoredPackages.add("com.bugsnag.android.repackaged.dslplatform.json")
diff --git a/bugsnag-plugin-android-anr/build.gradle.kts b/bugsnag-plugin-android-anr/build.gradle.kts
index 12457bd329..9f778ce9f9 100644
--- a/bugsnag-plugin-android-anr/build.gradle.kts
+++ b/bugsnag-plugin-android-anr/build.gradle.kts
@@ -1,12 +1,13 @@
plugins {
id("bugsnag-build-plugin")
- id("com.android.library")
}
bugsnagBuildOptions {
usesNdk = true
}
+apply(plugin = "com.android.library")
+
dependencies {
add("api", project(":bugsnag-android-core"))
}
diff --git a/bugsnag-plugin-android-ndk/build.gradle.kts b/bugsnag-plugin-android-ndk/build.gradle.kts
index 10104bb5ae..1006109c80 100644
--- a/bugsnag-plugin-android-ndk/build.gradle.kts
+++ b/bugsnag-plugin-android-ndk/build.gradle.kts
@@ -1,6 +1,5 @@
plugins {
id("bugsnag-build-plugin")
- id("com.android.library")
}
bugsnagBuildOptions {
@@ -8,12 +7,14 @@ bugsnagBuildOptions {
publishesPrefab = "bugsnag-ndk"
}
+apply(plugin = "com.android.library")
+
dependencies {
- api(project(":bugsnag-android-core"))
+ add("api", project(":bugsnag-android-core"))
}
afterEvaluate {
- tasks.create("prefabReleasePackage") {
+ tasks.getByName("prefabReleasePackage") {
doLast {
project.fileTree("build/intermediates/prefab_package/") {
include("**/abi.json")
diff --git a/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt b/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
index 5026b29086..3f0d29e096 100644
--- a/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
+++ b/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPlugin.kt
@@ -40,7 +40,7 @@ class BugsnagBuildPlugin : Plugin {
// load 3rd party gradle plugins
project.applyPlugins(bugsnag)
- val android = project.extensions.getByType(BaseExtension::class.java)
+ val android = project.extensions.getByType(LibraryExtension::class.java)
android.apply {
configureDefaults()
configureAndroidLint(project)
@@ -51,11 +51,13 @@ class BugsnagBuildPlugin : Plugin {
configureNdk(project)
bugsnag.publishesPrefab?.let { prefabModuleName ->
- (android as? LibraryExtension)?.run {
- configurePrefabPublishing(prefabModuleName)
- }
+ configurePrefabPublishing(prefabModuleName)
}
}
+
+ bugsnag.androidConfiguration.forEach { config ->
+ config(android)
+ }
}
// add 3rd party dependencies to the project
diff --git a/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPluginExtension.kt b/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPluginExtension.kt
index c1eabb8def..d2637be468 100644
--- a/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPluginExtension.kt
+++ b/buildSrc/src/main/kotlin/com/bugsnag/android/BugsnagBuildPluginExtension.kt
@@ -1,5 +1,6 @@
package com.bugsnag.android
+import com.android.build.api.dsl.LibraryExtension
import org.gradle.api.model.ObjectFactory
/**
@@ -9,6 +10,8 @@ import org.gradle.api.model.ObjectFactory
*/
open class BugsnagBuildPluginExtension(@Suppress("UNUSED_PARAMETER") objects: ObjectFactory) {
+ internal val androidConfiguration = ArrayList Unit>()
+
/**
* Whether this project compiles code or not. If this is set to false then unnecessary
* plugins are not applied, which speeds up the build. By default this is enabled.
@@ -25,4 +28,7 @@ open class BugsnagBuildPluginExtension(@Suppress("UNUSED_PARAMETER") objects: Ob
*/
open var publishesPrefab: String? = null
+ fun android(config: LibraryExtension.() -> Unit) {
+ androidConfiguration.add(config)
+ }
}
From 7d49790f4f29e4e15c67a182b0d689ac5c762612 Mon Sep 17 00:00:00 2001
From: YingYing Chen <40571804+YYChen01988@users.noreply.github.com>
Date: Mon, 13 May 2024 09:33:36 +0100
Subject: [PATCH 21/23] fix(event)Remove Failed delivery internal reports with
logger error message (#2026)
---
.../src/main/java/com/bugsnag/android/EventStore.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.kt
index bb1172c769..6eb471d22d 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStore.kt
@@ -208,7 +208,7 @@ internal class EventStore(
}
private fun handleEventFlushFailure(exc: Exception, eventFile: File) {
- delegate?.onErrorIOFailure(exc, eventFile, "Crash Report Deserialization")
+ logger.e(exc.message ?: "Failed to send event", exc)
deleteStoredFiles(setOf(eventFile))
}
From 2c5a6d847d307cf15fdd1f2cdcc1e1faf4fc4577 Mon Sep 17 00:00:00 2001
From: YingYing Chen <40571804+YYChen01988@users.noreply.github.com>
Date: Mon, 13 May 2024 11:55:56 +0100
Subject: [PATCH 22/23] Integrate bugsnag-android-performance into
bugsnag-android MazeRunner (#2024)
* feat(implementation)implement bugsnag performance to measure the start span
* fix(test)set bugsnag performance to environment variable and fix test
---
features/fixtures/mazerunner/app/build.gradle | 5 ++++-
.../mazerunner/app/src/main/AndroidManifest.xml | 1 +
.../com/bugsnag/android/mazerunner/MazerunnerApp.kt | 5 +++++
.../fixtures/mazerunner/jvm-scenarios/build.gradle | 7 ++++++-
.../scenarios/LoadConfigurationFromManifestScenario.kt | 3 +--
.../scenarios/LoadConfigurationKotlinScenario.kt | 2 +-
.../scenarios/MultiThreadedStartupScenario.kt | 4 ++--
.../bugsnag/android/mazerunner/scenarios/Scenario.kt | 10 +++++++++-
.../scenarios/SharedPrefMigrationScenario.kt | 3 +++
9 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/features/fixtures/mazerunner/app/build.gradle b/features/fixtures/mazerunner/app/build.gradle
index d63778abf7..aab4b30864 100644
--- a/features/fixtures/mazerunner/app/build.gradle
+++ b/features/fixtures/mazerunner/app/build.gradle
@@ -8,13 +8,15 @@ android {
ndkVersion parent.ext.ndkVersion
defaultConfig {
- minSdkVersion 16
+ minSdkVersion 17
targetSdkVersion 31
versionCode 34
versionName "1.1.14"
manifestPlaceholders = [
// omit any of the following placeholders to use the default values
bugsnagApiKey: "abc12312312312312312312312312312",
+ bugsnagPerformanceApiKey: System.getenv("BUGSNAG_PERFORMANCE_API_KEY")
+ ?: "abc12312312312312312312312312312",
bugsnagAppType: "test",
bugsnagAppVersion: "7.5.3",
bugsnagAutoDetectErrors: true,
@@ -90,6 +92,7 @@ dependencies {
}
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.annotation:annotation:1.2.0"
+ implementation "com.bugsnag:bugsnag-android-performance:1.2.2"
}
apply from: "../bugsnag-dependency.gradle"
diff --git a/features/fixtures/mazerunner/app/src/main/AndroidManifest.xml b/features/fixtures/mazerunner/app/src/main/AndroidManifest.xml
index 1e4571c14e..d0439114c9 100644
--- a/features/fixtures/mazerunner/app/src/main/AndroidManifest.xml
+++ b/features/fixtures/mazerunner/app/src/main/AndroidManifest.xml
@@ -35,6 +35,7 @@
/>
+
diff --git a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MazerunnerApp.kt b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MazerunnerApp.kt
index ad586c4f5d..f07e7fd46c 100644
--- a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MazerunnerApp.kt
+++ b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MazerunnerApp.kt
@@ -3,6 +3,9 @@ package com.bugsnag.android.mazerunner
import android.app.Application
import android.os.Build
import android.os.StrictMode
+import com.bugsnag.android.performance.BugsnagPerformance
+import com.bugsnag.android.performance.PerformanceConfiguration
+import com.bugsnag.android.performance.internal.InternalDebug
class MazerunnerApp : Application() {
@@ -11,6 +14,8 @@ class MazerunnerApp : Application() {
triggerStartupAnrIfRequired()
setupNonSdkUsageStrictMode()
triggerManualSessionIfRequired()
+ InternalDebug.spanBatchSizeSendTriggerPoint = 1
+ BugsnagPerformance.start(PerformanceConfiguration.load(this))
}
/**
diff --git a/features/fixtures/mazerunner/jvm-scenarios/build.gradle b/features/fixtures/mazerunner/jvm-scenarios/build.gradle
index 79f5df6035..c14c318d52 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/build.gradle
+++ b/features/fixtures/mazerunner/jvm-scenarios/build.gradle
@@ -10,7 +10,11 @@ android {
compileSdkVersion 31
defaultConfig {
- minSdkVersion 14
+ minSdkVersion 17
+ manifestPlaceholders = [
+ bugsnagPerformanceApiKey: System.getenv("BUGSNAG_PERFORMANCE_API_KEY")
+ ?: "abc12312312312312312312312312312",
+ ]
}
buildTypes {
@@ -40,6 +44,7 @@ dependencies {
project.logger.lifecycle("Using OkHttp 4 dependency in test fixture")
implementation "com.squareup.okhttp3:okhttp:4.9.1"
}
+ implementation "com.bugsnag:bugsnag-android-performance:1.2.2"
}
private boolean useLegacyOkHttp() {
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/LoadConfigurationFromManifestScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/LoadConfigurationFromManifestScenario.kt
index 72c04300b0..5162172192 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/LoadConfigurationFromManifestScenario.kt
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/LoadConfigurationFromManifestScenario.kt
@@ -30,8 +30,7 @@ internal class LoadConfigurationFromManifestScenario(
true
}
)
-
- reportBugsnagStartupDuration { Bugsnag.start(this.context, testConfig) }
+ measureBugsnagStartupDuration(this.context, testConfig)
}
override fun startScenario() {
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/LoadConfigurationKotlinScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/LoadConfigurationKotlinScenario.kt
index 46700a5620..73dd62feef 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/LoadConfigurationKotlinScenario.kt
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/LoadConfigurationKotlinScenario.kt
@@ -46,7 +46,7 @@ internal class LoadConfigurationKotlinScenario(
}
)
- reportBugsnagStartupDuration { Bugsnag.start(this.context, testConfig) }
+ measureBugsnagStartupDuration(this.context, testConfig)
}
override fun startScenario() {
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/MultiThreadedStartupScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/MultiThreadedStartupScenario.kt
index c29d85fd33..aea3d67d10 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/MultiThreadedStartupScenario.kt
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/MultiThreadedStartupScenario.kt
@@ -14,7 +14,7 @@ class MultiThreadedStartupScenario(
override fun startScenario() {
val startThread = thread(name = "AsyncStart") {
- reportBugsnagStartupDuration { Bugsnag.start(context, config) }
+ measureBugsnagStartupDuration(context, config)
}
thread(name = "leaveBreadcrumb") {
@@ -24,7 +24,7 @@ class MultiThreadedStartupScenario(
Bugsnag.leaveBreadcrumb("I'm leaving a breadcrumb on another thread")
Bugsnag.notify(Exception("Scenario complete"))
} catch (e: Exception) {
- reportBugsnagStartupDuration { Bugsnag.start(context, config) }
+ measureBugsnagStartupDuration(context, config)
Bugsnag.notify(e)
}
}
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/Scenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/Scenario.kt
index 232c5ef09d..eddb4e51f8 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/Scenario.kt
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/Scenario.kt
@@ -14,12 +14,14 @@ import android.os.HandlerThread
import android.os.Looper
import android.util.Log
import com.bugsnag.android.Bugsnag
+import com.bugsnag.android.Client
import com.bugsnag.android.Configuration
import com.bugsnag.android.mazerunner.BugsnagIntentParams
import com.bugsnag.android.mazerunner.MazerunnerHttpClient
import com.bugsnag.android.mazerunner.log
import com.bugsnag.android.mazerunner.multiprocess.MultiProcessService
import com.bugsnag.android.mazerunner.multiprocess.findCurrentProcessName
+import com.bugsnag.android.performance.measureSpan
import java.io.File
import kotlin.system.measureNanoTime
@@ -77,13 +79,19 @@ abstract class Scenario(
) { startup() }
}
+ fun measureBugsnagStartupDuration(context: Context, config: Configuration): Client {
+ return measureSpan("Bugsnag Startup") {
+ Bugsnag.start(context, config)
+ }
+ }
+
/**
* Initializes Bugsnag. It is possible to override this method if the scenario requires
* it - e.g., if the config needs to be loaded from the manifest.
*/
open fun startBugsnag(startBugsnagOnly: Boolean) {
this.startBugsnagOnly = startBugsnagOnly
- reportBugsnagStartupDuration { Bugsnag.start(context, config) }
+ measureBugsnagStartupDuration(context, config)
}
/**
diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/SharedPrefMigrationScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/SharedPrefMigrationScenario.kt
index 8f66d59c08..cbc9bb9375 100644
--- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/SharedPrefMigrationScenario.kt
+++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/SharedPrefMigrationScenario.kt
@@ -5,6 +5,7 @@ import android.content.Context
import com.bugsnag.android.Bugsnag
import com.bugsnag.android.Configuration
import com.bugsnag.android.mazerunner.BugsnagIntentParams
+import java.io.File
/**
* User/device information is migrated from the legacy [SharedPreferences] location
@@ -21,6 +22,8 @@ internal class SharedPrefMigrationScenario(
override fun startBugsnag(startBugsnagOnly: Boolean) {
persistLegacyPrefs()
+ // make sure there is no "leftover" device-id file to interfere with the test
+ File(context.filesDir, "device-id").delete()
super.startBugsnag(startBugsnagOnly)
}
From 2992145562254cbd8f05667d020b94b5b728d9d3 Mon Sep 17 00:00:00 2001
From: YYChen01988
Date: Wed, 15 May 2024 11:08:58 +0100
Subject: [PATCH 23/23] release v6.5.0
---
CHANGELOG.md | 2 +-
.../src/main/java/com/bugsnag/android/Notifier.kt | 2 +-
examples/sdk-app-example/app/build.gradle | 4 ++--
gradle.properties | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44c8f5d355..9e501cbd60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Changelog
-## TBD
+## 6.5.0 (2024-05-15)
### Enhancements
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 bea14c68a8..4cc446d679 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.4.0",
+ var version: String = "6.5.0",
var url: String = "https://bugsnag.com"
) : JsonStream.Streamable {
diff --git a/examples/sdk-app-example/app/build.gradle b/examples/sdk-app-example/app/build.gradle
index 9b25b2fd0c..28a225a86b 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.4.0"
- implementation "com.bugsnag:bugsnag-plugin-android-okhttp:6.4.0"
+ implementation "com.bugsnag:bugsnag-android:6.5.0"
+ implementation "com.bugsnag:bugsnag-plugin-android-okhttp:6.5.0"
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/gradle.properties b/gradle.properties
index 73dfdc3f4e..f27b1d23a3 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.4.0
+VERSION_NAME=6.5.0
GROUP=com.bugsnag
POM_SCM_URL=https://github.com/bugsnag/bugsnag-android
POM_SCM_CONNECTION=scm:git@github.com:bugsnag/bugsnag-android.git